Sigrid Solveig Haflínudóttiradd plan 9 stuff0b77c1f5 years agoHistory
1411 lines · 28.0 KB · C
RawDownload
/*
** C-port of FT2.09's XM replayer, by 8bitbubsy nov. 2020
**
** Note: This is not the exact same code used in the FT2 clone!
** This is a direct port meant to give bit-accurate results to
** FT2.08/FT2.09 (non-GUS mode). It's very handy to use as a
** reference if you are trying to make your own, accurate XM
** player.
*/

#define INSTR_HEADER_SIZE 263

#define DEFAULT_AMP 4
#define DEFAULT_MASTER_VOL 256

#include "common.h"
#include "pmplay.h"
#include "pmp_mix.h"
#include "snd_masm.h"
#include "tables.h"

#define SWAP16(value) \
( \
	(((uint16_t)((value) & 0x00FF)) << 8) | \
	(((uint16_t)((value) & 0xFF00)) >> 8)   \
)

 #ifdef __plan9__#pragma pack on#endif #ifdef _MSC_VER
#pragma pack(push)
#pragma pack(1)
#endif
typedef struct songHeaderTyp_t
{
	char sig[17], name[21], progName[20];
	uint16_t ver;
	int32_t headerSize;
	uint16_t len, repS, antChn, antPtn, antInstrs, flags, defTempo, defSpeed;
	uint8_t songTab[256];
}
#ifdef __GNUC__
__attribute__ ((packed))
#elif defined(__plan9__)#pragma pack on#endif
songHeaderTyp;

