ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

ft2_load_brr.c (3926B)


      1 /* Super Nintendo BRR sample loader (based on work by _astriid_, but heavily modified)
      2 **
      3 ** Note: Vol/loop sanitation is done in the last stage
      4 ** of sample loading, so you don't need to do that here.
      5 ** Do NOT close the file handle!
      6 */
      7 
      8 #include <stdio.h>
      9 #include <stdint.h>
     10 #include <stdbool.h>
     11 #include "../ft2_header.h"
     12 #include "../ft2_sample_ed.h"
     13 #include "../ft2_sysreqs.h"
     14 #include "../ft2_sample_loader.h"
     15 
     16 #define BRR_RATIO(x) (((x) * 16) / 9)
     17 
     18 static int16_t s1, s2;
     19 
     20 bool detectBRR(FILE *f)
     21 {
     22 	if (f == NULL)
     23 		return false;
     24 
     25 	uint32_t oldPos = (uint32_t)ftell(f);
     26 	fseek(f, 0, SEEK_END);
     27 	uint32_t filesize = (uint32_t)ftell(f);
     28 
     29 	const uint32_t filesizeMod9 = filesize % 9;
     30 
     31 	if (filesize < 11 || filesize > 65536 || (filesizeMod9 != 0 && filesizeMod9 != 2))
     32 		goto error; // definitely not a BRR file
     33 
     34 	rewind(f);
     35 
     36 	uint32_t blockBytes = filesize;
     37 	if (filesizeMod9 == 2) // skip loop block word
     38 	{
     39 		fseek(f, 2, SEEK_CUR);
     40 		blockBytes -= 2;
     41 	}
     42 
     43 	uint32_t numBlocks = blockBytes / 9;
     44 
     45 	// if the first block is the last, this is very unlikely to be a real BRR sample
     46 	uint8_t header = (uint8_t)fgetc(f);
     47 	if (header & 1)
     48 		goto error;
     49 
     50 	/* Test the shift range of a few blocks.
     51 	** While it's possible to have a shift value above 12 (illegal),
     52 	** it's rare in dumped BRR samples. I have personally seen 13,
     53 	** but never above, so let's test for >13 then.
     54 	*/
     55 	uint32_t blocksToTest = 8;
     56 	if (blocksToTest > numBlocks)
     57 		blocksToTest = numBlocks;
     58 
     59 	for (uint32_t i = 0; i < blocksToTest; i++)
     60 	{
     61 		const uint8_t shift = header >> 4;
     62 		if (shift > 13)
     63 			goto error;
     64 
     65 		fseek(f, 8, SEEK_CUR);
     66 		header = (uint8_t)fgetc(f);
     67 	}
     68 
     69 	fseek(f, oldPos, SEEK_SET);
     70 	return true;
     71 
     72 error:
     73 	fseek(f, oldPos, SEEK_SET);
     74 	return false;
     75 }
     76 
     77 static int16_t decodeSample(int8_t nybble, int32_t shift, int32_t filter)
     78 {
     79 	int32_t smp = (nybble << shift) >> 1;
     80 	if (shift >= 13)
     81 		smp &= ~2047; // invalid shift clamping
     82 
     83 	switch (filter)
     84 	{
     85 		default: break;
     86 
     87 		case 1:
     88 			smp += (s1 * 15) >> 4;
     89 			break;
     90 
     91 		case 2:
     92 			smp += (s1 * 61) >> 5;
     93 			smp -= (s2 * 15) >> 4;
     94 			break;
     95 
     96 		case 3:
     97 			smp += (s1 * 115) >> 6;
     98 			smp -= (s2 *  13) >> 4;
     99 			break;
    100 	}
    101 
    102 	// clamp as 16-bit, even if we decode into a 15-bit sample
    103 	smp = CLAMP(smp, -32768, 32767);
    104 
    105 	// 15-bit clip
    106 	if (smp & 16384)
    107 		smp |= ~16383;
    108 	else
    109 		smp &= 16383;
    110 
    111 	// shuffle last samples (store as 15-bit sample)
    112 	s2 = s1;
    113 	s1 = (int16_t)smp;
    114 
    115 	return (int16_t)(smp << 1); // multiply by two to get 16-bit scale
    116 }
    117 
    118 bool loadBRR(FILE *f, uint32_t filesize)
    119 {
    120 	sample_t *s = &tmpSmp;
    121 
    122 	uint32_t blockBytes = filesize, loopStart = 0;
    123 	if ((filesize % 9) == 2) // loop header present
    124 	{
    125 		uint16_t loopStartBlock;
    126 		fread(&loopStartBlock, 2, 1, f);
    127 		loopStart = BRR_RATIO(loopStartBlock);
    128 		blockBytes -= 2;
    129 	}
    130 
    131 	uint32_t sampleLength = BRR_RATIO(blockBytes);
    132 	if (!allocateSmpData(s, sampleLength, true))
    133 	{
    134 		loaderMsgBox("Not enough memory!");
    135 		return false;
    136 	}
    137 
    138 	uint32_t shift = 0, filter = 0;
    139 	bool loopFlag = false, endFlag = false;
    140 
    141 	s1 = s2 = 0; // clear last BRR samples (for decoding)
    142 
    143 	int16_t *ptr16 = (int16_t *)s->dataPtr;
    144 	for (uint32_t i = 0; i < blockBytes; i++)
    145 	{
    146 		const uint32_t blockOffset = i % 9;
    147 		const uint8_t byte = (uint8_t)fgetc(f);
    148 
    149 		if (blockOffset == 0) // this byte is the BRR header
    150 		{
    151 			shift = byte >> 4;
    152 			filter = (byte & 0x0C) >> 2;
    153 			loopFlag = !!(byte & 0x02);
    154 			endFlag = !!(byte & 0x01);
    155 			continue;
    156 		}
    157 
    158 		// decode samples
    159 		*ptr16++ = decodeSample((int8_t)byte >> 4, shift, filter);
    160 		*ptr16++ = decodeSample((int8_t)(byte << 4) >> 4, shift, filter);
    161 
    162 		if (endFlag && blockOffset == 8)
    163 		{
    164 			sampleLength = BRR_RATIO(i+1);
    165 			break;
    166 		}
    167 	}
    168 
    169 	s->volume = 64;
    170 	s->panning = 128;
    171 	s->flags |= SAMPLE_16BIT;
    172 	s->length = sampleLength;
    173 
    174 	if (loopFlag) // XXX: Maybe this is not how to do it..?
    175 	{
    176 		s->flags |= LOOP_FWD;
    177 		s->loopStart = loopStart;
    178 		s->loopLength = sampleLength - loopStart;
    179 	}
    180 
    181 	return true;
    182 }