Link to home
Start Free TrialLog in
Avatar of dolphin King
dolphin King

asked on

JAudiorecorder record freezing the app

i am trying to use jaudiorecorder to record sound to a stream from android but while recorder and start sending buffer the app is completley freeze any idea why ?

here is the current code

//audio recorder 
GBUFFSIZE := TJAudioRecord.JavaClass.getMinBufferSize(8000,
TJAudioFormat.JavaClass.CHANNEL_IN_MONO,
TJAudioFormat.JavaClass.ENCODING_PCM_16BIT);

Gstream := TJavaArray<Byte>.Create(GBUFFSIZE);

AudioRecorder:= TJAudioRecord.JavaClass.init(TJMediaRecorder_AudioSource.JavaClass.VOICE_COMMUNICATION,
8000,
TJAudioFormat.JavaClass.CHANNEL_IN_MONO,
TJAudioFormat.JavaClass.ENCODING_PCM_16BIT,
GBUFFSIZE);


//start recording

(AudioRecorder as JAudioRecord).startRecording;
papulaterecorder.Enabled := True;

//timer
procedure TForm1.papulaterecorderTimer(Sender: TObject);
var
  index: Integer;
  NewCount: Integer;
  Bytes: TIdBytes;
begin


// Read from the AudioRecover
NewCount:= (AudioRecorder as JAudioRecord).read(Gstream, 0, GBUFFSIZE);
if  (NewCount > 0) then
begin
Bytes := RawToBytes(Gstream.Data^, NewCount);
SendBuffer(Bytes, NewCount);
end;

end;


end;


procedure SendBuffer(Buffer: TIdBytes; BufferSize: Cardinal);
Var
Strm: TIdMemoryBufferStream;
begin

Strm := TIdMemoryBufferStream.Create(PByte(Buffer), BufferSize);
try
FTCP.Socket.WriteLn('stream');
FTCP.Socket.LargeStream := True;
FTCP.Socket.Write(Strm, 0, True);
finally
Strm.Free;
end;
end;

Open in new window

Avatar of Sinisa Vuk
Sinisa Vuk
Flag of Croatia image

App is freezing cause Indy is blocking (send with block until all is send) component. Maybe ICS (internet component suite) is the solution...
Then again ... it is better to use separate thread to send data....
Avatar of dolphin King
dolphin King

ASKER

but ics is not supporting on android yet , also i send the same way from vcl and it works normal

i suspect those variable that causes the  freeze  do  i sent the right data ? also what you mean by use separate thread

var
Bytes: TIdBytes;
..
Bytes := RawToBytes(Gstream.Data^, NewCount);
SendBuffer(Bytes, NewCount);

Open in new window


procedure SendBuffer(Buffer: TIdBytes; BufferSize: Cardinal);
Var
Strm: TIdMemoryBufferStream;
begin

Strm := TIdMemoryBufferStream.Create(PByte(Buffer), BufferSize);
try
FTCP.Socket.WriteLn('stream');
FTCP.Socket.LargeStream := True;
FTCP.Socket.Write(Strm, 0, True);
finally
Strm.Free;
end;
end;

Open in new window


on VCL i do

procedure Tform1.recorderData(Sender: TObject; const Buffer: Pointer; BufferSize: Cardinal; var FreeIt: Boolean);
var
Bytes: TIdBytes;
begin
FreeIt := True;

if (Buffer <> nil) and (BufferSize > 0) then
begin
try
Bytes := RawToBytes(Buffer^, BufferSize);
except
exit;
end;
begin
SendBuffer(Bytes, BufferSize);
end;
end;
end;

Open in new window


and it sended normal

on android when i comment out the sendbuffer the app is not freezing
also i suspend the rawtobyte converter i am not sure whats going on
Don't know too... Seem that Indy works different on android... Maybe some properties will help... hard to say...
i even changed the Gstream : TJavaArray<Byte>; To smallint still the same
ok i want to test something different can you show me how to rung that audio recorder in a worker thread ? i want to send the stream in a worker thread instead of a timer but i dont know how
also i see problem when some data sent it sent as 715 size it should be 1600 to allow the vcl player to playing it i am trying to look deeply

also i have changed the send buffer so now its looks like


procedure TForm1.timer1Timer(Sender: TObject);
var
  NewCount: Integer;
begin

NewCount:= (AudioRecorder as JAudioRecord).read(Gstream, 0, GBUFFSIZE);
if NewCount > 0 then
begin
try
ClientThread.SendBuffer(Gstream.Data, Gstream.Length);
except
end;

end;


end;


SendBuffer(Buffer: pointer; BufferSize: Cardinal);
Var
Strm: TIdMemoryBufferStream;
begin
ClientThread.Lock;
try

Strm := TIdMemoryBufferStream.Create(Buffer, BufferSize);
try
FTCP.Socket.WriteLn('Stream');
FTCP.Socket.LargeStream := True;
FTCP.Socket.Write(Strm, 0, True);
finally
Strm.Free;
end;
finally
ClientThread.Unlock;
end;
end;

