Sigrid Solveig HaflínudóttirMerge remote-tracking branch 'upstream/master'fbdd3103 years agoHistory
1126 lines · 27.2 KB · C
RawDownload
// 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t