Gyannea
January 27th, 2004, 06:02 AM
This note is for those of you struggling (like myself) with full duplex DirectX operations on the sound system. By "full duplex" I mean using DirectSoundCapture and DirectSound to receive (record) an incoming data stream and send (playback) the same or modified or even created data stream at exactly the same rate.
Whats the trick?
One would think that if you set the format (samplerate, bit depth, number of channels, etc.) of the capture buffer identical to that of the secondary playback buffer, everything would be okay. But it ain't!
I have a capture buffer which I read at a certain rate. I fill it with notifications so I read exactly that number of bytes by which the notifications are separated. Now I look at the difference between my readindex and the "safe" read index using the '->GetCurrentPosition()' method and compare the difference. That difference SHOULD be constant unless you do too much processing and you don't finish your work before the next notification is sent. If you do your work fast enough, it will be constant. In my case, it is! Good.
Now, what about the playback? Lets take that SAME data I just read and send it out the playback buffer! What a wonderful idea. Glad I thought of it. Now, since the playback buffer is formatted IDENTICAL to the capture buffer, wouldn't you think that everything would be nice and okay? It's not.
I do exactly the same test for the "safe" write position with my write pointer on the playback buffer. However, unlike the capture buffer, the "safe" pointer creeps upon my write pointer, eventually catching up and causing trash. For other formats (different sample rates), the reverse may be true, though I haven't experimented enough yet. It also appeared that if I picked a standard samplerate like 44100 Hz, the capture and send buffers kept in phase.
What is going on? Not sure. Here is my guess. The format of the secondary playback buffer and that of primary buffer (recall there is no primary capture buffer) are not the same! The primary buffer is set at a few preferred formats and your secondary buffer is converted from your format to the primary buffer format. If you are JUST playing back stuff, there is no problem. BUT if you are full duplexing, big problem!
THis guess comes from looking at the full duplex SDK example. There they create a primary buffer JUST for the purpose of setting its format. They never write to it. They write to a secondary buffer.
That means setting the cooperative level to that for a primary buffer. I find NO place where they reset the cooperative level to DSSCL_EXCLUSIVE for the secondary buffer. Okay, I try the same thing! I set the format of the primary buffer to that of the capture buffer, but I can't create the secondary buffer. It just won't get created, and the error message is NONE of those given. The secondary buffer object pointer returned is NULL, however, which means the buffer was not created...I just have no reason as to why it wasn't created.
So I am getting there. I will add the line of code resetting the cooperation level and see what happens.
What pisses me off is NOWHERE is this explained in the full duplex code given by Microsoft SDK. They just do it. No explanation for why they create the primary buffer. No explanation for why one needs to do a special set of actions to have "full duplex".
The problem is that there must still be more "special" things I have to do in order to accomplish my goals. Since my "full duplex" needs are not identical to those of the MS example, I can't just "copy" it. However, I do need the playback and capture rates to be IDENTICAL! None of this creeping crap.
A long post, I know. For those Direct-X sound people that made it this far, what do you know? Any insights would be GREATLY appreciated.
A frustrated Brian.
Whats the trick?
One would think that if you set the format (samplerate, bit depth, number of channels, etc.) of the capture buffer identical to that of the secondary playback buffer, everything would be okay. But it ain't!
I have a capture buffer which I read at a certain rate. I fill it with notifications so I read exactly that number of bytes by which the notifications are separated. Now I look at the difference between my readindex and the "safe" read index using the '->GetCurrentPosition()' method and compare the difference. That difference SHOULD be constant unless you do too much processing and you don't finish your work before the next notification is sent. If you do your work fast enough, it will be constant. In my case, it is! Good.
Now, what about the playback? Lets take that SAME data I just read and send it out the playback buffer! What a wonderful idea. Glad I thought of it. Now, since the playback buffer is formatted IDENTICAL to the capture buffer, wouldn't you think that everything would be nice and okay? It's not.
I do exactly the same test for the "safe" write position with my write pointer on the playback buffer. However, unlike the capture buffer, the "safe" pointer creeps upon my write pointer, eventually catching up and causing trash. For other formats (different sample rates), the reverse may be true, though I haven't experimented enough yet. It also appeared that if I picked a standard samplerate like 44100 Hz, the capture and send buffers kept in phase.
What is going on? Not sure. Here is my guess. The format of the secondary playback buffer and that of primary buffer (recall there is no primary capture buffer) are not the same! The primary buffer is set at a few preferred formats and your secondary buffer is converted from your format to the primary buffer format. If you are JUST playing back stuff, there is no problem. BUT if you are full duplexing, big problem!
THis guess comes from looking at the full duplex SDK example. There they create a primary buffer JUST for the purpose of setting its format. They never write to it. They write to a secondary buffer.
That means setting the cooperative level to that for a primary buffer. I find NO place where they reset the cooperative level to DSSCL_EXCLUSIVE for the secondary buffer. Okay, I try the same thing! I set the format of the primary buffer to that of the capture buffer, but I can't create the secondary buffer. It just won't get created, and the error message is NONE of those given. The secondary buffer object pointer returned is NULL, however, which means the buffer was not created...I just have no reason as to why it wasn't created.
So I am getting there. I will add the line of code resetting the cooperation level and see what happens.
What pisses me off is NOWHERE is this explained in the full duplex code given by Microsoft SDK. They just do it. No explanation for why they create the primary buffer. No explanation for why one needs to do a special set of actions to have "full duplex".
The problem is that there must still be more "special" things I have to do in order to accomplish my goals. Since my "full duplex" needs are not identical to those of the MS example, I can't just "copy" it. However, I do need the playback and capture rates to be IDENTICAL! None of this creeping crap.
A long post, I know. For those Direct-X sound people that made it this far, what do you know? Any insights would be GREATLY appreciated.
A frustrated Brian.