#include #include #include #include #include "CNFGRAPI.h" #include "SYSDEPNS.h" #include "UTIL/ENDIANAC.h" #include "UI/MYOSGLUE.h" #include "UI/COMOSGLU.h" #include "STRCONST.h" #include "OSGLUSD2.h" /* --- sound --- */ #if SoundEnabled tpSoundSamp TheSoundBuffer = nullpr; volatile static uint16_t ThePlayOffset; volatile static uint16_t TheFillOffset; volatile static uint16_t MinFilledSoundBuffs; #if dbglog_SoundBuffStats static uint16_t MaxFilledSoundBuffs; #endif static uint16_t TheWriteOffset; void Sound_Init0(void) { ThePlayOffset = 0; TheFillOffset = 0; TheWriteOffset = 0; } void Sound_Start0(void) { /* Reset variables */ MinFilledSoundBuffs = kSoundBuffers + 1; #if dbglog_SoundBuffStats MaxFilledSoundBuffs = 0; #endif } GLOBALOSGLUFUNC tpSoundSamp Sound_BeginWrite(uint16_t n, uint16_t *actL) { uint16_t ToFillLen = kAllBuffLen - (TheWriteOffset - ThePlayOffset); uint16_t WriteBuffContig = kOneBuffLen - (TheWriteOffset & kOneBuffMask); if (WriteBuffContig < n) { n = WriteBuffContig; } if (ToFillLen < n) { /* overwrite previous buffer */ #if dbglog_SoundStuff dbglog_writeln("sound buffer over flow"); #endif TheWriteOffset -= kOneBuffLen; } *actL = n; return TheSoundBuffer + (TheWriteOffset & kAllBuffMask); } #if 4 == kLn2SoundSampSz void ConvertSoundBlockToNative(tpSoundSamp p) { int i; for (i = kOneBuffLen; --i >= 0; ) { *p++ -= 0x8000; } } #else #define ConvertSoundBlockToNative(p) #endif void Sound_WroteABlock(void) { #if (4 == kLn2SoundSampSz) uint16_t PrevWriteOffset = TheWriteOffset - kOneBuffLen; tpSoundSamp p = TheSoundBuffer + (PrevWriteOffset & kAllBuffMask); #endif #if dbglog_SoundStuff dbglog_writeln("enter Sound_WroteABlock"); #endif ConvertSoundBlockToNative(p); TheFillOffset = TheWriteOffset; #if dbglog_SoundBuffStats { uint16_t ToPlayLen = TheFillOffset - ThePlayOffset; uint16_t ToPlayBuffs = ToPlayLen >> kLnOneBuffLen; if (ToPlayBuffs > MaxFilledSoundBuffs) { MaxFilledSoundBuffs = ToPlayBuffs; } } #endif } bool Sound_EndWrite0(uint16_t actL) { bool v; TheWriteOffset += actL; if (0 != (TheWriteOffset & kOneBuffMask)) { v = false; } else { /* just finished a block */ Sound_WroteABlock(); v = true; } return v; } void Sound_SecondNotify0(void) { if (MinFilledSoundBuffs <= kSoundBuffers) { if (MinFilledSoundBuffs > DesiredMinFilledSoundBuffs) { #if dbglog_SoundStuff dbglog_writeln("MinFilledSoundBuffs too high"); #endif IncrNextTime(); } else if (MinFilledSoundBuffs < DesiredMinFilledSoundBuffs) { #if dbglog_SoundStuff dbglog_writeln("MinFilledSoundBuffs too low"); #endif ++TrueEmulatedTime; } #if dbglog_SoundBuffStats dbglog_writelnNum("MinFilledSoundBuffs", MinFilledSoundBuffs); dbglog_writelnNum("MaxFilledSoundBuffs", MaxFilledSoundBuffs); MaxFilledSoundBuffs = 0; #endif MinFilledSoundBuffs = kSoundBuffers + 1; } } typedef uint16_t trSoundTemp; #define kCenterTempSound 0x8000 #define AudioStepVal 0x0040 #if 3 == kLn2SoundSampSz #define ConvertTempSoundSampleFromNative(v) ((v) << 8) #elif 4 == kLn2SoundSampSz #define ConvertTempSoundSampleFromNative(v) ((v) + kCenterSound) #else #error "unsupported kLn2SoundSampSz" #endif #if 3 == kLn2SoundSampSz #define ConvertTempSoundSampleToNative(v) ((v) >> 8) #elif 4 == kLn2SoundSampSz #define ConvertTempSoundSampleToNative(v) ((v) - kCenterSound) #else #error "unsupported kLn2SoundSampSz" #endif void SoundRampTo(trSoundTemp *last_val, trSoundTemp dst_val, tpSoundSamp *stream, int *len) { trSoundTemp diff; tpSoundSamp p = *stream; int n = *len; trSoundTemp v1 = *last_val; while ((v1 != dst_val) && (0 != n)) { if (v1 > dst_val) { diff = v1 - dst_val; if (diff > AudioStepVal) { v1 -= AudioStepVal; } else { v1 = dst_val; } } else { diff = dst_val - v1; if (diff > AudioStepVal) { v1 += AudioStepVal; } else { v1 = dst_val; } } --n; *p++ = ConvertTempSoundSampleToNative(v1); } *stream = p; *len = n; *last_val = v1; } struct SoundR { tpSoundSamp fTheSoundBuffer; volatile uint16_t (*fPlayOffset); volatile uint16_t (*fFillOffset); volatile uint16_t (*fMinFilledSoundBuffs); volatile trSoundTemp lastv; bool wantplaying; bool HaveStartedPlaying; }; typedef struct SoundR SoundR; static void audio_callback(void *udata, Uint8 *stream, int len) { uint16_t ToPlayLen; uint16_t FilledSoundBuffs; int i; SoundR *datp = (SoundR *)udata; tpSoundSamp CurSoundBuffer = datp->fTheSoundBuffer; uint16_t CurPlayOffset = *datp->fPlayOffset; trSoundTemp v0 = datp->lastv; trSoundTemp v1 = v0; tpSoundSamp dst = (tpSoundSamp)stream; #if kLn2SoundSampSz > 3 len >>= (kLn2SoundSampSz - 3); #endif #if dbglog_SoundStuff dbglog_writeln("Enter audio_callback"); dbglog_writelnNum("len", len); #endif label_retry: ToPlayLen = *datp->fFillOffset - CurPlayOffset; FilledSoundBuffs = ToPlayLen >> kLnOneBuffLen; if (! datp->wantplaying) { #if dbglog_SoundStuff dbglog_writeln("playing end transistion"); #endif SoundRampTo(&v1, kCenterTempSound, &dst, &len); ToPlayLen = 0; } else if (! datp->HaveStartedPlaying) { #if dbglog_SoundStuff dbglog_writeln("playing start block"); #endif if ((ToPlayLen >> kLnOneBuffLen) < 8) { ToPlayLen = 0; } else { tpSoundSamp p = datp->fTheSoundBuffer + (CurPlayOffset & kAllBuffMask); trSoundTemp v2 = ConvertTempSoundSampleFromNative(*p); #if dbglog_SoundStuff dbglog_writeln("have enough samples to start"); #endif SoundRampTo(&v1, v2, &dst, &len); if (v1 == v2) { #if dbglog_SoundStuff dbglog_writeln("finished start transition"); #endif datp->HaveStartedPlaying = true; } } } if (0 == len) { /* done */ if (FilledSoundBuffs < *datp->fMinFilledSoundBuffs) { *datp->fMinFilledSoundBuffs = FilledSoundBuffs; } } else if (0 == ToPlayLen) { #if dbglog_SoundStuff dbglog_writeln("under run"); #endif for (i = 0; i < len; ++i) { *dst++ = ConvertTempSoundSampleToNative(v1); } *datp->fMinFilledSoundBuffs = 0; } else { uint16_t PlayBuffContig = kAllBuffLen - (CurPlayOffset & kAllBuffMask); tpSoundSamp p = CurSoundBuffer + (CurPlayOffset & kAllBuffMask); if (ToPlayLen > PlayBuffContig) { ToPlayLen = PlayBuffContig; } if (ToPlayLen > len) { ToPlayLen = len; } for (i = 0; i < ToPlayLen; ++i) { *dst++ = *p++; } v1 = ConvertTempSoundSampleFromNative(p[-1]); CurPlayOffset += ToPlayLen; len -= ToPlayLen; *datp->fPlayOffset = CurPlayOffset; goto label_retry; } datp->lastv = v1; } static SoundR cur_audio; static bool HaveSoundOut = false; void Sound_Stop(void) { #if dbglog_SoundStuff dbglog_writeln("enter Sound_Stop"); #endif if (cur_audio.wantplaying && HaveSoundOut) { uint16_t retry_limit = 50; /* half of a second */ cur_audio.wantplaying = false; label_retry: if (kCenterTempSound == cur_audio.lastv) { #if dbglog_SoundStuff dbglog_writeln("reached kCenterTempSound"); #endif /* done */ } else if (0 == --retry_limit) { #if dbglog_SoundStuff dbglog_writeln("retry limit reached"); #endif /* done */ } else { /* give time back, particularly important if got here on a suspend event. */ #if dbglog_SoundStuff dbglog_writeln("busy, so sleep"); #endif (void) SDL_Delay(10); goto label_retry; } SDL_PauseAudio(1); } #if dbglog_SoundStuff dbglog_writeln("leave Sound_Stop"); #endif } void Sound_Start(void) { if ((! cur_audio.wantplaying) && HaveSoundOut) { Sound_Start0(); cur_audio.lastv = kCenterTempSound; cur_audio.HaveStartedPlaying = false; cur_audio.wantplaying = true; SDL_PauseAudio(0); } } void Sound_UnInit(void) { if (HaveSoundOut) { SDL_CloseAudio(); } } #define SOUND_SAMPLERATE 22255 /* = round(7833600 * 2 / 704) */ bool Sound_Init(void) { SDL_AudioSpec desired; Sound_Init0(); cur_audio.fTheSoundBuffer = TheSoundBuffer; cur_audio.fPlayOffset = &ThePlayOffset; cur_audio.fFillOffset = &TheFillOffset; cur_audio.fMinFilledSoundBuffs = &MinFilledSoundBuffs; cur_audio.wantplaying = false; desired.freq = SOUND_SAMPLERATE; #if 3 == kLn2SoundSampSz desired.format = AUDIO_U8; #elif 4 == kLn2SoundSampSz desired.format = AUDIO_S16SYS; #else #error "unsupported audio format" #endif desired.channels = 1; desired.samples = 1024; desired.callback = audio_callback; desired.userdata = (void *)&cur_audio; /* Open the audio device */ if (SDL_OpenAudio(&desired, NULL) < 0) { fprintf(stderr, "Couldn't open audio: %s\n", SDL_GetError()); } else { HaveSoundOut = true; Sound_Start(); /* This should be taken care of by LeaveSpeedStopped, but since takes a while to get going properly, start early. */ } return true; /* keep going, even if no sound */ } GLOBALOSGLUPROC Sound_EndWrite(uint16_t actL) { if (Sound_EndWrite0(actL)) { } } void Sound_SecondNotify(void) { if (HaveSoundOut) { Sound_SecondNotify0(); } } #endif