Open in new window



now it does send the stream but audio cannot be played i have trace the size comes its very small 714 , it should be  1600 to allow the vcl player to playing it i use  Tliveaudioplayer from wave audio package
As example - here is my old answer to similar question....

Try change to:
procedure SendBuffer(Buffer: TIdBytes; BufferSize: Cardinal);
Var
Strm: TIdMemoryBufferStream;
begin

Strm := TIdMemoryBufferStream.Create(PByte(Buffer), BufferSize);
try
FTCP.IOHandler.WriteLn('stream');
FTCP.IOHandler.LargeStream := True;
FTCP.IOHandler.Write(Strm, 0, True);
finally
Strm.Free;
end;
end;

Open in new window

the sending part is solved already see my updated code above i can send data to the vcl but no sound can be played ,  it does send the stream but audio cannot be played i have trace the size comes its very small 714 , it should be  1600 to allow the vcl player to playing it i use  Tliveaudioplayer from wave audio package


updated code again

procedure TForm1.timer1Timer(Sender: TObject);
var
  NewCount: Integer;
begin

NewCount:= (AudioRecorder as JAudioRecord).read(Gstream, 0, GBUFFSIZE);
if NewCount > 0 then
begin
try
ClientThread.SendBuffer(Gstream.Data, Gstream.Length);
except
end;

end;


end;


SendBuffer(Buffer: pointer; BufferSize: Cardinal);
Var
Strm: TIdMemoryBufferStream;
begin
ClientThread.Lock;
try

Strm := TIdMemoryBufferStream.Create(Buffer, BufferSize);
try
FTCP.Socket.WriteLn('Stream');
FTCP.Socket.LargeStream := True;
FTCP.Socket.Write(Strm, 0, True);
finally
Strm.Free;
end;
finally
ClientThread.Unlock;
end;
end;

Open in new window

try to wait to sending buffer fill up to 1600 bytes....

...
GBUFFSIZE := TJAudioRecord.JavaClass.getMinBufferSize(8000,
TJAudioFormat.JavaClass.CHANNEL_IN_MONO,
TJAudioFormat.JavaClass.ENCODING_PCM_16BIT);

Gstream := TJavaArray<Byte>.Create(GBUFFSIZE * 10);

AudioRecorder:= TJAudioRecord.JavaClass.init(TJMediaRecorder_AudioSource.JavaClass.VOICE_COMMUNICATION,
8000,
TJAudioFormat.JavaClass.CHANNEL_IN_MONO,
TJAudioFormat.JavaClass.ENCODING_PCM_16BIT,
GBUFFSIZE * 10);
...

procedure TForm1.timer1Timer(Sender: TObject);
var
  NewCount: Integer;
begin
  timer1.Enable := False;
  try
  //check for enough data....
NewCount:= (AudioRecorder as JAudioRecord).read(Gstream, 0, GBUFFSIZE * 10, 0);
{
[i]readMode 	int: one of READ_BLOCKING = 0, READ_NON_BLOCKING = 1.
With READ_BLOCKING, the read will block until all the requested data is read.
With READ_NON_BLOCKING, the read will return immediately after reading as much audio data as possible without blocking.[/i]
}
if NewCount > 0 then
begin
....

finally
  timer1.Enable := True;
end;
end; //timer1Timer
...

Open in new window

new count variable did not compiled
app force closed after starting to send current code

procedure TForm1.timer1Timer(Sender: TObject);
var
  NewCount: Integer;
begin


Timer1.Enabled := False;
 try

NewCount := 0;

NewCount:= (AudioRecorder as JAudioRecord).read(Micstream, 0, MICBUFFSIZE * 10);
if NewCount > 0 then
begin

ClientThread.SendBuffer(Micstream.Data, NewCount);


end;


finally
Timer1.Enabled := True;
end;



end;

Open in new window

try to comment line: ClientThread.SendBuffer(Micstream.Data, NewCount).... still crash?

you may try my source for SendBuffer
your send buffer is similar to the one i do , when  MICBUFFSIZE * 10 the app is closed when its MICBUFFSIZE only its send one time and the app is freezed
how you define MICBUFFSIZE?
same as you showed
MICBUFFSIZE := TJAudioRecord.JavaClass.getMinBufferSize(8000,
TJAudioFormat.JavaClass.CHANNEL_IN_MONO,
TJAudioFormat.JavaClass.ENCODING_PCM_16BIT);

Micstream := TJavaArray<Byte>.Create(MICBUFFSIZE * 10);

AudioRecorder:= TJAudioRecord.JavaClass.init(TJMediaRecorder_AudioSource.JavaClass.VOICE_COMMUNICATION,
8000,
TJAudioFormat.JavaClass.CHANNEL_IN_MONO,
TJAudioFormat.JavaClass.ENCODING_PCM_16BIT,
MICBUFFSIZE * 10);

