1 /*
2 * Copyright (c) 2009, The MilkyTracker Team.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * - Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * - Neither the name of the <ORGANIZATION> nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * SampleLoaderAIFF.cpp
32 * MilkyPlay
33 *
34 * Created by Peter Barth on 07.01.06.
35 *
36 */
37
38 #include "SampleLoaderAIFF.h"
39 #include "XMFile.h"
40 #include "XModule.h"
41 #include "LittleEndian.h"
42
43 const char* SampleLoaderAIFF::channelNames[] = {"Left","Right"};
44
SampleLoaderAIFF(const SYSCHAR * fileName,XModule & theModule)45 SampleLoaderAIFF::SampleLoaderAIFF(const SYSCHAR* fileName, XModule& theModule) :
46 SampleLoaderAbstract(fileName, theModule)
47 {
48 }
49
identifySample()50 bool SampleLoaderAIFF::identifySample()
51 {
52 return getNumChannels() != 0;
53 }
54
getNumChannels()55 mp_sint32 SampleLoaderAIFF::getNumChannels()
56 {
57 mp_ubyte ID[4], buffer[4];
58 mp_dword chunkLen;
59
60 XMFile f(theFileName);
61
62 f.read(ID, 4, 1);
63 if (memcmp(ID, "FORM", 4) != 0)
64 return 0;
65
66 f.seek(0);
67
68 bool hasFORM = false;
69 bool hasFVER = false;
70 bool hasCOMM = false;
71 bool hasSSND = false;
72
73 mp_sint32 numChannels = 0;
74
75 while (!(hasFORM && hasFVER && hasCOMM && hasSSND))
76 {
77
78 mp_uint32 bytesRead = f.read(ID, 4, 1);
79 if (bytesRead != 4)
80 break;
81
82 bytesRead = f.read(buffer, 4, 1);
83 if (bytesRead != 4)
84 break;
85
86 chunkLen = BigEndian::GET_DWORD(buffer);
87
88 switch (BigEndian::GET_DWORD(ID))
89 {
90 case 0x464F524D: // 'FORM'
91 {
92 f.read(buffer, 4, 1);
93 if (memcmp(buffer, "AIFC", 4) == 0)
94 hasFORM = true;
95 else if (memcmp(buffer, "AIFF", 4) == 0)
96 hasFORM = hasFVER = true;
97 break;
98 }
99
100 case 0x46564552 : // 'FVER'
101 {
102 hasFVER = true;
103 mp_uint32 pos = f.pos();
104 f.seek(pos + chunkLen);
105 break;
106 }
107
108 case 0x434F4D4D: // 'COMM'
109 {
110 hasCOMM = true;
111 mp_uint32 pos = f.pos();
112
113 f.read(buffer, 2, 1);
114 numChannels = BigEndian::GET_WORD(buffer);
115
116 f.seek(pos + chunkLen);
117 break;
118 }
119
120 case 0x53534E44 : // 'SSND'
121 {
122 hasSSND = true;
123 mp_uint32 pos = f.pos();
124 f.seek(pos + chunkLen);
125 break;
126 }
127
128 default:
129 {
130 mp_uint32 pos = f.pos();
131 f.seek(pos + chunkLen);
132 }
133 }
134 }
135
136 return (hasFORM && hasFVER && hasCOMM && hasSSND) ? numChannels : 0;
137 }
138
getChannelName(mp_sint32 channelIndex)139 const char* SampleLoaderAIFF::getChannelName(mp_sint32 channelIndex)
140 {
141 if (channelIndex < 2)
142 return channelNames[channelIndex];
143 else
144 return SampleLoaderAbstract::getChannelName(channelIndex);
145 }
146
147 struct AIFC_CommChunk
148 {
149 mp_uword numChannels; //number of channels
150 mp_uint32 numSampleFrames; //number of sample frames
151 mp_uword sampleSize; //number of bits per sample
152 mp_uint32 sampleRate; //number of frames per second
153 mp_uint32 compressionType; //compression type ID
154
AIFC_CommChunkAIFC_CommChunk155 AIFC_CommChunk() :
156 numChannels(0),
157 numSampleFrames(0),
158 sampleSize(0),
159 sampleRate(0),
160 compressionType(0)
161 {
162 }
163 };
164
loadSample(mp_sint32 index,mp_sint32 channelIndex)165 mp_sint32 SampleLoaderAIFF::loadSample(mp_sint32 index, mp_sint32 channelIndex)
166 {
167 mp_ubyte ID[4], buffer[4];
168 mp_dword chunkLen;
169 AIFC_CommChunk commChunk;
170
171 XMFile f(theFileName);
172
173 f.read(ID, 4, 1);
174 if (memcmp(ID, "FORM", 4) != 0)
175 return MP_LOADER_FAILED;
176
177 f.seek(0);
178
179 bool hasFORM = false;
180 bool hasFVER = false;
181 bool hasCOMM = false;
182 bool hasSSND = false;
183
184 bool aifc = false;
185 bool sowt = false;
186
187 mp_sbyte* sampleData = NULL;
188 mp_sint32 sampleDataLen = 0;
189
190 while (!(hasFORM && hasFVER && hasCOMM && hasSSND))
191 {
192
193 mp_uint32 bytesRead = f.read(ID, 4, 1);
194 if (bytesRead != 4)
195 break;
196
197 bytesRead = f.read(buffer, 4, 1);
198 if (bytesRead != 4)
199 break;
200
201 chunkLen = BigEndian::GET_DWORD(buffer);
202
203 switch (BigEndian::GET_DWORD(ID))
204 {
205 case 0x464F524D: // 'FORM'
206 {
207 f.read(buffer, 4, 1);
208 if (memcmp(buffer, "AIFC", 4) == 0)
209 {
210 hasFORM = true;
211 aifc = true;
212 }
213 else if (memcmp(buffer, "AIFF", 4) == 0)
214 {
215 hasFORM = hasFVER = true;
216 aifc = false;
217 }
218 break;
219 }
220
221 case 0x46564552 : // 'FVER'
222 {
223 hasFVER = true;
224 mp_uint32 pos = f.pos();
225 f.seek(pos + chunkLen);
226 break;
227 }
228
229 case 0x434F4D4D: // 'COMM'
230 {
231 hasCOMM = true;
232 //mp_uint32 pos = f.pos();
233
234 mp_ubyte* temp = new mp_ubyte[chunkLen];
235
236 f.read(temp, chunkLen, 1);
237
238 commChunk.numChannels = BigEndian::GET_WORD(temp);
239 commChunk.numSampleFrames = BigEndian::GET_DWORD(temp+2);
240 commChunk.sampleSize = BigEndian::GET_WORD(temp+6);
241 commChunk.sampleRate = BigEndian::GET_DWORD(temp+10) >> 16;
242
243 if (aifc)
244 {
245 commChunk.compressionType = BigEndian::GET_DWORD(temp+14);
246 mp_dword compressionName = BigEndian::GET_DWORD(temp+18);
247 sowt = (compressionName == 0x736F7774);
248 }
249
250 delete[] temp;
251
252 if ((commChunk.compressionType != 0) &&
253 (commChunk.compressionType != 0x4E4F4E45/*NONE*/))
254 {
255 return MP_LOADER_FAILED;
256 }
257
258 break;
259 }
260
261 case 0x53534E44 : // 'SSND'
262 {
263 hasSSND = true;
264 sampleDataLen = chunkLen;
265 sampleData = new mp_sbyte[chunkLen];
266 f.read(sampleData, chunkLen, 1);
267 break;
268 }
269
270 default:
271 {
272 mp_uint32 pos = f.pos();
273 f.seek(pos + chunkLen);
274 }
275 }
276 }
277
278 if (hasFORM && hasFVER && hasCOMM && hasSSND)
279 {
280 if ((commChunk.numChannels >= 1) &&
281 (commChunk.numChannels <= 2) &&
282 (commChunk.sampleSize == 8 ||
283 commChunk.sampleSize == 16))
284 {
285 sampleDataLen = (commChunk.numChannels * commChunk.numSampleFrames * commChunk.sampleSize) / 8;
286
287 TXMSample* smp = &theModule.smp[index];
288
289 if (smp->sample)
290 {
291 theModule.freeSampleMem((mp_ubyte*)smp->sample);
292 smp->sample = NULL;
293 }
294
295 smp->samplen = ((commChunk.sampleSize == 16) ? sampleDataLen >> 1 : sampleDataLen) / commChunk.numChannels;
296
297 smp->sample = (mp_sbyte*)theModule.allocSampleMem((commChunk.sampleSize == 16) ? (smp->samplen<<1) : smp->samplen);
298 if (smp->sample == NULL)
299 {
300 if (sampleData)
301 delete[] sampleData;
302 return MP_OUT_OF_MEMORY;
303 }
304
305 if (commChunk.sampleSize == 8)
306 {
307 mp_sbyte* ptr = (mp_sbyte*)smp->sample;
308 mp_sbyte* src = (mp_sbyte*)sampleData;
309
310 if (commChunk.numChannels == 1)
311 {
312 memcpy(ptr, src, smp->samplen);
313 }
314 else if (commChunk.numChannels == 2)
315 {
316 if (channelIndex == 0)
317 {
318 for (mp_uint32 i = 0; i < smp->samplen; i++)
319 {
320 *ptr = *src;
321 ptr++;
322 src+=2;
323 }
324 }
325 else if (channelIndex == 1)
326 {
327 for (mp_uint32 i = 0; i < smp->samplen; i++)
328 {
329 *ptr = *(src+1);
330 ptr++;
331 src+=2;
332 }
333 }
334 else if (channelIndex == -1)
335 {
336 for (mp_uint32 i = 0; i < smp->samplen; i++)
337 {
338 *ptr = (mp_sbyte)(((mp_sword)(*src) + (mp_sword)(*(src+1))) >> 1);
339 ptr++;
340 src+=2;
341 }
342 }
343 }
344 }
345 else if (commChunk.sampleSize == 16)
346 {
347 mp_uword* ptr = (mp_uword*)smp->sample;
348 mp_ubyte* src = (mp_ubyte*)sampleData;
349
350 if (commChunk.numChannels == 1)
351 {
352 for (mp_uint32 i = 0; i < smp->samplen; i++)
353 {
354 *ptr = sowt ? LittleEndian::GET_WORD(src) : BigEndian::GET_WORD(src);
355 ptr++;
356 src+=2;
357 }
358 }
359 else if (commChunk.numChannels == 2)
360 {
361 if (channelIndex == 0)
362 {
363 for (mp_uint32 i = 0; i < smp->samplen; i++)
364 {
365 *ptr = sowt ? LittleEndian::GET_WORD(src) : BigEndian::GET_WORD(src);
366 ptr++;
367 src+=4;
368 }
369 }
370 else if (channelIndex == 1)
371 {
372 for (mp_uint32 i = 0; i < smp->samplen; i++)
373 {
374 *ptr = sowt ? LittleEndian::GET_WORD(src+2) : BigEndian::GET_WORD(src+2);
375 ptr++;
376 src+=4;
377 }
378 }
379 else if (channelIndex == -1)
380 {
381 for (mp_uint32 i = 0; i < smp->samplen; i++)
382 {
383 *ptr = sowt ?
384 (mp_sword)(((mp_sint32)((mp_sword)LittleEndian::GET_WORD(src)) + (mp_sint32)((mp_sword)LittleEndian::GET_WORD(src+2))) >> 1) :
385 (mp_sword)(((mp_sint32)((mp_sword)BigEndian::GET_WORD(src)) + (mp_sint32)((mp_sword)BigEndian::GET_WORD(src+2))) >> 1);
386 ptr++;
387 src+=4;
388 }
389 }
390 }
391 }
392
393 delete[] sampleData;
394
395 smp->loopstart = 0;
396 smp->looplen = 0;
397 smp->type = 0;
398 if (commChunk.sampleSize == 16)
399 smp->type |= 16;
400
401 nameToSample(preferredDefaultName, smp);
402
403 XModule::convertc4spd(commChunk.sampleRate, &smp->finetune, &smp->relnote);
404
405 return MP_OK;
406 }
407 }
408
409 delete[] sampleData;
410
411 return MP_LOADER_FAILED;
412 }
413
saveSample(const SYSCHAR * fileName,mp_sint32 index)414 mp_sint32 SampleLoaderAIFF::saveSample(const SYSCHAR* fileName, mp_sint32 index)
415 {
416 return MP_UNSUPPORTED;
417 }
418