diff --git a/aegisub/src/audio_player_dsound2.cpp b/aegisub/src/audio_player_dsound2.cpp index 4499af4f5..cc51f46bb 100644 --- a/aegisub/src/audio_player_dsound2.cpp +++ b/aegisub/src/audio_player_dsound2.cpp @@ -236,10 +236,12 @@ void DirectSoundPlayer2Thread::Run() int64_t next_input_frame = 0; DWORD buffer_offset = 0; bool playback_should_be_running = false; + int current_latency = wanted_latency; + const DWORD wanted_latency_bytes = wanted_latency*waveFormat.nSamplesPerSec*provider->GetBytesPerSample()/1000; while (running) { - DWORD wait_result = WaitForMultipleObjects(sizeof(events_to_wait)/sizeof(HANDLE), events_to_wait, FALSE, wanted_latency); + DWORD wait_result = WaitForMultipleObjects(sizeof(events_to_wait)/sizeof(HANDLE), events_to_wait, FALSE, current_latency); switch (wait_result) { @@ -255,6 +257,9 @@ void DirectSoundPlayer2Thread::Run() void *buf; buffer_offset = 0; + if (FAILED(bfr->SetCurrentPosition(0))) + REPORT_ERROR("Could not reset playback buffer cursor before filling first buffer.") + HRESULT res = bfr->Lock(buffer_offset, 0, &buf, &buf_size, 0, 0, DSBLOCK_ENTIREBUFFER); while (FAILED(res)) // yes, while, so I can break out of it without a goto! { @@ -274,14 +279,30 @@ void DirectSoundPlayer2Thread::Run() REPORT_ERROR("Could not lock buffer for playback.") } - buffer_offset += FillAndUnlockBuffers(buf, buf_size, 0, 0, next_input_frame, bfr.obj); + // Clear the buffer in case we can't fill it completely + memset(buf, 0, buf_size); + + DWORD bytes_filled = FillAndUnlockBuffers(buf, buf_size, 0, 0, next_input_frame, bfr.obj); + buffer_offset += bytes_filled; if (buffer_offset >= bufSize) buffer_offset -= bufSize; if (FAILED(bfr->SetCurrentPosition(0))) REPORT_ERROR("Could not reset playback buffer cursor before playback.") - if (FAILED(bfr->Play(0, 0, DSBPLAY_LOOPING))) - REPORT_ERROR("Could not start looping playback.") + if (bytes_filled < wanted_latency_bytes) + { + // Very short playback length, do without streaming playback + current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*provider->GetBytesPerSample()); + if (FAILED(bfr->Play(0, 0, 0))) + REPORT_ERROR("Could not start single-buffer playback.") + } + else + { + // We filled the entire buffer so there's reason to do streaming playback + current_latency = wanted_latency; + if (FAILED(bfr->Play(0, 0, DSBPLAY_LOOPING))) + REPORT_ERROR("Could not start looping playback.") + } SetEvent(is_playing); playback_should_be_running = true; @@ -342,7 +363,8 @@ void DirectSoundPlayer2Thread::Run() if (!(status & DSBSTATUS_LOOPING)) { - // Not really what we expected... + // Not looping playback... + // hopefully we only triggered timeout after being done with the buffer bfr->Stop(); ResetEvent(is_playing); playback_should_be_running = false; @@ -380,9 +402,27 @@ void DirectSoundPlayer2Thread::Run() REPORT_ERROR("Could not lock buffer for filling.") } - buffer_offset += FillAndUnlockBuffers(buf1, buf1sz, buf2, buf2sz, next_input_frame, bfr.obj); + DWORD bytes_filled = FillAndUnlockBuffers(buf1, buf1sz, buf2, buf2sz, next_input_frame, bfr.obj); + buffer_offset += bytes_filled; if (buffer_offset >= bufSize) buffer_offset -= bufSize; + if (bytes_filled < 1024) + { + // Arbitrary low number, we filled in very little so better get back to filling in the rest with silence + // really fast... set latency to zero in this case. + current_latency = 0; + } + else if (bytes_filled < wanted_latency_bytes) + { + // Didn't fill as much as we wanted to, let's get back to filling sooner than normal + current_latency = (bytes_filled*1000) / (waveFormat.nSamplesPerSec*provider->GetBytesPerSample()); + } + else + { + // Plenty filled in, do regular latency + current_latency = wanted_latency; + } + break; }