typedef struct modSampleTyp
{
	char name[22];
	uint16_t len;
	uint8_t fine, vol;
	uint16_t repS, repL;
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
modSampleTyp;

typedef struct songMOD31HeaderTyp
{
	char name[20];
	modSampleTyp sample[31];
	uint8_t len, repS, songTab[128];
	char Sig[4];
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
songMOD31HeaderTyp;

typedef struct songMOD15HeaderTyp
{
	char name[20];
	modSampleTyp sample[15];
	uint8_t len, repS, songTab[128];
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
songMOD15HeaderTyp;

typedef struct sampleHeaderTyp_t
{
	int32_t len, repS, repL;
	uint8_t vol;
	int8_t fine;
	uint8_t typ, pan;
	int8_t relTon;
	uint8_t skrap;
	char name[22];
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
sampleHeaderTyp;

typedef struct instrHeaderTyp_t
{
	int32_t instrSize;
	char name[22];
	uint8_t typ;
	uint16_t antSamp;
	int32_t sampleSize;
	uint8_t ta[96];
	int16_t envVP[12][2], envPP[12][2];
	uint8_t envVPAnt, envPPAnt, envVSust, envVRepS, envVRepE, envPSust, envPRepS;
	uint8_t envPRepE, envVTyp, envPTyp, vibTyp, vibSweep, vibDepth, vibRate;
	uint16_t fadeOut;
	uint8_t midiOn, midiChannel;
	int16_t midiProgram, midiBend;
	int8_t mute;
	uint8_t reserved[15];
	sampleHeaderTyp samp[32];
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
instrHeaderTyp;

typedef struct patternHeaderTyp_t
{
	int32_t patternHeaderSize;
	uint8_t typ;
	uint16_t pattLen, dataLen;
}
#ifdef __GNUC__
__attribute__ ((packed))
#endif
patternHeaderTyp;
#ifdef _MSC_VER
#pragma pack(pop)
#endif
 #ifdef __plan9__#pragma pack off#endif static int32_t soundBufferSize;

// globalized
volatile bool interpolationFlag, volumeRampingFlag, moduleLoaded, musicPaused, WAVDump_Flag;
bool linearFrqTab;
volatile const uint16_t *note2Period;
uint16_t pattLens[256];
int16_t PMPTmpActiveChannel, boostLevel = DEFAULT_AMP;
int32_t masterVol = DEFAULT_MASTER_VOL, PMPLeft = 0;
int32_t realReplayRate, quickVolSizeVal, speedVal;
int32_t frequenceDivFactor, frequenceMulFactor;
uint32_t CDA_Amp = 8*DEFAULT_AMP;
tonTyp *patt[256];
instrTyp *instr[1+128];
songTyp song;
stmTyp stm[32];
// ------------------

// 8bb: added these for loader
typedef struct
{
	uint8_t *_ptr, *_base;
	bool _eof;
	size_t _cnt, _bufsiz;
} MEMFILE;

static MEMFILE *mopen(const uint8_t *src, uint32_t length);
static void mclose(MEMFILE **buf);
static size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf);
static bool meof(MEMFILE *buf);
static void mseek(MEMFILE *buf, int32_t offset, int32_t whence);
static void mrewind(MEMFILE *buf);
// --------------------------

static void resetMusic(void);
static void freeAllPatterns(void);
static void setFrqTab(bool linear);

static CIType *getVoice(int32_t ch) // 8bb: added this
{
	if (ch < 0 || ch > 31)
		return NULL;

	return &CI[chnReloc[ch]];
}

/***************************************************************************
 *        ROUTINES FOR SAMPLE HANDLING ETC.                                *
 ***************************************************************************/

// 8bb: modifies wrapped sample after loop/end (for branchless mixer interpolation)
static void fixSample(sampleTyp *s)
{
	if (s->pek == NULL)
		return; // empty sample

	const bool sample16Bit = (s->typ >> 4) & 1;
	uint8_t loopType = s->typ & 3;
	int16_t *ptr16 = (int16_t *)s->pek;
	int32_t len = s->len;
	int32_t loopStart = s->repS;
	int32_t loopEnd = s->repS + s->repL;

	if (sample16Bit)
	{
		len >>= 1;
		loopStart >>= 1;
		loopEnd >>= 1;
	}

	if (len < 1)
		return;

	/* 8bb:
	** This is the exact bit test order of which FT2 handles
	** the sample fix in SND_MASM.ASM.
	**
	** This order is important for rare cases where both the
	** "forward" and "pingpong" loop bits are set at once.
	**
	** This means that if both flags are set, the mixer will
	** play the sample with pingpong looping, but the sample fix
	** is handled as if it was a forward loop. This results in
	** the wrong interpolation tap sample being written after the
	** loop end point.
	*/

	if (loopType & 1)
	{
		// forward loop
		if (sample16Bit)
			ptr16[loopEnd] = ptr16[loopStart];
		else
			s->pek[loopEnd] = s->pek[loopStart];

		return;
	}
	else if (loopType & 2)
	{
		// pingpong loop
		if (sample16Bit)
			ptr16[loopEnd] = ptr16[loopEnd-1];
		else
			s->pek[loopEnd] = s->pek[loopEnd-1];
	}
	else
	{
		// no loop
		if (sample16Bit)
			ptr16[len] = 0;
		else
			s->pek[len] = 0;
	}
}

static void checkSampleRepeat(int32_t nr, int32_t nr2)
{
	instrTyp *i = instr[nr];
	if (i == NULL) return;
	sampleTyp *s = &i->samp[nr2];

	if (s->repS < 0) s->repS = 0;
	if (s->repL < 0) s->repL = 0;
	if (s->repS > s->len) s->repS = s->len;
	if (s->repS+s->repL > s->len) s->repL = s->len - s->repS;
}

static void upDateInstrs(void)
{
	for (int32_t i = 0; i <= 128; i++)
	{
		instrTyp *ins = instr[i];
		if (ins == NULL)
			continue;

		sampleTyp *s = ins->samp;
		for (int32_t j = 0; j < 16; j++, s++)
		{
			checkSampleRepeat(i, j);
			fixSample(s);

			if (s->pek == NULL)
			{
				s->len = 0;
				s->repS = 0;
				s->repL = 0;
			}
		}
	}
}

static bool patternEmpty(uint16_t nr)
{
	if (patt[nr] == NULL)
		return true;

	const uint8_t *scanPtr = (const uint8_t *)patt[nr];
	const int32_t scanLen = pattLens[nr] * song.antChn * sizeof (tonTyp);

	for (int32_t i = 0; i < scanLen; i++)
	{
		if (scanPtr[i] != 0)
			return false;
	}

	return true;
}

static bool allocateInstr(uint16_t i)
{
	if (instr[i] != NULL)
		return true;

	instrTyp *p = (instrTyp *)calloc(1, sizeof (instrTyp));
	if (p == NULL)
		return false;

	sampleTyp *s = p->samp;
	for (int32_t j = 0; j < 16; j++, s++)
	{
		s->pan = 128;
		s->vol = 64;
	}

	instr[i] = p;
	return true;
}

static void freeInstr(uint16_t nr)
{
	if (nr > 128)
		return;

	instrTyp *ins = instr[nr];
	if (ins == NULL)
		return;

	sampleTyp *s = ins->samp;
	for (uint8_t i = 0; i < 16; i++, s++)
	{
		if (s->pek != NULL)
			free(s->pek);
	}

	free(ins);
	instr[nr] = NULL;
}

static void freeAllInstr(void)
{
	for (uint16_t i = 0; i <= 128; i++)
		freeInstr(i);
}

static void freeAllPatterns(void) // 8bb: added this one, since it's handy
{
	for (int32_t i = 0; i < 256; i++)
	{
		if (patt[i] != NULL)
		{
			free(patt[i]);
			patt[i] = NULL;
		}

		pattLens[i] = 64;
	}
}

static void delta2Samp(int8_t *p, uint32_t len, bool sample16Bit)
{
	if (sample16Bit)
	{
		len >>= 1;
	
		int16_t *p16 = (int16_t *)p;

		int16_t olds16 = 0;
		for (uint32_t i = 0; i < len; i++)
		{
			const int16_t news16 = p16[i] + olds16;
			p16[i] = news16;
			olds16 = news16;
		}
	}
	else
	{
		int8_t *p8 = (int8_t *)p;

		int8_t olds8 = 0;
		for (uint32_t i = 0; i < len; i++)
		{
			const int8_t news8 = p8[i] + olds8;
			p8[i] = news8;
			olds8 = news8;
		}
	}
}

static void unpackPatt(uint8_t *dst, uint16_t inn, uint16_t len, uint8_t antChn)
{
	if (dst == NULL)
		return;

	const uint8_t *src = dst + inn;
	const int32_t srcEnd = len * (sizeof (tonTyp) * antChn);

	int32_t srcIdx = 0;
	for (int32_t i = 0; i < len; i++)
	{
		for (int32_t j = 0; j < antChn; j++)
		{
			if (srcIdx >= srcEnd)
				return; // error!

			const uint8_t note = *src++;
			if (note & 0x80)
			{
				*dst++ = (note & 0x01) ? *src++ : 0;
				*dst++ = (note & 0x02) ? *src++ : 0;
				*dst++ = (note & 0x04) ? *src++ : 0;
				*dst++ = (note & 0x08) ? *src++ : 0;
				*dst++ = (note & 0x10) ? *src++ : 0;
			}
			else
			{
				*dst++ = note;
				*dst++ = *src++;
				*dst++ = *src++;
				*dst++ = *src++;
				*dst++ = *src++;
			}

			// 8bb: added this. If note is overflowing (>97), remove it (prevent LUT buffer overrun)
			if (*(dst-5) > 97)
				*(dst-5) = 0;

			srcIdx += sizeof (tonTyp);
		}
	}
}

void freeMusic(void)
{
	stopMusic();
	freeAllInstr();
	freeAllPatterns();

	song.tempo = 6;
	song.speed = 125;
	song.timer = 1;

	setFrqTab(true);
	resetMusic();
}

void stopVoices(void)
{
	lockMixer();

	stmTyp *ch = stm;
	for (uint8_t i = 0; i < 32; i++, ch++)
	{
		ch->tonTyp = 0;
		ch->relTonNr = 0;
		ch->instrNr = 0;
		ch->instrSeg = instr[0]; // 8bb: placeholder instrument
		ch->status = IS_Vol;

		ch->realVol = 0;
		ch->outVol = 0;
		ch->oldVol = 0;
		ch->finalVol = 0;
		ch->oldPan = 128;
		ch->outPan = 128;
		ch->finalPan = 128;
		ch->vibDepth = 0;
	}

	unlockMixer();
}

static void resetMusic(void)
{
	song.timer = 1;
	stopVoices();
	setPos(0, 0);
}

void setPos(int32_t pos, int32_t row) // -1 = don't change
{
	if (pos != -1)
	{
		song.songPos = (int16_t)pos;
		if (song.len > 0 && song.songPos >= song.len)
			song.songPos = song.len - 1;

		song.pattNr = song.songTab[song.songPos];
		song.pattLen = pattLens[song.pattNr];

Lines 1–500 of 1411.

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