ft2-clone

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

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 }