[wdmaudiodev] Re: buffering samples in an APO

  • From: Matthew van Eerde <Matthew.van.Eerde@xxxxxxxxxxxxx>
  • To: "wdmaudiodev@xxxxxxxxxxxxx" <wdmaudiodev@xxxxxxxxxxxxx>
  • Date: Mon, 22 Dec 2014 20:18:32 +0000

Suppose I feed your APO the following signal:


a.       1 million samples of silence

b.      an impulse

c.       1 million more samples of silence.

Your APO will very likely produce the following output:


a.       1 million samples of something that looks a lot like silence

b.      (potentially) a little bit more of something that looks a lot like 
silence

c.       something that looks a lot like an impulse

d.      (potentially) the after-effects of the impulse, which may 
sooner-or-later look a lot like silence

GetLatency(...) should return the length of the "b." phase, converted from 
samples into hundred-nanoseconds.

For some APOs, this will actually be zero; for example, the volume APO just 
multiplies all of the samples that go through it by a constant, and adds no 
delay.

For other APOs, such as the one in my example, there will be a fixed delay of 
768 samples between input and output.

For other APOs, there will be a delay which varies depending on the format 
used. It is expected that the delay will be constant for the life of the 
stream, though; that is, once LockForProcess(...) has been called on a given 
APO instance, the latency cannot vary anymore for that instance.

From: wdmaudiodev-bounce@xxxxxxxxxxxxx 
[mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx] On Behalf Of Abhinav Singh
Sent: Monday, December 22, 2014 11:14 AM
To: wdmaudiodev@xxxxxxxxxxxxx
Subject: [wdmaudiodev] Re: buffering samples in an APO

Hi Matthew
I am actually buffering 1024 samples..so the simple approach i explained in the 
previous post works pretty well, though what you suggested would work better 
when the number of samples required are less.

Also what exactly should GetLatency() return? the time taken to return from 
each call to APOProcess() or the formula you mentioned?

________________________________
From: Matthew.van.Eerde@xxxxxxxxxxxxx<mailto:Matthew.van.Eerde@xxxxxxxxxxxxx>
To: wdmaudiodev@xxxxxxxxxxxxx<mailto:wdmaudiodev@xxxxxxxxxxxxx>
Subject: [wdmaudiodev] Re: buffering samples in an APO
Date: Wed, 17 Dec 2014 19:10:27 +0000
Hmm... to err on the side of caution, let me make sure I understand correctly. 
The following is a naïve approach:

APOProcess call

You are handed

You do

You output

1

448 samples

Append 448 samples to incoming queue
Incoming queue now has 448 samples
Outgoing queue is empty

448 samples of silence

2

448 samples

Append 448 samples to incoming queue
Process 768 samples
Output 448 samples
Incoming queue now has 128 samples
Outgoing queue now has 320 samples


448 samples of data

3

448 samples

Append 448 samples to incoming queue
Incoming queue now has 576 samples
Outgoing queue now has 320 samples

448 samples of silence

4

448 samples

Append 448 samples to incoming queue
Process 768 samples
Output 448 samples
Incoming queue now has 256 samples
Outgoing queue now has 640 samples

448 samples of data


This approach won't work; you're alternating between outputting data and 
silence, which will cause glitchiness. Instead, adopt a fixed delay equal to 
your internal processing buffer size by pre-filling your output queue with 768 
samples of silence.

APOProcess call

You are handed

You do

You output

1

448 samples

Append 448 samples to incoming queue
Incoming queue now has 448 samples
Output 448 samples of silence
Outgoing queue now has 320 samples of silence

448 samples of silence

2

448 samples

Append 448 samples to incoming queue
Process 768 samples
Output 448 samples
    320 of silence
    128 of processed data
Incoming queue now has 128 samples
Outgoing queue now has 640 samples


320 samples of silence
128 of processed data

3

448 samples

Append 448 samples to incoming queue
Incoming queue now has 576 samples
Output 448 samples
Outgoing queue now has 192 samples

448 samples of processed data

4

448 samples

Append 448 samples to incoming queue
Process 768 samples
Output 448 samples
Incoming queue now has 256 samples
Outgoing queue now has 512 samples

448 samples of processed data


