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