ref: c05b1bd0e60840e1e4e7b7d01b2aa5b52cfcaaff
dir: /pmp_mix.c/
#include <assert.h> #include <stdint.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> #include <math.h> #include "pmplay.h" #include "pmp_main.h" #include "snd_masm.h" #include "tables.h" // fast 32-bit -> 16-bit clamp #define CLAMP16(i) if ((int16_t)(i) != i) i = 0x7FFF ^ (i >> 31) static bool dump_Flag; static int32_t oldReplayRate; // globalized int16_t chnReloc[32]; int32_t *CDA_MixBuffer = NULL; CIType CI[32 * 2]; // ------------ static void mix_UpdateChannel(int32_t nr, WaveChannelInfoType *WCI); void P_SetSpeed(uint16_t bpm) { // 8bb: added this if (bpm == 0) bpm = 125; speedVal = ((realReplayRate + realReplayRate) + (realReplayRate >> 1)) / bpm; // 8bb: same as doing "((realReplayRate * 5) / 2) / bpm" } void P_StartTone(sampleTyp *s, int32_t smpStartPos) { WaveChannelInfoType WCI; WCI.SStartPos = smpStartPos; WCI.SBase = s->pek; WCI.SLen = s->len; WCI.SRepS = s->repS; WCI.SRepL = s->repL; WCI.SType = s->typ; WCI.Status = Status_StartTone+Status_StopTone; mix_UpdateChannel(PMPTmpActiveChannel, &WCI); } // 8bb: added these two bool mix_Init(int32_t audioBufferSize) { CDA_MixBuffer = (int32_t *)malloc(audioBufferSize * (2 * sizeof (int32_t))); if (CDA_MixBuffer == NULL) return false; PMPLeft = 0; return true; } void mix_Free(void) { if (CDA_MixBuffer != NULL) { free(CDA_MixBuffer); CDA_MixBuffer = NULL; } } // -------------------- static void updateVolume(CIType *v, int32_t volIPLen) { const uint32_t vol = v->SVol * CDA_Amp; v->SLVol1 = (vol * panningTab[256-v->SPan]) >> (32-28); v->SRVol1 = (vol * panningTab[ v->SPan]) >> (32-28); if (volumeRampingFlag) { v->SLVolIP = (v->SLVol1 - v->SLVol2) / volIPLen; v->SRVolIP = (v->SRVol1 - v->SRVol2) / volIPLen; v->SVolIPLen = volIPLen; } } static void mix_UpdateChannel(int32_t nr, WaveChannelInfoType *WCI) { CIType *v = &CI[chnReloc[nr]]; const uint8_t status = WCI->Status; if (status & Status_StopTone) { if (volumeRampingFlag) { // 8bb: fade out current voice v->SType |= SType_Fadeout; v->SVol = 0; updateVolume(v, quickVolSizeVal); // 8bb: swap current voice with neighbor chnReloc[nr] ^= 1; v = &CI[chnReloc[nr]]; } v->SType = SType_Off; } if (status & Status_SetPan) v->SPan = (uint8_t)WCI->SPan; if (status & Status_SetVol) { uint16_t vol = WCI->SVol; if (vol > 0) vol--; // 8bb: 0..256 -> 0..255 ( FT2 does this to prevent mul overflow in updateVolume() ) v->SVol = (uint8_t)vol; } if (status & (Status_SetVol+Status_SetPan)) updateVolume(v, (status & Status_QuickVol) ? quickVolSizeVal : speedVal); if (status & Status_SetFrq) v->SFrq = WCI->SFrq; if (status & Status_StartTone) { int32_t len; uint8_t type = WCI->SType; const bool sample16Bit = (type >> 4) & 1; if (type & (SType_Fwd+SType_Rev)) { int32_t repL = WCI->SRepL; int32_t repS = WCI->SRepS; if (sample16Bit) { repL >>= 1; repS >>= 1; v->SRevBase = (int16_t *)WCI->SBase + (repS+repS+repL); } else { v->SRevBase = (int8_t *)WCI->SBase + (repS+repS+repL); } v->SRepL = repL; v->SRepS = repS; len = repS + repL; } else { type &= ~(SType_Fwd+SType_Rev); // 8bb: keep loop flags only len = WCI->SLen; if (sample16Bit) len >>= 1; if (len == 0) return; } // 8bb: overflown 9xx (set sample offset), cut voice (voice got ended earlier in "if (status & Status_StopTone)") if (WCI->SStartPos >= len) return; v->SLen = len; v->SPos = WCI->SStartPos; v->SPosDec = 0; v->SBase = WCI->SBase; v->SMixType = (sample16Bit * 4) + (volumeRampingFlag * 2) + interpolationFlag; v->SType = type; } } static void mix_UpdateChannelVolPanFrq(void) { WaveChannelInfoType WCI; stmTyp *ch = stm; for (int32_t i = 0; i < song.antChn; i++, ch++) { uint8_t newStatus = 0; const uint8_t status = ch->status; ch->status = 0; if (status == 0) continue; if (status & IS_Vol) { WCI.SVol = ch->finalVol; newStatus |= Status_SetVol; } if (status & IS_QuickVol) newStatus |= Status_QuickVol; if (status & IS_Pan) { WCI.SPan = ch->finalPan; newStatus |= Status_SetPan; } if (status & IS_Period) { WCI.SFrq = getFrequenceValue(ch->finalPeriod); newStatus |= Status_SetFrq; } WCI.Status = newStatus; mix_UpdateChannel(i, &WCI); } } void mix_ClearChannels(void) // 8bb: rewritten to handle all voices instead of song.antChn { lockMixer(); memset(CI, 0, sizeof (CI)); CIType *v = CI; for (int16_t i = 0; i < 32*2; i++, v++) { v->SPan = 128; v->SType = SType_Off; } for (int16_t i = 0; i < 32; i++) chnReloc[i] = i+i; unlockMixer(); } static void mix_SaveIPVolumes(void) { CIType *v = CI; for (int32_t i = 0; i < song.antChn*2; i++, v++) { // 8bb: this cuts any active fade-out voices (volume ramping) if (v->SType & SType_Fadeout) v->SType = SType_Off; v->SLVol2 = v->SLVol1; v->SRVol2 = v->SRVol1; v->SVolIPLen = 0; } } void mix_UpdateBuffer(int16_t *buffer, int32_t numSamples) { if (numSamples <= 0) return; if (musicPaused || WAVDump_Flag) { memset(buffer, 0, numSamples * (2 * sizeof (int16_t))); return; } assert(CDA_MixBuffer != NULL); memset(CDA_MixBuffer, 0, numSamples * (2 * sizeof (int32_t))); int32_t c = 0; int32_t a = numSamples; while (a > 0) { if (PMPLeft == 0) { mix_SaveIPVolumes(); mainPlayer(); mix_UpdateChannelVolPanFrq(); PMPLeft = speedVal; } int32_t b = a; if (b > PMPLeft) b = PMPLeft; // 8bb: fix for PMPMix32Proc() silent mix (vol=0) if (b > 65535) b = 65535; CIType *v = CI; for (int32_t i = 0; i < song.antChn*2; i++, v++) PMPMix32Proc(v, b, c); c += b; a -= b; PMPLeft -= b; } numSamples *= 2; // 8bb: stereo /* 8bb: Done a bit differently since we don't use a ** Sound Blaster with its master volume setting. ** Instead we change the amplitude here. */ if (masterVol == 256) // 8bb: max master volume, no need to change amp { for (int32_t i = 0; i < numSamples; i++) { int32_t out32 = CDA_MixBuffer[i] >> 8; CLAMP16(out32); buffer[i] = (int16_t)out32; } } else { for (int32_t i = 0; i < numSamples; i++) { int32_t out32 = CDA_MixBuffer[i] >> 8; CLAMP16(out32); out32 = (out32 * masterVol) >> 8; buffer[i] = (int16_t)out32; } } } bool dump_Init(int32_t frq, int32_t amp, int16_t songPos) { setPos(songPos, 0); oldReplayRate = realReplayRate; realReplayRate = frq; updateReplayRate(); CDA_Amp = 8 * amp; mix_ClearChannels(); stopVoices(); song.globVol = 64; speedVal = (frq*5 / 2) / song.speed; quickVolSizeVal = frq / 200; dump_Flag = false; return true; } void dump_Close(void) { stopVoices(); realReplayRate = oldReplayRate; updateReplayRate(); } bool dump_EndOfTune(int32_t endSongPos) { bool returnValue = (dump_Flag && song.pattPos == 0 && song.timer == 1) || (song.tempo == 0); // 8bb: FT2 bugfix for EEx (pattern delay) on first row of a pattern if (song.pattDelTime2 > 0) returnValue = false; if (song.songPos == endSongPos && song.pattPos == 0 && song.timer == 1) dump_Flag = true; return returnValue; } int32_t dump_GetFrame(int16_t *p) // 8bb: returns bytes mixed { mix_SaveIPVolumes(); mainPlayer(); mix_UpdateChannelVolPanFrq(); memset(CDA_MixBuffer, 0, speedVal * (2 * sizeof (int32_t))); CIType *v = CI; for (int32_t i = 0; i < song.antChn*2; i++, v++) PMPMix32Proc(v, speedVal, 0); const int32_t numSamples = speedVal * 2; // 8bb: *2 for stereo for (int32_t i = 0; i < numSamples; i++) { int32_t out32 = CDA_MixBuffer[i] >> 8; CLAMP16(out32); p[i] = (int16_t)out32; } return speedVal * (2 * sizeof (int16_t)); }