Correct way to convert buffer

Please post bug reports, feature requests, or any question regarding the DELPHI AREA projects here.

Correct way to convert buffer

Postby mook » September 9th, 2012, 6:43 am

I'm playing with the audio components for 3 weeks now but I can't get wave to GSM conversion working.
As a base I took the broadcaster and receiver samples.

Initially the LiveAudioRecorderData procedure just sends the buffer that is supplied.
I modified it as below.
targetformat is simply a TWaveStorage which has the GSM sample loaded.

Code: Select all
procedure TMainForm.LiveAudioRecorderData(Sender: TObject;
  const Buffer: Pointer; BufferSize: Cardinal; var FreeIt: Boolean);
var
  I: Integer;
  SrcFormat: TWaveFormatEx;
  Data: Pointer;
  DataSize: DWORD;
  Data2: Pointer;
  DataSize2: DWORD;
begin
  FreeIt := True;

  SetPCMAudioFormatS(@SrcFormat, LiveAudioRecorder.PCMFormat);
  if ConvertWaveFormat(@srcFormat, Buffer, BufferSize,
    targetformat.Wave.WaveFormat, Data, DataSize)
  then edit4.text := 'yes'
  else edit4.text := 'no';

  if ConvertWaveFormat(targetformat.Wave.WaveFormat, Data, DataSize,
    @srcFormat, Data2, DataSize2)
  then edit5.text := 'yes'
  else edit5.text := 'no';

  edit6.Text := inttostr(DataSize);
  edit7.Text := inttostr(DataSize2);

  for I := tcpServer.Socket.ActiveConnections - 1 downto 0 do
    with tcpServer.Socket.Connections[I] do
      if Data = Self then // the client is ready
        SendBuf(Data^, DataSize);
//        SendBuf(Buffer^, BufferSize);

end;


When I select PCM 22Khz 8bit mono or 16 bit mono I see that the conversion goes well, edit 4 displays "yes".
Also converting it back works fine because edit5 will also display yes.

On the receiver sample I have also put a TWaveStorage with the exact same GSM sample wave file loaded.
However, using ConvertWaveFormat already returns false.
I did check the size of the data and the broadcaster send 455 bytes of data and that is also the amount I receive.

Is ConvertWaveFormat the best way to convert the buffer to GSM and back to PCM?

PS: I did read the README over and over, and I actually went through all 31 pages on this forum to look for answers.
Some people must have succeeded in this, the FRN client is the proof of that, but I can't figure out how to do it.

Can you give me some directions about correctly converting the buffer to GSM and back?

Thanks,
Mark.
mook
Member
Member
 
Posts: 2
Joined: September 9th, 2012, 6:38 am

Re: Correct way to convert buffer

Postby Kambiz » September 9th, 2012, 10:16 am

There is memory leak in your code. When you no more need the conversion buffers, you should release the memory allocated for them.

Code: Select all
ReallocMem(Data, 0);
ReallocMem(Data2, 0);

The rest of your code seems fine to me.

Could you please post the code used in the receiver?
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Re: Correct way to convert buffer

Postby mook » September 9th, 2012, 7:39 pm

Hi Kambiz,

In advance, thank you for your time and effort.
Below is the receiver part.

Code: Select all
procedure TMainForm.tcpClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
  WaveFormatSize: Integer;
  ReceiveData: Pointer;
  Data: Pointer;
  ReceiveSize: Integer;
  //mark
  SrcFormat: TWaveFormatEx; //mark
  DecompData: Pointer; //mark
  DecompSize: Cardinal;
begin
  try
    if not LiveAudioPlayer.Active then
    begin
      Socket.ReceiveBuf(WaveFormatSize, SizeOf(WaveFormatSize));
      ReallocMem(WaveFormat, WaveFormatSize);
      try
        Socket.ReceiveBuf(WaveFormat^, WaveFormatSize);
      except
        ReallocMem(WaveFormat, 0);
        raise;
      end;
      LiveAudioPlayer.Active := True;
    end
    else
    begin
      Sleep(0); // giving chance to the player thread for using the audio buffer
      ReceiveSize := Socket.ReceiveLength;
      //This part about the blockalign might perhaps not work anymore?
      //Do I still need it in some way?
      //I think not because I will always get full blocks out of the
      //decompressed GSM wave, correct?
    //  if BlockAlign > 1 then
     //   Dec(ReceiveSize, ReceiveSize mod BlockAlign);
      if ReceiveSize <> 0 then
      begin
      //skipping here cause I don't know the decompressed size yet
     //  Data := AudioBuffer.BeginUpdate(DataSize);
        try
          //allocating memory because I don't use audiobuffer.beginupdate
//          ReallocMem(ReceiveData, ReceiveSize); //doesn't work, must use getmem?
          GetMem(ReceiveData, ReceiveSize);
          Socket.ReceiveBuf(ReceiveData^, ReceiveSize);
          edit2.Text := IntToStr(ReceiveSize);
          edit4.text := inttostr(strtoint(edit4.text)+1);

          //getting target format, which is simply received from broadcaster
          SetPCMAudioFormatS(@SrcFormat, LiveAudioPlayer.PCMFormat);
          //converting GSM back to PCM
          if ConvertWaveFormat(targetformat.Wave.WaveFormat, ReceiveData, ReceiveSize, @srcFormat, DecompData, DecompSize)
            then edit1.Text := 'yes'
            else edit1.text := 'no';

            //convert succesfull so we add the data to audiobuffer.
            //Unfortunately, convertwaveformat always returns false so I don't
            //even know if the code below is valid or not.
            if edit1.text = 'yes' then
            begin
              Data := AudioBuffer.BeginUpdate(DecompSize);
              //Is this actually the correct way to copy data between pointers ranges?
              //Or does this copy decompdata pointer value (mem address value) to Data?
              //Any suggestions about how to copy the data?
              Data := DecompData;
              AudioBuffer.EndUpdate;
              ReallocMem(DecompData, 0);
            end;

        finally
          //releasing the buffer that received the data from the socket.
          FreeMem(ReceiveData, ReceiveSize);

        end;
      end;
    end;
  except
    tcpClient.Active := False;
    Application.HandleException(Self);
  end;
end;


The first part is just standard from your example application.
The PCM format is send just like it has always been.
Only the 2nd part I have tried to change.

In theory, you did:
- check the size of the receive buffer to meet the blockalign requirement
- allocate memory through audiobuffer.beginupdate
- receive raw wave data
- run audiobuffer.endupdate to leave the critical section.
What I tried to do:
- check the size of the receive buffer
- allocate the same amount of memory
- receive the data in the receivedata buffer
- convert from gsm to pcm with converwaveformat
- if succeeeded, run audiobuffer.beginupdate with new PCM buffer size
- copy data from decompressed buffer
- run audiobuffer.endupdate
- unset memory of decompressed buffer
- unset memory of socket receive buffer

I did not use critical sections.
Should I do that?

And what can be the cause that the convertwaveformat is returning false?
Is there a way to get an error message, or is there I way I can dump the buffers to file or anything to see if the data has not been changed during transmission?

Thanks again,
Mark.

PS: I have uploaded my source code, perhaps it helps...
Attachments
MyTest.zip
(25.62 KiB) Downloaded 44 times
mook
Member
Member
 
Posts: 2
Joined: September 9th, 2012, 6:38 am

Re: Correct way to convert buffer

Postby Kambiz » September 9th, 2012, 9:38 pm

Mark,

First, you're welcome! :)

You commented the following piece of code based on a wrong assumption:

Code: Select all
      //This part about the blockalign might perhaps not work anymore?
      //Do I still need it in some way?
      //I think not because I will always get full blocks out of the
      //decompressed GSM wave, correct?
    //  if BlockAlign > 1 then
     //   Dec(ReceiveSize, ReceiveSize mod BlockAlign);

Why is that wrong? It's a TCP/IP connection, then although you transmit the whole block, but you may receive the data in chunks. You have to buffer all the block's data before being able to work on it. In my code I used modulus operator to read data when one or more whole blocks are ready. However, it is only possible for wave formats with fixed block size. I don't know whether GSM uses variable or fixed block size. If it uses variable block size, you have to transmit the size of block as a header of the actual data. I've used the same tactic to transmit and receive the wave format.

Synchronizing access to audio buffer is essential because socket connection and audio player both have their own threads. The TAudioBuffer takes care of this issue. However, the following code is not correct usage of TAudioBuffer:

Code: Select all
Data := AudioBuffer.BeginUpdate(DecompSize);
//Is this actually the correct way to copy data between pointers ranges?
//Or does this copy decompdata pointer value (mem address value) to Data?
//Any suggestions about how to copy the data?
Data := DecompData;
AudioBuffer.EndUpdate;
ReallocMem(DecompData, 0);

That code actually has no side effect. Here is the correct usage:

Code: Select all
Data := AudioBuffer.BeginUpdate(DecompSize);
move(DecompData^, Data^, DecompSize);
AudioBuffer.EndUpdate;
ReallocMem(DecompData, 0);

It's better you write your own version of TAudioBuffer class, because in the above code we need to copy data from one buffer to another that can be avoided.

Regarding ReallocMem, in the first call the pointer variable must be initialized with nil.
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm


Return to DELPHI AREA Projects

Who is online

Users browsing this forum: No registered users and 2 guests

cron