Sigrid Solveig Haflínudóttirfix warnings1dc9e673 years agoHistory
1022 lines · 21.6 KB · C
RawDownload
/* Experimental audio sampling support.** There may be several bad practices here, as I don't really** have the proper knowledge on this stuff.**** Some functions like sin() may be different depending on** math library implementation, but we don't use pt2_math.c** replacements for speed reasons.*/ // 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 <stdbool.h>#include "pt2_textout.h"#include "pt2_sampler.h"#include "pt2_visuals.h"#include "pt2_helpers.h"#include "pt2_bmp.h"#include "pt2_audio.h"#include "pt2_tables.h"#include "pt2_config.h"#include "pt2_sampling.h"#include "pt2_math.h" // PT2_PI#include "pt2_replayer.h" enum{	SAMPLE_LEFT = 0,	SAMPLE_RIGHT = 1,	SAMPLE_MIX  = 2}; // this may change after opening the audio input device#define SAMPLING_BUFFER_SIZE 1024 // after several tests, these values yields a good trade-off between quality and compute time#define SINC_TAPS 64#define SINC_TAPS_BITS 6 /* log2(SINC_TAPS) */#define SINC_PHASES 4096#define MID_TAP ((SINC_TAPS/2)*SINC_PHASES) #define SAMPLE_PREVIEW_WITDH 194#define SAMPLE_PREVIEW_HEIGHT 38#define MAX_INPUT_DEVICES 99#define VISIBLE_LIST_ENTRIES 4 static volatile bool callbackBusy, displayingBuffer, samplingEnded;static bool audioDevOpen;static char *audioInputDevs[MAX_INPUT_DEVICES];static uint8_t samplingNote = 33, samplingFinetune = 4; // period 124, max safe period for PAL Paulastatic int16_t displayBuffer[SAMPLING_BUFFER_SIZE];static int32_t samplingMode = SAMPLE_MIX, inputFrequency, roundedOutputFrequency;static int32_t numAudioInputDevs, audioInputDevListOffset, selectedDev;static int32_t bytesSampled, maxSamplingLength, inputBufferSize;static double dOutputFrequency, *dSincTable, *dKaiserTable, *dSamplingBuffer, *dSamplingBufferOrig;static SDL_AudioDeviceID recordDev; /*** ----------------------------------------------------------------------------------** Sinc code taken from the OpenMPT project (has a similar BSD license), and modified** ----------------------------------------------------------------------------------*/ static double Izero(double y) // Compute Bessel function Izero(y) using a series approximation{	double s = 1.0, ds = 1.0, d = 0.0;	const double epsilon = 1E-9; // 8bb: 1E-7 -> 1E-9 for added precision (still fast to calculate) 	do	{		d = d + 2.0;		ds = ds * (y * y) / (d * d);		s = s + ds;	}	while (ds > epsilon * s); 	return s;} bool initKaiserTable(void) // called once on tracker init{	dKaiserTable = (double *)malloc(SINC_TAPS * SINC_PHASES * sizeof (double));	if (dKaiserTable == NULL)	{		showErrorMsgBox("Out of memory!");		return false;	} 	const double beta = 9.6377;	const double izeroBeta = Izero(beta); 	for (int32_t i = 0; i < SINC_TAPS*SINC_PHASES; i++)	{		double fkaiser;		int32_t ix = (SINC_TAPS-1) - (i & (SINC_TAPS-1)); 		ix = (ix * SINC_PHASES) + (i >> SINC_TAPS_BITS);		if (ix == MID_TAP)		{			fkaiser = 1.0;		}		else		{			const double x = (ix - MID_TAP) * (1.0 / SINC_PHASES);			const double xMul = 1.0 / ((SINC_TAPS/2) * (SINC_TAPS/2));			fkaiser = Izero(beta * sqrt(1.0 - x * x * xMul)) / izeroBeta;		} 		dKaiserTable[i] = fkaiser;	} 	return true;} void freeKaiserTable(void){	if (dKaiserTable != NULL)	{		free(dKaiserTable);		dKaiserTable = NULL;	}} // calculated after completion of sampling (before downsampling)static bool initSincTable(double cutoff){	dSincTable = (double *)malloc(SINC_TAPS * SINC_PHASES * sizeof (double));	if (dSincTable == NULL)		return false; 	if (cutoff > 1.0)		cutoff = 1.0; 	const double kPi = PT2_PI * cutoff;	for (int32_t i = 0; i < SINC_TAPS*SINC_PHASES; i++)	{		double fsinc;		int32_t ix = (SINC_TAPS-1) - (i & (SINC_TAPS-1)); 		ix = (ix * SINC_PHASES) + (i >> SINC_TAPS_BITS);		if (ix == MID_TAP)		{			fsinc = 1.0;		}		else		{			const double x = (ix - MID_TAP) * (1.0 / SINC_PHASES);			const double xPi = x * kPi; 			fsinc = (sin(xPi) / xPi) * dKaiserTable[i];		} 		dSincTable[i] = fsinc * cutoff;	} 	return true;} static void freeSincTable(void){	if (dSincTable != NULL)	{		free(dSincTable);		dSincTable = NULL;	}} static double sinc(const double *dSmpData, const double dPhase){	const int32_t phase = (int32_t)(dPhase * SINC_PHASES);	const double *dSincLUT = &dSincTable[phase << SINC_TAPS_BITS]; 	double dSmp = 0.0;	for (int32_t i = 0; i < SINC_TAPS; i++)		dSmp += dSmpData[i] * dSincLUT[i]; 	return dSmp;} /*** ----------------------------------------------------------------------------------** ----------------------------------------------------------------------------------*/ static void listAudioDevices(void); static void updateOutputFrequency(void){	if (samplingNote > 35)		samplingNote = 35; 	int32_t period = periodTable[((samplingFinetune & 0xF) * 37) + samplingNote];	if (period < 113) // also happens in our "set period" Paula function		period = 113; 	dOutputFrequency = (double)PAULA_PAL_CLK / period;	roundedOutputFrequency = (int32_t)(dOutputFrequency + 0.5);} static void SDLCALL samplingCallback(void *userdata, Uint8 *stream, int len){	callbackBusy = true;	USED(callbackBusy); 	if (!displayingBuffer)	{		if (len > SAMPLING_BUFFER_SIZE)			len = SAMPLING_BUFFER_SIZE; 		const int16_t *L =  (int16_t *)stream;		const int16_t *R = ((int16_t *)stream) + 1; 		int16_t *dst16 = displayBuffer; 		if (samplingMode == SAMPLE_LEFT)		{			for (int32_t i = 0; i < len; i++)				dst16[i] = L[i << 1];		}		else if (samplingMode == SAMPLE_RIGHT)		{			for (int32_t i = 0; i < len; i++)				dst16[i] = R[i << 1];		}		else		{			for (int32_t i = 0; i < len; i++)				dst16[i] = (L[i << 1] + R[i << 1]) >> 1;		}	} 	if (audio.isSampling)	{		if (bytesSampled+len > maxSamplingLength)			len = maxSamplingLength - bytesSampled; 		if (len > inputBufferSize)			len = inputBufferSize; 		const int16_t *L = (int16_t *)stream;		const int16_t *R = ((int16_t *)stream) + 1; 		double *dSmp = &dSamplingBuffer[bytesSampled]; 		if (samplingMode == SAMPLE_LEFT)		{			for (int32_t i = 0; i < len; i++)				dSmp[i] = L[i << 1] * (1.0 / 32768.0);		}		else if (samplingMode == SAMPLE_RIGHT)		{			for (int32_t i = 0; i < len; i++)				dSmp[i] = R[i << 1] * (1.0 / 32768.0);		}		else		{			for (int32_t i = 0; i < len; i++)				dSmp[i] = (L[i << 1] + R[i << 1]) * (1.0 / (32768.0 * 2.0));		} 		bytesSampled += len;		if (bytesSampled >= maxSamplingLength)		{			audio.isSampling = true;			samplingEnded = true;		}	} 	callbackBusy = false;	USED(userdata);} static void stopInputAudio(void){	if (recordDev > 0)	{		SDL_CloseAudioDevice(recordDev);		recordDev = 0;	}	callbackBusy = false;} static void startInputAudio(void){	SDL_AudioSpec want, have; 	if (recordDev > 0)		stopInputAudio(); 	if (numAudioInputDevs == 0 || selectedDev >= numAudioInputDevs)	{		audioDevOpen = false;		return;	} 	assert(roundedOutputFrequency > 0); 	memset(&want, 0, sizeof (SDL_AudioSpec));	want.freq = config.audioInputFrequency;	want.format = AUDIO_S16;	want.channels = 2;	want.callback = samplingCallback;	want.userdata = NULL;	want.samples = SAMPLING_BUFFER_SIZE; 	recordDev = SDL_OpenAudioDevice(audioInputDevs[selectedDev], true, &want, &have, 0);	audioDevOpen = (recordDev != 0); 	inputFrequency = have.freq;	inputBufferSize = have.samples; 	SDL_PauseAudioDevice(recordDev, false);} static void selectAudioDevice(int32_t dev){	if (dev < 0)		return; 	if (numAudioInputDevs == 0)	{		listAudioDevices();		return;	} 	if (dev >= numAudioInputDevs)		return; 	listAudioDevices(); 	stopInputAudio();	selectedDev = dev;	listAudioDevices();	startInputAudio(); 	changeStatusText(ui.statusMessage);} void renderSampleMonitor(void){	blit32(120, 44, 200, 55, sampleMonitorBMP);	memset(displayBuffer, 0, sizeof (displayBuffer));} void freeAudioDeviceList(void){	for (int32_t i = 0; i < numAudioInputDevs; i++)	{		if (audioInputDevs[i] != NULL)		{			free(audioInputDevs[i]);			audioInputDevs[i] = NULL;		}	}} static void scanAudioDevices(void){	freeAudioDeviceList(); 	numAudioInputDevs = SDL_GetNumAudioDevices(true);	if (numAudioInputDevs > MAX_INPUT_DEVICES)		numAudioInputDevs = MAX_INPUT_DEVICES; 	for (int32_t i = 0; i < numAudioInputDevs; i++)	{		const char *deviceName = SDL_GetAudioDeviceName(i, true);		if (deviceName == NULL)		{			numAudioInputDevs--; // hide device			continue;		} 		const uint32_t stringLen = (uint32_t)strlen(deviceName); 		audioInputDevs[i] = (char *)malloc(stringLen + 2);		if (audioInputDevs[i] == NULL)			break; 		if (stringLen > 0)			strcpy(audioInputDevs[i], deviceName); 		audioInputDevs[i][stringLen+1] = '\0'; // UTF-8 needs double null termination (XXX: citation needed)	} 	audioInputDevListOffset = 0; // reset scroll position 	if (selectedDev >= numAudioInputDevs)		selectedDev = 0;} static void listAudioDevices(void){	fillRect(3, 219, 163, 33, PAL_BACKGRD); 	if (numAudioInputDevs == 0)	{		textOut(16, 219+13, "NO DEVICES FOUND!", video.palette[PAL_QADSCP]);		return;	} 	for (int32_t i = 0; i < VISIBLE_LIST_ENTRIES; i++)	{		const int32_t dev = audioInputDevListOffset+i;		if (audioInputDevListOffset+i >= numAudioInputDevs)			break; 		if (dev == selectedDev)			fillRect(4, 219+1+(i*(FONT_CHAR_H+3)), 161, 8, video.palette[PAL_GENBKG2]); 		if (audioInputDevs[dev] != NULL)			textOutTightN(2+2, 219+2+(i*(FONT_CHAR_H+3)), audioInputDevs[dev], 23, video.palette[PAL_QADSCP]);	}} static void drawSamplingNote(void){	assert(samplingNote < 36);	const char *str = config.accidental ? noteNames2[2+samplingNote]: noteNames1[2+samplingNote];	textOutBg(262, 230, str, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);} static void drawSamplingFinetune(void){	textOutBg(254, 219, ftuneStrTab[samplingFinetune & 0xF], video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);} static void drawSamplingFrequency(void){	char str[16];	sprintf(str, "%05dHZ", roundedOutputFrequency); 	const int32_t maxSafeFrequency = (int32_t)(PAL_PAULA_MAX_SAFE_HZ + 0.5); // rounded	textOutBg(262, 208, str, roundedOutputFrequency <= maxSafeFrequency ? video.palette[PAL_GENTXT] : 0x8C0F0F, video.palette[PAL_GENBKG]);} static void drawSamplingModeCross(void){	// clear old crosses	fillRect(4, 208, 6, 5, video.palette[PAL_GENBKG]);	fillRect(51, 208, 6, 5, video.palette[PAL_GENBKG]);	fillRect(105, 208, 6, 5, video.palette[PAL_GENBKG]); 	int16_t x;	if (samplingMode == SAMPLE_LEFT)		x = 3;	else if (samplingMode == SAMPLE_RIGHT)		x = 50;	else		x = 104; 	charOut(x, 208, 'X', video.palette[PAL_GENTXT]);} static void showCurrSample(void){	updateCurrSample(); 	// reset sampler screen attributes	sampler.loopStartPos = 0;	sampler.loopEndPos = 0;	editor.markStartOfs = -1;	editor.markEndOfs = -1;	editor.samplePos = 0;	hideSprite(SPRITE_LOOP_PIN_LEFT);	hideSprite(SPRITE_LOOP_PIN_RIGHT); 	renderSampleData();} void renderSamplingBox(void){	changeStatusText("PLEASE WAIT ...");	flipFrame();	hpc_ResetCounters(&video.vblankHpc); 	editor.sampleZero = false;	editor.blockMarkFlag = false; 	// remove all open screens (except sampler)	if (ui.diskOpScreenShown  || ui.posEdScreenShown || ui.editOpScreenShown)	{		ui.diskOpScreenShown = false;		ui.posEdScreenShown = false;		ui.editOpScreenShown = false; 		displayMainScreen();	}	setStatusMessage("ALL RIGHT", DO_CARRY); 	blit32(0, 203, 320, 52, samplingBoxBMP); 	updateOutputFrequency();	drawSamplingNote();	drawSamplingFinetune();	drawSamplingFrequency();

Lines 1–500 of 1022.

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