Open in new window



when removing *10 send 714 for one time and the app freeze
can you help me  on how to use the recorder in worker thread ?
Sorry, I'm back on Monday here...
its ok i will be waiting
i did that recording part in a thread , but still cannot send audio to the server something prevent me

type
    TAudiocallbackproc = procedure (Sender: TObject;  Sbuffer:pointer; SBufferSize: Cardinal) of object;

    TAudioStreamThread = class(TThread)
      private
        FonAudiocallbackproc : TAudiocallbackproc;
        FCs: TCriticalSection;
        Fbuffer : pointer;
        FBufferSize : Cardinal;
        //recorder
        AudioRecorder: JAudioRecord;
        MICBUFFSIZE: Integer;
        Micstream : TJavaArray<Byte>;
        procedure DoAudiocallbackproc;
        //
      protected
        procedure Execute; override;
      Public
      constructor Create(CreateSuspended: Boolean; AonAudiocallbackproc: TAudiocallbackproc); reintroduce;
      destructor Destroy; override;
      end;


{ TAudioStreamThread }

constructor TAudioStreamThread.Create(CreateSuspended: Boolean;
  AonAudiocallbackproc: TAudiocallbackproc);
begin
inherited Create(CreateSuspended);
FreeOnTerminate := True;
FCs := TCriticalSection.Create;
FonAudiocallbackproc := AonAudiocallbackproc;
end;

destructor TAudioStreamThread.Destroy;
begin
FreeAndNil(FCs);
  inherited;
end;

procedure TAudioStreamThread.DoAudiocallbackproc;
begin
if Assigned(FonAudiocallbackproc) then
begin
FonAudiocallbackproc(self, Fbuffer, FBufferSize);
end;
end;

procedure TAudioStreamThread.Execute;
var
NewCount : integer;
begin

// microphone

MICBUFFSIZE := TJAudioRecord.JavaClass.getMinBufferSize(8000,
TJAudioFormat.JavaClass.CHANNEL_IN_MONO,
TJAudioFormat.JavaClass.ENCODING_PCM_16BIT);
Micstream := TJavaArray<Byte>.Create(MICBUFFSIZE);
AudioRecorder:= TJAudioRecord.JavaClass.init(TJMediaRecorder_AudioSource.JavaClass.VOICE_COMMUNICATION,
8000,
TJAudioFormat.JavaClass.CHANNEL_IN_MONO,
TJAudioFormat.JavaClass.ENCODING_PCM_16BIT,
MICBUFFSIZE);

(AudioRecorder as JAudioRecord).startRecording;

NewCount := 0;
while not terminated  do
begin
NewCount:= (AudioRecorder as JAudioRecord).read(Micstream, 0, MICBUFFSIZE);
if NewCount > 0 then
begin
Fbuffer := Micstream.Data;
FBufferSize :=  Micstream.Length;
CThread.SendBuffer(Fbuffer, FBufferSize);
end;
end;

end;


procedure TCThread.SendBuffer(Buffer: pointer; BufferSize: Cardinal);
Var
Strm: TIdMemoryBufferStream;
begin
crit.Lock;
try
if not FTCP.Connected then
begin
exit;
end;

Strm := TIdMemoryBufferStream.Create(Buffer, BufferSize);
try
FTCP.Socket.WriteLn('Stream');
FTCP.Socket.LargeStream := True;
FTCP.Socket.Write(Strm, 0, True);
finally
Strm.Free;
end;
finally
crit.Unlock;
end;
end;

Open in new window

more details when debugging the server when i send from vcl its alway send 1600 size , on android when i send its sends huge amount of numbers and they are not showing unless i got disconnected
Are these numbers readl data? (compare to vcl type)
Maybe there is a problem transforming from TJavaArray to TIDByteArray or similar type.... when sending using Indy.....
Try to make small project which will send predefined (known) set of data from android to server... and check if comes ok. Make this data smaller than 1600 and in second case larger...
the  problem is the Fmx client does not send the buffer contentiously like the vcl , its send all of them after disconnected
Found that other users had same issue too...Like here... But Remy Lebeau (TeamB) did not post his resolution....
Do you check for Connected property on Client side? Seems that this could make problems...
thats seems global indy problem i read too many topics around that
i removed connected property check , still the same problem seems more likely indy bug , i really dont know what to do now , i wanted to broadcast audio from android device to vcl project
Try this TCPServerClient library ... It is build around Indy ..but maybe works better...This components simplified net transport - could be  asynchronous or synchronous transfer...
Some issues could be due to incorrect string/char handling.... You could try to disable zero indexing too....
it does not compatible with xe8 :) i dont know if there is other way to send the stream ? as example send it as string then convert it back to a stream on server side or even on client side ?
ASKER CERTIFIED SOLUTION
Avatar of Sinisa Vuk
Sinisa Vuk
Flag of Croatia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial