Sigrid Solveig Haflínudóttirfix warnings1dc9e673 years agoHistory
683 lines · 16.9 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 <stdlib.h>#include <stdint.h>#include <stdbool.h>#include <math.h>#include <SDL2/SDL.h>#ifdef _WIN32#include <io.h>#else#include <unistd.h>#endif#include <fcntl.h>#include <sys/types.h>#include <sys/stat.h>#include <limits.h>#include "pt2_audio.h"#include "pt2_helpers.h"#include "pt2_config.h"#include "pt2_textout.h"#include "pt2_scopes.h"#include "pt2_visuals_sync.h"#include "pt2_downsample2x.h"#include "pt2_replayer.h"#include "pt2_paula.h" // cumulative mid/side normalization factor (1/sqrt(2))*(1/sqrt(2))#define STEREO_NORM_FACTOR 0.5 #define INITIAL_DITHER_SEED 0x12345000 static uint8_t panningMode;static int32_t randSeed = INITIAL_DITHER_SEED, stereoSeparation = 100;static double *dMixBufferL, *dMixBufferR;static double dPrngStateL, dPrngStateR, dSideFactor;static SDL_AudioDeviceID dev; audio_t audio; // globalized void setAmigaFilterModel(uint8_t model){	if (audio.amigaModel == model)		return; // same state as before! 	const bool audioWasntLocked = !audio.locked;	if (audioWasntLocked)		lockAudio(); 	audio.amigaModel = model; 	const int32_t paulaMixFrequency = audio.oversamplingFlag ? audio.outputRate*2 : audio.outputRate;	paulaSetup(paulaMixFrequency, audio.amigaModel); 	if (audioWasntLocked)		unlockAudio();} void toggleAmigaFilterModel(void){	const bool audioWasntLocked = !audio.locked;	if (audioWasntLocked)		lockAudio(); 	audio.amigaModel ^= 1; 	const int32_t paulaMixFrequency = audio.oversamplingFlag ? audio.outputRate*2 : audio.outputRate;	paulaSetup(paulaMixFrequency, audio.amigaModel); 	if (audioWasntLocked)		unlockAudio(); 	if (audio.amigaModel == MODEL_A500)		displayMsg("AUDIO: AMIGA 500");	else		displayMsg("AUDIO: AMIGA 1200");} void setLEDFilter(bool state){	if (audio.ledFilterEnabled == state)		return; // same state as before! 	const bool audioWasntLocked = !audio.locked;	if (audioWasntLocked)		lockAudio(); 	audio.ledFilterEnabled = state;	paulaWriteByte(0xBFE001, (uint8_t)audio.ledFilterEnabled << 1); 	if (audioWasntLocked)		unlockAudio();} void toggleLEDFilter(void){	const bool audioWasntLocked = !audio.locked;	if (audioWasntLocked)		lockAudio(); 	audio.ledFilterEnabled ^= 1;	paulaWriteByte(0xBFE001, (uint8_t)audio.ledFilterEnabled << 1); 	if (audioWasntLocked)		unlockAudio();} void lockAudio(void){	if (dev != 0)		SDL_LockAudioDevice(dev); 	audio.locked = true; 	audio.resetSyncTickTimeFlag = true;	resetChSyncQueue();} void unlockAudio(void){	if (dev != 0)		SDL_UnlockAudioDevice(dev); 	audio.resetSyncTickTimeFlag = true;	resetChSyncQueue(); 	audio.locked = false;} void resetAudioDithering(void){	randSeed = INITIAL_DITHER_SEED;	dPrngStateL = 0.0;	dPrngStateR = 0.0;} static inline int32_t random32(void){	// LCG random 32-bit generator (quite good and fast)	randSeed *= 134775813;	randSeed++;	return randSeed;} #define NORM_FACTOR 2.0 /* nominally correct, but can clip from high-pass filter overshoot */ static inline void processMixedSamplesAmigaPanning(int32_t i, int16_t *out){	int32_t smp32;	double dPrng; 	double dL = dMixBufferL[i];	double dR = dMixBufferR[i]; 	// normalize w/ phase-inversion (A500/A1200 has a phase-inverted audio signal)	dL *= NORM_FACTOR * (-INT16_MAX / (double)PAULA_VOICES);	dR *= NORM_FACTOR * (-INT16_MAX / (double)PAULA_VOICES); 	// left channel - 1-bit triangular dithering (high-pass filtered)	dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5	dL = (dL + dPrng) - dPrngStateL;	dPrngStateL = dPrng;	smp32 = (int32_t)dL;	CLAMP16(smp32);	out[0] = (int16_t)smp32; 	// right channel - 1-bit triangular dithering (high-pass filtered)	dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5	dR = (dR + dPrng) - dPrngStateR;	dPrngStateR = dPrng;	smp32 = (int32_t)dR;	CLAMP16(smp32);	out[1] = (int16_t)smp32;} static inline void processMixedSamples(int32_t i, int16_t *out){	int32_t smp32;	double dPrng; 	double dL = dMixBufferL[i];	double dR = dMixBufferR[i]; 	// apply stereo separation	const double dOldL = dL;	const double dOldR = dR;	double dMid  = (dOldL + dOldR) * STEREO_NORM_FACTOR;	double dSide = (dOldL - dOldR) * dSideFactor;	dL = dMid + dSide;	dR = dMid - dSide; 	// normalize w/ phase-inversion (A500/A1200 has a phase-inverted audio signal)	dL *= NORM_FACTOR * (-INT16_MAX / (double)PAULA_VOICES);	dR *= NORM_FACTOR * (-INT16_MAX / (double)PAULA_VOICES); 	// left channel - 1-bit triangular dithering (high-pass filtered)	dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5	dL = (dL + dPrng) - dPrngStateL;	dPrngStateL = dPrng;	smp32 = (int32_t)dL;	CLAMP16(smp32);	out[0] = (int16_t)smp32; 	// right channel - 1-bit triangular dithering (high-pass filtered)	dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5	dR = (dR + dPrng) - dPrngStateR;	dPrngStateR = dPrng;	smp32 = (int32_t)dR;	CLAMP16(smp32);	out[1] = (int16_t)smp32;} static inline void processMixedSamplesAmigaPanning_2x(int32_t i, int16_t *out) // 2x oversampling{	int32_t smp32;	double dPrng, dL, dR; 	// 2x downsampling (decimation)	const uint32_t offset1 = (i << 1) + 0;	const uint32_t offset2 = (i << 1) + 1;	dL = decimate2x_L(dMixBufferL[offset1], dMixBufferL[offset2]);	dR = decimate2x_R(dMixBufferR[offset1], dMixBufferR[offset2]); 	// normalize w/ phase-inversion (A500/A1200 has a phase-inverted audio signal)	dL *= NORM_FACTOR * (-INT16_MAX / (double)PAULA_VOICES);	dR *= NORM_FACTOR * (-INT16_MAX / (double)PAULA_VOICES); 	// left channel - 1-bit triangular dithering (high-pass filtered)	dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5	dL = (dL + dPrng) - dPrngStateL;	dPrngStateL = dPrng;	smp32 = (int32_t)dL;	CLAMP16(smp32);	out[0] = (int16_t)smp32; 	// right channel - 1-bit triangular dithering (high-pass filtered)	dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5	dR = (dR + dPrng) - dPrngStateR;	dPrngStateR = dPrng;	smp32 = (int32_t)dR;	CLAMP16(smp32);	out[1] = (int16_t)smp32;} static inline void processMixedSamples_2x(int32_t i, int16_t *out) // 2x oversampling{	int32_t smp32;	double dPrng, dL, dR; 	// 2x downsampling (decimation)	const uint32_t offset1 = (i << 1) + 0;	const uint32_t offset2 = (i << 1) + 1;	dL = decimate2x_L(dMixBufferL[offset1], dMixBufferL[offset2]);	dR = decimate2x_R(dMixBufferR[offset1], dMixBufferR[offset2]); 	// apply stereo separation	const double dOldL = dL;	const double dOldR = dR;	double dMid  = (dOldL + dOldR) * STEREO_NORM_FACTOR;	double dSide = (dOldL - dOldR) * dSideFactor;	dL = dMid + dSide;	dR = dMid - dSide; 	// normalize w/ phase-inversion (A500/A1200 has a phase-inverted audio signal)	dL *= NORM_FACTOR * (-INT16_MAX / (double)PAULA_VOICES);	dR *= NORM_FACTOR * (-INT16_MAX / (double)PAULA_VOICES); 	// left channel - 1-bit triangular dithering (high-pass filtered)	dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5	dL = (dL + dPrng) - dPrngStateL;	dPrngStateL = dPrng;	smp32 = (int32_t)dL;	CLAMP16(smp32);	out[0] = (int16_t)smp32; 	// right channel - 1-bit triangular dithering (high-pass filtered)	dPrng = random32() * (0.5 / INT32_MAX); // -0.5..0.5	dR = (dR + dPrng) - dPrngStateR;	dPrngStateR = dPrng;	smp32 = (int32_t)dR;	CLAMP16(smp32);	out[1] = (int16_t)smp32;} void outputAudio(int16_t *target, int32_t numSamples){	if (audio.oversamplingFlag) // 2x oversampling	{		paulaGenerateSamples(dMixBufferL, dMixBufferR, numSamples*2); 		// downsample, normalize and dither		int16_t out[2];		int16_t *outStream = target;		if (stereoSeparation == 100)		{			for (int32_t i = 0; i < numSamples; i++)			{				processMixedSamplesAmigaPanning_2x(i, out);				*outStream++ = out[0];				*outStream++ = out[1];			}		}		else		{			for (int32_t i = 0; i < numSamples; i++)			{				processMixedSamples_2x(i, out);				*outStream++ = out[0];				*outStream++ = out[1];			}		}	}	else	{		paulaGenerateSamples(dMixBufferL, dMixBufferR, numSamples); 		// normalize and dither		int16_t out[2];		int16_t *outStream = target;		if (stereoSeparation == 100)		{			for (int32_t i = 0; i < numSamples; i++)			{				processMixedSamplesAmigaPanning(i, out);				*outStream++ = out[0];				*outStream++ = out[1];			}		}		else		{			for (int32_t i = 0; i < numSamples; i++)			{				processMixedSamples(i, out);				*outStream++ = out[0];				*outStream++ = out[1];			}		}	}} static void SDLCALL audioCallback(void *userdata, Uint8 *stream, int len){	if (editor.mod2WavOngoing || editor.pat2SmpOngoing) // send silence to sound output device	{		memset(stream, 0, len);		return;	} 	int16_t *streamOut = (int16_t *)stream; 	uint32_t samplesLeft = (uint32_t)len / 4;	while (samplesLeft > 0)	{		if (audio.tickSampleCounter == 0) // new replayer tick		{			if (editor.songPlaying)			{				intMusic(); // PT replayer ticker (also sets audio.samplesPerTickInt and audio.samplesPerTickFrac)				fillVisualsSyncBuffer();			} 			audio.tickSampleCounter = audio.samplesPerTickInt; 			audio.tickSampleCounterFrac += audio.samplesPerTickFrac;			if (audio.tickSampleCounterFrac >= BPM_FRAC_SCALE)			{				audio.tickSampleCounterFrac &= BPM_FRAC_MASK;				audio.tickSampleCounter++;			}		} 		uint32_t samplesToMix = samplesLeft;		if (samplesToMix > audio.tickSampleCounter)			samplesToMix = audio.tickSampleCounter; 		outputAudio(streamOut, samplesToMix);		streamOut += samplesToMix * 2; // *2 for stereo 		audio.tickSampleCounter -= samplesToMix;		samplesLeft -= samplesToMix;	} 	USED(userdata);} void audioSetStereoSeparation(uint8_t percentage) // 0..100 (percentage){	assert(percentage <= 100); 	stereoSeparation = percentage;	dSideFactor = (percentage / 100.0) * STEREO_NORM_FACTOR;} void generateBpmTable(double dAudioFreq, bool vblankTimingFlag){	const bool audioWasntLocked = !audio.locked;	if (audioWasntLocked)		lockAudio(); 	for (int32_t bpm = MIN_BPM; bpm <= MAX_BPM; bpm++)	{		const int32_t i = bpm - MIN_BPM; // index for tables 		double dBpmHz;		if (vblankTimingFlag)			dBpmHz = AMIGA_PAL_VBLANK_HZ;		else			dBpmHz = ciaBpm2Hz(bpm); 		const double dSamplesPerTick = dAudioFreq / dBpmHz; 		double dSamplesPerTickInt;		double dSamplesPerTickFrac = modf(dSamplesPerTick, &dSamplesPerTickInt); 		audio.samplesPerTickIntTab[i] = (uint32_t)dSamplesPerTickInt;		audio.samplesPerTickFracTab[i] = (uint64_t)((dSamplesPerTickFrac * BPM_FRAC_SCALE) + 0.5); // rounded	} 	audio.tickSampleCounter = 0;	audio.tickSampleCounterFrac = 0; 	if (audioWasntLocked)		unlockAudio();} static void generateTickLengthTable(bool vblankTimingFlag){	for (int32_t bpm = MIN_BPM; bpm <= MAX_BPM; bpm++)	{		const int32_t i = bpm - MIN_BPM; // index for tables 		double dHz;		if (vblankTimingFlag)			dHz = AMIGA_PAL_VBLANK_HZ;		else			dHz = ciaBpm2Hz(bpm); 		// BPM -> Hz -> tick length for performance counter (syncing visuals to audio)		const double dTickTime = (double)hpcFreq.freq64 / dHz; 		double dTimeInt;		double dTimeFrac = modf(dTickTime, &dTimeInt); 		audio.tickTimeIntTab[i] = (uint32_t)dTimeInt;		audio.tickTimeFracTab[i] = (uint64_t)((dTimeFrac * TICK_TIME_FRAC_SCALE) + 0.5); // rounded	}} void updateReplayerTimingMode(void){	const bool audioWasntLocked = !audio.locked;	if (audioWasntLocked)		lockAudio(); 	const bool vblankTimingMode = (editor.timingMode == TEMPO_MODE_VBLANK);	generateBpmTable(audio.outputRate, vblankTimingMode);	generateTickLengthTable(vblankTimingMode); 	if (audioWasntLocked)		unlockAudio();} bool setupAudio(void){	SDL_AudioSpec want, have; 	want.freq = config.soundFrequency;	want.samples = (uint16_t)config.soundBufferSize;	want.format = AUDIO_S16;	want.channels = 2;	want.callback = audioCallback;	want.userdata = NULL; 	dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);	if (dev == 0)	{		showErrorMsgBox("Couldn't open audio device:\n\"%s\"\n\nDo you have an audio device enabled and plugged in?", SDL_GetError());		return false;	} 	// lower than this is not safe for the BLEP synthesis in the mixer	const int32_t minFreq = (int32_t)(PAULA_PAL_CLK / 113.0 / 2.0)+1; // /2 because we do 2x oversampling	if (have.freq < minFreq)	{		showErrorMsgBox("Unable to open audio: An audio rate below %dHz can't be used!", minFreq);		return false;	} 	if (have.format != AUDIO_S16)	{		showErrorMsgBox("Couldn't open audio device:\nThis program only supports 16-bit audio streams. Sorry!");		return false;	} 	audio.outputRate = have.freq;	audio.audioBufferSize = have.samples;	audio.oversamplingFlag = (audio.outputRate < 96000); // we do 2x oversampling if the audio output rate is below 96kHz

Lines 1–500 of 683.

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