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 * SampleLoaderIFF.cpp
32 * MilkyPlay
33 *
34 * Created by Peter Barth on 07.01.06.
35 *
36 */
37
38 #include "SampleLoaderIFF.h"
39 #include "XMFile.h"
40 #include "XModule.h"
41 #include "LittleEndian.h"
42
SampleLoaderIFF(const SYSCHAR * fileName,XModule & theModule)43 SampleLoaderIFF::SampleLoaderIFF(const SYSCHAR* fileName, XModule& theModule) :
44 SampleLoaderAbstract(fileName, theModule)
45 {
46 }
47
identifySample()48 bool SampleLoaderIFF::identifySample()
49 {
50 return getNumChannels() != 0;
51 }
52
getNumChannels()53 mp_sint32 SampleLoaderIFF::getNumChannels()
54 {
55 mp_ubyte ID[4], buffer[4];
56 mp_dword chunkLen;
57
58 XMFile f(theFileName);
59
60 f.read(ID, 4, 1);
61 if (memcmp(ID, "FORM", 4) != 0)
62 return 0;
63
64 f.seek(0);
65
66 bool hasFORM = false;
67 bool hasVHDR = false;
68 bool hasBODY = false;
69
70 while (!(hasFORM && hasVHDR && hasBODY))
71 {
72
73 mp_uint32 bytesRead = f.read(ID, 4, 1);
74 if (bytesRead != 4)
75 break;
76
77 bytesRead = f.read(buffer, 4, 1);
78 if (bytesRead != 4)
79 break;
80
81 chunkLen = BigEndian::GET_DWORD(buffer);
82
83 switch (BigEndian::GET_DWORD(ID))
84 {
85 case 0x464F524D: // 'FORM'
86 {
87 f.read(buffer, 4, 1);
88 if (memcmp(buffer, "8SVX", 4) == 0)
89 hasFORM = true;
90 else if (memcmp(buffer, "16SV", 4) == 0)
91 hasFORM = true;
92
93 break;
94 }
95
96 /*case 0x4E414D45: // 'NAME'
97 {
98 break;
99 }*/
100
101 case 0x56484452: // 'VHDR'
102 {
103 hasVHDR = true;
104 mp_uint32 pos = f.pos();
105 f.seek(pos + chunkLen);
106 break;
107 }
108
109 case 0x424F4459: // 'BODY'
110 {
111 hasBODY = true;
112 mp_uint32 pos = f.pos();
113 f.seek(pos + chunkLen);
114 break;
115 }
116
117 default:
118 {
119 mp_uint32 pos = f.pos();
120 f.seek(pos + chunkLen);
121 }
122 }
123
124 // odd chunks are padded with zero
125 if (chunkLen&1)
126 f.readByte();
127 }
128
129 return (hasFORM && hasVHDR && hasBODY) ? 1 : 0;
130 }
131
132 /* 8SVX Voice8Header Structure Definition */
133 struct SVX8_Vhdr
134 {
135 mp_dword Oneshothi; /* Num Samples in high octave 1-shot part */
136 mp_dword Repeathi; /* Num Samples in high octave repeat part */
137 mp_dword Samplescycle; /* Num Samples/cycle in high octave, else 0 */
138 mp_uword Samplessec; /* Samples Per Second - Sampling Rate */
139 mp_ubyte Octaves; /* Number of Octaves */
140 mp_ubyte Compression; /* Compression algorithm used - see below */
141 mp_sint32 Volume; /* Volume from 0 to Unity - see below */
142 };
143
loadSample(mp_sint32 index,mp_sint32 channelIndex)144 mp_sint32 SampleLoaderIFF::loadSample(mp_sint32 index, mp_sint32 channelIndex)
145 {
146 mp_ubyte ID[4], buffer[4];
147 mp_dword chunkLen;
148 SVX8_Vhdr vhdr;
149
150 XMFile f(theFileName);
151
152 f.read(ID, 4, 1);
153 if (memcmp(ID, "FORM", 4) != 0)
154 return MP_LOADER_FAILED;
155
156 f.seek(0);
157
158 bool hasFORM = false;
159 bool hires = false;
160 bool hasVHDR = false;
161 bool hasBODY = false;
162
163 mp_ubyte* name = NULL;
164 mp_ubyte* anno = NULL;
165
166 mp_sbyte* sampleData = NULL;
167 mp_sint32 sampleDataLen = 0;
168
169 mp_dword iffSize = 0;
170
171 while (!(hasFORM && hasVHDR && hasBODY))
172 {
173
174 mp_uint32 bytesRead = f.read(ID, 4, 1);
175 if (bytesRead != 4)
176 break;
177
178 bytesRead = f.read(buffer, 4, 1);
179 if (bytesRead != 4)
180 break;
181
182 chunkLen = BigEndian::GET_DWORD(buffer);
183
184 switch (BigEndian::GET_DWORD(ID))
185 {
186 case 0x464F524D: // 'FORM'
187 {
188 f.read(buffer, 4, 1);
189 if (memcmp(buffer, "8SVX", 4) == 0)
190 {
191 hasFORM = true;
192 hires = false;
193 }
194 else if (memcmp(buffer, "16SV", 4) == 0)
195 {
196 hasFORM = true;
197 hires = true;
198 }
199
200 iffSize = chunkLen;
201
202 break;
203 }
204
205 case 0x4E414D45: // 'NAME'
206 {
207 if (name)
208 delete[] name;
209
210 name = new mp_ubyte[chunkLen+1];
211 if (name == NULL)
212 {
213 if (sampleData)
214 delete[] sampleData;
215 if (name)
216 delete[] name;
217 if (anno)
218 delete[] anno;
219 return MP_OUT_OF_MEMORY;
220 }
221
222 f.read(name, 1, chunkLen);
223 name[chunkLen] = '\0';
224 break;
225 }
226
227 case 0x414E4E4F : // 'ANNO'
228 {
229 if (anno)
230 delete[] anno;
231
232 anno = new mp_ubyte[chunkLen+1];
233 if (anno == NULL)
234 {
235 if (sampleData)
236 delete[] sampleData;
237 if (name)
238 delete[] name;
239 if (anno)
240 delete[] anno;
241 return MP_OUT_OF_MEMORY;
242 }
243
244 f.read(anno, 1, chunkLen);
245 anno[chunkLen] = '\0';
246 break;
247 }
248
249 case 0x56484452: // 'VHDR'
250 {
251 hasVHDR = true;
252 mp_uint32 pos = f.pos();
253
254 if (chunkLen < 20)
255 {
256 if (sampleData)
257 delete[] sampleData;
258 if (name)
259 delete[] name;
260 if (anno)
261 delete[] anno;
262 return MP_LOADER_FAILED;
263 }
264
265 f.read(buffer, 1, 4);
266 vhdr.Oneshothi = BigEndian::GET_DWORD(buffer); // sample len
267 f.read(buffer, 1, 4);
268 vhdr.Repeathi = BigEndian::GET_DWORD(buffer); // loop start
269 f.read(buffer, 1, 4);
270 vhdr.Samplescycle = BigEndian::GET_DWORD(buffer); // loop len
271 f.read(buffer, 1, 2);
272 vhdr.Samplessec = BigEndian::GET_WORD(buffer); // c4 speed
273 vhdr.Octaves = f.readByte();
274 vhdr.Compression = f.readByte();
275 f.read(buffer, 1, 4);
276 vhdr.Volume = BigEndian::GET_DWORD(buffer); // volume
277
278 f.seek(pos + chunkLen);
279 break;
280 }
281
282 case 0x424F4459: // 'BODY'
283 {
284 if (!hasVHDR)
285 goto bail;
286
287 mp_uint32 pos = f.pos();
288
289 switch (vhdr.Compression)
290 {
291 // uncompressed IFF
292 case 0:
293 {
294 hasBODY = true;
295
296 // check for some nasty invalid TFMX IFF
297 // (contains zero length body but file contains actually some data)
298 if (chunkLen == 0)
299 {
300 sampleDataLen = iffSize - f.pos();
301 }
302 else
303 {
304 sampleDataLen = chunkLen;
305 }
306
307 if (vhdr.Oneshothi < chunkLen)
308 vhdr.Oneshothi = chunkLen;
309
310 if (sampleDataLen > (signed)vhdr.Oneshothi)
311 sampleDataLen = vhdr.Oneshothi;
312 else if (sampleDataLen < (signed)vhdr.Oneshothi)
313 sampleDataLen = vhdr.Oneshothi;
314
315 sampleData = new mp_sbyte[sampleDataLen];
316 if (sampleData == NULL)
317 {
318 if (sampleData)
319 delete[] sampleData;
320 if (name)
321 delete[] name;
322 if (anno)
323 delete[] anno;
324 return MP_OUT_OF_MEMORY;
325 }
326 memset(sampleData, 0, sampleDataLen);
327
328 f.read(sampleData, 1, (signed)chunkLen > sampleDataLen ? sampleDataLen : chunkLen);
329 break;
330 }
331
332 case 1:
333 case 2:
334 {
335 if (hires)
336 goto bail;
337
338 hasBODY = true;
339
340 mp_sbyte *Body,*src,*dst;
341 mp_ubyte d;
342 mp_sbyte x;
343 mp_sint32 i,n,lim;
344 mp_sbyte fibtab[16] = { -34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21 };
345 mp_sbyte exptab[16] = {-128,-64,-32,-16,-8,-4,-2,-1,0,1,2,4,8,16,32,64};
346
347 Body = new mp_sbyte[chunkLen];
348 if (!Body)
349 {
350 if (sampleData)
351 delete[] sampleData;
352 if (name)
353 delete[] name;
354 if (anno)
355 delete[] anno;
356 return MP_OUT_OF_MEMORY;
357 }
358
359 f.read(Body, 1, chunkLen);
360
361 sampleDataLen = chunkLen<<1;
362 sampleData = new mp_sbyte[sampleDataLen];
363
364 if (!sampleData)
365 {
366 if (sampleData)
367 delete[] sampleData;
368 if (name)
369 delete[] name;
370 if (anno)
371 delete[] anno;
372 if (Body)
373 delete[] Body;
374 return MP_OUT_OF_MEMORY;
375 }
376
377 /* Fibonacci Delta Decompression */
378 mp_sbyte* CodeToDelta = NULL;
379 if (vhdr.Compression == 1)
380 CodeToDelta = fibtab;
381 /* Exponential Delta Decompression */
382 else
383 CodeToDelta = exptab;
384
385 src = Body+2;
386 dst = sampleData;
387 n = chunkLen-2;
388 x = Body[1];
389 lim = n << 1;
390 for(i = 0; i < lim; ++i)
391 {
392 d = src[i >> 1];
393 if(i & 1) d &= 0xF;
394 else d >>= 4;
395 x += CodeToDelta[d];
396 dst[i] = x;
397 }
398 delete[] Body;
399
400 if ((signed)vhdr.Oneshothi < sampleDataLen)
401 vhdr.Oneshothi = sampleDataLen;
402
403 break;
404 }
405
406 default:
407 hasBODY = false;
408 goto bail;
409
410
411 }
412
413 f.seek(pos + chunkLen);
414 break;
415 }
416
417 default:
418 {
419 bail:
420 mp_uint32 pos = f.pos();
421 f.seek(pos + chunkLen);
422 }
423 }
424
425 // odd chunks are padded with zero
426 if (chunkLen&1)
427 f.readByte();
428
429 }
430
431 if (hasFORM && hasVHDR && hasBODY)
432 {
433 TXMSample* smp = &theModule.smp[index];
434
435 if (smp->sample)
436 {
437 theModule.freeSampleMem((mp_ubyte*)smp->sample);
438 smp->sample = NULL;
439 }
440
441 smp->samplen = hires ? sampleDataLen >> 1 : sampleDataLen;
442
443 smp->sample = (mp_sbyte*)theModule.allocSampleMem(hires ? (smp->samplen<<1) : smp->samplen);
444 if (smp->sample == NULL)
445 {
446 if (sampleData)
447 delete[] sampleData;
448 if (name)
449 delete[] name;
450 if (anno)
451 delete[] anno;
452 return MP_OUT_OF_MEMORY;
453 }
454
455 memcpy(smp->sample, sampleData, sampleDataLen);
456
457 if (hires)
458 {
459 // huuuu? 16 bit IFF samples are little endian? how stupid is that?
460 mp_uword* ptr = (mp_uword*)smp->sample;
461 for (mp_uint32 i = 0; i < smp->samplen; i++)
462 {
463 *ptr = LittleEndian::GET_WORD(ptr);
464 ptr++;
465 }
466 }
467
468 delete[] sampleData;
469
470 smp->loopstart = hires ? vhdr.Repeathi >> 1 : vhdr.Repeathi;
471 smp->looplen = hires ? vhdr.Samplescycle >> 1 : vhdr.Samplescycle;
472 mp_sint32 vol = (vhdr.Volume*255)>>16;
473 smp->vol = (mp_ubyte)(vol > 255 ? 255 : vol);
474 smp->pan = 0x80;
475 smp->flags = 3;
476
477 memset(smp->name, 0, sizeof(smp->name));
478
479 if (name || anno)
480 {
481 const char* buff = name ? (const char*)name : (const char*)anno;
482
483 nameToSample(buff, smp);
484
485 if (name)
486 delete[] name;
487 if (anno)
488 delete[] anno;
489 }
490 else
491 {
492 nameToSample(preferredDefaultName, smp);
493 }
494
495 smp->type = 0;
496
497 if (smp->looplen)
498 smp->type |= 1;
499 if (hires)
500 smp->type |= 16;
501
502 XModule::convertc4spd(vhdr.Samplessec, &smp->finetune, &smp->relnote);
503
504 if (smp->samplen > vhdr.Oneshothi)
505 smp->samplen = vhdr.Oneshothi;
506
507 return MP_OK;
508 }
509
510 delete[] name;
511
512 delete[] sampleData;
513
514 return MP_LOADER_FAILED;
515 }
516
swapW(mp_uword x)517 static inline mp_uword swapW(mp_uword x)
518 {
519 return (x>>8)+((x&255)<<8);
520 }
521
swapDW(mp_dword x)522 static inline mp_dword swapDW(mp_dword x)
523 {
524 return (mp_dword)swapW(x>>16) | (mp_dword)swapW(x&0xFFFF) << 16;
525 }
526
saveSample(const SYSCHAR * fileName,mp_sint32 index)527 mp_sint32 SampleLoaderIFF::saveSample(const SYSCHAR* fileName, mp_sint32 index)
528 {
529 TXMSample* smp = &theModule.smp[index];
530
531 XMFile f(fileName, true);
532
533 f.write("FORM", 1, 4);
534
535 bool hires = (smp->type & 16) != 0;
536
537 mp_dword chunkLen = 70 + (hires ? smp-> samplen << 1 : smp-> samplen);
538 f.writeDword(swapDW(chunkLen));
539
540 f.write(hires ? "16SV" : "8SVX", 1, 4);
541
542 f.write("NAME", 1, 4);
543 f.writeDword(swapDW(22));
544 f.write(smp->name, 1, 22);
545
546 SVX8_Vhdr vhdr;
547
548 vhdr.Oneshothi = hires ? smp->samplen << 1 : smp->samplen;
549 vhdr.Repeathi = (smp->type & 3) ? (hires ? smp->loopstart << 1 : smp->loopstart) : 0;
550 vhdr.Samplescycle = (smp->type & 3) ? (hires ? smp->looplen << 1 : smp->looplen) : 0;
551 vhdr.Samplessec = XModule::getc4spd(smp->relnote,smp->finetune);
552 vhdr.Octaves = 1;
553 vhdr.Compression = 0;
554 vhdr.Volume = (smp->vol*65536)/255;
555
556 f.write("VHDR", 1, 4);
557 f.writeDword(swapDW(0x14));
558 f.writeDword(swapDW(vhdr.Oneshothi));
559 f.writeDword(swapDW(vhdr.Repeathi));
560 f.writeDword(swapDW(vhdr.Samplescycle));
561 f.writeWord(swapW(vhdr.Samplessec));
562 f.writeByte(vhdr.Octaves);
563 f.writeByte(vhdr.Compression);
564 f.writeDword(swapDW(vhdr.Volume));
565
566 f.write("BODY", 1, 4);
567 f.writeDword(swapDW(hires ? smp->samplen << 16 : smp->samplen));
568
569 if (smp->type & 16)
570 {
571 for (mp_uint32 i = 0; i < smp->samplen; i++)
572 f.writeWord(smp->getSampleValue(i));
573 }
574 else
575 {
576 for (mp_uint32 i = 0; i < smp->samplen; i++)
577 f.writeByte(smp->getSampleValue(i));
578 }
579
580 return MP_OK;
581 }
582