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 }