4 ** Copyright (C) 1994,95,96,97 Bernardo Innocenti
6 ** Instrument loading/saving/handling routines.
9 #define IFFPARSE_V37_NAMES_ONLY
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>
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>
26 #include "XModulePriv.h"
30 #define UNITY 0x10000L
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[]);
42 struct Library *DataTypesBase = NULL;
43 struct Library *ToccataBase = NULL;
47 GLOBALCALL LONG LoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename)
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.
53 LONG err = IFFERR_NOTIFF;
55 /* Try loading with DataTypes */
57 if (GuiSwitches.UseDataTypes)
58 if (DataTypesBase = OpenLibrary ("datatypes.library", 39L))
60 err = DTLoadInstrument (si, num, filename);
61 CloseLibrary (DataTypesBase); DataTypesBase = NULL;
65 /* Try again using built-in loaders */
69 struct IFFHandle *iff;
73 if (iff->iff_Stream = (ULONG) Open (filename, MODE_OLDFILE))
77 if (!(err = OpenIFF (iff, IFFF_READ)))
79 if (!(err = ParseIFF (iff, IFFPARSE_RAWSTEP)))
81 LONG type = CurrentChunk (iff)->cn_Type;
85 err = Load8SVXInstrument (si, num, iff, filename);
89 err = LoadMAUDInstrument (si, num, iff, filename);
97 ShowMessage (MSG_UNKNOWN_IFF, buf);
98 err = ERROR_OBJECT_WRONG_TYPE;
105 Close (iff->iff_Stream);
111 else err = ERROR_NO_FREE_STORE;
114 if (err == IFFERR_MANGLED || err == IFFERR_SYNTAX)
115 ShowMessage (MSG_ILLEGAL_IFF_STRUCTURE);
117 if (err == IFFERR_NOTIFF)
121 if (mode = ShowRequestArgs (MSG_SELECT_RAW_MODE,
122 MSG_RAW_MODES, NULL))
123 err = RawLoadInstrument (si, num, filename, mode);
129 if (err) LastErr = err;
135 static LONG DTLoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename)
141 if (dto = NewDTObject (filename,
142 DTA_GroupID, GID_SOUND,
145 struct VoiceHeader *vhdr;
151 SDTA_VoiceHeader, &vhdr,
152 SDTA_SampleLength, &Len,
154 SDTA_Sample, &Sample,
155 DTA_Title, &instname,
158 /* Detach sample from DataType Object, so it won't
159 * be freed when we dispose the object.
161 SetDTAttrs (dto, NULL, NULL,
165 /* Create new instrument */
167 if (!xmAddInstrument (si, num,
168 INSTRA_Name, instname,
169 INSTRA_Sample, Sample,
171 INSTRA_Repeat, vhdr->vh_OneShotHiSamples,
172 INSTRA_Replen, vhdr->vh_RepeatHiSamples,
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.
182 INSTRA_Volume, (vhdr->vh_Volume > 64) ?
183 ((vhdr->vh_Volume * 64) / UNITY) : vhdr->vh_Volume,
185 err = ERROR_NO_FREE_STORE;
187 DB(kprintf ("Vol: %ld InstVol: %ld\n", Vol, si->Instr[num]->Volume));
189 else err = RETURN_FAIL;
193 DTA_ErrorString, &errorstring,
195 ShowMessage (MSG_DATATYPES_ERROR, errorstring);
197 DisposeDTObject (dto);
206 GLOBALCALL LONG Load8SVXInstrument (struct SongInfo *si, ULONG num, struct IFFHandle *iff, CONST_STRPTR filename)
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.
213 struct ContextNode *cn;
214 struct VoiceHeader vhdr;
215 struct InstrumentHeader insthdr;
218 BOOL is_valid_8svx = FALSE,
219 insthdr_loaded = FALSE;
221 static LONG stopchunks[] =
229 /* Put the file name if the optional NAME propriety is missing */
232 strncpy (name, FilePart (filename), 63);
237 if (err = StopChunks (iff, stopchunks, 4))
240 if (err = StopOnExit (iff, ID_8SVX, ID_FORM))
245 if (err = ParseIFF (iff, IFFPARSE_SCAN))
247 if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK;
248 break; /* Free resources & exit */
251 if ((cn = CurrentChunk (iff)) && (cn->cn_Type == ID_8SVX))
257 if ((err = ReadChunkBytes (iff, &insthdr, sizeof (insthdr))) !=
258 sizeof (insthdr)) return err;
260 if (!num) num = insthdr.Num;
265 if ((err = ReadChunkBytes (iff, &vhdr, sizeof (vhdr))) !=
266 sizeof (vhdr)) return err;
270 if (!xmAddInstrumentA (si, num, NULL))
271 return ERROR_NO_FREE_STORE;
273 if (!num) num = si->LastInstrument;
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,
283 is_valid_8svx = TRUE;
290 struct Instrument *instr;
295 xmAddInstrumentA (si, num, NULL);
296 if (!num) num = si->LastInstrument;
299 if (!(instr = si->Instr[num]))
300 return ERROR_NO_FREE_STORE;
302 if (!(sample = AllocVec (cn->cn_Size, MEMF_SAMPLE)))
303 return ERROR_NO_FREE_STORE;
305 xmSetInstrument (si, num,
306 INSTRA_Sample, sample,
307 INSTRA_Length, cn->cn_Size,
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
315 if ((err = ReadChunkBytes (iff, instr->Sample,
319 is_valid_8svx = TRUE;
324 ReadChunkBytes (iff, name, min(cn->cn_Size, 63));
325 name[63] = '\0'; /* Ensure string termination */
336 xmSetInstrument (si, num,
342 if (vhdr.vh_Compression == CMP_FIBDELTA)
345 struct Instrument *instr = si->Instr[num];
347 if (buf = AllocVec (instr->Length * 2, MEMF_SAMPLE))
349 DUnpack (instr->Sample, instr->Length + 2, buf);
350 FreeVec (instr->Sample);
352 xmSetInstrument (si, num,
354 INSTRA_Length, instr->Length * 2,
358 else if (vhdr.vh_Compression != CMP_NONE)
359 ShowMessage (MSG_UNKNOWN_COMPRESSION);
362 else err = IFFERR_MANGLED;
369 /* Number of samples loaded & processed at one time */
370 #define MAUDBLOCKSIZE 32768
372 static LONG LoadMAUDInstrument (struct SongInfo *si, ULONG num, struct IFFHandle *iff, CONST_STRPTR filename)
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.
379 struct ContextNode *cn;
380 struct MaudHeader mhdr;
382 BOOL is_valid_maud = FALSE;
385 static LONG stopchunks[] =
393 /* Put the file name if the optional NAME propriety is missing */
397 strncpy (name, FilePart (filename), 63);
402 if (err = StopChunks (iff, stopchunks, 3))
405 if (err = StopOnExit (iff, ID_MAUD, ID_FORM))
411 if (err = ParseIFF (iff, IFFPARSE_SCAN))
413 if (err == IFFERR_EOF || err == IFFERR_EOC) err = RETURN_OK;
414 break; /* Free resources & exit */
417 if ((cn = CurrentChunk (iff)) && (cn->cn_Type == ID_MAUD))
422 if ((err = ReadChunkBytes (iff, &mhdr, sizeof (mhdr))) !=
423 sizeof (mhdr)) return err;
425 if ((mhdr.mhdr_SampleSizeU != 8) && (mhdr.mhdr_SampleSizeU != 16))
427 ShowMessage (MSG_SAMPLE_WRONG_SIZE, mhdr.mhdr_SampleSizeU);
428 return IFFERR_SYNTAX;
431 if (mhdr.mhdr_ChannelInfo != MCI_MONO)
433 ShowMessage (MSG_SAMPLE_NOT_MONO, mhdr.mhdr_ChannelInfo);
434 return IFFERR_SYNTAX;
437 if (mhdr.mhdr_Channels != 1)
439 ShowMessage (MSG_SAMPLE_WRONG_NUMBER_OF_CHANNELS, mhdr.mhdr_Channels);
440 return IFFERR_SYNTAX;
443 is_valid_maud = TRUE;
449 struct Instrument *instr;
453 return IFFERR_SYNTAX;
455 if (!(sample = AllocVec ((mhdr.mhdr_Samples + 1) & (~1), MEMF_SAMPLE)))
456 return ERROR_NO_FREE_STORE;
458 if (!(instr = xmAddInstrument (si, num,
459 INSTRA_Sample, sample,
460 INSTRA_Length, (mhdr.mhdr_Samples + 1) & (~1),
464 return ERROR_NO_FREE_STORE;
467 if (mhdr.mhdr_SampleSizeU == 8) /* 8 bit */
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
474 if ((err = ReadChunkBytes (iff, instr->Sample,
475 instr->Length)) == 0) return err;
477 SampChangeSign8 (instr->Sample, instr->Length);
480 else if (mhdr.mhdr_SampleSizeU == 16) /* 16 bit */
483 ULONG actual, current = 0;
485 if (!(tmp = AllocPooled (Pool, MAUDBLOCKSIZE * ((mhdr.mhdr_SampleSizeC + 7) / 8))))
486 return ERROR_NO_FREE_STORE;
488 if (mhdr.mhdr_Compression != MCOMP_NONE)
489 if (!(ToccataBase = MyOpenLibrary ("toccata.library", 0L)))
491 FreePooled (Pool, tmp, MAUDBLOCKSIZE * sizeof (WORD));
492 CantOpenLib ("toccata.library", 0L);
493 return ERROR_INVALID_RESIDENT_LIBRARY;
498 actual = ReadChunkBytes (iff, tmp, MAUDBLOCKSIZE * ((mhdr.mhdr_SampleSizeC + 7) / 8));
500 if (actual == 0) break;
502 /* Filter (tmp, actual); */
504 switch (mhdr.mhdr_Compression)
507 /* Convert 8bit A-Law data to 8bit signed data */
508 T_Convert (tmp, instr->Sample + current, actual, TMODE_ALAW, TMODE_LINEAR_8);
513 /* Convert 8bit µ-Law data to 8bit signed data */
514 T_Convert (tmp, instr->Sample + current, actual, TMODE_ULAW, TMODE_LINEAR_8);
519 /* Convert 16bit signed data to 8bit signed data */
521 for (i = 0; (i < actual) && (current < mhdr.mhdr_Samples); i++, current++)
522 instr->Sample[current] = tmp[i] >> 8;
527 SampChangeSign8 (instr->Sample, instr->Length);
528 FreePooled (Pool, tmp, MAUDBLOCKSIZE * sizeof (WORD));
529 CloseLibrary (ToccataBase);
533 is_valid_maud = TRUE;
538 ReadChunkBytes (iff, name, min(cn->cn_Size, 63));
549 xmSetInstrument (si, num,
555 if (mhdr.mhdr_Compression == CMP_FIBDELTA)
558 struct Instrument *instr = si->Instr[num];
560 if (buf = AllocVec (instr->Length * 2, MEMF_SAMPLE))
562 DUnpack (instr->Sample, instr->Length + 2, buf);
563 FreeVec (instr->Sample);
565 xmSetInstrument (si, num,
567 INSTRA_Length, instr->Length * 2,
571 else if (mhdr.mhdr_Compression != CMP_NONE)
572 ShowMessage (MSG_UNKNOWN_COMPRESSION);
575 else err = IFFERR_NOTIFF;
582 static LONG RawLoadInstrument (struct SongInfo *si, ULONG num, CONST_STRPTR filename, UWORD mode)
584 /* Load a raw file to the instrument slot pointed by inst.
585 * mode 1 - signed 8bit
590 struct FileInfoBlock *fib;
591 struct Instrument *instr;
595 if (lock = Lock (filename, ACCESS_READ))
598 if (fib = AllocDosObject (DOS_FIB, NULL))
600 if (Examine (lock, fib))
604 FreeDosObject (DOS_FIB, fib);
606 else err = ERROR_NO_FREE_STORE;
610 if (fh = OpenFromLock (lock))
614 lock = NULL; /* OpenFromLock() relinquished our lock! */
616 if (sample = AllocVec (len, MEMF_SAMPLE))
618 if (instr = xmAddInstrument (si, num,
619 INSTRA_Sample, sample,
622 INSTRA_Name, FilePart (filename),
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 ;-)
630 Read (fh, sample, len);
633 SampChangeSign8 (sample, instr->Length);
638 err = ERROR_NO_FREE_STORE;
641 else err = ERROR_NO_FREE_STORE;
648 UnLock (lock); /* Will be NULL if OpenFromLock() was successful */
657 GLOBALCALL LONG SaveInstrument (struct Instrument *inst, CONST_STRPTR filename)
660 struct IFFHandle *iff;
663 if (iff = AllocIFF())
665 if (iff->iff_Stream = (ULONG) Open (filename, MODE_NEWFILE))
669 if (!(err = OpenIFF (iff, IFFF_WRITE)))
671 err = Save8SVXInstrument (inst, 0, iff);
675 Close (iff->iff_Stream);
681 else return ERROR_NO_FREE_STORE;
685 if (GuiSwitches.InstrSaveIcons)
687 PutIcon ("def_Instrument", filename);
691 /* Remove incomplete file */
693 DeleteFile (filename);
701 GLOBALCALL LONG Save8SVXInstrument (struct Instrument *instr, ULONG num, struct IFFHandle *iff)
703 /* Save the instrument pointed by inst to a standard IFF 8SVX file.
706 struct VoiceHeader vhdr;
712 if (err = PushChunk (iff, ID_8SVX, ID_FORM, IFFSIZE_UNKNOWN))
719 struct InstrumentHeader insthdr;
722 insthdr.Type = ITYPE_SAMPLE8;
723 insthdr.FineTune = instr->FineTune;
725 if (err = PushChunk (iff, ID_8SVX, ID_INST, sizeof (insthdr)))
727 if ((err = WriteChunkBytes (iff, &insthdr, sizeof (insthdr))) !=
730 if (err = PopChunk (iff)) return err; /* Pop INST */
735 if (vhdr.vh_RepeatHiSamples = instr->Replen)
737 vhdr.vh_OneShotHiSamples = instr->Repeat;
740 vhdr.vh_OneShotHiSamples = instr->Length;
742 vhdr.vh_SamplesPerHiCycle = 0;
743 vhdr.vh_SamplesPerSec = 8363;
745 vhdr.vh_Compression = CMP_NONE;
746 vhdr.vh_Volume = (instr->Volume * UNITY) / 64;
748 if (err = PushChunk (iff, ID_8SVX, ID_VHDR, sizeof (vhdr)))
750 if ((err = WriteChunkBytes (iff, &vhdr, sizeof (vhdr))) !=
753 if (err = PopChunk (iff)) return err; /* Pop VHDR */
758 ULONG l = strlen (instr->Name) + 1;
760 if (err = PushChunk (iff, ID_8SVX, ID_NAME, l))
762 if ((err = WriteChunkBytes (iff, instr->Name, l)) != l)
764 if (err = PopChunk (iff)) return err; /* Pop NAME */
771 if (PushChunk (iff, ID_8SVX, ID_BODY, instr->Length))
773 if ((err = WriteChunkBytes (iff, instr->Sample, instr->Length)) !=
774 instr->Length) return err;
775 if (err = PopChunk (iff)) return err; /* Pop BODY */
778 PopChunk (iff); /* Pop 8SVX */
785 GLOBALCALL void OptimizeInstruments (struct SongInfo *si)
787 /* Remove useless sample data (cut beyond loops and zero-tails) */
791 struct Instrument *instr;
793 for (i = 1 ; i <= si->LastInstrument ; i++)
795 if (!(instr = si->Instr[i])) continue;
796 if (!instr->Sample) continue;
798 newlen = instr->Length;
802 if (instr->Length > instr->Repeat + instr->Replen)
803 newlen = instr->Repeat + instr->Replen; /* Cut instrument after loop */
809 instr->Repeat = 0; /* Kill null loops */
811 /* Kill instrument zero-tail.
812 * In order to reduce the instrument even more,
813 * 1 & -1 are treated the same as zero.
816 tail = instr->Sample + instr->Length - 1;
817 while ((*tail < 1) && (*tail > -1) && (tail > instr->Sample))
820 newlen = tail - instr->Sample;
821 if (newlen & 1) newlen++; /* Pad instrument size to words */
823 /* leave 2 end zeroes to prevent an audible end-of-instrument click. */
824 if (newlen) newlen += 2;
828 xmRemInstrument (si, i); /* This instrument is mute! Free it... */
829 else if (newlen < instr->Length)
831 /* Resize the instrument if necessary */
834 /* Allocate memory for optimized instrument */
835 if (!(newinstr = AllocVec (newlen, MEMF_SAMPLE)))
837 ShowMessage (MSG_NO_MEMORY_TO_OPTIMIZE_INSTR, i);
838 continue; /* Better luck with next instrument :) */
841 ShowMessage (MSG_INSTR_WILL_SHRINK, i, instr->Length, newlen);
843 /* Copy first part of instrument */
844 CopyMem (instr->Sample, newinstr, newlen);
846 /* Free old instrument */
847 FreeVec (instr->Sample);
849 /* Replace with new instrument */
850 xmSetInstrument (si, i,
851 INSTRA_Sample, newinstr,
852 INSTRA_Length, newlen,
861 GLOBALCALL void RemDupInstruments (struct SongInfo *si)
862 /* Find out identical patterns and cut them out */
865 struct Instrument *insta, *instb;
866 struct Pattern *patt;
870 for (i = 1; i < si->LastInstrument; i++) /* '<' instead of '<=' is ok here... */
872 if (!(insta = si->Instr[i])) continue;
873 if (!insta->Length) continue;
875 for (j = i + 1; j <= si->LastInstrument ; j++)
877 if (!(instb = si->Instr[j])) continue;
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)
886 if (!memcmp (insta->Sample, instb->Sample, insta->Length))
888 xmRemInstrument (si, j);
890 for (k = 0; k < si->NumPatterns; k++)
892 if (!(patt = si->Patt[k])) continue;
894 for (w = 0; w < patt->Tracks; w++)
896 note = patt->Notes[w];
897 for (v = 0; v < patt->Lines; v++, note++)
898 if (note->Inst == j) note->Inst = i;
902 ShowMessage (MSG_INSTR_DUPES_REMOVED, i, j);
912 GLOBALCALL void RemapInstruments (struct SongInfo *si)
914 /* Remove empty slots between instruments, to allow those module formats
915 * that support less instruments to use even the last instruments.
919 UBYTE newpos[MAXINSTRUMENTS] = { 0 };
920 struct Instrument *instr;
921 struct Pattern *patt;
926 DB (kprintf ("Before - LastInstrument = %ld", si->LastInstrument));
928 /* Build instrument remap table & compress instrument slots */
929 for (i = 1, j = 0; i <= si->LastInstrument; i++)
931 if (!(instr = si->Instr[i])) continue;
938 if (j != i) DoMethod ((Object *)si, SNGM_SWAPINSTRUMENTS, i, j);
945 for (i = 0 ; i < si->NumPatterns ; i++)
949 for (j = 0 ; j < patt->Tracks ; j++)
951 note = patt->Notes[j];
953 for (k = 0; k < patt->Lines ; k++, note++)
956 note->Inst = newpos[note->Inst];
963 DB (kprintf ("After - LastInstrument = %ld", si->LastInstrument));
968 GLOBALCALL void RemUnusedInstruments (struct SongInfo *si)
970 ULONG usecount[MAXINSTRUMENTS] = { 0 };
971 struct Pattern *patt;
975 for (i = 0; i < si->NumPatterns; i++)
977 if (!(patt = si->Patt[i])) continue;
979 for (j = 0; j < patt->Tracks; j++)
981 note = patt->Notes[j];
983 for (k = 0; k < patt->Lines; k++, note++)
984 usecount[note->Inst]++;
988 for (i = 1; i <= si->LastInstrument; i++)
990 if ((usecount[i] == 0) && si->Instr[i])
992 ShowMessage (MSG_INSTR_UNUSED, i);
993 xmRemInstrument (si, i);
1000 /* DUnpack.c --- Fibonacci Delta decompression by Steve Hayes */
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};
1006 static BYTE D1Unpack (UBYTE source[], LONG n, BYTE dest[], BYTE x)
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.
1018 for (i = 0; i < lim; ++i)
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 */
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 */
1033 static void DUnpack (UBYTE source[], LONG n, BYTE dest[])
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.
1042 D1Unpack (source+2, n-2, dest, (BYTE)source[1]);
1047 GLOBALCALL void SampChangeSign8 (UBYTE *samp, ULONG len)
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.
1052 * TODO: optimize with a LONG oriented loop
1056 samp[--len] ^= 0x80;