[DirectSound] Problems with stuttering sound / filling buffer

Started by
3 comments, last by mm_1234 13 years, 12 months ago
Hey, I have troubles playing signed 16 bit PCM audio data with DirectSound. It sounds as if the bytes in the buffer are mixed up and they sort of overlap or something... don't really know what's going on, maybe someone here has an idea. Here is the relevant method which refills the buffer continuously: ///////////////////////////////////////////////////////////////////////////// // some relevant class members: LPDIRECTSOUND m_handle; LPDIRECTSOUNDBUFFER m_primary_buffer; LPDIRECTSOUNDBUFFER m_secondary_buffer; QMemArray<short> m_buffer; // holds the mixed audio data in PCM format QMutex *m_mutex; // protects m_buffer unsigned m_last_section; // remembers the last fill section in the secondary buffer #define DEV_HW_BUFFER_SIZE 8000 // this is the WAVEFORMATEX I'm using: WAVEFORMATEX wfx; memset(&wfx, 0, sizeof(wfx)); wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = 1; wfx.nSamplesPerSec = 8000; wfx.wBitsPerSample = 16; wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; wfx.cbSize = 0; ///////////////////////////////////////////////////////////////////////////// // it is assured that the device is successfully opened and secondary buffer is // completely filled prior to entering this method, m_is_playing is set to false, // m_last_section is 0 void runThread() { ASSERT(m_is_open); m_is_running = true; // start playing if not already started if (!m_is_playing) { HRESULT dsrval = m_secondary_buffer->Play(0, 0, DSBPLAY_LOOPING); if (dsrval != DS_OK) ASSERT(false); m_is_playing = true; } while (true) { if (checkForStop() || !m_is_running) // check for thread's end return; LPVOID buffer1 = NULL; LPVOID buffer2 = NULL; DWORD buffer_size1 = 0; DWORD buffer_size2 = 0; DWORD pos = 0; //-------------------------------------------------------------------------- // determine play position and status of secondary buffer filling m_secondary_buffer->GetCurrentPosition(&pos, NULL); unsigned current_section = (pos < DEV_HW_BUFFER_SIZE / 2) ? 0 : 1; if (current_section == m_last_section) { Sleep(100); // sleep 100ms continue; } //-------------------------------------------------------------------------- // lock secondary buffer HRESULT dsrval = m_secondary_buffer->Lock(m_last_section * DEV_HW_BUFFER_SIZE / 2, DEV_HW_BUFFER_SIZE / 2, &buffer1, &buffer_size1, &buffer2, &buffer_size2, 0); if (dsrval != DS_OK) ASSERT(false); if (m_buffer.count() < buffer_size1 + buffer_size2) { // not enough data in m_buffer left, wait for refill from outside dsrval = m_secondary_buffer->Unlock(buffer1, buffer_size1, buffer2, buffer_size2); if (dsrval != DS_OK) ASSERT(false); continue; } //-------------------------------------------------------------------------- // fill it with data m_mutex->lock(); memcpy(buffer1, m_buffer.data(), buffer_size1 * sizeof(char)); if (buffer_size2 > 0) memcpy(buffer2, m_buffer.data() + buffer_size1 * sizeof(char), buffer_size2 * sizeof(char)); moveArrayData(&m_buffer, buffer_size1 + buffer_size2); // shifts buffer content // to the left m_mutex->unlock(); //-------------------------------------------------------------------------- // unlock secondary buffer dsrval = m_secondary_buffer->Unlock(buffer1, buffer_size1, buffer2, buffer_size2); if (dsrval != DS_OK) ASSERT(false); m_last_section = current_section; } } ///////////////////////////////////////////////////////////////////////////// greetings.. ..Markus
Advertisement
Though you split and update your ring buffer by 0.5 sec, maybe the wait is a bit large, though it's 0.1 ( < 0.5). Try a smaller value for the sleep.

Then from the code you sent it's not quite clear how you keep track of the already sent samples. Ok, I see you shift the contents of m_buffer, but how does that buffer know, how much was shifted ? (Edit: See P.S. below)

Then maybe your mutex disturbs. You did not include the code, but I guess you are using it to "lock" your m_buffer. Is your update of m_buffer costly, i.e. can a calculation become longer than 0.5 sec ?

Anyway, I'd suggest dumping all streamed samples to a file or so and later compare it with your input. Or even use a calculated one like a sawtooth which will let you more easily reconstruct leaks/overlaps.

Furthermore start with a simpler non-concurrent variant (without mutex) and start from there.

Hope that helps.


PS: Sorry, maybe I misinterpreted: Do you load the entire m_buffer once and for all and then just shift ? In that case you don't need to moveArrayData at all, just use another offset when updating the secondary buffer.
Dear unbird,

Thanks for your reply. The audio output thread is accessed through an interface because I already programmed audio output for Linux with ALSA which was a lot easier. I'm struggling with the M$ crap since almost a month and can't get it going though it's only a few lines of code. This means the whole thing already works and I don't have to look anywhere else except the OS-specific code. And since the device and all the buffers are created successfully (I checked all the HRESULT return values) I can limit the mistake down to this secondary buffer play/fill method.

m_buffer needs to be protected by a mutex because it is reloaded with new audio data once in a while. New data will be appended to the back of it while data which is ready for playing will be taken from the front. Thus every write operation to the buffer needs to be protected.

I don't think that speed/timing is the problem. The CPU usage while playing is very low. Refilling m_buffer takes below 1ms. I already tried to limit the sleep time (10ms) but the result is the same.

I already tried a larger buffer value for the secondary buffer (4 seconds) and still get that stuttering! Once I also initially loaded 10s of data into the secondary buffer and didn't try to refill it. Those 10s played *perfectly*.


What's worrying me is that the audio output sounds as if it is played too fast. But it's not the pitch, the output is jumpy and drops frames.
Are you creating a hardware buffer? If so, try software - hardware buffers have a lot of problems when it comes to streaming (Especially when it comes to using notifications).
OMG, I found my error.

memcpy(..) takes x 8 bit values and copies them into the secondary buffer but moveArrayData(..) removes x 16 bit values from m_buffer! I'm removeing twice of the size I'm actually playing!!! Removing only x/2 short values from m_buffer works and plays the sound without flaws.

That was the problem all the time. Sometimes it just helps talking to someone. :)

This topic is closed to new replies.

Advertisement