Eventually you will get a final APOProcess call (though you won't know it at 
the time.) Your incoming queue and your outgoing queue will still have data in 
them. That's fine; the APO is under no obligation to flush its internal queues, 
or to let Windows know what the state of its internal queues are, other than by 
implementing IAudioProcessingObject::GetLatency().

Since your latency is 768 samples, your GetLatency implementation should return 
1.0 * 768 * HNS_PER_SECOND / WAVEFORMATEX.nSamplesPerSec; for 44.1 data, this 
is 174150 hundred-nanoseconds, or 17.4 ms.

From: wdmaudiodev-bounce@xxxxxxxxxxxxx<mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx> 
[mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx] On Behalf Of Abhinav Singh
Sent: Wednesday, December 17, 2014 10:41 AM
To: wdmaudiodev@xxxxxxxxxxxxx<mailto:wdmaudiodev@xxxxxxxxxxxxx>
Subject: [wdmaudiodev] Re: buffering samples in an APO

I totally forgot to implement GetLatency(..)..will add it to the APO.

However, my problem is slightly different. I have tried to explain it clearer 
with an example below:

  *   I am maintaining my own internal input and output buffer(separate from 
the FLOAT32 buffer point passed to the APOProcess(..) function.
  *   At 44.1 kHz, I will get 448 samples per call to APOProcess().
  *   However, since my DSP algorithm only works with say exactly 768 samples, 
i will internally buffer the input received from APOProcess(...) until i have 
enough samples to process. I am also maintaining an internal output buffer 
where i will store the processed audio samples.
  *   I process audio samples when the samples count in my internal buffer 
exceeds 767 and store the processed audio stream in my internal output buffer.
  *   If i have enough samples in my internal output buffer(>=448),i will copy 
to APOProcess(..)'s output buffer otherwise i just set the first 448 samples in 
the APOProcess(..)'s output buffer with zero.
  *   So the very first call to APOProces(...), i will just buffer the input 
and send zeros as the output.
  *   However during the second call to APOProcess(..) i will have enough data 
to process and also send valid output(not zeros).
  *   Now, at the very end , my own internal input and output buffer might have 
some samples leftover and i was wondering if there are any workaround around 
this buffering problem without changing the DSP algorithm.



________________________________
From: soccerl@xxxxxxxxxxxxx<mailto:soccerl@xxxxxxxxxxxxx>
To: wdmaudiodev@xxxxxxxxxxxxx<mailto:wdmaudiodev@xxxxxxxxxxxxx>
Subject: [wdmaudiodev] Re: buffering samples in an APO
Date: Tue, 16 Dec 2014 00:13:35 +0000
Is this a 100% repro?  Which OS version is it?

An APO has no idea on when the stream will end. If an audio application stops 
its streaming before the end of stream, I am not sure whether APO could do 
anything with this situation.

From: wdmaudiodev-bounce@xxxxxxxxxxxxx<mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx> 
[mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx] On Behalf Of Matthew van Eerde
Sent: Monday, December 15, 2014 10:23 AM
To: wdmaudiodev@xxxxxxxxxxxxx<mailto:wdmaudiodev@xxxxxxxxxxxxx>
Subject: [wdmaudiodev] Re: buffering samples in an APO

Make sure you implement IAudioProcessingObject::GetLatency(...), and report the 
delay between your input and output.

If an app cares about its sound playing all the way through to the very last 
byte, the design is for the IAudioClient to pad silence onto the end of the 
stream, and keep pumping silence until IAudioClock indicates that the very last 
byte has made it all the way through the speaker.

From: wdmaudiodev-bounce@xxxxxxxxxxxxx<mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx> 
[mailto:wdmaudiodev-bounce@xxxxxxxxxxxxx] On Behalf Of Abhinav Singh
Sent: Monday, December 15, 2014 10:12 AM
To: wdmaudiodev@xxxxxxxxxxxxx<mailto:wdmaudiodev@xxxxxxxxxxxxx>
Subject: [wdmaudiodev] buffering samples in an APO

I am trying to integrate a DSP algorithm with my GFX APO which requires a fixed 
number of samples greater than the number of samples i get every time the Audio 
Engine calls the APOProcess() function of my APO. So i buffer the samples until 
i have the required number of samples and feed the audio engine zeros until i 
have valid output to feed to the engine. This works but there is a loss of few 
samples at the very end(some samples remain unprocessed in the input buffer 
while some are left over in the output buffer ). Is there any workaround to 
this problem?

Other related posts: