capture audio with Wave Audio v1.62 using threads

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

capture audio with Wave Audio v1.62 using threads

Postby Tommyjunge » January 31st, 2006, 7:14 pm

I e-mailed a question regarding capturing audio with Wave Audio v1.62 to Kambiz and he emailed me the following answer:

> Hi Thomas,
>
> Without seeing your code, it's difficult to figure out the problem.
> However,
> generally you can consider following items:
>
> 1. Reduce the size of each buffer (BufferLength <= 50) and instead increase
> the number of them (BufferCount >= 10).
> 2. Let recording and play back execute in a separate thread (Async = True).
> 3. Do your calculations on a separate thread too.
>
> Please post your questions at http://www.delphiarea.com/forum/, thank you.
>
> Regards,
> Kambiz

First of all, thank you very much for answering, Kambiz!

I would like to follow the above suggestions but I am no expert in programming threads.

Now my question is:
I know how to use the components of WaveAudio on a form. I can just move them from the palette to the form.
But how can I use them inside a thread object without a form?
What kind of code do I need to write? Do I have to modify the WaveAudio units?

Thanks again for your help,

Thomas
Tommyjunge
Active Member
Active Member
 
Posts: 17
Joined: January 31st, 2006, 7:03 pm

Postby Kambiz » February 2nd, 2006, 9:34 pm

To record or playback audio asynchronously (in a separate thread), you just need to set Async property of the appropriate components to True.

That's all. ;)
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby Tommyjunge » February 3rd, 2006, 10:53 am

Hello Kambiz,

thanks for the quick answer!
Sorry, but I'm not an expert in Threading. I still have a question:

In my application I drop LiveAudioRecorder on my main form =>
type
TForm1 = class(TForm)
LiveAudioRecorder1: TLiveAudioRecorder;

This makes LiveAudioRecorder1 belong to the class TForm1.
Now I'm confused because an additional thread belongs to a class of its own (TThread).
Now, my understanding is that Form1 belongs to the primary thread of the application. Does that mean, that LiveAudioRecorder1 can only be used in the primary thread?
Or can I create a new thread in which I call Form1.LiveRecorder1.somemethod?
In which thread does the callback function run in that case then?

Thanks again for your help,

Thomas
Tommyjunge
Active Member
Active Member
 
Posts: 17
Joined: January 31st, 2006, 7:03 pm

Postby Kambiz » February 3rd, 2006, 11:45 am

Well, seems you need to know more about threading. I do suggest to read Multithreading - The Delphi Way.
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby Tommyjunge » February 3rd, 2006, 1:27 pm

Thanks for the tutorial quote. I'll do my reading and come back.

One last question for now:

How do I record with a samplerate of 96000Hz using WaveAudio?
PCMWaveFormat of your component only allows for max 48000Hz, my soundcard can also handle 96000Hz.

Thanks, Thomas
Tommyjunge
Active Member
Active Member
 
Posts: 17
Joined: January 31st, 2006, 7:03 pm

Postby Kambiz » February 3rd, 2006, 1:53 pm

To do so, you have to set PCMFormat property to nonePCM, and then feed the format to the component using the OnFormat event.
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby Tommyjunge » February 4th, 2006, 8:35 pm

Hello Kambiz,

I'm through with my reading on threads and I also studied a little bit the internals of your WaveAudio-package and the Windows Multimedia API. Now I see somewhat clearer.

I understand that the property Async set to true will make Windows start the capture in a separate thread which I don't even have to worry about.

I have made a very simple program to play with your package.
See below the most important functions.

Code: Select all
procedure TForm1.Button1Click(Sender: TObject);
begin
SetLength(Mono16,0);  //Array shall hold the sampled data
LiveAudioRecorder1.Active:=true;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
LiveAudioRecorder1.Active:=false;
end;

procedure TForm1.LiveAudioRecorder1Format(Sender: TObject;
  out pWaveFormat: PWaveFormatEx; var FreeIt: Boolean);
begin
  pWaveFormat^.wFormatTag  :=WAVE_FORMAT_PCM;
  pWaveFormat^.nChannels   :=2;
  pWaveFormat^.nSamplesPerSec  :=96000;
  pWaveFormat^.nAvgBytesPerSec :=96000*4;
  pWaveFormat^.nBlockAlign :=  4;
  pWaveFormat^.wBitsPerSample:=16;
  pWaveFormat^.cbSize :=0;
end;

Now a few questions:
1. When I use non-pcm Audioformat I get an access violation in procedure TForm1.LiveAudioRecorder1Format. I cannot write into the WaveFormatEx - structure.
WHAT AM I DOING WRONG?

2. With async=false and a standard pcm-format all runs fine. With Button1 I start recording and with Button2 I stop it. Then I can restart with Button1.
With async=true I cannot restart after I stopped the recording.
WHY? WHAT DO I HAVE TO DO TO BE ABLE TO RESTART?

3. I noticed that the application indeed sets up 3Threads (4 with async=true). What is their job?

Thanks again for your help,

Thomas
Tommyjunge
Active Member
Active Member
 
Posts: 17
Joined: January 31st, 2006, 7:03 pm

Postby Kambiz » February 5th, 2006, 12:19 am

  1. You have to allocate memory for the wave format record.

    Code: Select all
    procedure TForm1.LiveAudioRecorder1Format(Sender: TObject;
      out pWaveFormat: PWaveFormatEx; var FreeIt: Boolean);
    begin

      GetMem(pWaveFormat, SizeOf(TWaveFormatEx)); // allocate memory
      FreeIt := True; // let the component release the allocated memory

      pWaveFormat^.wFormatTag  :=WAVE_FORMAT_PCM;
      pWaveFormat^.nChannels   :=2;
      pWaveFormat^.nSamplesPerSec  :=96000;
      pWaveFormat^.nAvgBytesPerSec :=96000*4;
      pWaveFormat^.nBlockAlign :=  4;
      pWaveFormat^.wBitsPerSample:=16;
      pWaveFormat^.cbSize :=0;
    end;
  2. You may need to follow the following topic:
    http://www.delphiarea.com/forum/viewtopic.php?t=613
  3. One is main VCL thread, one is Recorder/Player thread (when Async is True), the others are OS threads executed by some API calls.
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby Tommyjunge » February 5th, 2006, 8:41 am

Hello Kambiz,

thanks for the quick answers!

ad 2:
Yes, seems to be the same problem like discussed in the other thread.
I will also test on some other machines. Mine is an AMD Duron 1GHz on a K7 motherboard with VIA chipset and integrated audio. A bit old but still running fine.

ad 3:
Is there a possibility to increase the priority of the audiocapture-thread above normal? As I recall my reading correctly, the thread would have to be suspended to be able do so?!?

Best regards,

Thomas
Tommyjunge
Active Member
Active Member
 
Posts: 17
Joined: January 31st, 2006, 7:03 pm

Postby Tommyjunge » February 5th, 2006, 9:55 am

ad 2:
Have some interesting results, will post in the other thread.

ad 1:
I have allocated the memory, I can set the properties now, but I get another error now:
"not supported audio format"
I know my card supports 96000Hz pcm, even though the Windows-capabilities function denies that. What do I have to do to record in that mode?
Do I have to use another FormatTag than
pWaveFormat^.wFormatTag :=WAVE_FORMAT_PCM;
or are you doing the checking too strictly?

I have one more question:
Can I use the WaveAudio-Package also to capture 24bit audiodata from a soundblaster 24bit card? I have seen that Windows offers an extra Waveformat-record for nonstandard formats, namely WAVEFORMATEXTENSIBLE structure. Is this implemented in WaveAudio?

Regards Thomas
Tommyjunge
Active Member
Active Member
 
Posts: 17
Joined: January 31st, 2006, 7:03 pm

Postby Tommyjunge » February 5th, 2006, 6:48 pm

Hello Kambiz,

I have some news. I solved the problem with the lost samples.
Strange thing was that I lost SAMPLES, not BUFFERS.

I have replaced the audio driver against a newer version. Now I loose buffers if I overload the system, quite understandable. If no overload condition, everything is fine.

I also got WaveAudio to record at 96000 Samples per second. Fine!

But still I have a few questions remaining. Let me please summarize them:

A:
Is there a possibility to increase the priority of the audiocapture-thread above normal? As I recall my reading correctly, the thread would have to be suspended to be able do so?!?

B:
Can I use the WaveAudio-Package also to capture 24bit audiodata from a soundblaster 24bit card? I have seen that Windows offers an extra Waveformat-record for nonstandard formats, namely WAVEFORMATEXTENSIBLE structure. Is this implemented in WaveAudio?

And of course I would like to solve the problem with async=true. I will follow up on that other thread.

Thanks again for this fruitful discussion. There don't seem to be many experts on this topic out there.

Regards, Thomas
Tommyjunge
Active Member
Active Member
 
Posts: 17
Joined: January 31st, 2006, 7:03 pm

Postby Kambiz » February 5th, 2006, 10:11 pm

  1. In the current release of the package, I didn't consider priority for the thread. I'll add it in the next release.
  2. You can allocate a WAVEFORMATEXTENSIBLE structure and pass its pointer to pWaveFormat parameter of OnFormat event.
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby Tommyjunge » February 8th, 2006, 7:13 am

Hello Kambiz,

one more question:

Do I have to save the captured Audio buffer in the callback function or, can I just save the pointer, set freeit to false and use the buffer with the data later?
How would I then manually release/free the memory of the buffer if I don't need them any longer?

Regards, Thomas
Tommyjunge
Active Member
Active Member
 
Posts: 17
Joined: January 31st, 2006, 7:03 pm

Postby Kambiz » February 8th, 2006, 10:54 am

Yes, you can.

Call FreeMem procedure to release the buffer.
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2429
Joined: March 7th, 2003, 7:10 pm

Postby Tommyjunge » February 14th, 2006, 11:06 am

Hello Kambiz,

I still have a problem with lost samples. I have tried to attach a picture to clarify what I'm talking about. Problems occur more seldom now but still happen.

I have stripped down my application to capture only one single buffer and then stop. Here is the code of my callback function.

Code: Select all
procedure TForm1.LiveAudioRecorder1Data(Sender: TObject;
  const Buffer: Pointer; BufferSize: Cardinal; var FreeIt: Boolean);
var
 B16: array of smallint absolute Buffer;
 N,i: integer;
 xmax: integer;
begin
  LiveAudioRecorder1.Active:=false;
  N := BufferSize Div 2;
  xmax:=Length(Mono16);
  SetLength(Mono16,xmax+N);

  For i := 0 to N-1 Do
    begin
    Mono16[xmax+i]:=B16[i];
    end;
  Timer2.Enabled:=true;
  FreeIt:=true; 
end;


During the capture my program doesn't do anything. After the capture is finished I start data analysis and plotting by activating Timer2 in the callback function. After Plotting is done I start the capture again.

Now I find really strange things:
With a sample rate of 96000 Hz I get the sporadic jumps like in the picture. The strange thing is that the jumps always occur at the same position in the buffer, namely at sample number 2884 (in the picture you have to add 64 to the x-values as I discard the first 64 samples because they are rubbish), which corresponds to almost exactly 30 milliseconds from sample start. This is independent of the chosen buffer length. I tried 60, 80 and 100ms buffer length, but always find the problem at sample 2820.

With a sample rate of 48000 Hz I cannot create this problem.

Do you have an explanation for this strange thing or better a hint how I could find the cause?

Thanks, Thomas
Attachments
jump.gif
my corrupted audio data
jump.gif (11.73 KiB) Viewed 5959 times
Tommyjunge
Active Member
Active Member
 
Posts: 17
Joined: January 31st, 2006, 7:03 pm

Next

Return to DELPHI AREA Projects

Who is online

Users browsing this forum: No registered users and 3 guests

cron