mmozeiko
Although it sometimes drops few samples in startup at first or second frame (I'm guessing it is because Windows is figuring out or caching something in background).
To avoid start-up glitches with rendering streams, clients should not call Start until the audio engine has been initially loaded with data by calling the IAudioRenderClient::GetBuffer and IAudioRenderClient::ReleaseBuffer methods on the rendering interface.Filling the audio buffer at initialization is not the solution since it would introduce latency (since we can't overwrite previous samples). One solution is to start the buffer the first time we fill data in it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | int SamplesToWrite = 0; UINT32 SoundPaddingSize; if (SUCCEEDED(GlobalSoundClient->GetCurrentPadding(&SoundPaddingSize))) { int MaxSampleCount = (int)(SoundOutput.SecondaryBufferSize - SoundPaddingSize); SamplesToWrite = (int) SoundOutput.LatencySampleCount - SoundPaddingSize; if (SamplesToWrite < 0) { SamplesToWrite = 0; } assert(SamplesToWrite <= MaxSampleCount); } /* Instead of int SamplesToWrite = 0; UINT32 SoundPaddingSize; if (SUCCEEDED(GlobalSoundClient->GetCurrentPadding(&SoundPaddingSize))) { SamplesToWrite = (int)(SoundOutput.SecondaryBufferSize - SoundPaddingSize); if (SamplesToWrite > SoundOutput.LatencySampleCount) { SamplesToWrite = SoundOutput.LatencySampleCount; } } */ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | UINT32 pendingSamples; UINT32 samplesToFill = (uint32)(targetSecondsPerFrame * (real32)globalSoundOutput.samplesPerSecond); UINT32 skipSamples = 0; UINT64 playPosition, queryWallClock, flipPlayPosition, playPositionOnNextFrame; globalSoundOutputClock->GetPosition(&playPosition, &queryWallClock); globalSoundClient->GetCurrentPadding(&pendingSamples); flipPlayPosition = playPosition + (uint64)(secondsRemainingUntilFrameFlip * (real32)globalSoundOutput.samplesPerSecond); playPositionOnNextFrame = playPosition + (uint64)(targetSecondsPerFrame * (real32)globalSoundOutput.samplesPerSecond); if(flipPlayPosition > (playPosition + pendingSamples)) { if(firstFrameAudio) { skipSamples = (UINT32)(flipPlayPosition - (playPosition + pendingSamples)); } else { samplesToFill += (UINT32)((flipPlayPosition - (playPosition + pendingSamples))); } } else { UINT32 alreadyFilled = (UINT32)(((playPosition + pendingSamples) - flipPlayPosition)); if(samplesToFill > alreadyFilled) { samplesToFill -= alreadyFilled; } else { samplesToFill = 0; } } UINT64 currentFillLevel = (playPosition + pendingSamples + skipSamples + samplesToFill); UINT64 minimumFillLevel =playPositionOnNextFrame + (UINT64)(((real32)globalSoundOutput.latency/1000.0f) * (real32)(globalSoundOutput.samplesPerSecond)); if(minimumFillLevel > currentFillLevel) { samplesToFill += (UINT32)((minimumFillLevel - currentFillLevel)); } BYTE *wasapiMemory; if((samplesToFill + skipSamples) > 0) { HRESULT bufferAcquisition =globalSoundOutputClientDevice->GetBuffer((samplesToFill + skipSamples), &wasapiMemory); if(FAILED(bufferAcquisition)) { ASSERT(!"FAILED TO ACQUIRE BUFFER"); } gameSoundBuffer.memory = (int16*)((BYTE*)(wasapiMemory) + (skipSamples * globalSoundOutput.bytesPerSample)); gameSoundBuffer.samplesToOutput = samplesToFill; gameSoundBuffer.samplesPerSecond = globalSoundOutput.samplesPerSecond; ASSERT((gameSoundBuffer.samplesToOutput/globalSoundOutput.bytesPerSample) < globalSoundOutput.soundBufferSize); gameGetSoundSamples(&gameMemory, &gameSoundBuffer); HRESULT bufferRelease =globalSoundOutputClientDevice->ReleaseBuffer((samplesToFill + skipSamples), 0); if(FAILED(bufferRelease)) { ASSERT(!"FAILED TO RELEASE BUFFER"); } if(firstFrameAudio) { firstFrameAudio = false; playResult = globalSoundClient->Start(); if(FAILED(playResult)) { ASSERT(!"FAILED TO START PLAYING"); } } } #if HANDMADE_INTERNAL soundDebugMarkers[soundDebugCurrentMarker].outputPlayCursor = (DWORD)playPosition * globalSoundOutput.bytesPerSample; soundDebugMarkers[soundDebugCurrentMarker].outputWriteCursor = (DWORD)(playPosition + pendingSamples) * globalSoundOutput.bytesPerSample; soundDebugMarkers[soundDebugCurrentMarker].outputStartLocation = (DWORD)(playPosition + pendingSamples + skipSamples) * globalSoundOutput.bytesPerSample; soundDebugMarkers[soundDebugCurrentMarker].targetCursor = (DWORD)(playPosition + pendingSamples + skipSamples + samplesToFill) * globalSoundOutput.bytesPerSample; soundDebugMarkers[soundDebugCurrentMarker].flipPlayCursor = (DWORD)(flipPlayPosition * globalSoundOutput.bytesPerSample); sprintf_s(title, "ElapsedFrameTime:%.2fms, Pending:%.2fms, Sum:%.2fms",secondsElapsedFromFrameStartToAudioWriteBegin * 1000.0f, ((real32)pendingSamples/(real32)globalSoundOutput.samplesPerSecond) * 1000.0f, (secondsElapsedFromFrameStartToAudioWriteBegin * 1000.0f) + ( ((real32)pendingSamples/(real32)globalSoundOutput.samplesPerSecond) * 1000.0f)); //THE SUM IS ALWAYS ABOUT 30ms ON A LOW LATENCY CARD SetWindowText(windowHandle,title); #endif |