Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 200
  • Last Modified:

FMX and jaudiotracker playing memory stream

i am using wave audio component on vcl project and i am using TliveAudiorecorder to send audio in memorystream to indy tcp server and the server side send this stream back to indy tcp clients

clients that uses vcl application receive the audio on normally and play it with Tliveaudioplayer

but i wonder can i read and play this stream on android ?

i tried to simulate the player so i used the Android API AudioTrack to play this stream at the following code i have set audiotrack encoding same as the encoding i have used to record data on vcl using the liveaudiorecorder which was Mono 16bit 8000 HZ but when ever the stream arrived the android app crashed and closed when the following line called
    MSCLIENT.ReadBuffer(AudioStream.Data^, AudioDataSize);

    (AudioPlay as JAudioTrack).write(AudioStream, 0, AudioDataSize);

Open in new window


here is the player code

var
    Enable_Play: Boolean;
    AudioPlay: JAudioTrack;
    AudioStream: TJavaArray<SmallInt>;
    Audiodataset: Integer;

Audiodataset := TJAudioTrack.JavaClass.getMinBufferSize(8000,
                                                     TJAudioFormat.JavaClass.CHANNEL_OUT_MONO,
                                                     TJAudioFormat.JavaClass.ENCODING_PCM_16BIT);
  AudioStream:= TJavaArray<SmallInt>.Create(Audiodataset);
  AudioPlay:= TJAudioTrack.JavaClass.init(TJAudioManager.JavaClass.STREAM_MUSIC,
                                              8000,
                                              TJAudioFormat.JavaClass.CHANNEL_OUT_MONO,
                                              TJAudioFormat.JavaClass.ENCODING_PCM_16BIT,
                                              Audiodataset,
                                              TJAudioTrack.JavaClass.MODE_STREAM);

//recive audio 

if Command = 'Stream' then
begin

Sleep(0);
if (MSTream.Size > 10) then
begin


if Enable_Play <> True then
begin
Enable_Play:= True;
(AudioPlay as JAudioTrack).play();
end;

AudioDataSize := MSTream.Size;

MSCLIENT.ReadBuffer(AudioStream.Data^, AudioDataSize);
(AudioPlay as JAudioTrack).write(AudioStream, 0, AudioDataSize);
end;

end;

Open in new window


any idea why the app crashed when audio received ? also i notice that whin i change the sample rate to 44100 i got the sound with no issue bit the sound comes like Donald duck because its not the same sample rate
0
dolphin King
Asked:
dolphin King
  • 19
  • 10
1 Solution
 
Sinisa VukCommented:
Try to double buffer size..
..
Audiodataset := TJAudioTrack.JavaClass.getMinBufferSize(8000,
                                                     TJAudioFormat.JavaClass.CHANNEL_OUT_MONO,
                                                     TJAudioFormat.JavaClass.ENCODING_PCM_16BIT);
Audiodataset := Audiodataset *2;
...

Open in new window


... and second .. you are trying to read whole stream buffer: AudioDataSize := MSTream.Size;
This is wrong ... because you allocate Audiodataset bytes....So...
...
//AudioDataSize := MSTream.Size;

AudioDataSize := MSCLIENT.ReadBuffer(AudioStream.Data^, Audiodataset); //try to read max buff size - if not it will read all available...
(AudioPlay as JAudioTrack).write(AudioStream, 0, AudioDataSize);

Open in new window

0
 
dolphin KingAuthor Commented:
i will try that and come back with feedback i really stuck into this from months ago
0
 
dolphin KingAuthor Commented:
i got compiler error

[DCC Error] audiostrm.pas(716): E2010 Incompatible types: 'Cardinal' and 'procedure, untyped pointer or untyped parameter'

on that line

AudioDataSize := MSCLIENT.ReadBuffer(AudioStream.Data^, Audiodataset);

Open in new window

0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
Sinisa VukCommented:
Sorry, ReadBuffer doesn't return size... Use Read:
AudioDataSize := MSCLIENT.Read(AudioStream.Data^, Audiodataset);
0
 
dolphin KingAuthor Commented:
ohhh it works but the sound is cutting alot here is current code

if Command = 'AUDIO' then
begin
Sleep(0);
if (MSCLIENT.Size > 10) then
begin
if Enable_Play <> True then
begin
Enable_Play:= True;
(AudioPlay as JAudioTrack).play();
end;

AudioDataSize := MSCLIENT.Read(AudioStream.Data^, Audiodataset);
try
(AudioPlay as JAudioTrack).write(AudioStream, 0, AudioDataSize);
except
end;

end;

Open in new window

0
 
dolphin KingAuthor Commented:
the sound arrived but its breaks each buffer
0
 
dolphin KingAuthor Commented:
i just want make the arrived sound works normal without cutting . currently the sounds play and data arrived good but its play with breaks , any tweak around this even if will be a little delay but to make the sound works normal
0
 
