From 966cf5e2629a89039edf3a68819ea98c663ba73f Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 20 Aug 2020 17:52:56 +0200 Subject: [PATCH] - fixed the sound in Exhumed's intro movie. Taking a cue from GDX to properly synchronize it because PCExhumed's approach looked broken by design. It had no frame rate sync and solely depended on the low level audio stream for it. --- source/blood/src/sound.cpp | 5 +- source/exhumed/src/movie.cpp | 123 ++++++++++++++++++---------------- source/exhumed/src/status.cpp | 1 - source/exhumed/src/view.cpp | 2 - 4 files changed, 70 insertions(+), 61 deletions(-) diff --git a/source/blood/src/sound.cpp b/source/blood/src/sound.cpp index ffe24b065..34cc797a8 100644 --- a/source/blood/src/sound.cpp +++ b/source/blood/src/sound.cpp @@ -100,11 +100,12 @@ static void S_AddBloodSFX(int lumpnum) S_sfx[sfxnum].bLoadRAW = true; S_sfx[sfxnum].LoopStart = LittleLong(sfx->loopStart); //S_sfx[sfxnum].Volume = sfx->relVol / 255.f; This cannot be done because this volume setting is optional. - S_sfx[sfxnum].UserData.Resize(2); + S_sfx[sfxnum].UserData.Resize(3); int* udata = (int*)S_sfx[sfxnum].UserData.Data(); udata[0] = sfx->pitch; udata[1] = sfx->pitchRange; - udata[2] = sfx->relVol; } + udata[2] = sfx->relVol; + } } void sndInit(void) diff --git a/source/exhumed/src/movie.cpp b/source/exhumed/src/movie.cpp index 1de4a4b8d..3c30c9167 100644 --- a/source/exhumed/src/movie.cpp +++ b/source/exhumed/src/movie.cpp @@ -26,6 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "sound.h" #include "v_2ddrawer.h" #include "animtexture.h" +#include "s_music.h" BEGIN_PS_NS @@ -43,15 +44,27 @@ uint8_t bankbuf[kSampleRate]; uint32_t bankptr = 0; uint32_t banktail = 0; -uint32_t lSoundBytesRead = 0; -uint32_t lSoundBytesUsed = 0; - uint8_t lh[32] = { 0 }; static uint8_t* CurFrame = NULL; bool bServedSample = false; palette_t moviepal[256]; +const int numAudioBlocks = 20; +int sfxnum = -1; + +struct AudioData +{ + int16_t samples[2205 * numAudioBlocks]; // must be a multiple of the stream buffer size + int nWrite; + int nRead; +}; + +SoundStream* stream; +AudioData audio; +bool bAudioStarted; + +SoundHandle shandle; int ReadFrame(FileReader &fp, uint8_t *palette) { @@ -94,32 +107,16 @@ int ReadFrame(FileReader &fp, uint8_t *palette) } case kFrameSound: { - Printf("Reading sound block size %d...\n", nSize); - - if (lSoundBytesRead - lSoundBytesUsed >= kSampleRate) + auto sfxx = soundEngine->GetSounds(); + auto buffer = fp.Read(nSize); + assert(buffer.Size() == 2205); + auto wbuffer = audio.samples + audio.nWrite * 2205; + for (int i = 0; i < 2205; i++) { - //DebugOut("ReadFrame() - Sound buffer full\n"); - fp.Seek(nSize, FileReader::SeekCur); + wbuffer[i] = (buffer[i] - 128) << 8; } - else - { - //mutex_lock(&mutex); - - int nRead = fp.Read((char*)bankbuf + bankptr, nSize); - - lSoundBytesRead += nRead; - bankptr += nSize; - - assert(nSize == nRead); - assert(bankptr <= kSampleRate); - - if (bankptr >= kSampleRate) { - bankptr -= kSampleRate; // loop back to start - } - - //mutex_unlock(&mutex); - } - + audio.nWrite++; + if (audio.nWrite >= numAudioBlocks) audio.nWrite = 0; continue; } case kFrameImage: @@ -183,6 +180,15 @@ static void ServeSample(const char** ptr, uint32_t* length) } #endif +static bool StreamCallbackFunc(SoundStream* stream, void* buff, int len, void* userdata) +{ + memcpy(buff, &audio.samples[audio.nRead], len); + audio.nRead += len / 2; + if (audio.nRead >= countof(audio.samples)) audio.nRead = 0; + return true; +} + + void PlayMovie(const char* fileName) { uint8_t palette[768]; @@ -190,7 +196,6 @@ void PlayMovie(const char* fileName) CurFrame = f.Data(); int bDoFade = true; - int hFx = -1; auto fp = fileSystem.OpenFileReader(fileName); if (!fp.isOpen()) { @@ -207,70 +212,76 @@ void PlayMovie(const char* fileName) // clear keys inputState.ClearAllInput(); + bAudioStarted = false; if (bDoFade) { StartFadeIn(); } + memset(audio.samples, 0, sizeof(audio.samples)); + audio.nWrite = 5; // play 5 blocks (i.e. half a second) of silence to get ahead of the stream. For this video it isn't necessary to sync it perfectly. - int angle = 1536; - int z = 0; + double angle = 1536; + double z = 0; AnimTextures animtex; animtex.SetSize(AnimTexture::Paletted, 200, 320); + auto sfxx = soundEngine->GetSounds(); + + if (!bAudioStarted) + { + // start audio playback + stream = S_CreateCustomStream(4410, 22050, 1, StreamCallbackFunc, nullptr); + bAudioStarted = true; + } + // Read a frame in first if (ReadFrame(fp, palette)) { - // start audio playback (fixme) -#if 0 - hFx = FX_StartDemandFeedPlayback(ServeSample, kSampleRate, 0, snd_fxvolume, snd_fxvolume, snd_fxvolume, FX_MUSIC_PRIORITY, fix16_one, -1); -#else - hFx = -1; -#endif - int fn = 0; - while (!inputState.keyBufferWaiting()) + int ototalclock = totalclock + 12; + int lastclock = totalclock; + while (true)// !inputState.keyBufferWaiting()) { HandleAsync(); - // audio is king for sync - if the backend doesn't need any more samples yet, - // don't process any more movie file data. - if (!bServedSample && hFx > 0) { - continue; - } - - bServedSample = false; - if (z < 65536) { // Zoom - normal zoom is 65536. - z += 2048; + z += 2048 * (totalclock - lastclock) / 8.; } + if (z > 65536) z = 65536; if (angle != 0) { - angle += 16; - if (angle == 2048) { + angle += 16. * (totalclock-lastclock) / 8. ; + if (angle >= 2048) { angle = 0; } } + lastclock = totalclock; // I have no idea why this needs double buffering now. fn ^= 1; animtex.SetFrame(palette, CurFrame); - rotatesprite(160 << 16, 100 << 16, z, angle+512, -1, 0, 1, RS_AUTO | RS_YFLIP, 0, 0, xdim - 1, ydim - 1, animtex.GetFrame()); + + rotatesprite(160 << 16, 100 << 16, int(z), int(angle+512), -1, 0, 1, RS_AUTO | RS_YFLIP, 0, 0, xdim - 1, ydim - 1, animtex.GetFrame()); + +#if 0 if (bDoFade) { bDoFade = DoFadeIn(); } +#endif videoNextPage(); - if (ReadFrame(fp, palette) == 0) { - break; + if (totalclock >= ototalclock) + { + ototalclock += 12; + if (ReadFrame(fp, palette) == 0) { + break; + } } } } - if (hFx > 0) { - //FX_StopSound(hFx); - } - + S_StopCustomStream(stream); if (inputState.keyBufferWaiting()) { inputState.keyGetChar(); } diff --git a/source/exhumed/src/status.cpp b/source/exhumed/src/status.cpp index 537298b60..57aab9431 100644 --- a/source/exhumed/src/status.cpp +++ b/source/exhumed/src/status.cpp @@ -722,7 +722,6 @@ private: if (chunkFlag & 2) flags |= DI_MIRRORY; DrawGraphic(tileGetTexture(tile), x, y, flags, 1, -1, -1, 1, 1); - //overwritesprite(x, y, tile, 0, nStat, 0); nFrameBase++; } } diff --git a/source/exhumed/src/view.cpp b/source/exhumed/src/view.cpp index bca8e575a..05a24ad00 100644 --- a/source/exhumed/src/view.cpp +++ b/source/exhumed/src/view.cpp @@ -470,8 +470,6 @@ void DrawView(int smoothRatio, bool sceneonly) } DrawMap(); - - DrawSnakeCamStatus(); } } }