Olav SørensenPushed v1.62 code4188e803 years agoHistory
1857 lines · 41.3 KB · C
RawDownload
// C port of ProTracker 2.3D's replayer (with some modifications, but still accurate) // for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER#include <crtdbg.h>#endif #include <stdint.h>#include <stdbool.h>#include "pt2_audio.h"#include "pt2_helpers.h"#include "pt2_tables.h"#include "pt2_config.h"#include "pt2_visuals.h"#include "pt2_scopes.h"#include "pt2_paula.h"#include "pt2_visuals_sync.h"#include "pt2_posed.h" static bool posJumpAssert, pBreakFlag, modRenderDone;static bool doStopSong; // from F00 (Set Speed)static int8_t pBreakPosition, oldRow, modPattern;static uint8_t pattDelTime, lowMask = 0xFF, pattDelTime2;static int16_t modPos, oldPattern, oldPos;static uint16_t DMACONtemp;static int32_t modBPM, oldBPM, oldSpeed, ciaSetBPM; static const uint8_t funkTable[16] = // EFx (FunkRepeat/InvertLoop){	0x00, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0D,	0x10, 0x13, 0x16, 0x1A, 0x20, 0x2B, 0x40, 0x80}; void gotoNextMulti(void){	cursor.channel = (editor.multiModeNext[cursor.channel] - 1) & 3;	cursor.pos = cursor.channel * 6;	updateCursorPos();} double ciaBpm2Hz(int32_t bpm){	if (bpm == 0)		return 0.0; 	const uint32_t ciaPeriod = 1773447 / bpm; // yes, PT truncates here	return (double)CIA_PAL_CLK / (ciaPeriod+1); // +1, CIA triggers on underflow} void updatePaulaLoops(void) // used after manipulating sample loop points while Paula is live{	const bool audioWasntLocked = !audio.locked;	if (audioWasntLocked)		lockAudio(); 	for (int32_t i = 0; i < PAULA_VOICES; i++)	{		const moduleChannel_t *ch = &song->channels[i];		if (ch->n_samplenum == editor.currSample) // selected sample matches channel's sample?		{			const moduleSample_t *s = &song->samples[editor.currSample]; 			const uint32_t voiceAddr = 0xDFF0A0 + (i * 16);			paulaWritePtr(voiceAddr + 0, ch->n_start + s->loopStart);			paulaWriteWord(voiceAddr + 4, (uint16_t)(s->loopLength >> 1)); 			setVisualsDataPtr(i, ch->n_start + s->loopStart);			setVisualsLength(i, (uint16_t)(s->loopLength >> 1));		}	} 	if (audioWasntLocked)		unlockAudio();} void turnOffVoices(void){	const bool audioWasntLocked = !audio.locked;	if (audioWasntLocked)		lockAudio(); 	paulaWriteWord(0xDFF096, 0x000F); // turn off all voice DMAs	setVisualsDMACON(0x000F); 	// clear all volumes	for (int32_t i = 0; i < PAULA_VOICES; i++)	{		const uint32_t voiceAddr = 0xDFF0A0 + (i * 16);		paulaWriteWord(voiceAddr + 8, 0); 		setVisualsVolume(i, 0);	} 	// reset dithering/filter states (so that every playback session is identical)	resetAudioDithering(); 	if (audioWasntLocked)		unlockAudio(); 	editor.tuningToneFlag = false;} void initializeModuleChannels(module_t *m){	assert(m != NULL); 	memset(m->channels, 0, sizeof (m->channels)); 	moduleChannel_t *ch = m->channels;	for (uint8_t i = 0; i < PAULA_VOICES; i++, ch++)	{		ch->n_chanindex = i;		ch->n_dmabit = 1 << i;	}} void setReplayerPosToTrackerPos(void){	if (song == NULL)		return; 	modPattern = (int8_t)song->currPattern;	modPos = song->currPos;	song->row = song->currRow;	song->tick = 0;} module_t *createEmptyMod(void){	module_t *m = (module_t *)calloc(1, sizeof (module_t));	if (m == NULL)		goto error; 	// allocate memory for all sample data blocks (+ 2 extra, for quirk + safety)	const size_t allocLen = (MOD_SAMPLES + 2) * config.maxSampleLength; 	m->sampleData = (int8_t *)calloc(allocLen, 1);	if (m->sampleData == NULL)		goto error; 	for (int32_t i = 0; i < MAX_PATTERNS; i++)	{		m->patterns[i] = (note_t *)calloc(1, MOD_ROWS * sizeof (note_t) * PAULA_VOICES);		if (m->patterns[i] == NULL)			goto error;	} 	m->header.songLength = 1; 	moduleSample_t *s = m->samples;	for (int32_t i = 0; i < MOD_SAMPLES; i++, s++)	{		// setup GUI text pointers		s->volumeDisp = &s->volume;		s->lengthDisp = &s->length;		s->loopStartDisp = &s->loopStart;		s->loopLengthDisp = &s->loopLength; 		s->loopLength = 2; 		// sample data offsets (sample data = one huge buffer to rule them all)		s->offset = config.maxSampleLength * i;	} 	initializeModuleChannels(m); 	return m; error:	if (m != NULL)	{		for (int32_t i = 0; i < MAX_PATTERNS; i++)		{			if (m->patterns[i] != NULL)				free(m->patterns[i]);		} 		if (m->sampleData != NULL)			free(m->sampleData); 		free(m);	} 	return NULL;} void modSetSpeed(int32_t speed){	song->currSpeed = song->speed = speed;	song->tick = 0;} void doStopIt(bool resetPlayMode){	editor.songPlaying = false; 	pattDelTime = pattDelTime2 = 0; 	if (resetPlayMode)	{		editor.playMode = PLAY_MODE_NORMAL;		editor.currMode = MODE_IDLE;		pointerSetModeThreadSafe(POINTER_MODE_IDLE, true);	} 	if (song != NULL)	{		moduleChannel_t *ch = song->channels;		for (int32_t i = 0; i < PAULA_VOICES; i++, ch++)		{			ch->n_wavecontrol = 0;			ch->n_glissfunk = 0;			ch->n_finetune = 0;			ch->n_loopcount = 0;		}	} 	doStopSong = false; // just in case this flag was stuck from command F00 (stop song)} void setPattern(int16_t pattern){	if (pattern > MAX_PATTERNS-1)		pattern = MAX_PATTERNS-1; 	song->currPattern = modPattern = (int8_t)pattern;} void storeTempVariables(void) // this one is accessed in other files, so non-static{	oldBPM = song->currBPM;	oldRow = song->currRow;	oldPos = song->currPos;	oldSpeed = song->currSpeed;	oldPattern = song->currPattern;} static void setVUMeterHeight(moduleChannel_t *ch){	if (editor.muted[ch->n_chanindex])		return; 	uint8_t vol = ch->n_volume;	if ((ch->n_cmd & 0xF00) == 0xC00) // handle Cxx effect		vol = ch->n_cmd & 0xFF; 	if (vol > 64)		vol = 64; 	if (!editor.songPlaying)	{		editor.vuMeterVolumes[ch->n_chanindex] = vuMeterHeights[vol];	}	else	{		ch->syncVuVolume = vol;		ch->syncFlags |= UPDATE_VUMETER;	}} static void updateFunk(moduleChannel_t *ch){	const int8_t funkSpeed = ch->n_glissfunk >> 4;	if (funkSpeed == 0)		return; 	ch->n_funkoffset += funkTable[funkSpeed];	if (ch->n_funkoffset >= 128)	{		ch->n_funkoffset = 0; 		if (ch->n_loopstart != NULL && ch->n_wavestart != NULL) // ProTracker bugfix		{			if (++ch->n_wavestart >= ch->n_loopstart + (ch->n_replen << 1))				ch->n_wavestart = ch->n_loopstart; 			*ch->n_wavestart = -1 - *ch->n_wavestart;		}	}} static void setGlissControl(moduleChannel_t *ch){	ch->n_glissfunk = (ch->n_glissfunk & 0xF0) | (ch->n_cmd & 0x0F);} static void setVibratoControl(moduleChannel_t *ch){	ch->n_wavecontrol = (ch->n_wavecontrol & 0xF0) | (ch->n_cmd & 0x0F);} static void setFineTune(moduleChannel_t *ch){	ch->n_finetune = ch->n_cmd & 0xF;} static void jumpLoop(moduleChannel_t *ch){	if (song->tick != 0)		return; 	if ((ch->n_cmd & 0xF) == 0)	{		ch->n_pattpos = song->row;	}	else	{		if (ch->n_loopcount == 0)			ch->n_loopcount = ch->n_cmd & 0xF;		else if (--ch->n_loopcount == 0)			return; 		pBreakPosition = ch->n_pattpos;		pBreakFlag = true; 		// stuff used for MOD2WAV to determine if the song has reached its end		if (editor.mod2WavOngoing)		{			for (int32_t tempParam = pBreakPosition; tempParam <= song->row; tempParam++)				editor.rowVisitTable[(modPos * MOD_ROWS) + tempParam] = false;		}	}} static void setTremoloControl(moduleChannel_t *ch){	ch->n_wavecontrol = ((ch->n_cmd & 0xF) << 4) | (ch->n_wavecontrol & 0xF);} /* This is a little used effect, despite being present in original ProTracker.** E8x was sometimes entirely replaced with code used for demo fx syncing in** demo mod players, so it can be turned off by looking at DISABLE_E8X in** protracker.ini if you so desire.*/static void karplusStrong(moduleChannel_t *ch){	int8_t a, b; 	if (config.disableE8xEffect)		return; 	if (ch->n_loopstart == NULL)		return; // ProTracker bugfix 	int8_t *ptr8 = ch->n_loopstart;	int16_t end = ((ch->n_replen * 2) & 0xFFFF) - 2;	do	{		a = ptr8[0];		b = ptr8[1];		*ptr8++ = (a + b) >> 1;	}	while (--end >= 0); 	a = ptr8[0];	b = ch->n_loopstart[0];	*ptr8 = (a + b) >> 1;} static void doRetrg(moduleChannel_t *ch){	const uint32_t voiceAddr = 0xDFF0A0 + (ch->n_chanindex * 16); 	paulaWriteWord(0xDFF096, ch->n_dmabit); // voice DMA off	paulaWritePtr(voiceAddr + 0, ch->n_start); // n_start is increased on 9xx	paulaWriteWord(voiceAddr + 4, ch->n_length);	paulaWriteWord(voiceAddr + 6, ch->n_period);	paulaWriteWord(0xDFF096, 0x8000 | ch->n_dmabit); // voice DMA on		// these take effect after the current DMA cycle is done	paulaWritePtr(voiceAddr + 0, ch->n_loopstart);	paulaWriteWord(voiceAddr + 4, ch->n_replen); 	// update tracker visuals 	setVisualsDMACON(ch->n_dmabit);	setVisualsDataPtr(ch->n_chanindex, ch->n_start);	setVisualsLength(ch->n_chanindex, ch->n_length);	setVisualsPeriod(ch->n_chanindex, ch->n_period);	setVisualsDMACON(0x8000 | ch->n_dmabit); 	setVisualsDataPtr(ch->n_chanindex, ch->n_loopstart);	setVisualsLength(ch->n_chanindex, ch->n_replen); 	// set spectrum analyzer state for this channel	ch->syncAnalyzerVolume = ch->n_volume;	ch->syncAnalyzerPeriod = ch->n_period;	ch->syncFlags |= UPDATE_SPECTRUM_ANALYZER; 	setVUMeterHeight(ch);} static void retrigNote(moduleChannel_t *ch){	if ((ch->n_cmd & 0xF) > 0)	{		if (song->tick == 0 && (ch->n_note & 0xFFF) > 0)			return; 		if (song->tick % (ch->n_cmd & 0xF) == 0)			doRetrg(ch);	}} static void volumeSlide(moduleChannel_t *ch){	uint8_t param = ch->n_cmd & 0xFF;	if ((param & 0xF0) == 0)	{		ch->n_volume -= param & 0x0F;		if (ch->n_volume < 0)			ch->n_volume = 0;	}	else	{		ch->n_volume += param >> 4;		if (ch->n_volume > 64)			ch->n_volume = 64;	}} static void volumeFineUp(moduleChannel_t *ch){	if (song->tick == 0)	{		ch->n_volume += ch->n_cmd & 0xF;		if (ch->n_volume > 64)			ch->n_volume = 64;	}} static void volumeFineDown(moduleChannel_t *ch){	if (song->tick == 0)	{		ch->n_volume -= ch->n_cmd & 0xF;		if (ch->n_volume < 0)			ch->n_volume = 0;	}} static void noteCut(moduleChannel_t *ch){	if (song->tick == (ch->n_cmd & 0xF))		ch->n_volume = 0;} static void noteDelay(moduleChannel_t *ch){	if (song->tick == (ch->n_cmd & 0xF) && (ch->n_note & 0xFFF) > 0)		doRetrg(ch);} static void patternDelay(moduleChannel_t *ch){	if (song->tick == 0 && pattDelTime2 == 0)		pattDelTime = (ch->n_cmd & 0xF) + 1;} static void funkIt(moduleChannel_t *ch){	if (song->tick == 0)	{		ch->n_glissfunk = ((ch->n_cmd & 0xF) << 4) | (ch->n_glissfunk & 0xF); 		if ((ch->n_glissfunk & 0xF0) > 0)			updateFunk(ch);	}} static void positionJump(moduleChannel_t *ch){	// original PT doesn't do this check, but we have to	if (editor.playMode != PLAY_MODE_PATTERN || (editor.currMode == MODE_RECORD && editor.recordMode != RECORD_PATT))		modPos = (ch->n_cmd & 0xFF) - 1; // B00 results in -1, but it safely jumps to order 0 	pBreakPosition = 0;	posJumpAssert = true;} static void volumeChange(moduleChannel_t *ch){	ch->n_volume = ch->n_cmd & 0xFF;	if ((uint8_t)ch->n_volume > 64)		ch->n_volume = 64;} static void patternBreak(moduleChannel_t *ch){	pBreakPosition = (((ch->n_cmd & 0xF0) >> 4) * 10) + (ch->n_cmd & 0x0F);	if ((uint8_t)pBreakPosition > 63)		pBreakPosition = 0; 	posJumpAssert = true;} static void setSpeed(moduleChannel_t *ch){	if ((ch->n_cmd & 0xFF) > 0)	{

Lines 1–500 of 1857.

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