// for finding memory leaks in debug mode with Visual Studio#if defined _DEBUG && defined _MSC_VER#include <crtdbg.h>#endif #include <stdio.h>#include <stdint.h>#include <math.h>#include "ft2_header.h"#include "ft2_config.h"#include "scopes/ft2_scopes.h"#include "ft2_video.h"#include "ft2_gui.h"#include "ft2_midi.h"#include "ft2_wav_renderer.h"#include "ft2_tables.h"#include "ft2_structs.h"#include "mixer/ft2_mix.h"#include "mixer/ft2_center_mix.h"#include "mixer/ft2_silence_mix.h" // hide POSIX warnings#ifdef _MSC_VER#pragma warning(disable: 4996)#endif #define INITIAL_DITHER_SEED 0x12345000 static int32_t smpShiftValue;static uint32_t oldAudioFreq, tickTimeLenInt, randSeed = INITIAL_DITHER_SEED;static uint64_t tickTimeLenFrac;static double dAudioNormalizeMul, dSqrtPanningTable[256+1], dPrngStateL, dPrngStateR;static voice_t voice[MAX_CHANNELS * 2]; // globalizedaudio_t audio;pattSyncData_t *pattSyncEntry;chSyncData_t *chSyncEntry;chSync_t chSync;pattSync_t pattSync;volatile bool pattQueueClearing, chQueueClearing; void resetCachedMixerVars(void){ channel_t *ch = channel; for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++) ch->oldFinalPeriod = -1; voice_t *v = voice; for (int32_t i = 0; i < MAX_CHANNELS*2; i++, v++) v->oldDelta = 0;} void stopVoice(int32_t i){ voice_t *v; v = &voice[i]; memset(v, 0, sizeof (voice_t)); v->panning = 128; // clear "fade out" voice too v = &voice[MAX_CHANNELS + i]; memset(v, 0, sizeof (voice_t)); v->panning = 128;} bool setNewAudioSettings(void) // only call this from the main input/video thread{ pauseAudio(); if (!setupAudio(CONFIG_HIDE_ERRORS)) { // set back old known working settings config.audioFreq = audio.lastWorkingAudioFreq; config.specialFlags &= ~(BITDEPTH_16 + BITDEPTH_32 + BUFFSIZE_512 + BUFFSIZE_1024 + BUFFSIZE_2048); config.specialFlags |= audio.lastWorkingAudioBits; if (audio.lastWorkingAudioDeviceName != NULL) { if (audio.currOutputDevice != NULL) { free(audio.currOutputDevice); audio.currOutputDevice = NULL; } audio.currOutputDevice = strdup(audio.lastWorkingAudioDeviceName); } // also update config audio radio buttons if we're on that screen at the moment if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO) setConfigAudioRadioButtonStates(); // if it didn't work to use the old settings again, then something is seriously wrong... if (!setupAudio(CONFIG_HIDE_ERRORS)) okBox(0, "System message", "Couldn't find a working audio mode... You'll get no sound / replayer timer!"); resumeAudio(); return false; } resumeAudio(); setWavRenderFrequency(audio.freq); setWavRenderBitDepth((config.specialFlags & BITDEPTH_32) ? 32 : 16); return true;} // amp = 1..32, masterVol = 0..256void setAudioAmp(int16_t amp, int16_t masterVol, bool bitDepth32Flag){ amp = CLAMP(amp, 1, 32); masterVol = CLAMP(masterVol, 0, 256); double dAmp = (amp * masterVol) / (32.0 * 256.0); if (!bitDepth32Flag) dAmp *= 32768.0; dAudioNormalizeMul = dAmp;} void decreaseMasterVol(void){ if (config.masterVol >= 16) config.masterVol -= 16; else config.masterVol = 0; setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); // if Config -> Audio is open, update master volume scrollbar if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO) drawScrollBar(SB_MASTERVOL_SCROLL);} void increaseMasterVol(void){ if (config.masterVol < (256-16)) config.masterVol += 16; else config.masterVol = 256; setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); // if Config -> Audio is open, update master volume scrollbar if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO) drawScrollBar(SB_MASTERVOL_SCROLL);} void setNewAudioFreq(uint32_t freq) // for song-to-WAV rendering{ if (freq == 0) return; oldAudioFreq = audio.freq; audio.freq = freq; const bool mustRecalcTables = audio.freq != oldAudioFreq; if (mustRecalcTables) calcReplayerVars(audio.freq);} void setBackOldAudioFreq(void) // for song-to-WAV rendering{ const bool mustRecalcTables = audio.freq != oldAudioFreq; audio.freq = oldAudioFreq; if (mustRecalcTables) calcReplayerVars(audio.freq);} void setMixerBPM(int32_t bpm){ if (bpm < MIN_BPM || bpm > MAX_BPM) return; int32_t i = bpm - MIN_BPM; audio.samplesPerTickInt = audio.samplesPerTickIntTab[i]; audio.samplesPerTickFrac = audio.samplesPerTickFracTab[i]; // for audio/video sync timestamp tickTimeLenInt = audio.tickTimeIntTab[i]; tickTimeLenFrac = audio.tickTimeFracTab[i];} void audioSetVolRamp(bool volRamp){ lockMixerCallback(); audio.volumeRampingFlag = volRamp; unlockMixerCallback();} void audioSetInterpolationType(uint8_t interpolationType){ lockMixerCallback(); audio.interpolationType = interpolationType; // set sinc polyphase LUT pointers if (config.interpolation == INTERPOLATION_SINC8) { fKaiserSinc = fKaiserSinc_8; fDownSample1 = fDownSample1_8; fDownSample2 = fDownSample2_8; } else if (config.interpolation == INTERPOLATION_SINC16) { fKaiserSinc = fKaiserSinc_16; fDownSample1 = fDownSample1_16; fDownSample2 = fDownSample2_16; } unlockMixerCallback();} void calcPanningTable(void){ // same formula as FT2's panning table (with 0.0 .. 1.0 scale) for (int32_t i = 0; i <= 256; i++) dSqrtPanningTable[i] = sqrt(i / 256.0);} static void voiceUpdateVolumes(int32_t i, uint8_t status){ voice_t *v = &voice[i]; v->fTargetVolumeL = (float)(v->dVolume * dSqrtPanningTable[256-v->panning]); v->fTargetVolumeR = (float)(v->dVolume * dSqrtPanningTable[ v->panning]); if (!audio.volumeRampingFlag) { // volume ramping is disabled, set volume directly v->fCurrVolumeL = v->fTargetVolumeL; v->fCurrVolumeR = v->fTargetVolumeR; v->volumeRampLength = 0; return; } // now we need to handle volume ramping const bool voiceSampleTrigger = !!(status & IS_Trigger); if (voiceSampleTrigger) { // sample is about to start, ramp out/in at the same time if (v->fCurrVolumeL > 0.0f || v->fCurrVolumeR > 0.0f) { // setup fadeout voice voice_t *f = &voice[MAX_CHANNELS+i]; *f = *v; // store current voice in respective fadeout ramp voice const float fVolumeLDiff = 0.0f - f->fCurrVolumeL; const float fVolumeRDiff = 0.0f - f->fCurrVolumeR; f->volumeRampLength = audio.quickVolRampSamples; // 5ms const float fVolumeRampLength = (float)(int32_t)f->volumeRampLength; f->fVolumeLDelta = fVolumeLDiff / fVolumeRampLength; f->fVolumeRDelta = fVolumeRDiff / fVolumeRampLength; f->isFadeOutVoice = true; } // make current voice fade in from zero when it starts v->fCurrVolumeL = v->fCurrVolumeR = 0.0f; } if (!voiceSampleTrigger && v->fTargetVolumeL == v->fCurrVolumeL && v->fTargetVolumeR == v->fCurrVolumeR) { v->volumeRampLength = 0; // no ramp needed for now } else { const float fVolumeLDiff = v->fTargetVolumeL - v->fCurrVolumeL; const float fVolumeRDiff = v->fTargetVolumeR - v->fCurrVolumeR; // IS_QuickVol = 5ms, otherwise the duration of a tick v->volumeRampLength = (status & IS_QuickVol) ? audio.quickVolRampSamples : audio.samplesPerTickInt; const float fVolumeRampLength = (float)(int32_t)v->volumeRampLength; v->fVolumeLDelta = fVolumeLDiff / fVolumeRampLength; v->fVolumeRDelta = fVolumeRDiff / fVolumeRampLength; }} static void voiceTrigger(int32_t ch, sample_t *s, int32_t position){ voice_t *v = &voice[ch]; int32_t length = s->length; int32_t loopStart = s->loopStart; int32_t loopLength = s->loopLength; int32_t loopEnd = s->loopStart + s->loopLength; uint8_t loopType = GET_LOOPTYPE(s->flags); bool sample16Bit = !!(s->flags & SAMPLE_16BIT); if (s->dataPtr == NULL || length < 1) { v->active = false; // shut down voice (illegal parameters) return; } if (loopLength < 1) // disable loop if loopLength is below 1 loopType = 0; if (sample16Bit) { v->base16 = (const int16_t *)s->dataPtr; v->revBase16 = &v->base16[loopStart + loopEnd]; // for pingpong loops v->leftEdgeTaps16 = s->leftEdgeTapSamples16 + SINC_MAX_LEFT_TAPS; } else { v->base8 = s->dataPtr; v->revBase8 = &v->base8[loopStart + loopEnd]; // for pingpong loops v->leftEdgeTaps8 = s->leftEdgeTapSamples8 + SINC_MAX_LEFT_TAPS; } v->hasLooped = false; // for sinc interpolation special case v->samplingBackwards = false; v->loopType = loopType; v->sampleEnd = (loopType == LOOP_OFF) ? length : loopEnd; v->loopStart = loopStart; v->loopLength = loopLength; v->position = position; v->positionFrac = 0; // if position overflows, shut down voice (f.ex. through 9xx command) if (v->position >= v->sampleEnd) { v->active = false; return; } v->mixFuncOffset = ((int32_t)sample16Bit * 12) + (audio.interpolationType * 3) + loopType; v->active = true;} void resetRampVolumes(void){ voice_t *v = voice; for (int32_t i = 0; i < song.numChannels; i++, v++) { v->fCurrVolumeL = v->fTargetVolumeL; v->fCurrVolumeR = v->fTargetVolumeR; v->volumeRampLength = 0; }} void updateVoices(void){ channel_t *ch = channel; voice_t *v = voice; for (int32_t i = 0; i < song.numChannels; i++, ch++, v++) { const uint8_t status = ch->tmpStatus = ch->status; // (tmpStatus is used for audio/video sync queue) if (status == 0) continue; ch->status = 0; if (status & IS_Vol) { v->dVolume = ch->dFinalVol; // set scope volume const int32_t scopeVolume = (int32_t)((SCOPE_HEIGHT * ch->dFinalVol) + 0.5); // rounded v->scopeVolume = (uint8_t)scopeVolume; } if (status & IS_Pan) v->panning = ch->finalPan; if (status & (IS_Vol + IS_Pan)) voiceUpdateVolumes(i, status); if (status & IS_Period) { // use cached values when possible if (ch->finalPeriod != ch->oldFinalPeriod) { ch->oldFinalPeriod = ch->finalPeriod; if (ch->finalPeriod == 0) // in FT2, period 0 -> delta 0 { v->scopeDelta = 0; v->oldDelta = 0; v->fSincLUT = fKaiserSinc; } else { const double dHz = dPeriod2Hz(ch->finalPeriod); const uintCPUWord_t delta = v->oldDelta = (intCPUWord_t)((dHz * audio.dHz2MixDeltaMul) + 0.5); // Hz -> fixed-point delta (rounded) // decide which polyphase sinc LUT to use according to resampling ratio if (delta <= (uintCPUWord_t)(1.1875 * MIXER_FRAC_SCALE)) v->fSincLUT = fKaiserSinc; else if (delta <= (uintCPUWord_t)(1.5 * MIXER_FRAC_SCALE)) v->fSincLUT = fDownSample1; else v->fSincLUT = fDownSample2; // set scope delta const double dHz2ScopeDeltaMul = SCOPE_FRAC_SCALE / (double)SCOPE_HZ; v->scopeDelta = (intCPUWord_t)((dHz * dHz2ScopeDeltaMul) + 0.5); // Hz -> fixed-point delta (rounded) } } v->delta = v->oldDelta; } if (status & IS_Trigger) voiceTrigger(i, ch->smpPtr, ch->smpStartPos); }} void resetAudioDither(void){ randSeed = INITIAL_DITHER_SEED; dPrngStateL = 0.0; dPrngStateR = 0.0;} static inline int32_t random32(void){ // LCG 32-bit random randSeed *= 134775813; randSeed++; return (int32_t)randSeed;} static void sendSamples16BitDitherStereo(uint8_t *stream, uint32_t sampleBlockLength){ int32_t out32; double dOut, dPrng; int16_t *streamPointer16 = (int16_t *)stream; for (uint32_t i = 0; i < sampleBlockLength; i++) { // left channel - 1-bit triangular dithering dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5 dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul; dOut = (dOut + dPrng) - dPrngStateL; dPrngStateL = dPrng; out32 = (int32_t)dOut; CLAMP16(out32); *streamPointer16++ = (int16_t)out32; // right channel - 1-bit triangular dithering dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5 dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul; dOut = (dOut + dPrng) - dPrngStateR; dPrngStateR = dPrng; out32 = (int32_t)dOut; CLAMP16(out32); *streamPointer16++ = (int16_t)out32; // clear what we read from the mixing buffer audio.fMixBufferL[i] = 0.0f; audio.fMixBufferR[i] = 0.0f; }} static void sendSamples32BitStereo(uint8_t *stream, uint32_t sampleBlockLength){ double dOut; float *fStreamPointer32 = (float *)stream; for (uint32_t i = 0; i < sampleBlockLength; i++) { // left channel dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul; dOut = CLAMP(dOut, -1.0, 1.0); *fStreamPointer32++ = (float)dOut; // right channel dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul; dOut = CLAMP(dOut, -1.0, 1.0); *fStreamPointer32++ = (float)dOut; // clear what we read from the mixing buffer audio.fMixBufferL[i] = 0.0f; audio.fMixBufferR[i] = 0.0f; }} static void doChannelMixing(int32_t bufferPosition, int32_t samplesToMix){ voice_t *v = voice; // normal voices voice_t *r = &voice[MAX_CHANNELS]; // volume ramp fadeout-voices for (int32_t i = 0; i < song.numChannels; i++, v++, r++) {Lines 1–500 of 1126. Load more
// for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER #include <crtdbg.h> #endif #include <stdio.h> #include <stdint.h> #include <math.h> #include "ft2_header.h" #include "ft2_config.h" #include "scopes/ft2_scopes.h" #include "ft2_video.h" #include "ft2_gui.h" #include "ft2_midi.h" #include "ft2_wav_renderer.h" #include "ft2_tables.h" #include "ft2_structs.h" #include "mixer/ft2_mix.h" #include "mixer/ft2_center_mix.h" #include "mixer/ft2_silence_mix.h" // hide POSIX warnings #ifdef _MSC_VER #pragma warning(disable: 4996) #endif #define INITIAL_DITHER_SEED 0x12345000 static int32_t smpShiftValue; static uint32_t oldAudioFreq, tickTimeLenInt, randSeed = INITIAL_DITHER_SEED; static uint64_t tickTimeLenFrac; static double dAudioNormalizeMul, dSqrtPanningTable[256+1], dPrngStateL, dPrngStateR; static voice_t voice[MAX_CHANNELS * 2]; // globalized audio_t audio; pattSyncData_t *pattSyncEntry; chSyncData_t *chSyncEntry; chSync_t chSync; pattSync_t pattSync; volatile bool pattQueueClearing, chQueueClearing; void resetCachedMixerVars(void) { channel_t *ch = channel; for (int32_t i = 0; i < MAX_CHANNELS; i++, ch++) ch->oldFinalPeriod = -1; voice_t *v = voice; for (int32_t i = 0; i < MAX_CHANNELS*2; i++, v++) v->oldDelta = 0; } void stopVoice(int32_t i) { voice_t *v; v = &voice[i]; memset(v, 0, sizeof (voice_t)); v->panning = 128; // clear "fade out" voice too v = &voice[MAX_CHANNELS + i]; memset(v, 0, sizeof (voice_t)); v->panning = 128; } bool setNewAudioSettings(void) // only call this from the main input/video thread { pauseAudio(); if (!setupAudio(CONFIG_HIDE_ERRORS)) { // set back old known working settings config.audioFreq = audio.lastWorkingAudioFreq; config.specialFlags &= ~(BITDEPTH_16 + BITDEPTH_32 + BUFFSIZE_512 + BUFFSIZE_1024 + BUFFSIZE_2048); config.specialFlags |= audio.lastWorkingAudioBits; if (audio.lastWorkingAudioDeviceName != NULL) { if (audio.currOutputDevice != NULL) { free(audio.currOutputDevice); audio.currOutputDevice = NULL; } audio.currOutputDevice = strdup(audio.lastWorkingAudioDeviceName); } // also update config audio radio buttons if we're on that screen at the moment if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO) setConfigAudioRadioButtonStates(); // if it didn't work to use the old settings again, then something is seriously wrong... if (!setupAudio(CONFIG_HIDE_ERRORS)) okBox(0, "System message", "Couldn't find a working audio mode... You'll get no sound / replayer timer!"); resumeAudio(); return false; } resumeAudio(); setWavRenderFrequency(audio.freq); setWavRenderBitDepth((config.specialFlags & BITDEPTH_32) ? 32 : 16); return true; } // amp = 1..32, masterVol = 0..256 void setAudioAmp(int16_t amp, int16_t masterVol, bool bitDepth32Flag) { amp = CLAMP(amp, 1, 32); masterVol = CLAMP(masterVol, 0, 256); double dAmp = (amp * masterVol) / (32.0 * 256.0); if (!bitDepth32Flag) dAmp *= 32768.0; dAudioNormalizeMul = dAmp; } void decreaseMasterVol(void) { if (config.masterVol >= 16) config.masterVol -= 16; else config.masterVol = 0; setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); // if Config -> Audio is open, update master volume scrollbar if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO) drawScrollBar(SB_MASTERVOL_SCROLL); } void increaseMasterVol(void) { if (config.masterVol < (256-16)) config.masterVol += 16; else config.masterVol = 256; setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32)); // if Config -> Audio is open, update master volume scrollbar if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO) drawScrollBar(SB_MASTERVOL_SCROLL); } void setNewAudioFreq(uint32_t freq) // for song-to-WAV rendering { if (freq == 0) return; oldAudioFreq = audio.freq; audio.freq = freq; const bool mustRecalcTables = audio.freq != oldAudioFreq; if (mustRecalcTables) calcReplayerVars(audio.freq); } void setBackOldAudioFreq(void) // for song-to-WAV rendering { const bool mustRecalcTables = audio.freq != oldAudioFreq; audio.freq = oldAudioFreq; if (mustRecalcTables) calcReplayerVars(audio.freq); } void setMixerBPM(int32_t bpm) { if (bpm < MIN_BPM || bpm > MAX_BPM) return; int32_t i = bpm - MIN_BPM; audio.samplesPerTickInt = audio.samplesPerTickIntTab[i]; audio.samplesPerTickFrac = audio.samplesPerTickFracTab[i]; // for audio/video sync timestamp tickTimeLenInt = audio.tickTimeIntTab[i]; tickTimeLenFrac = audio.tickTimeFracTab[i]; } void audioSetVolRamp(bool volRamp) { lockMixerCallback(); audio.volumeRampingFlag = volRamp; unlockMixerCallback(); } void audioSetInterpolationType(uint8_t interpolationType) { lockMixerCallback(); audio.interpolationType = interpolationType; // set sinc polyphase LUT pointers if (config.interpolation == INTERPOLATION_SINC8) { fKaiserSinc = fKaiserSinc_8; fDownSample1 = fDownSample1_8; fDownSample2 = fDownSample2_8; } else if (config.interpolation == INTERPOLATION_SINC16) { fKaiserSinc = fKaiserSinc_16; fDownSample1 = fDownSample1_16; fDownSample2 = fDownSample2_16; } unlockMixerCallback(); } void calcPanningTable(void) { // same formula as FT2's panning table (with 0.0 .. 1.0 scale) for (int32_t i = 0; i <= 256; i++) dSqrtPanningTable[i] = sqrt(i / 256.0); } static void voiceUpdateVolumes(int32_t i, uint8_t status) { voice_t *v = &voice[i]; v->fTargetVolumeL = (float)(v->dVolume * dSqrtPanningTable[256-v->panning]); v->fTargetVolumeR = (float)(v->dVolume * dSqrtPanningTable[ v->panning]); if (!audio.volumeRampingFlag) { // volume ramping is disabled, set volume directly v->fCurrVolumeL = v->fTargetVolumeL; v->fCurrVolumeR = v->fTargetVolumeR; v->volumeRampLength = 0; return; } // now we need to handle volume ramping const bool voiceSampleTrigger = !!(status & IS_Trigger); if (voiceSampleTrigger) { // sample is about to start, ramp out/in at the same time if (v->fCurrVolumeL > 0.0f || v->fCurrVolumeR > 0.0f) { // setup fadeout voice voice_t *f = &voice[MAX_CHANNELS+i]; *f = *v; // store current voice in respective fadeout ramp voice const float fVolumeLDiff = 0.0f - f->fCurrVolumeL; const float fVolumeRDiff = 0.0f - f->fCurrVolumeR; f->volumeRampLength = audio.quickVolRampSamples; // 5ms const float fVolumeRampLength = (float)(int32_t)f->volumeRampLength; f->fVolumeLDelta = fVolumeLDiff / fVolumeRampLength; f->fVolumeRDelta = fVolumeRDiff / fVolumeRampLength; f->isFadeOutVoice = true; } // make current voice fade in from zero when it starts v->fCurrVolumeL = v->fCurrVolumeR = 0.0f; } if (!voiceSampleTrigger && v->fTargetVolumeL == v->fCurrVolumeL && v->fTargetVolumeR == v->fCurrVolumeR) { v->volumeRampLength = 0; // no ramp needed for now } else { const float fVolumeLDiff = v->fTargetVolumeL - v->fCurrVolumeL; const float fVolumeRDiff = v->fTargetVolumeR - v->fCurrVolumeR; // IS_QuickVol = 5ms, otherwise the duration of a tick v->volumeRampLength = (status & IS_QuickVol) ? audio.quickVolRampSamples : audio.samplesPerTickInt; const float fVolumeRampLength = (float)(int32_t)v->volumeRampLength; v->fVolumeLDelta = fVolumeLDiff / fVolumeRampLength; v->fVolumeRDelta = fVolumeRDiff / fVolumeRampLength; } } static void voiceTrigger(int32_t ch, sample_t *s, int32_t position) { voice_t *v = &voice[ch]; int32_t length = s->length; int32_t loopStart = s->loopStart; int32_t loopLength = s->loopLength; int32_t loopEnd = s->loopStart + s->loopLength; uint8_t loopType = GET_LOOPTYPE(s->flags); bool sample16Bit = !!(s->flags & SAMPLE_16BIT); if (s->dataPtr == NULL || length < 1) { v->active = false; // shut down voice (illegal parameters) return; } if (loopLength < 1) // disable loop if loopLength is below 1 loopType = 0; if (sample16Bit) { v->base16 = (const int16_t *)s->dataPtr; v->revBase16 = &v->base16[loopStart + loopEnd]; // for pingpong loops v->leftEdgeTaps16 = s->leftEdgeTapSamples16 + SINC_MAX_LEFT_TAPS; } else { v->base8 = s->dataPtr; v->revBase8 = &v->base8[loopStart + loopEnd]; // for pingpong loops v->leftEdgeTaps8 = s->leftEdgeTapSamples8 + SINC_MAX_LEFT_TAPS; } v->hasLooped = false; // for sinc interpolation special case v->samplingBackwards = false; v->loopType = loopType; v->sampleEnd = (loopType == LOOP_OFF) ? length : loopEnd; v->loopStart = loopStart; v->loopLength = loopLength; v->position = position; v->positionFrac = 0; // if position overflows, shut down voice (f.ex. through 9xx command) if (v->position >= v->sampleEnd) { v->active = false; return; } v->mixFuncOffset = ((int32_t)sample16Bit * 12) + (audio.interpolationType * 3) + loopType; v->active = true; } void resetRampVolumes(void) { voice_t *v = voice; for (int32_t i = 0; i < song.numChannels; i++, v++) { v->fCurrVolumeL = v->fTargetVolumeL; v->fCurrVolumeR = v->fTargetVolumeR; v->volumeRampLength = 0; } } void updateVoices(void) { channel_t *ch = channel; voice_t *v = voice; for (int32_t i = 0; i < song.numChannels; i++, ch++, v++) { const uint8_t status = ch->tmpStatus = ch->status; // (tmpStatus is used for audio/video sync queue) if (status == 0) continue; ch->status = 0; if (status & IS_Vol) { v->dVolume = ch->dFinalVol; // set scope volume const int32_t scopeVolume = (int32_t)((SCOPE_HEIGHT * ch->dFinalVol) + 0.5); // rounded v->scopeVolume = (uint8_t)scopeVolume; } if (status & IS_Pan) v->panning = ch->finalPan; if (status & (IS_Vol + IS_Pan)) voiceUpdateVolumes(i, status); if (status & IS_Period) { // use cached values when possible if (ch->finalPeriod != ch->oldFinalPeriod) { ch->oldFinalPeriod = ch->finalPeriod; if (ch->finalPeriod == 0) // in FT2, period 0 -> delta 0 { v->scopeDelta = 0; v->oldDelta = 0; v->fSincLUT = fKaiserSinc; } else { const double dHz = dPeriod2Hz(ch->finalPeriod); const uintCPUWord_t delta = v->oldDelta = (intCPUWord_t)((dHz * audio.dHz2MixDeltaMul) + 0.5); // Hz -> fixed-point delta (rounded) // decide which polyphase sinc LUT to use according to resampling ratio if (delta <= (uintCPUWord_t)(1.1875 * MIXER_FRAC_SCALE)) v->fSincLUT = fKaiserSinc; else if (delta <= (uintCPUWord_t)(1.5 * MIXER_FRAC_SCALE)) v->fSincLUT = fDownSample1; else v->fSincLUT = fDownSample2; // set scope delta const double dHz2ScopeDeltaMul = SCOPE_FRAC_SCALE / (double)SCOPE_HZ; v->scopeDelta = (intCPUWord_t)((dHz * dHz2ScopeDeltaMul) + 0.5); // Hz -> fixed-point delta (rounded) } } v->delta = v->oldDelta; } if (status & IS_Trigger) voiceTrigger(i, ch->smpPtr, ch->smpStartPos); } } void resetAudioDither(void) { randSeed = INITIAL_DITHER_SEED; dPrngStateL = 0.0; dPrngStateR = 0.0; } static inline int32_t random32(void) { // LCG 32-bit random randSeed *= 134775813; randSeed++; return (int32_t)randSeed; } static void sendSamples16BitDitherStereo(uint8_t *stream, uint32_t sampleBlockLength) { int32_t out32; double dOut, dPrng; int16_t *streamPointer16 = (int16_t *)stream; for (uint32_t i = 0; i < sampleBlockLength; i++) { // left channel - 1-bit triangular dithering dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5 dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul; dOut = (dOut + dPrng) - dPrngStateL; dPrngStateL = dPrng; out32 = (int32_t)dOut; CLAMP16(out32); *streamPointer16++ = (int16_t)out32; // right channel - 1-bit triangular dithering dPrng = random32() * (0.5 / INT32_MAX); // -0.5 .. 0.5 dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul; dOut = (dOut + dPrng) - dPrngStateR; dPrngStateR = dPrng; out32 = (int32_t)dOut; CLAMP16(out32); *streamPointer16++ = (int16_t)out32; // clear what we read from the mixing buffer audio.fMixBufferL[i] = 0.0f; audio.fMixBufferR[i] = 0.0f; } } static void sendSamples32BitStereo(uint8_t *stream, uint32_t sampleBlockLength) { double dOut; float *fStreamPointer32 = (float *)stream; for (uint32_t i = 0; i < sampleBlockLength; i++) { // left channel dOut = (double)audio.fMixBufferL[i] * dAudioNormalizeMul; dOut = CLAMP(dOut, -1.0, 1.0); *fStreamPointer32++ = (float)dOut; // right channel dOut = (double)audio.fMixBufferR[i] * dAudioNormalizeMul; dOut = CLAMP(dOut, -1.0, 1.0); *fStreamPointer32++ = (float)dOut; // clear what we read from the mixing buffer audio.fMixBufferL[i] = 0.0f; audio.fMixBufferR[i] = 0.0f; } } static void doChannelMixing(int32_t bufferPosition, int32_t samplesToMix) { voice_t *v = voice; // normal voices voice_t *r = &voice[MAX_CHANNELS]; // volume ramp fadeout-voices for (int32_t i = 0; i < song.numChannels; i++, v++, r++) {