diff --git a/src/handmade.cpp b/src/handmade.cpp index c0b2748..5dc24d6 100644 --- a/src/handmade.cpp +++ b/src/handmade.cpp @@ -40,7 +40,7 @@ internal void renderWeirdGradient(GameOffscreenBuffer *buffer, int xOffset, int } } -internal void gameUpdateAndRender(GameMemory *memory, GameOffscreenBuffer *videoBuf, GameInput *input, GameSoundOutputBuffer *soundBuf) { +internal void gameUpdateAndRender(GameMemory *memory, GameOffscreenBuffer *videoBuf, GameInput *input) { Assert(sizeof(GameState) <= memory->permanentStorageSize); GameState *state = (GameState*)memory->permanentStorage; @@ -61,26 +61,29 @@ internal void gameUpdateAndRender(GameMemory *memory, GameOffscreenBuffer *video GameControllerInput *controllerInput = &input->controllers[controllerIndex]; if (controllerInput->isAnalog) { state->toneHz = 440 + (int)(128.0f*controllerInput->stickAvgX); - state->greenOffset -= (int)(4.0f*controllerInput->stickAvgX); - state->blueOffset += (int)(4.0f*controllerInput->stickAvgY); + state->greenOffset -= (int)(20.0f*controllerInput->stickAvgX); + state->blueOffset += (int)(20.0f*controllerInput->stickAvgY); } else { if (controllerInput->stickRight.endedDown) { state->toneHz = 440 + 128; - state->greenOffset -= 4; + state->greenOffset -= 10; } else if (controllerInput->stickLeft.endedDown) { state->toneHz = 440 - 128; - state->greenOffset += 4; + state->greenOffset += 10; } else { - state->toneHz = 440; + //state->toneHz = 440; } if (controllerInput->stickUp.endedDown) { - state->blueOffset += 4; + state->blueOffset += 10; } else if (controllerInput->stickDown.endedDown) { - state->blueOffset -= 4; + state->blueOffset -= 10; } } } - outputSineSound(soundBuf, state); renderWeirdGradient(videoBuf, state->greenOffset, state->blueOffset); } + +internal void gameGetSoundSamples(GameMemory *memory, GameSoundOutputBuffer *soundBuf) { + outputSineSound(soundBuf, (GameState*)memory->permanentStorage); +} diff --git a/src/handmade.h b/src/handmade.h index bde946a..bca6093 100644 --- a/src/handmade.h +++ b/src/handmade.h @@ -125,7 +125,8 @@ struct GameState { }; // === Game to platform services === -void gameUpdateAndRender(GameMemory *memory, GameInput *input, GameOffscreenBuffer *videoBuf, GameSoundOutputBuffer *soundBuf); +internal void gameUpdateAndRender(GameMemory *memory, GameInput *input, GameOffscreenBuffer *videoBuf); +internal void gameGetSoundSamples(GameMemory *memory, GameSoundOutputBuffer *soundBuf); // === Platform to game services === #if HANDMADE_INTERNAL diff --git a/src/win32_handmade.cpp b/src/win32_handmade.cpp index 0341c62..caeff4e 100644 --- a/src/win32_handmade.cpp +++ b/src/win32_handmade.cpp @@ -367,7 +367,6 @@ inline real32 win32GetSecondsElapsed(LARGE_INTEGER start, LARGE_INTEGER end) { // int monitorRefreshHz = 60; // int gameUpdateHz = monitorRefreshHz / 2; -#define framesOfAudioLatency 3 #define monitorRefreshHz 60 #define gameUpdateHz (monitorRefreshHz / 2) @@ -395,11 +394,16 @@ internal void win32DebugSyncDisplay(Win32OffscreenBuffer *screenBuffer, int mark int renderWidth = screenBuffer->width - 2 *padX; real32 pxPerSoundBufferEntry = (real32)renderWidth / (real32)soundOutput->secondaryBufferSize; + int pitch = screenBuffer->width*screenBuffer->bytesPerPixel; for (int markerIndex = 0; markerIndex < markerCount; markerIndex++) { Win32DebugTimeMarker *thisMarker = &markers[markerIndex]; - win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->playCursor, 0xFFFFFFFF); - win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->writeCursor, 0xFFFF0000); + real32 alpha = ((real32)(markerIndex + 1) / (real32)markerCount); + int x = padX + (int)(pxPerSoundBufferEntry * (real32)thisMarker->writeCursor); + uint8 *pixel = (uint8 *)screenBuffer->memory + top * pitch + x*screenBuffer->bytesPerPixel; + uint32 newPixel = (uint32)((real32)alpha * 0xFFFFFFFF + (1.0f - alpha) * (*(uint32 *)pixel)); + win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->playCursor, newPixel); + win32DrawSoundBufferMarker(screenBuffer, soundOutput, pxPerSoundBufferEntry, padX, top, bottom, thisMarker->writeCursor, 0x00FF0000); } } @@ -456,7 +460,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin soundOutput.runningSampleIndex = 0; soundOutput.bytesPerSample = sizeof(int16)*2; soundOutput.secondaryBufferSize = soundOutput.samplesPerSecond*soundOutput.bytesPerSample; - soundOutput.latencySampleCount = framesOfAudioLatency * (soundOutput.samplesPerSecond / gameUpdateHz); + soundOutput.safetyBytes = (soundOutput.samplesPerSecond * soundOutput.bytesPerSample / gameUpdateHz) / 3; int16 *samples = (int16*)VirtualAlloc(NULL, soundOutput.secondaryBufferSize, MEM_COMMIT, PAGE_READWRITE); @@ -478,11 +482,12 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin globalSecondaryBuffer->Play(0, 0, DSBPLAY_LOOPING); LARGE_INTEGER lastWorkCounter = win32GetWallClock(); + LARGE_INTEGER flipWallClock = win32GetWallClock(); int64 lastCycleCount = __rdtsc(); - bool32 soundIsValid = false; - DWORD lastPlayCursor = 0; + DWORD audioLatencyBytes = 0; + real32 audioLatencySeconds = 0; while (globalRunning) { GameControllerInput *oldKeyboardController = &oldInput->controllers[0]; @@ -561,33 +566,75 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin videoBuffer.memory = globalBackBuffer.memory; videoBuffer.width = globalBackBuffer.width; videoBuffer.height = globalBackBuffer.height; + gameUpdateAndRender(&gameMemory, &videoBuffer, newInput); + + DWORD playCursor = 0; + DWORD writeCursor = 0; + bool soundIsValid = false; + + if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) { + if (!soundIsValid) { + soundOutput.runningSampleIndex = writeCursor / soundOutput.bytesPerSample; + soundIsValid = true; + } + DWORD byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; + + // TODO(dledda): episode 20 ~2:00, something's wrong though, gotta sort it out. + + DWORD expectedSoundBytesPerFrame = (soundOutput.samplesPerSecond * soundOutput.bytesPerSample) / gameUpdateHz; + DWORD expectedFrameBoundaryByte = playCursor + expectedSoundBytesPerFrame; + DWORD safeWriteCursor = writeCursor; + if (safeWriteCursor < playCursor) { + safeWriteCursor += soundOutput.secondaryBufferSize; + } + Assert(safeWriteCursor >= playCursor); + safeWriteCursor += soundOutput.safetyBytes + 2000; + bool audioIsLowLatency = safeWriteCursor >= expectedFrameBoundaryByte; + + DWORD targetCursor = 0; + if (audioIsLowLatency) { + targetCursor = expectedFrameBoundaryByte + expectedSoundBytesPerFrame; + } else { + targetCursor = writeCursor + expectedSoundBytesPerFrame + soundOutput.safetyBytes; + } + targetCursor %= soundOutput.secondaryBufferSize; - // Sound test - DWORD byteToLock = 0; - DWORD targetCursor = lastPlayCursor; - DWORD bytesToWrite = 0; - if (soundIsValid) { - byteToLock = (soundOutput.runningSampleIndex*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; - targetCursor = (lastPlayCursor + soundOutput.latencySampleCount*soundOutput.bytesPerSample) % soundOutput.secondaryBufferSize; - if (byteToLock == targetCursor) { - bytesToWrite = 0; - } else if (byteToLock > targetCursor) { + DWORD bytesToWrite = 0; + if (byteToLock > targetCursor) { bytesToWrite = soundOutput.secondaryBufferSize - byteToLock + targetCursor; } else { bytesToWrite = targetCursor - byteToLock; } - } - GameSoundOutputBuffer soundBuffer = {}; - soundBuffer.samplesPerSecond = soundOutput.samplesPerSecond; - soundBuffer.sampleCount = bytesToWrite / soundOutput.bytesPerSample; - soundBuffer.samples = samples; + GameSoundOutputBuffer soundBuffer = {}; + soundBuffer.samplesPerSecond = soundOutput.samplesPerSecond; + soundBuffer.sampleCount = bytesToWrite / soundOutput.bytesPerSample; + soundBuffer.samples = samples; + gameGetSoundSamples(&gameMemory, &soundBuffer); +#if HANDMADE_INTERNAL + Assert(debugTimeMarkerIndex < ArrayCount(debugMarkers)); + + Win32DebugTimeMarker *marker = &debugMarkers[debugTimeMarkerIndex++]; + + DWORD unwrappedWriteCursor = writeCursor; + if (unwrappedWriteCursor < targetCursor) { + unwrappedWriteCursor += soundOutput.secondaryBufferSize; + } + audioLatencyBytes = unwrappedWriteCursor - playCursor; + audioLatencySeconds = (((real32)audioLatencyBytes / (real32)soundOutput.bytesPerSample) / (real32)soundOutput.samplesPerSecond); - gameUpdateAndRender(&gameMemory, &videoBuffer, newInput, &soundBuffer); - if (soundIsValid) { + if (debugTimeMarkerIndex >= ArrayCount(debugMarkers)) { + debugTimeMarkerIndex = 0; + } + + globalSecondaryBuffer->GetCurrentPosition(&marker->playCursor, &marker->writeCursor); +#endif win32FillSoundBuffer(&soundOutput, byteToLock, bytesToWrite, &soundBuffer); + } else { + soundIsValid = false; } + real32 secondsElapsedForFrame = win32GetSecondsElapsed(lastWorkCounter, win32GetWallClock()); if (secondsElapsedForFrame < targetSecondsPerFrame) { if (sleepIsGranular) { @@ -610,32 +657,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, PSTR commandLin #endif win32DrawBufferInWindow(&globalBackBuffer, window); - - DWORD playCursor = 0; - DWORD writeCursor = 0; - if (SUCCEEDED(globalSecondaryBuffer->GetCurrentPosition(&playCursor, &writeCursor))) { - lastPlayCursor = playCursor; - if (!soundIsValid) { - soundOutput.runningSampleIndex = writeCursor / soundOutput.bytesPerSample; - soundIsValid = true; - } - } else { - soundIsValid = false; - } - -#if HANDMADE_INTERNAL - { - Assert(debugTimeMarkerIndex < ArrayCount(debugMarkers)); - - Win32DebugTimeMarker *marker = &debugMarkers[debugTimeMarkerIndex++]; - - if (debugTimeMarkerIndex >= ArrayCount(debugMarkers)) { - debugTimeMarkerIndex = 0; - } - - globalSecondaryBuffer->GetCurrentPosition(&marker->playCursor, &marker->writeCursor); - } -#endif + flipWallClock = win32GetWallClock(); #if 0 real64 msElapsed = (real32)(1000.0f*secondsElapsedForFrame) / globalPerfCountFrequency; diff --git a/src/win32_handmade.h b/src/win32_handmade.h index c1f1250..977f7b5 100644 --- a/src/win32_handmade.h +++ b/src/win32_handmade.h @@ -20,7 +20,7 @@ struct Win32SoundOutput { int bytesPerSample; int secondaryBufferSize; real32 tSine; - int latencySampleCount; + int safetyBytes; }; struct Win32DebugTimeMarker {