Solved

FMX and jaudiotracker playing memory stream

Posted on 2016-11-14
29
36 Views
Last Modified: 2016-11-23
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
Comment
Question by:dolphin King
  • 19
  • 10
29 Comments
 
LVL 25

Expert Comment

by:Sinisa Vuk
ID: 41896732
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
 

Author Comment

by:dolphin King
ID: 41896748
i will try that and come back with feedback i really stuck into this from months ago
0
 

Author Comment

by:dolphin King
ID: 41897576
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
 
LVL 25

Expert Comment

by:Sinisa Vuk
ID: 41897590
Sorry, ReadBuffer doesn't return size... Use Read:
AudioDataSize := MSCLIENT.Read(AudioStream.Data^, Audiodataset);
0
 

Author Comment

by:dolphin King
ID: 41897612
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
 

Author Comment

by:dolphin King
ID: 41897638
the sound arrived but its breaks each buffer
0
 

Author Comment

by:dolphin King
ID: 41897688
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
 

Author Comment

by:dolphin King
ID: 41898932
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
 
LVL 25

Expert Comment

by:Sinisa Vuk
ID: 41899033
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
 

Author Comment

by:dolphin King
ID: 41899038
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
 

Author Comment

by:dolphin King
ID: 41899046
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
 

Author Comment

by:dolphin King
ID: 41899052
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
 
LVL 25

Expert Comment

by:Sinisa Vuk
ID: 41899078
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
 

Author Comment

by:dolphin King
ID: 41899098
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
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 25

Expert Comment

by:Sinisa Vuk
ID: 41899120
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
 

Author Comment

by:dolphin King
ID: 41899130
result comes out very slow audio  and little scratchy also while loop cause the app hand on
0
 

Author Comment

by:dolphin King
ID: 41899137
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
 
LVL 25

Expert Comment

by:Sinisa Vuk
ID: 41899142
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
 

Author Comment

by:dolphin King
ID: 41899148
the scratch reduced but the sound is too slow
0
 

Author Comment

by:dolphin King
ID: 41899160
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
 
LVL 25

Expert Comment

by:Sinisa Vuk
ID: 41899171
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
 

Author Comment

by:dolphin King
ID: 41899176
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
 
LVL 25

Expert Comment

by:Sinisa Vuk
ID: 41899193
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
 

Author Comment

by:dolphin King
ID: 41899221
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
 

Author Comment

by:dolphin King
ID: 41899247
also i checked the Audiodataset  value it has the same integer each time audio stream starts
0
 
LVL 25

Expert Comment

by:Sinisa Vuk
ID: 41899253
..and value of Audiodataset is?
0
 

Author Comment

by:dolphin King
ID: 41899262
the value is 656
0
 

Author Comment

by:dolphin King
ID: 41899295
i really don't know what to do now
0
 
LVL 25

Accepted Solution

by:
Sinisa Vuk earned 500 total points
ID: 41899317
...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

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Samsung Galaxy Tab S 5 73
Viber 1 32
Delphi IDE crash without error message ... 7 60
Delphi inherited method 6 42
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
I recently asked a question (http://www.experts-exchange.com/Programming/Smartphones/Android/Q_28684946.html) about Computer Inventory applications for Mobile Devices.  I was specifically interested in an app I could use on my android phone.  The be…
This video is in connection to the article "The case of a missing mobile phone (https://www.experts-exchange.com/articles/28474/The-Case-of-a-Missing-Mobile-Phone.html)". It will help one to understand clearly the steps to track a lost android phone.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now