Jump to content
cocobom

How to play sound via stream in android platform

Recommended Posts

In Win32 mode, we can specify a stream and give it to the sndplaysound function to play the sound, but on Android platform, it seems that we can only specify a sound file through TMediaPlayer class for playing. Is there a better way?

Share this post


Link to post

Internally, that is downloading an audio file from a URL, and then playing it.  That is not the same as playing audio samples from memory or a resource.  AFAIK, FireMonkey simply does not implement that kind of functionality natively, so you will have to implement it manually.  See this StackOverflow post if you want to implement custom handling of other audio sources.

Edited by Remy Lebeau

Share this post


Link to post
Guest

It's a test I made at the time, if you make it better please share it with us, like I did.

is both client and server.

unit Unit2;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.ScrollBox, FMX.Memo, FMX.Edit,
  IdContext, IdTCPConnection, IdTCPClient, IdBaseComponent, IdComponent,
  IdCustomTCPServer, IdTCPServer, IdIOHandler, IdIOHandlerStream,
  IdIOHandlerSocket, IdIOHandlerStack, IdServerIOHandler,
  IdServerIOHandlerSocket, IdServerIOHandlerStack, FMX.ListBox;

type
  TMain = class(TForm)
    Button_ServerListen: TButton;
    Panel1: TPanel;
    Label1: TLabel;
    Memo_Debug: TMemo;
    Label_RunControl: TLabel;
    Timer_RunControl: TTimer;
    IdTCPServer: TIdTCPServer;
    IdTCPClient: TIdTCPClient;
    cTimerRecorder: TTimer;
    Button_ClientConnect: TButton;
    Timer_Connect: TTimer;
    Edit_RemoteIP: TEdit;
    ComboBox_SelectIP: TComboBox;
    procedure FormCreate(Sender: TObject);
    procedure Timer_RunControlTimer(Sender: TObject);
    procedure IdTCPServerExecute(AContext: TIdContext);
    procedure IdTCPServerConnect(AContext: TIdContext);
    procedure IdTCPServerDisconnect(AContext: TIdContext);
    procedure IdTCPClientConnected(Sender: TObject);
    procedure cTimerRecorderTimer(Sender: TObject);
    procedure IdTCPClientDisconnected(Sender: TObject);
    procedure Timer_ConnectTimer(Sender: TObject);
    procedure Button_ClientConnectClick(Sender: TObject);
    procedure ComboBox_SelectIPChange(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    Procedure Debug(From, Msg:String);
  end;

var
  Main: TMain;

implementation

Uses System.IOUtils,
     System.Diagnostics, // TickCount
     Androidapi.JNI,
     Androidapi.JNIBridge, Androidapi.JNI.JavaTypes,
     Androidapi.JNI.Media,
     Androidapi.JNI.Net, Androidapi.Helpers, Androidapi.JNI.GraphicsContentViewText; // Wifi

{$R *.fmx}

Var // Server
 sAudioTrack: JAudioTrack;
 sMemory:TMemoryStream;
 sBuffer:TJavaArray<Byte>;

Var // Client
 cMemory:TMemoryStream;
 cBuffer:TJavaArray<Byte>;
 cAudioRecord:JAudioRecord;
 cBufferSize:Integer;

Var
 SampleRate:Integer;
 AudioFormat:Integer;
 tCountdown:Byte=2;
 InternalTimer:Integer=0;

 // ------------------------------


function GetWifiIpAddress: String;
var
  WifiManagerObj: JObject;
  WifiManager: JWifiManager;
  WifiInfo: JWifiInfo;
  ip: Integer;

begin
  WifiManagerObj := SharedActivityContext.getSystemService(TJContext.JavaClass.WIFI_SERVICE);
  WifiManager := TJWifiManager.Wrap((WifiManagerObj as ILocalObject).GetObjectID);
  WifiInfo := WifiManager.getConnectionInfo();
  ip := WifiInfo.getIpAddress and $FFFFFFFF;
  result := Format('%d.%d.%d.%d', [(IP) and $FF, (IP shr 8) and $FF, (IP shr 16) and $FF, (IP shr 24) and $FF]);
end;

procedure TMain.Timer_RunControlTimer(Sender: TObject);
begin
 Label_RunControl.Text:=IntToStr(Random(65535));
end;

function MemoryStreamToString(M: TMemoryStream): string;
begin
  SetString(Result, PChar(M.Memory), M.Size div SizeOf(Char));
end;

procedure TMain.ComboBox_SelectIPChange(Sender: TObject);
begin
 Edit_RemoteIP.Text:=Trim(ComboBox_SelectIP.Items.Strings[ComboBox_SelectIP.ItemIndex]);
end;

Procedure TMain.Debug(From, Msg:String);
Begin
 Msg:=From+'> '+Trim(Msg);
 Memo_Debug.Lines.Add(Msg);
 Memo_Debug.SelStart := Length(Memo_Debug.Text);
 Memo_Debug.SelLength := 0;
 Memo_Debug.ScrollBy(0, Memo_Debug.Lines.Count);
 Sleep(1);
End;

procedure TMain.Button_ClientConnectClick(Sender: TObject);
begin
 IdTCPClient.Host:=Edit_RemoteIP.Text;
 IdTCPClient.Port:=5555;
 IdTCPClient.ConnectTimeout:=3000;
 IdTCPClient.Connect;
end;

procedure TMain.Timer_ConnectTimer(Sender: TObject);
begin
 tCountdown:=tCountdown-1;
 Button_ClientConnect.Text:='Bağlan ('+tCountDown.ToString+')';
 if tCountdown<1 then
 Begin
  Timer_Connect.Enabled:=False;
  // --------------------------
  TThread.CreateAnonymousThread(
        procedure()
        begin
            TThread.Synchronize(TThread.CurrentThread,
            procedure
            begin
             Button_ClientConnectClick(Sender);
            end);
        end).Start;
      End;
end;

procedure TMain.FormCreate(Sender: TObject);
begin
 Edit_RemoteIP.Text:=GetWifiIpAddress;

 //SAMPLE_RATE  16000 - 22050 - 44100
 SampleRate:=44100;
 AudioFormat:=TJAudioFormat.JavaClass.ENCODING_PCM_16BIT;

 // 3764, 8192
 cbufferSize:=TJAudioRecord.JavaClass.getMinBufferSize(SampleRate, TJAudioFormat.JavaClass.CHANNEL_IN_MONO, AudioFormat);
  Debug('*','cBufferSize = '+cBufferSize.ToString());

 if (cbufferSize=TJAudioRecord.JavaClass.ERROR_BAD_VALUE) then
  Debug('*','cbufferSize = ERROR_BAD_VALUE');

 cAudioRecord:=TJAudioRecord.JavaClass.init(TJMediaRecorder_AudioSource.JavaClass.MIC, SampleRate, TJAudioFormat.JavaClass.CHANNEL_IN_MONO, AudioFormat, cBufferSize);
 sAudioTrack:=TJAudioTrack.JavaClass.init(TJAudioManager.JavaClass.STREAM_MUSIC, SampleRate, TJAudioFormat.JavaClass.CHANNEL_OUT_MONO, AudioFormat, cBufferSize, TJAudioTrack.JavaClass.MODE_STREAM); // MODE_STREAM - MODE_STATIC

 if (cAudioRecord.getState <> TJAudioRecord.JavaClass.STATE_INITIALIZED) then
  Debug('*','cAudioRecord.getState = '+cAudioRecord.getState.ToString()+'<>STATE_INITIALIZED');

 cBuffer:=TJavaArray<Byte>.create(cBufferSize);
 sBuffer:=TJavaArray<Byte>.create(cBufferSize);

 sMemory:=TMemoryStream.Create;
 cMemory:=TMemoryStream.Create;

 Debug('*','# Server Start Event.');
 IdTCPServer.DefaultPort:=5555;
 IdTCPServer.Bindings.Add.IP:=Edit_RemoteIP.Text;
 IdTCPServer.Active:=True;
 if IdTCPServer.Active then
 Begin
  Button_ServerListen.Enabled:=False;
  Button_ServerListen.Text:='Server Aktif';
  Debug('*','Server ('+Edit_RemoteIP.Text+':'+IdTCPServer.DefaultPort.ToString+') Bekliyor.');
 End;

 InternalTimer:=0;
 TThread.CreateAnonymousThread(
        procedure()
        begin
         Repeat
          Sleep(100);
          InternalTimer:=InternalTimer+1;
         Until (Application.Terminated);
        End).Start;

// Timer_Connect.Enabled:=True;
end;

procedure TMain.IdTCPServerConnect(AContext: TIdContext);
begin
 Debug('S', AContext.Connection.Socket.Binding.PeerIP+':'+AContext.Connection.Socket.Binding.PeerPort.ToString+' Bağlandı.');
 Debug('S','sAudioTrack.Play');
 sAudioTrack.Play;
end;

procedure TMain.IdTCPServerDisconnect(AContext: TIdContext);
begin
 Debug('S', AContext.Connection.Socket.Binding.PeerIP+':'+AContext.Connection.Socket.Binding.PeerPort.ToString+' Koptu.');
 Debug('S','sAudioTrack.Stop');
 sAudioTrack.stop;
end;

procedure TMain.IdTCPServerExecute(AContext: TIdContext);
Var
 rLen,x:Integer;

begin

 if AContext.Connection.IOHandler.Readable then
 begin
  rLen:=0;
  Try
   rLen:=StrToIntdef(AContext.Connection.IOHandler.Readln,0);
  Except End;
   //Debug('S','rLen = '+rLen.ToString);

  if rLen>0 then
  Begin
   sMemory.Clear;
   Try
    AContext.Connection.IOHandler.ReadStream(sMemory, rLen, False);
   Except End;

   if sMemory.Size>0 then
   Begin
//     thread içinde yaz, bufferden sbuffer.size kadar okuyabilirsin, fazlası?
    sMemory.Position:=0;
    repeat
     x:=sMemory.Read(sBuffer.data^, sBuffer.Length);
     sAudioTrack.Write(sBuffer, 0, sBuffer.Length);
    until (x=0);
   End;

  End;

 End;
End;

procedure TMain.IdTCPClientConnected(Sender: TObject);
begin
 Debug('C','TCPClient.Connected.');
 Debug('C','cAudioRecord.Start');
 cMemory.Clear;
 cAudioRecord.startRecording;
 cTimerRecorder.Enabled:=True;
end;

procedure TMain.IdTCPClientDisconnected(Sender: TObject);
begin
 Debug('C','TCPClient.Disconnected.');
 Debug('C','cAudioRecord.Stop');
 cTimerRecorder.Enabled:=False;
 cAudioRecord.Stop;
end;

procedure TMain.cTimerRecorderTimer(Sender: TObject);
Var
 rLen:Integer;

begin
 cTimerRecorder.Enabled:=False;

 if IdTCPClient.Connected then
 Begin

  InternalTimer:=0;
  cMemory.Clear;
  Repeat
   Try
    rLen:=cAudioRecord.Read(cBuffer, 0, cBuffer.Length);
    cMemory.Write(cBuffer.Data^, rLen);
   Except End;
   Application.ProcessMessages;
  Until InternalTimer>9;
  rLen:=cMemory.Size;

  // buradan sonra thread ile yollasın.
  Debug('c',rLen.ToString()+'kb uploading.');

  Try
   IdTCPClient.IOHandler.WriteLn(cMemory.Size.ToString);
  Except End;

  if rLen<>cMemory.Size then
   Debug('#','error on cmem size')
  else
  Begin
   cMemory.Position:=0;
   Try
    IdTCPClient.IOHandler.Write(cMemory, rLen, False);
   Except End;
  End;

 End;

 cTimerRecorder.Enabled:=True;
end;

// stream size 50kb dan büyükse upload et


end.

 

Sound Streamming - Remote Ses Buffering.rar

Edited by Guest

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×