dolphin KingAuthor Commented:
i still cant figure out how to fix the sound , it comes out really scratchy i searched a lot for solution cant find out . please help
0
 
Sinisa VukCommented:
Hmm...You need separate thread to fill your running buffer ... Maybe there is an event ... when buffer is low to get more data.
Try to implement threaded version - like in this example...

...unfortunately .. cannot test by myself... but trying to help through suggestions....
0
 
dolphin KingAuthor Commented:
the stream already in a thread or do you mean the audio ? the audio comes out very good but its scratchy like delay for a bit between each buffer
0
 
dolphin KingAuthor Commented:
the example i see is making the player works in a  thread , and that's what already my player do ,  it works in call back thread mode
0
 
dolphin KingAuthor Commented:
and if this help . here is how my VCL receive the audio with no issue , i wanted to simulate the player on android


if Command = 'Stream' then
begin
try
EnterCriticalSection(Section);
try
Sleep(0);
if MSCLIENT.Size > 10 then
begin
AudioDataSize := MSCLIENT.Size;


if BlockAlign > 1 then
Dec(AudioDataSize, AudioDataSize mod Cardinal(BlockAlign));
AudioData := AudioBuffer.BeginUpdate(AudioDataSize);
try
MSCLIENT.ReadBuffer(AudioData^, AudioDataSize);
finally
AudioBuffer.EndUpdate;
end;
end;
finally
LeaveCriticalSection(Section);
end;
except
// handle other exceptions here
end;
end;

Open in new window

0
 
Sinisa VukCommented:
yea... cannot se the rest of your code... but how you set network data into MSTream? Did you set position to 0 before fill ?
Looks buffer is not read till end correctly....
Buffer: PByte;
...
while ((MSCLIENT.Size - MSCLIENT.Position) >= Audiodataset) do //there is still data in a stream
begin
  AudioDataSize := MSCLIENT.Read(AudioStream.Data^, Audiodataset);
  try
    (AudioPlay as JAudioTrack).write(AudioStream, 0, AudioDataSize);
  except
  end;
end;
//possible remaining buffer move to start position for later... and incoming data just append to ... 
...
 GetMem(Buffer, (MSCLIENT.Size - MSCLIENT.Position));
  try
      AudioDataSize := MSCLIENT.Read(Buffer^, (MSCLIENT.Size - MSCLIENT.Position));
      MSCLIENT.Position := 0;
      MSCLIENT.Write(Buffer^, AudioDataSize);
  finally
    FreeMem(Buffer);
  end;
...

Open in new window

0
 
dolphin KingAuthor Commented:
now no sound plays at all current code Full code

procedure TForm1.CallbackProc(Sender: TObject;  Command : string; S : String; istream : Boolean; Var MSGET : TMemorystream);
var
Params: array [1 .. 200] of String;
ParamsCount, P: Integer;
AudioDataSize: Cardinal;
i,b:Integer;
Buffer: PByte;
begin
ParamsCount := 0;
while (S <> '') and (ParamsCount < 200) do
begin
Inc(ParamsCount);
P := Pos(Sep, S);
if P = 0 then
Params[ParamsCount] := S
else
begin
Params[ParamsCount] := Copy(S, 1, P - 1);
Delete(S, 1, P + 5);
end;
end;

try

if istream = True then
begin
MSCLIENT := TMemoryStream.Create;
MSCLIENT.LoadFromStream(MSGET);
MSCLIENT.Position := 0;
end;

if Command = 'Stream' then
begin
Sleep(0);


while ((MSCLIENT.Size - MSCLIENT.Position) >= Audiodataset) do //there is still data in a stream
begin
  AudioDataSize := MSCLIENT.Read(AudioStream.Data^, Audiodataset);
  try
    (AudioPlay as JAudioTrack).write(AudioStream, 0, AudioDataSize);
  except
  end;
end;

GetMem(Buffer, (MSCLIENT.Size - MSCLIENT.Position));
  try
      AudioDataSize := MSCLIENT.Read(Buffer^, (MSCLIENT.Size - MSCLIENT.Position));
      MSCLIENT.Position := 0;
      MSCLIENT.Write(Buffer^, AudioDataSize);
  finally
    FreeMem(Buffer);
  end;



end;






finally
FreeAndNil(MSCLIENT);
end;
//end handle


end;

Open in new window

0
 
Sinisa VukCommented:
hm... you are creating stream temporary ... so, my code is not of use... we need to start over...

...
if istream = True then
begin
MSCLIENT := TMemoryStream.Create;
MSCLIENT.LoadFromStream(MSGET);
MSCLIENT.Position := 0;
end;

if Command = 'Stream' then
begin
Sleep(0);


while ( MSCLIENT.Position < MSCLIENT.Size ) do //there is still data in a stream
begin
  AudioDataSize := MSCLIENT.Read(AudioStream.Data^, Audiodataset);
  try
    (AudioPlay as JAudioTrack).write(AudioStream, 0, AudioDataSize);
  except
  end;
end;

end;


finally
FreeAndNil(MSCLIENT);
end;
//end handle

Open in new window

0
 
dolphin KingAuthor Commented:
result comes out very slow audio  and little scratchy also while loop cause the app hand on
0
 
dolphin KingAuthor Commented:
the audio is better now when i do

if ( MSCLIENT.Position < MSCLIENT.Size ) then //there is still data in a stream
begin
  AudioDataSize := MSCLIENT.Read(AudioStream.Data^, Audiodataset);
  try
    (AudioPlay as JAudioTrack).write(AudioStream, 0, AudioDataSize);
  except
  end;
end;

Open in new window



but still there is a little scratch
0
 
Sinisa VukCommented:
apparently Audiodataset size is too small (tell me how much is?)... Try make it larger... Instead of Audiodataset := Audiodataset *2 set
Audiodataset := Audiodataset *2000
0
 
dolphin KingAuthor Commented:
the scratch reduced but the sound is too slow
0
 
dolphin KingAuthor Commented:
also when i set sleep to 30 the scratch reduced more but still the voice breaks , its better than before but its still breaks i don't know if should be silence between each read to make it works normal
0
 
Sinisa VukCommented:
you should  call callback procedure when enough data comes... if it is to few - small data comes to player - and sound is choppy. So you notice changes when set sleep - more data comes from net to MSGET. Remove sleep and check how much data is waiting to fill MSGET and call callback proc when comes about a 'second' of material for playing...
0
 
dolphin KingAuthor Commented:
thats very hard to me to get in coding do you mean i should calculate the memory stream size then play on specific size ?
0
 
Sinisa VukCommented:
In place where you receive data into MSGET - do not read into MSGET until size of data (ics > cli.GetRcvdCount) is greater then 16 kb
Did you get value for  Audiodataset? (before multiply with 2000)?

http://www.theaudioarchive.com/TAA_Resources_File_Size.htm
0
 
dolphin KingAuthor Commented:
i removed the audiodataset = *1 because it makes the sound to slow

here is how my current data looks like

Audiodataset := TJAudioTrack.JavaClass.getMinBufferSize(8000,
TJAudioFormat.JavaClass.CHANNEL_OUT_MONO,
TJAudioFormat.JavaClass.ENCODING_PCM_16BIT);

//Audiodataset := Audiodataset *2000;

 AudioStream:= TJavaArray<SmallInt>.Create(Audiodataset);
 AudioPlay:= TJAudioTrack.JavaClass.init(TJAudioManager.JavaClass.STREAM_MUSIC,
                                              8000,
                                              TJAudioFormat.JavaClass.CHANNEL_OUT_MONO,
                                              TJAudioFormat.JavaClass.ENCODING_PCM_16BIT,
                                              Audiodataset,
                                              TJAudioTrack.JavaClass.MODE_STREAM);

Open in new window

0
 
dolphin KingAuthor Commented:
also i checked the Audiodataset  value it has the same integer each time audio stream starts
0
 
Sinisa VukCommented:
..and value of Audiodataset is?
0
 
dolphin KingAuthor Commented:
the value is 656
0
 
dolphin KingAuthor Commented:
i really don't know what to do now
0
 
Sinisa VukCommented:
...and how about:
...
AudioStream: TJavaArray<Byte>;
...
AudioStream:= TJavaArray<Byte>.Create(Audiodataset);
AudioPlay:= TJAudioTrack.JavaClass.init(TJAudioManager.JavaClass.STREAM_MUSIC,
                                              8000,
                                              TJAudioFormat.JavaClass.CHANNEL_OUT_MONO,
                                              TJAudioFormat.JavaClass.ENCODING_PCM_16BIT,
                                              Audiodataset * 10,
                                              TJAudioTrack.JavaClass.MODE_STREAM);
...
if istream = True then
begin
MSCLIENT := TMemoryStream.Create;
MSCLIENT.LoadFromStream(MSGET);
MSCLIENT.Position := 0;
end;

if Command = 'Stream' then
begin
  while ( MSCLIENT.Position < MSCLIENT.Size ) do //there is still data in a stream
begin
  AudioDataSize := MSCLIENT.Read(AudioStream.Data^, Audiodataset);
  try
    (AudioPlay as JAudioTrack).write(AudioStream, 0, AudioDataSize);
  except
  end;
end;

end;


finally
FreeAndNil(MSCLIENT);
end;
//end handle

Open in new window

https://developer.android.com/reference/android/media/AudioTrack.html
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 19
  • 10
Tackle projects and never again get stuck behind a technical roadblock.
Join Now