Initial commit.
[amiga/xmodule.git] / Instr.c
1 /*
2 **      Instr.c
3 **
4 **      Copyright (C) 1994,95,96,97 Bernardo Innocenti
5 **
6 **      Instrument loading/saving/handling routines.
7 */
8
9 #define IFFPARSE_V37_NAMES_ONLY
10
11 #include <exec/libraries.h>
12 #include <exec/memory.h>
13 #include <datatypes/soundclass.h>
14 #include <libraries/iffparse.h>
15 #include <libraries/maud.h>
16 #include <libraries/toccata.h>
17 #include <libraries/xmoduleclass.h>
18
19 #include <proto/exec.h>
20 #include <proto/dos.h>
21 #include <proto/iffparse.h>
22 #include <proto/datatypes.h>
23 #include <proto/toccata.h>
24 #include <proto/xmodule.h>
25
26 #include "XModulePriv.h"
27 #include "Gui.h"
28
29
30 #define UNITY 0x10000L
31
32
33 /* Local function prototypes */
34 static LONG DTLoadInstrument    (struct SongInfo *si, ULONG num, CONST_STRPTR filename);
35 static LONG LoadMAUDInstrument  (struct SongInfo *si, ULONG num, struct IFFHandle *iff, CONST_STRPTR filename);
36 static LONG RawLoadInstrument   (struct SongInfo *si, ULONG num, CONST_STRPTR filename, UWORD mode);
37 static BYTE D1Unpack                    (UBYTE source[], LONG n, BYTE dest[], BYTE x);
38 static void DUnpack                             (UBYTE source[], LONG n, BYTE dest[]);
39
40
41
42 struct Library *DataTypesBase   = NULL;
43 struct Library *ToccataBase     = NULL;
44
45
46
47 GLOBALCALL LONG LoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename)
48
49 /* Load an instrument file to the instrument slot <num> in the passed song.
50  * Will use DataTypes if available, otherwise it will use the built-in loaders.
51  */
52 {
53         LONG err = IFFERR_NOTIFF;
54
55         /* Try loading with DataTypes */
56
57         if (GuiSwitches.UseDataTypes)
58                 if (DataTypesBase = OpenLibrary ("datatypes.library", 39L))
59                 {
60                         err = DTLoadInstrument (si, num, filename);
61                         CloseLibrary (DataTypesBase);   DataTypesBase = NULL;
62                 }
63
64
65         /* Try again using built-in loaders */
66
67         if (err)
68         {
69                 struct IFFHandle *iff;
70
71                 if (iff = AllocIFF())
72                 {
73                         if (iff->iff_Stream = (ULONG) Open (filename, MODE_OLDFILE))
74                         {
75                                 InitIFFasDOS (iff);
76
77                                 if (!(err = OpenIFF (iff, IFFF_READ)))
78                                 {
79                                         if (!(err = ParseIFF (iff, IFFPARSE_RAWSTEP)))
80                                         {
81                                                 LONG type = CurrentChunk (iff)->cn_Type;
82                                                 switch (type)
83                                                 {
84                                                         case ID_8SVX:
85                                                                         err = Load8SVXInstrument (si, num, iff, filename);
86                                                                 break;
87
88                                                         case ID_MAUD:
89                                                                         err = LoadMAUDInstrument (si, num, iff, filename);
90                                                                 break;
91
92                                                         default:
93                                                         {
94                                                                 UBYTE buf[5];
95
96                                                                 IDtoStr (type, buf);
97                                                                 ShowMessage (MSG_UNKNOWN_IFF, buf);
98                                                                 err = ERROR_OBJECT_WRONG_TYPE;
99                                                                 break;
100                                                         }
101                                                 }
102                                         }
103                                 }
104
105                                 Close (iff->iff_Stream);
106                         }
107                         else err = IoErr();
108
109                         FreeIFF (iff);
110                 }
111                 else err = ERROR_NO_FREE_STORE;
112         }
113
114         if (err == IFFERR_MANGLED || err == IFFERR_SYNTAX)
115                 ShowMessage (MSG_ILLEGAL_IFF_STRUCTURE);
116
117         if (err == IFFERR_NOTIFF)
118         {
119                 LONG mode;
120
121                 if (mode = ShowRequestArgs (MSG_SELECT_RAW_MODE,
122                 MSG_RAW_MODES, NULL))
123                         err = RawLoadInstrument (si, num, filename, mode);
124                 else err = 0;
125         }
126
127         UpdateInstrList();
128
129         if (err) LastErr = err;
130         return err;
131 }
132
133
134
135 static LONG DTLoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename)
136 {
137         Object  *dto;
138         UBYTE   *instname;
139         LONG     err = 0;
140
141         if (dto = NewDTObject (filename,
142                 DTA_GroupID, GID_SOUND,
143                 TAG_DONE))
144         {
145                 struct VoiceHeader *vhdr;
146                 LONG Len, Vol;
147                 UBYTE *Sample;
148                 UBYTE *errorstring;
149
150                 if (GetDTAttrs (dto,
151                         SDTA_VoiceHeader,       &vhdr,
152                         SDTA_SampleLength,      &Len,
153                         SDTA_Volume,            &Vol,
154                         SDTA_Sample,            &Sample,
155                         DTA_Title,                      &instname,
156                         TAG_DONE) == 5)
157                 {
158                         /* Detach sample from DataType Object, so it won't
159                          * be freed when we dispose the object.
160                          */
161                         SetDTAttrs (dto, NULL, NULL,
162                                 SDTA_Sample, NULL,
163                                 TAG_DONE);
164
165                         /* Create new instrument */
166
167                         if (!xmAddInstrument (si, num,
168                                 INSTRA_Name,    instname,
169                                 INSTRA_Sample,  Sample,
170                                 INSTRA_Length,  Len,
171                                 INSTRA_Repeat,  vhdr->vh_OneShotHiSamples,
172                                 INSTRA_Replen,  vhdr->vh_RepeatHiSamples,
173
174                                 /* The sound.datatype _should_ return
175                                  * volumes in the normal Amiga range 0-64.
176                                  * However, the 8svx.datatype behaves
177                                  * differently: it returns the volume in the
178                                  * standard 8SVX format, where the maximum
179                                  * volume is $10000.  Here is a good
180                                  * workaround to this bug.
181                                  */
182                                 INSTRA_Volume,  (vhdr->vh_Volume > 64) ?
183                                         ((vhdr->vh_Volume * 64) / UNITY) : vhdr->vh_Volume,
184                                 TAG_DONE))
185                                 err = ERROR_NO_FREE_STORE;
186
187                                 DB(kprintf ("Vol: %ld InstVol: %ld\n", Vol, si->Instr[num]->Volume));
188                 }
189                 else err = RETURN_FAIL;
190
191
192                 if (GetDTAttrs (dto,
193                         DTA_ErrorString, &errorstring,
194                         TAG_DONE) == 1)
195                         ShowMessage (MSG_DATATYPES_ERROR, errorstring);
196
197                 DisposeDTObject (dto);
198         }
199         else err = IoErr();
200
201         return err;
202 }
203
204
205
206 GLOBALCALL LONG Load8SVXInstrument (struct SongInfo *si, ULONG num, struct IFFHandle *iff, CONST_STRPTR filename)
207
208 /* Load an IFF 8SVX file to the instrument slot <num>.  If <num> is 0, it requires
209  * the INST chunk and it uses the num stored there.
210  * Can decode Fibonacci Delta encoded samples.
211  */
212 {
213         struct ContextNode              *cn;
214         struct VoiceHeader               vhdr;
215         struct InstrumentHeader  insthdr;
216         LONG    err;
217         UBYTE   name[64];
218         BOOL    is_valid_8svx = FALSE,
219                         insthdr_loaded = FALSE;
220
221         static LONG stopchunks[] =
222         {
223                 ID_8SVX, ID_INST,
224                 ID_8SVX, ID_VHDR,
225                 ID_8SVX, ID_BODY,
226                 ID_8SVX, ID_NAME
227         };
228
229         /* Put the file name if the optional NAME propriety is missing */
230         if (filename)
231         {
232                 strncpy (name, FilePart (filename), 63);
233                 name[63] = '\0';
234         }
235         else name[0] = '\0';
236
237         if (err = StopChunks (iff, stopchunks, 4))
238                 return err;
239
240         if (err = StopOnExit (iff, ID_8SVX, ID_FORM))
241                 return err;
242
243         while (1)
244         {
245                 if (err = ParseIFF (iff, IFFPARSE_SCAN))
246                 {
247                         if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK;
248                         break; /* Free resources & exit */
249                 }
250
251                 if ((cn = CurrentChunk (iff)) && (cn->cn_Type == ID_8SVX))
252                 {
253                         switch (cn->cn_ID)
254                         {
255                                 case ID_INST:
256                                 {
257                                         if ((err = ReadChunkBytes (iff, &insthdr, sizeof (insthdr))) !=
258                                                 sizeof (insthdr)) return err;
259
260                                         if (!num) num = insthdr.Num;
261                                 }
262
263                                 case ID_VHDR:
264                                 {
265                                         if ((err = ReadChunkBytes (iff, &vhdr, sizeof (vhdr))) !=
266                                                 sizeof (vhdr)) return err;
267
268                                         if (!is_valid_8svx)
269                                         {
270                                                 if (!xmAddInstrumentA (si, num, NULL))
271                                                         return ERROR_NO_FREE_STORE;
272
273                                                 if (!num) num = si->LastInstrument;
274                                         }
275
276                                         xmSetInstrument (si, num,
277                                                 INSTRA_Repeat,          (vhdr.vh_RepeatHiSamples ? vhdr.vh_OneShotHiSamples : 0),
278                                                 INSTRA_Replen,          vhdr.vh_RepeatHiSamples,
279                                                 INSTRA_Volume,          (vhdr.vh_Volume * 64) / UNITY,
280                                                 INSTRA_FineTune,        insthdr_loaded ? insthdr.FineTune : 0,
281                                                 TAG_DONE);
282
283                                         is_valid_8svx = TRUE;
284
285                                         break;
286                                 }
287
288                                 case ID_BODY:
289                                 {
290                                         struct Instrument *instr;
291                                         BYTE *sample;
292
293                                         if (!is_valid_8svx)
294                                         {
295                                                 xmAddInstrumentA (si, num, NULL);
296                                                 if (!num) num = si->LastInstrument;
297                                         }
298
299                                         if (!(instr = si->Instr[num]))
300                                                 return ERROR_NO_FREE_STORE;
301
302                                         if (!(sample = AllocVec (cn->cn_Size, MEMF_SAMPLE)))
303                                                 return ERROR_NO_FREE_STORE;
304
305                                         xmSetInstrument (si, num,
306                                                 INSTRA_Sample,  sample,
307                                                 INSTRA_Length,  cn->cn_Size,
308                                                 TAG_DONE);
309
310                                         /* We only require that at least some data is
311                                          * read.  This way if, say, you have a corrupted
312                                          * 8SVX file, you can still load part of the
313                                          * instrument data.
314                                          */
315                                         if ((err = ReadChunkBytes (iff, instr->Sample,
316                                                 cn->cn_Size)) < 0)
317                                                 return err;
318
319                                         is_valid_8svx = TRUE;
320                                         break;
321                                 }
322
323                                 case ID_NAME:
324                                         ReadChunkBytes (iff, name, min(cn->cn_Size, 63));
325                                         name[63] = '\0'; /* Ensure string termination */
326                                         break;
327
328                                 default:
329                                         break;
330                         }
331                 }
332         }
333
334         if (is_valid_8svx)
335         {
336                 xmSetInstrument (si, num,
337                         INSTRA_Name,    name,
338                         TAG_DONE);
339
340                 if (!err)
341                 {
342                         if (vhdr.vh_Compression == CMP_FIBDELTA)
343                         {
344                                 BYTE *buf;
345                                 struct Instrument *instr = si->Instr[num];
346
347                                 if (buf = AllocVec (instr->Length * 2, MEMF_SAMPLE))
348                                 {
349                                         DUnpack (instr->Sample, instr->Length + 2, buf);
350                                         FreeVec (instr->Sample);
351
352                                         xmSetInstrument (si, num,
353                                                 INSTRA_Sample,  buf,
354                                                 INSTRA_Length,  instr->Length * 2,
355                                                 TAG_DONE);
356                                 }
357                         }
358                         else if (vhdr.vh_Compression != CMP_NONE)
359                                 ShowMessage (MSG_UNKNOWN_COMPRESSION);
360                 }
361         }
362         else err = IFFERR_MANGLED;
363
364         return err;
365 }
366
367
368
369 /* Number of samples loaded & processed at one time */
370 #define MAUDBLOCKSIZE 32768
371
372 static LONG LoadMAUDInstrument (struct SongInfo *si, ULONG num, struct IFFHandle *iff, CONST_STRPTR filename)
373
374 /* Load an IFF MAUD file to the instrument slot <num> of the passed song.
375  * MAUD is the standard file format for Macrosystem's audio boards
376  * Toccata and Maestro.
377  */
378 {
379         struct ContextNode      *cn;
380         struct MaudHeader mhdr;
381         LONG err;
382         BOOL    is_valid_maud = FALSE;
383         UBYTE   name[64];
384
385         static LONG stopchunks[] =
386         {
387                 ID_MAUD, ID_MHDR,
388                 ID_MAUD, ID_MDAT,
389                 ID_MAUD, ID_NAME
390         };
391
392
393         /* Put the file name if the optional NAME propriety is missing */
394
395         if (filename)
396         {
397                 strncpy (name, FilePart (filename), 63);
398                 name[63] = '\0';
399         }
400         else name[0] = '\0';
401
402         if (err = StopChunks (iff, stopchunks, 3))
403                 return err;
404
405         if (err = StopOnExit (iff, ID_MAUD, ID_FORM))
406                 return err;
407
408
409         while (1)
410         {
411                 if (err = ParseIFF (iff, IFFPARSE_SCAN))
412                 {
413                         if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK;
414                         break; /* Free resources & exit */
415                 }
416
417                 if ((cn = CurrentChunk (iff)) && (cn->cn_Type == ID_MAUD))
418                 {
419                         switch (cn->cn_ID)
420                         {
421                                 case ID_MHDR:
422                                         if ((err = ReadChunkBytes (iff, &mhdr, sizeof (mhdr))) !=
423                                                 sizeof (mhdr)) return err;
424
425                                         if ((mhdr.mhdr_SampleSizeU != 8) && (mhdr.mhdr_SampleSizeU != 16))
426                                         {
427                                                 ShowMessage (MSG_SAMPLE_WRONG_SIZE, mhdr.mhdr_SampleSizeU);
428                                                 return IFFERR_SYNTAX;
429                                         }
430
431                                         if (mhdr.mhdr_ChannelInfo != MCI_MONO)
432                                         {
433                                                 ShowMessage (MSG_SAMPLE_NOT_MONO, mhdr.mhdr_ChannelInfo);
434                                                 return IFFERR_SYNTAX;
435                                         }
436
437                                         if (mhdr.mhdr_Channels != 1)
438                                         {
439                                                 ShowMessage (MSG_SAMPLE_WRONG_NUMBER_OF_CHANNELS, mhdr.mhdr_Channels);
440                                                 return IFFERR_SYNTAX;
441                                         }
442
443                                         is_valid_maud = TRUE;
444                                         break;
445
446                                 case ID_MDAT:
447                                 {
448                                         ULONG i;
449                                         struct Instrument *instr;
450                                         BYTE *sample;
451
452                                         if (!is_valid_maud)
453                                                 return IFFERR_SYNTAX;
454
455                                         if (!(sample = AllocVec ((mhdr.mhdr_Samples + 1) & (~1), MEMF_SAMPLE)))
456                                                 return ERROR_NO_FREE_STORE;
457
458                                         if (!(instr = xmAddInstrument (si, num,
459                                                 INSTRA_Sample,  sample,
460                                                 INSTRA_Length,  (mhdr.mhdr_Samples + 1) & (~1),
461                                                 TAG_DONE)))
462                                         {
463                                                 FreeVec (sample);
464                                                 return ERROR_NO_FREE_STORE;
465                                         }
466
467                                         if (mhdr.mhdr_SampleSizeU == 8)                         /* 8 bit */
468                                         {
469                                                 /* We only require that at least some data is
470                                                  * read.  This way if, say, you have a corrupted
471                                                  * MAUD file, you can still load part of the
472                                                  * sample data.
473                                                  */
474                                                 if ((err = ReadChunkBytes (iff, instr->Sample,
475                                                         instr->Length)) == 0) return err;
476
477                                                 SampChangeSign8 (instr->Sample, instr->Length);
478
479                                         }
480                                         else if (mhdr.mhdr_SampleSizeU == 16)           /* 16 bit */
481                                         {
482                                                 WORD *tmp;
483                                                 ULONG actual, current = 0;
484
485                                                 if (!(tmp = AllocPooled (Pool, MAUDBLOCKSIZE * ((mhdr.mhdr_SampleSizeC + 7) / 8))))
486                                                         return ERROR_NO_FREE_STORE;
487
488                                                 if (mhdr.mhdr_Compression != MCOMP_NONE)
489                                                         if (!(ToccataBase = MyOpenLibrary ("toccata.library", 0L)))
490                                                         {
491                                                                 FreePooled (Pool, tmp, MAUDBLOCKSIZE * sizeof (WORD));
492                                                                 CantOpenLib ("toccata.library", 0L);
493                                                                 return ERROR_INVALID_RESIDENT_LIBRARY;
494                                                         }
495
496                                                 for (;;)
497                                                 {
498                                                         actual = ReadChunkBytes (iff, tmp, MAUDBLOCKSIZE * ((mhdr.mhdr_SampleSizeC + 7) / 8));
499
500                                                         if (actual == 0) break;
501
502                                                         /* Filter (tmp, actual); */
503
504                                                         switch (mhdr.mhdr_Compression)
505                                                         {
506                                                                 case MCOMP_ALAW:
507                                                                         /* Convert 8bit A-Law data to 8bit signed data */
508                                                                         T_Convert (tmp, instr->Sample + current, actual, TMODE_ALAW, TMODE_LINEAR_8);
509                                                                         current += actual;
510                                                                         break;
511
512                                                                 case MCOMP_ULAW:
513                                                                         /* Convert 8bit Âµ-Law data to 8bit signed data */
514                                                                         T_Convert (tmp, instr->Sample + current, actual, TMODE_ULAW, TMODE_LINEAR_8);
515                                                                         current += actual;
516                                                                         break;
517
518                                                                 default:
519                                                                         /* Convert 16bit signed data to 8bit signed data */
520                                                                         actual >>= 1;
521                                                                         for (i = 0; (i < actual) && (current < mhdr.mhdr_Samples); i++, current++)
522                                                                                 instr->Sample[current] = tmp[i] >> 8;
523                                                                         break;
524                                                         }
525                                                 }
526
527                                                 SampChangeSign8 (instr->Sample, instr->Length);
528                                                 FreePooled (Pool, tmp, MAUDBLOCKSIZE * sizeof (WORD));
529                                                 CloseLibrary (ToccataBase);
530                                                 ToccataBase = NULL;
531                                         }
532
533                                         is_valid_maud = TRUE;
534                                         break;
535                                 }
536
537                                 case ID_NAME:
538                                         ReadChunkBytes (iff, name, min(cn->cn_Size, 63));
539                                         break;
540
541                                 default:
542                                         break;
543                         }
544                 }
545         }
546
547         if (is_valid_maud)
548         {
549                 xmSetInstrument (si, num,
550                 INSTRA_Name,    name,
551                 TAG_DONE);
552
553                 if (!err)
554                 {
555                         if (mhdr.mhdr_Compression == CMP_FIBDELTA)
556                         {
557                                 BYTE *buf;
558                                 struct Instrument *instr = si->Instr[num];
559
560                                 if (buf = AllocVec (instr->Length * 2, MEMF_SAMPLE))
561                                 {
562                                         DUnpack (instr->Sample, instr->Length + 2, buf);
563                                         FreeVec (instr->Sample);
564
565                                         xmSetInstrument (si, num,
566                                                 INSTRA_Sample,  buf,
567                                                 INSTRA_Length,  instr->Length * 2,
568                                                 TAG_DONE);
569                                 }
570                         }
571                         else if (mhdr.mhdr_Compression != CMP_NONE)
572                                 ShowMessage (MSG_UNKNOWN_COMPRESSION);
573                 }
574         }
575         else err = IFFERR_NOTIFF;
576
577         return err;
578 }
579
580
581
582 static LONG RawLoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename, UWORD mode)
583
584 /* Load a raw file to the instrument slot pointed by inst.
585  * mode   1 - signed 8bit
586  *        2 - unsigned 8bit
587  */
588 {
589         BPTR lock, fh;
590         struct FileInfoBlock *fib;
591         struct Instrument *instr;
592         LONG err = 0;
593         ULONG len;
594
595         if (lock = Lock (filename, ACCESS_READ))
596         {
597                 /* Get file size */
598                 if (fib = AllocDosObject (DOS_FIB, NULL))
599                 {
600                         if (Examine (lock, fib))
601                                 len = fib->fib_Size;
602                         else
603                                 err = IoErr();
604                         FreeDosObject (DOS_FIB, fib);
605                 }
606                 else err = ERROR_NO_FREE_STORE;
607
608                 if (!err)
609                 {
610                         if (fh = OpenFromLock (lock))
611                         {
612                                 BYTE *sample;
613
614                                 lock = NULL;    /* OpenFromLock() relinquished our lock! */
615
616                                 if (sample = AllocVec (len, MEMF_SAMPLE))
617                                 {
618                                         if (instr = xmAddInstrument (si, num,
619                                                 INSTRA_Sample,  sample,
620                                                 INSTRA_Length,  len,
621                                                 INSTRA_Volume,  64,
622                                                 INSTRA_Name,    FilePart (filename),
623                                                 TAG_DONE))
624                                         {
625                                                 /* We do not check for failure here to
626                                                  * be more error tolerant.  This way you
627                                                  * can load at least part of an instrument
628                                                  * from a corrupted file ;-)
629                                                  */
630                                                 Read (fh, sample, len);
631
632                                                 if (mode == 2)
633                                                         SampChangeSign8 (sample, instr->Length);
634                                         }
635                                         else
636                                         {
637                                                 FreeVec (sample);
638                                                 err = ERROR_NO_FREE_STORE;
639                                         }
640                                 }
641                                 else err = ERROR_NO_FREE_STORE;
642
643                                 Close (fh);
644                         }
645                         else err = IoErr();
646                 }
647
648                 UnLock (lock);  /* Will be NULL if OpenFromLock() was successful */
649         }
650         else err = IoErr();
651
652         return err;
653 }
654
655
656
657 GLOBALCALL LONG SaveInstrument (struct Instrument *inst, CONST_STRPTR filename)
658 {
659         LONG err;
660         struct IFFHandle *iff;
661
662
663         if (iff = AllocIFF())
664         {
665                 if (iff->iff_Stream = (ULONG) Open (filename, MODE_NEWFILE))
666                 {
667                         InitIFFasDOS (iff);
668
669                         if (!(err = OpenIFF (iff, IFFF_WRITE)))
670                         {
671                                 err = Save8SVXInstrument (inst, 0, iff);
672                                 CloseIFF (iff);
673                         }
674
675                         Close (iff->iff_Stream);
676                 }
677                 else err = IoErr();
678
679                 FreeIFF (iff);
680         }
681         else return ERROR_NO_FREE_STORE;
682
683         if (!err)
684         {
685                 if (GuiSwitches.InstrSaveIcons)
686                         /* Write icon */
687                         PutIcon ("def_Instrument", filename);
688         }
689         else
690         {
691                 /* Remove incomplete file */
692                 LastErr = err;
693                 DeleteFile (filename);
694         }
695
696         return (err);
697 }
698
699
700
701 GLOBALCALL LONG Save8SVXInstrument (struct Instrument *instr, ULONG num, struct IFFHandle *iff)
702
703 /* Save the instrument pointed by inst to a standard IFF 8SVX file.
704  */
705 {
706         struct VoiceHeader vhdr;
707         LONG err;
708
709
710         /* Write 8SVX */
711
712         if (err = PushChunk (iff, ID_8SVX, ID_FORM, IFFSIZE_UNKNOWN))
713                 return err;
714
715
716         /* Write INST */
717         if (num)
718         {
719                 struct InstrumentHeader insthdr;
720
721                 insthdr.Num = num;
722                 insthdr.Type = ITYPE_SAMPLE8;
723                 insthdr.FineTune = instr->FineTune;
724
725                 if (err = PushChunk (iff, ID_8SVX, ID_INST, sizeof (insthdr)))
726                         return err;
727                 if ((err = WriteChunkBytes (iff, &insthdr, sizeof (insthdr))) !=
728                         sizeof (insthdr))
729                         return err;
730                 if (err = PopChunk (iff)) return err;   /* Pop INST */
731         }
732
733         /* Write VHDR */
734
735         if (vhdr.vh_RepeatHiSamples = instr->Replen)
736                 /* Loop */
737                 vhdr.vh_OneShotHiSamples = instr->Repeat;
738         else
739                 /* No Loop */
740                 vhdr.vh_OneShotHiSamples = instr->Length;
741
742         vhdr.vh_SamplesPerHiCycle = 0;
743         vhdr.vh_SamplesPerSec = 8363;
744         vhdr.vh_Octaves = 1;
745         vhdr.vh_Compression = CMP_NONE;
746         vhdr.vh_Volume = (instr->Volume * UNITY) / 64;
747
748         if (err = PushChunk (iff, ID_8SVX, ID_VHDR, sizeof (vhdr)))
749                 return err;
750         if ((err = WriteChunkBytes (iff, &vhdr, sizeof (vhdr))) !=
751                 sizeof (vhdr))
752                 return err;
753         if (err = PopChunk (iff)) return err;   /* Pop VHDR */
754
755
756         /* Write NAME */
757         {
758                 ULONG l = strlen (instr->Name) + 1;
759
760                 if (err = PushChunk (iff, ID_8SVX, ID_NAME, l))
761                         return err;
762                 if ((err = WriteChunkBytes (iff, instr->Name, l)) != l)
763                         return err;
764                 if (err = PopChunk (iff)) return err;   /* Pop NAME */
765         }
766
767         /* Write BODY */
768
769         if (instr->Sample)
770         {
771                 if (PushChunk (iff, ID_8SVX, ID_BODY, instr->Length))
772                         return err;
773                 if ((err = WriteChunkBytes (iff, instr->Sample, instr->Length)) !=
774                         instr->Length) return err;
775                 if (err = PopChunk (iff)) return err;   /* Pop BODY */
776         }
777
778         PopChunk (iff); /* Pop 8SVX */
779
780         return err;
781 }
782
783
784
785 GLOBALCALL void OptimizeInstruments (struct SongInfo *si)
786
787 /* Remove useless sample data (cut beyond loops and zero-tails) */
788 {
789         UWORD   i;
790         ULONG   newlen;
791         struct Instrument *instr;
792
793         for (i = 1 ; i <= si->LastInstrument ; i++)
794         {
795                 if (!(instr = si->Instr[i])) continue;
796                 if (!instr->Sample) continue;
797
798                 newlen = instr->Length;
799
800                 if (instr->Replen)
801                 {
802                         if (instr->Length > instr->Repeat + instr->Replen)
803                                 newlen = instr->Repeat + instr->Replen; /* Cut instrument after loop */
804                 }
805                 else
806                 {
807                         BYTE *tail;
808
809                         instr->Repeat = 0;      /* Kill null loops */
810
811                         /* Kill instrument zero-tail.
812                          * In order to reduce the instrument even more,
813                          * 1 & -1 are treated the same as zero.
814                          */
815
816                         tail = instr->Sample + instr->Length - 1;
817                         while ((*tail < 1) && (*tail > -1) && (tail > instr->Sample))
818                                 tail--;
819
820                         newlen = tail - instr->Sample;
821                         if (newlen & 1) newlen++;       /* Pad instrument size to words */
822
823                         /* leave 2 end zeroes to prevent an audible end-of-instrument click. */
824                         if (newlen) newlen += 2;
825                 }
826
827                 if (newlen == 0)
828                         xmRemInstrument (si, i);        /* This instrument is mute!  Free it... */
829                 else if (newlen < instr->Length)
830                 {
831                         /* Resize the instrument if necessary */
832                         BYTE *newinstr;
833
834                         /* Allocate memory for optimized instrument */
835                         if (!(newinstr = AllocVec (newlen, MEMF_SAMPLE)))
836                         {
837                                 ShowMessage (MSG_NO_MEMORY_TO_OPTIMIZE_INSTR, i);
838                                 continue;       /* Better luck with next instrument :) */
839                         }
840
841                         ShowMessage (MSG_INSTR_WILL_SHRINK, i, instr->Length, newlen);
842
843                         /* Copy first part of instrument */
844                         CopyMem (instr->Sample, newinstr, newlen);
845
846                         /* Free old instrument */
847                         FreeVec (instr->Sample);
848
849                         /* Replace with new instrument */
850                         xmSetInstrument (si, i,
851                                 INSTRA_Sample,  newinstr,
852                                 INSTRA_Length,  newlen,
853                                 TAG_DONE);
854
855                 }
856         }
857 }
858
859
860
861 GLOBALCALL void RemDupInstruments (struct SongInfo *si)
862 /* Find out identical patterns and cut them out */
863 {
864         ULONG i, j, k, w, v;
865         struct Instrument *insta, *instb;
866         struct Pattern *patt;
867         struct Note *note;
868
869
870         for (i = 1; i < si->LastInstrument; i++)        /* '<' instead of '<=' is ok here... */
871         {
872                 if (!(insta = si->Instr[i])) continue;
873                 if (!insta->Length) continue;
874
875                 for (j = i + 1; j <= si->LastInstrument ; j++)
876                 {
877                         if (!(instb = si->Instr[j])) continue;
878
879                         if (insta->Length == instb->Length &&
880                                 insta->Repeat == instb->Repeat &&
881                                 insta->Replen == instb->Replen &&
882                                 insta->Volume == instb->Volume &&
883                                 insta->Type == instb->Type &&
884                                 insta->FineTune == instb->FineTune)
885                         {
886                                 if (!memcmp (insta->Sample, instb->Sample, insta->Length))
887                                 {
888                                         xmRemInstrument (si, j);
889
890                                         for (k = 0; k < si->NumPatterns; k++)
891                                         {
892                                                 if (!(patt = si->Patt[k])) continue;
893
894                                                 for (w = 0; w < patt->Tracks; w++)
895                                                 {
896                                                         note = patt->Notes[w];
897                                                         for (v = 0; v < patt->Lines; v++, note++)
898                                                                 if (note->Inst == j) note->Inst = i;
899                                                 }
900                                         }
901
902                                         ShowMessage (MSG_INSTR_DUPES_REMOVED, i, j);
903                                 }
904                         }
905                 }
906         }
907
908 }
909
910
911
912 GLOBALCALL void RemapInstruments (struct SongInfo *si)
913
914 /* Remove empty slots between instruments, to allow those module formats
915  * that support less instruments to use even the last instruments.
916  */
917 {
918         UWORD i, j, k;
919         UBYTE newpos[MAXINSTRUMENTS] = { 0 };
920         struct Instrument *instr;
921         struct Pattern *patt;
922         struct Note *note;
923
924         /* newpos[0] = 0; */
925
926         DB (kprintf ("Before - LastInstrument = %ld", si->LastInstrument));
927
928         /* Build instrument remap table &  compress instrument slots */
929         for (i = 1, j = 0; i <= si->LastInstrument; i++)
930         {
931                 if (!(instr = si->Instr[i])) continue;
932
933                 if (instr->Length)
934                 {
935                         j++;
936                         newpos[i] = j;
937
938                         if (j != i) DoMethod ((Object *)si, SNGM_SWAPINSTRUMENTS, i, j);
939                 }
940                 else newpos[i] = 0;
941         }
942
943
944         /* Update score */
945         for (i = 0 ; i < si->NumPatterns ; i++)
946         {
947                 patt = si->Patt[i];
948
949                 for (j = 0 ; j < patt->Tracks ; j++)
950                 {
951                         note = patt->Notes[j];
952
953                         for (k = 0; k < patt->Lines ; k++, note++)
954                                 if (note->Note)
955                                 {
956                                         note->Inst = newpos[note->Inst];
957                                         if (!note->Inst)
958                                                 note->Note = 0;
959                                 }
960                 }
961         }
962
963         DB (kprintf ("After - LastInstrument = %ld", si->LastInstrument));
964 }
965
966
967
968 GLOBALCALL void RemUnusedInstruments (struct SongInfo *si)
969 {
970         ULONG usecount[MAXINSTRUMENTS] = { 0 };
971         struct Pattern *patt;
972         struct Note *note;
973         UWORD i, j, k;
974
975         for (i = 0; i < si->NumPatterns; i++)
976         {
977                 if (!(patt = si->Patt[i])) continue;
978
979                 for (j = 0; j < patt->Tracks; j++)
980                 {
981                         note = patt->Notes[j];
982
983                         for (k = 0; k < patt->Lines; k++, note++)
984                                 usecount[note->Inst]++;
985                 }
986         }
987
988         for (i = 1; i <= si->LastInstrument; i++)
989         {
990                 if ((usecount[i] == 0) && si->Instr[i])
991                 {
992                         ShowMessage (MSG_INSTR_UNUSED, i);
993                         xmRemInstrument (si, i);
994                 }
995         }
996 }
997
998
999
1000 /* DUnpack.c --- Fibonacci Delta decompression by Steve Hayes */
1001
1002 /* Fibonacci delta encoding for sound data */
1003 static const BYTE codeToDelta[16] = {-34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21};
1004
1005
1006 static BYTE D1Unpack (UBYTE source[], LONG n, BYTE dest[], BYTE x)
1007
1008 /* Unpack Fibonacci-delta encoded data from n byte source
1009  * buffer into 2*n byte dest buffer, given initial data
1010  * value x.  It returns the last data value x so you can
1011  * call it several times to incrementally decompress the data.
1012  */
1013 {
1014         UBYTE d;
1015         LONG i, lim;
1016
1017         lim = n << 1;
1018         for (i = 0; i < lim; ++i)
1019         {
1020                 /* Decode a data nibble, high nibble then low nibble */
1021                 d = source[i >> 1];             /* get a pair of nibbles                */
1022                 if (i & 1)                              /* select low or high nibble    */
1023                         d &= 0xf;                       /* mask to get the low nibble   */
1024                 else
1025                         d >>= 4;                        /* shift to get the high nibble */
1026                 x += codeToDelta[d];    /* add in the decoded delta             */
1027                 dest[i] = x;                    /* store a 1 byte sample                */
1028         }
1029         return x;
1030 }
1031
1032
1033 static void DUnpack (UBYTE source[], LONG n, BYTE dest[])
1034
1035 /* Unpack Fibonacci-delta encoded data from n byte
1036  * source buffer into 2*(n-2) byte dest buffer.
1037  * Source buffer has a pad byte, an 8-bit initial
1038  * value, followed by n-2 bytes comprising 2*(n-2)
1039  * 4-bit encoded samples.
1040  */
1041 {
1042         D1Unpack (source+2, n-2, dest, (BYTE)source[1]);
1043 }
1044
1045
1046
1047 GLOBALCALL void SampChangeSign8 (UBYTE *samp, ULONG len)
1048
1049 /* Performs a sign conversion on a 8bit sample.  The same function can be
1050  * used to convert a signed sample into an unsigned one and vice versa.
1051  *
1052  * TODO: optimize with a LONG oriented loop
1053  */
1054 {
1055         while (len)
1056                 samp[--len] ^= 0x80;
1057 }