ft2_sample_loader.c (7144B)
1 // for finding memory leaks in debug mode with Visual Studio 2 #if defined _DEBUG && defined _MSC_VER 3 #include <crtdbg.h> 4 #endif 5 6 #include <stdio.h> 7 #include <stdint.h> 8 #include <stdbool.h> 9 #include <math.h> 10 #include "ft2_header.h" 11 #include "ft2_gui.h" 12 #include "ft2_unicode.h" 13 #include "ft2_audio.h" 14 #include "ft2_sample_ed.h" 15 #include "ft2_mouse.h" 16 #include "ft2_diskop.h" 17 #include "ft2_structs.h" 18 19 #ifdef HAS_LIBFLAC 20 bool loadFLAC(FILE *f, uint32_t filesize); 21 #endif 22 23 bool detectBRR(FILE *f); 24 bool loadBRR(FILE *f, uint32_t filesize); 25 26 bool loadAIFF(FILE *f, uint32_t filesize); 27 bool loadIFF(FILE *f, uint32_t filesize); 28 bool loadRAW(FILE *f, uint32_t filesize); 29 bool loadWAV(FILE *f, uint32_t filesize); 30 31 enum 32 { 33 FORMAT_UNKNOWN = 0, 34 FORMAT_IFF = 1, 35 FORMAT_WAV = 2, 36 FORMAT_AIFF = 3, 37 FORMAT_FLAC = 4, 38 FORMAT_BRR = 5 39 }; 40 41 // file extensions accepted by Disk Op. in sample mode 42 char *supportedSmpExtensions[] = 43 { 44 "iff", "raw", "wav", "snd", "smp", "sam", "aif", "pat", 45 "aiff","flac","brr", // IMPORTANT: Remember comma after last entry!!! 46 47 "END_OF_LIST" // do NOT move, remove or edit this line! 48 }; 49 50 // globals for sample loaders 51 bool loadAsInstrFlag, smpFilenameSet; 52 char *smpFilename; 53 uint8_t sampleSlot; 54 sample_t tmpSmp; 55 // -------------------------- 56 57 static volatile bool sampleIsLoading; 58 static SDL_Thread *thread; 59 60 static void freeTmpSample(sample_t *s); 61 62 // Crude sample detection routine. These aren't always accurate detections! 63 static int8_t detectSample(FILE *f) 64 { 65 uint8_t D[512]; 66 67 uint32_t oldPos = ftell(f); 68 rewind(f); 69 memset(D, 0, sizeof (D)); 70 fread(D, 1, sizeof (D), f); 71 fseek(f, oldPos, SEEK_SET); 72 73 if (!memcmp("fLaC", &D[0], 4)) // XXX: Kinda lousy detection... 74 return FORMAT_FLAC; 75 76 if (!memcmp("FORM", &D[0], 4) && (!memcmp("8SVX", &D[8], 4) || !memcmp("16SV", &D[8], 4))) 77 return FORMAT_IFF; 78 79 if (!memcmp("RIFF", &D[0], 4) && !memcmp("WAVE", &D[8], 4)) 80 return FORMAT_WAV; 81 82 if (!memcmp("FORM", &D[0], 4) && (!memcmp("AIFF", &D[8], 4) || !memcmp("AIFC", &D[8], 4))) 83 return FORMAT_AIFF; 84 85 if (detectBRR(f)) 86 return FORMAT_BRR; 87 88 return FORMAT_UNKNOWN; 89 } 90 91 static int32_t SDLCALL loadSampleThread(void *ptr) 92 { 93 if (editor.tmpFilenameU == NULL) 94 { 95 loaderMsgBox("General I/O error during loading!"); 96 goto loadError; 97 } 98 99 FILE *f = UNICHAR_FOPEN(editor.tmpFilenameU, "rb"); 100 if (f == NULL) 101 { 102 loaderMsgBox("General I/O error during loading! Is the file in use?"); 103 goto loadError; 104 } 105 106 int8_t format = detectSample(f); 107 fseek(f, 0, SEEK_END); 108 uint32_t filesize = ftell(f); 109 110 if (filesize == 0) 111 { 112 fclose(f); 113 loaderMsgBox("Error loading sample: The file is empty!"); 114 goto loadError; 115 } 116 117 bool sampleLoaded = false; 118 119 rewind(f); 120 switch (format) 121 { 122 case FORMAT_FLAC: 123 { 124 #ifdef HAS_LIBFLAC 125 sampleLoaded = loadFLAC(f, filesize); 126 #else 127 loaderMsgBox("Can't load sample: Program is not compiled with FLAC support!"); 128 #endif 129 } 130 break; 131 132 case FORMAT_IFF: sampleLoaded = loadIFF(f, filesize); break; 133 case FORMAT_WAV: sampleLoaded = loadWAV(f, filesize); break; 134 case FORMAT_AIFF: sampleLoaded = loadAIFF(f, filesize); break; 135 case FORMAT_BRR: sampleLoaded = loadBRR(f, filesize); break; 136 default: sampleLoaded = loadRAW(f, filesize); break; 137 } 138 fclose(f); 139 140 if (!sampleLoaded) 141 goto loadError; 142 143 // sample loaded successfully! 144 145 if (!smpFilenameSet) // if we didn't set a custom sample name in the loader, set it to its filename 146 { 147 char *tmpFilename = unicharToCp850(editor.tmpFilenameU, true); 148 if (tmpFilename != NULL) 149 { 150 int32_t i = (int32_t)strlen(tmpFilename); 151 while (i--) 152 { 153 if (tmpFilename[i] == DIR_DELIMITER) 154 break; 155 } 156 157 char *tmpPtr = tmpFilename; 158 if (i > 0) 159 tmpPtr += i+1; 160 161 sanitizeFilename(tmpPtr); 162 163 int32_t filenameLen = (int32_t)strlen(tmpPtr); 164 for (i = 0; i < 22; i++) 165 { 166 if (i < filenameLen) 167 tmpSmp.name[i] = tmpPtr[i]; 168 else 169 tmpSmp.name[i] = '\0'; 170 } 171 172 free(tmpFilename); 173 } 174 } 175 176 fixString(tmpSmp.name, 21); // remove leading spaces from sample filename 177 178 lockMixerCallback(); 179 if (loadAsInstrFlag) // if loaded in instrument mode 180 { 181 freeInstr(editor.curInstr); 182 memset(song.instrName[editor.curInstr], 0, 23); 183 } 184 185 if (instr[editor.curInstr] == NULL) 186 allocateInstr(editor.curInstr); 187 188 if (instr[editor.curInstr] == NULL) 189 { 190 loaderMsgBox("Not enough memory!"); 191 goto loadError; 192 } 193 194 sample_t *s = &instr[editor.curInstr]->smp[sampleSlot]; 195 196 freeSample(editor.curInstr, sampleSlot); 197 memcpy(s, &tmpSmp, sizeof (sample_t)); 198 199 sanitizeSample(s); 200 201 fixSample(s); // prepares sample for branchless resampling interpolation 202 fixInstrAndSampleNames(editor.curInstr); 203 204 unlockMixerCallback(); 205 206 setSongModifiedFlag(); 207 208 // when caught in main/video thread, it disables busy mouse and sets sampleIsLoading to true 209 editor.updateCurSmp = true; 210 211 return true; 212 213 loadError: 214 setMouseBusy(false); 215 freeTmpSample(&tmpSmp); 216 sampleIsLoading = false; 217 return false; 218 219 (void)ptr; 220 } 221 222 static void freeTmpSample(sample_t *s) 223 { 224 freeSmpData(s); 225 } 226 227 void removeSampleIsLoadingFlag(void) 228 { 229 sampleIsLoading = false; 230 } 231 232 bool loadSample(UNICHAR *filenameU, uint8_t smpNr, bool instrFlag) 233 { 234 if (sampleIsLoading || filenameU == NULL) 235 return false; 236 237 // setup message box functions 238 loaderMsgBox = myLoaderMsgBoxThreadSafe; 239 loaderSysReq = okBoxThreadSafe; 240 241 if (editor.curInstr == 0) 242 { 243 loaderMsgBox("The zero-instrument cannot hold instrument data!"); 244 return false; 245 } 246 247 sampleSlot = smpNr; 248 loadAsInstrFlag = instrFlag; 249 sampleIsLoading = true; 250 smpFilenameSet = false; 251 252 memset(&tmpSmp, 0, sizeof (tmpSmp)); 253 UNICHAR_STRCPY(editor.tmpFilenameU, filenameU); 254 255 mouseAnimOn(); 256 thread = SDL_CreateThread(loadSampleThread, NULL, NULL); 257 if (thread == NULL) 258 { 259 sampleIsLoading = false; 260 loaderMsgBox("Couldn't create thread!"); 261 return false; 262 } 263 264 SDL_DetachThread(thread); 265 return true; 266 } 267 268 void normalizeSigned32Bit(int32_t *sampleData, uint32_t sampleLength) 269 { 270 uint32_t i; 271 272 uint32_t sampleVolPeak = 0; 273 for (i = 0; i < sampleLength; i++) 274 { 275 const uint32_t sample = ABS(sampleData[i]); 276 if (sampleVolPeak < sample) 277 sampleVolPeak = sample; 278 } 279 280 if (sampleVolPeak <= 0) 281 return; 282 283 const double dGain = (double)INT32_MAX / sampleVolPeak; 284 for (i = 0; i < sampleLength; i++) 285 sampleData[i] = (int32_t)(sampleData[i] * dGain); 286 } 287 288 void normalize32BitFloatToSigned16Bit(float *fSampleData, uint32_t sampleLength) 289 { 290 uint32_t i; 291 292 float fSampleVolPeak = 0.0f; 293 for (i = 0; i < sampleLength; i++) 294 { 295 const float fSample = fabsf(fSampleData[i]); 296 if (fSampleVolPeak < fSample) 297 fSampleVolPeak = fSample; 298 } 299 300 if (fSampleVolPeak <= 0.0f) 301 return; 302 303 const float fGain = (float)INT16_MAX / fSampleVolPeak; 304 for (i = 0; i < sampleLength; i++) 305 fSampleData[i] *= fGain; 306 } 307 308 void normalize64BitFloatToSigned16Bit(double *dSampleData, uint32_t sampleLength) 309 { 310 uint32_t i; 311 312 double dSampleVolPeak = 0.0; 313 for (i = 0; i < sampleLength; i++) 314 { 315 const double dSample = fabs(dSampleData[i]); 316 if (dSampleVolPeak < dSample) 317 dSampleVolPeak = dSample; 318 } 319 320 if (dSampleVolPeak <= 0.0) 321 return; 322 323 const double dGain = (double)INT16_MAX / dSampleVolPeak; 324 for (i = 0; i < sampleLength; i++) 325 dSampleData[i] *= dGain; 326 }