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