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