#include #include #include #include #include #include "doomdef.h" #include "m_swap.h" #include "templates.h" #include "timidity.h" #include "sf2.h" #include "i_soundfont.h" namespace Timidity { extern std::unique_ptr gus_sfreader; } using namespace Timidity; #define cindex(identifier) (uint8_t)(((size_t)&((SFGenComposite *)1)->identifier - 1) / 2) class CIOErr {}; class CBadForm {}; class CBadVer {}; struct ListHandler { uint32_t ID; void (*Parser)(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen); }; enum { GENF_InstrOnly = 1, // Only valid at intstrument level GENF_PresetOnly = 2, // Only valid at preset level GENF_Range = 4, // Value is a range, GENF_Index = 8, // Value is an index (aka unsigned) GENF_32768_Ok = 16, // -32768 is a valid value }; struct GenDef { short Min; short Max; uint8_t StructIndex; uint8_t Flags; }; static const GenDef GenDefs[] = { /* 0 */ { 0, 32767, cindex(startAddrsOffset), GENF_InstrOnly }, /* 1 */ { -32768, 0, cindex(endAddrsOffset), GENF_InstrOnly }, /* 2 */ { -32768, 32767, cindex(startLoopAddrsOffset), GENF_InstrOnly }, /* 3 */ { -32768, 32767, cindex(endLoopAddrsOffset), GENF_InstrOnly }, /* 4 */ { 0, 32767, cindex(startAddrsCoarseOffset), GENF_InstrOnly }, /* 5 */ { -12000, 12000, cindex(modLfoToPitch), 0 }, /* 6 */ { -12000, 12000, cindex(vibLfoToPitch), 0 }, /* 7 */ { -12000, 12000, cindex(modEnvToPitch), 0 }, /* 8 */ { 1500, 13500, cindex(initialFilterFc), 0 }, /* 9 */ { 0, 960, cindex(initialFilterQ), 0 }, /* 10 */ { -12000, 12000, cindex(modLfoToFilterFc), 0 }, /* 11 */ { -12000, 12000, cindex(modEnvToFilterFc), 0 }, /* 12 */ { -32768, 0, cindex(endAddrsCoarseOffset), 0 }, /* 13 */ { -960, 960, cindex(modLfoToVolume), 0 }, /* 14 */ { 0, 0, 255 /* unused1 */, 0 }, /* 15 */ { 0, 1000, cindex(chorusEffectsSend), 0 }, /* 16 */ { 0, 1000, cindex(reverbEffectsSend), 0 }, /* 17 */ { -500, 500, cindex(pan), 0 }, /* 18 */ { 0, 0, 255 /* unused2 */, 0 }, /* 19 */ { 0, 0, 255 /* unused3 */, 0 }, /* 20 */ { 0, 0, 255 /* unused4 */, 0 }, /* 21 */ { -12000, 5000, cindex(delayModLFO), GENF_32768_Ok }, /* 22 */ { -16000, 4500, cindex(freqModLFO), 0 }, /* 23 */ { -12000, 5000, cindex(delayVibLFO), GENF_32768_Ok }, /* 24 */ { -16000, 4500, cindex(freqVibLFO), 0 }, /* 25 */ { -12000, 5000, cindex(delayModEnv), GENF_32768_Ok }, /* 26 */ { -12000, 8000, cindex(attackModEnv), GENF_32768_Ok }, /* 27 */ { -12000, 5000, cindex(holdModEnv), GENF_32768_Ok }, /* 28 */ { -12000, 8000, cindex(decayModEnv), 0 }, /* 29 */ { 0, 1000, cindex(sustainModEnv), 0 }, /* 30 */ { -12000, 8000, cindex(releaseModEnv), 0 }, /* 31 */ { -1200, 1200, cindex(keynumToModEnvHold), 0 }, /* 32 */ { -1200, 1200, cindex(keynumToModEnvDecay), 0 }, /* 33 */ { -12000, 5000, cindex(delayVolEnv), GENF_32768_Ok }, /* 34 */ { -12000, 8000, cindex(attackVolEnv), GENF_32768_Ok }, /* 35 */ { -12000, 5000, cindex(holdVolEnv), GENF_32768_Ok }, /* 36 */ { -12000, 5000, cindex(decayVolEnv), 0 }, /* 37 */ { 0, 1440, cindex(sustainVolEnv), 0 }, /* 38 */ { -12000, 8000, cindex(releaseVolEnv), 0 }, /* 39 */ { -1200, 1200, cindex(keynumToVolEnvHold), 0 }, /* 40 */ { -1200, 1200, cindex(keynumToVolEnvDecay), 0 }, /* 41 */ { -32768, 32767, 255 /* instrument */, GENF_Index | GENF_PresetOnly }, /* 42 */ { 0, 0, 255 /* reserved1 */, 0 }, /* 43 */ { 0, 127, 255 /* keyRange */, GENF_Range }, /* 44 */ { 0, 127, 255 /* velRange */, GENF_Range }, /* 45 */ { -32768, 32767, cindex(startLoopAddrsCoarseOffset), GENF_InstrOnly }, /* 46 */ { 0, 127, cindex(keynum), GENF_InstrOnly }, /* 47 */ { 1, 127, cindex(velocity), GENF_InstrOnly }, /* 48 */ { 0, 1440, cindex(initialAttenuation), 0 }, /* 49 */ { 0, 0, 255 /* reserved2 */, 0 }, /* 50 */ { -32768, 32767, cindex(endLoopAddrsCoarseOffset), GENF_InstrOnly }, /* 51 */ { -120, 120, cindex(coarseTune), 0 }, /* 52 */ { -99, 99, cindex(fineTune), 0 }, /* 53 */ { -32768, 32767, 255 /* sampleID */, GENF_Index | GENF_InstrOnly }, /* 54 */ { -32768, 32767, cindex(sampleModes), GENF_InstrOnly }, /* 55 */ { 0, 0, 255 /* reserved3 */, 0 }, /* 56 */ { 0, 1200, cindex(scaleTuning), 0 }, /* 57 */ { 1, 127, cindex(exclusiveClass), GENF_InstrOnly }, /* 58 */ { 0, 127, cindex(overridingRootKey), GENF_InstrOnly }, }; static const SFGenComposite DefaultGenerators = { { { 0, 127 } }, // keyRange { 0, 127 }, // velRange { 0 }, // instrument/sampleID 0, // modLfoToPitch 0, // vibLfoToPitch 0, // modEnvToPitch 13500, // initialFilterFc 0, // initialFilterQ 0, // modLfoToFilterFc 0, // modEnvToFilterFc 0, // modLfoToVolume 0, // chorusEffectsSend 0, // reverbEffectsSend 0, // pan -12000, // delayModLFO 0, // freqModLFO -12000, // delayVibLFO 0, // freqVibLFO -12000, // delayModEnv -12000, // attackModEnv -12000, // holdModEnv -12000, // decayModEnv 0, // sustainModEnv -12000, // releaseModEnv 0, // keynumToModEnvHold 0, // keynumToModEnvDecay -12000, // delayVolEnv -12000, // attackVolEnv -12000, // holdVolEnv -12000, // decayVolEnv 0, // sustainVolEnv -12000, // releaseVolEnv 0, // keynumToVolEnvHold 0, // keynumToVolEnvDecay 0, // initialAttenuation 0, // coarseTune 0, // fineTune 100, // scaleTuning 0, 0, // startAddrs(Coarse)Offset 0, 0, // endAddrs(Coarse)Offset 0, 0, // startLoop(Coarse)Offset 0, 0, // endLoop(Coarse)Offset -1, // keynum -1, // velocity 0, // sampleModes 0, // exclusiveClass -1 // overridingRootKey }; static void ParseIfil(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen); static void ParseSmpl(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen); static void ParseSm24(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen); static void ParsePhdr(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen); static void ParseBag(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen); static void ParseMod(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen); static void ParseGen(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen); static void ParseInst(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen); static void ParseShdr(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen); ListHandler INFOHandlers[] = { { ID_ifil, ParseIfil }, { 0, 0 } }; ListHandler SdtaHandlers[] = { { ID_smpl, ParseSmpl }, { ID_sm24, ParseSm24 }, { 0, 0 } }; ListHandler PdtaHandlers[] = { { ID_phdr, ParsePhdr }, { ID_pbag, ParseBag }, { ID_pmod, ParseMod }, { ID_pgen, ParseGen }, { ID_inst, ParseInst }, { ID_ibag, ParseBag }, { ID_imod, ParseMod }, { ID_igen, ParseGen }, { ID_shdr, ParseShdr }, { 0, 0 } }; static double timecent_to_sec(int16_t timecent) { if (timecent == -32768) return 0; return pow(2.0, timecent / 1200.0); } static int32_t to_offset(int offset) { return (int32_t)offset << (7+15); } static int32_t calc_rate(Renderer *song, int diff, double sec) { double rate; if(sec < 0.006) sec = 0.006; if(diff == 0) diff = 255; diff <<= (7+15); rate = ((double)diff / song->rate) * song->control_ratio / sec; return (int32_t)rate; } static inline uint32_t read_id(FileReader &f) { uint32_t id; if (f.Read(&id, 4) != 4) { throw CIOErr(); } return id; } static inline int read_byte(FileReader &f) { uint8_t x; if (f.Read(&x, 1) != 1) { throw CIOErr(); } return x; } static inline int read_char(FileReader &f) { int8_t x; if (f.Read(&x, 1) != 1) { throw CIOErr(); } return x; } static inline int read_uword(FileReader &f) { uint16_t x; if (f.Read(&x, 2) != 2) { throw CIOErr(); } return LittleShort(x); } static inline int read_sword(FileReader &f) { int16_t x; if (f.Read(&x, 2) != 2) { throw CIOErr(); } return LittleShort(x); } static inline uint32_t read_dword(FileReader &f) { uint32_t x; if (f.Read(&x, 4) != 4) { throw CIOErr(); } return LittleLong(x); } static inline void read_name(FileReader &f, char name[21]) { if (f.Read(name, 20) != 20) { throw CIOErr(); } name[20] = 0; } static inline void skip_chunk(FileReader &f, uint32_t len) { // RIFF, like IFF, adds an extra pad byte to the end of // odd-sized chunks so that new chunks are always on even // byte boundaries. if (f.Seek(len + (len & 1), FileReader::SeekCur) != 0) { throw CIOErr(); } } static void check_list(FileReader &f, uint32_t id, uint32_t filelen, uint32_t &chunklen) { if (read_id(f) != ID_LIST) { throw CBadForm(); } chunklen = read_dword(f); if (chunklen + 8 > filelen) { throw CBadForm(); } if (read_id(f) != id) { throw CBadForm(); } } static void ParseIfil(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen) { uint16_t major, minor; if (chunklen != 4) { throw CBadForm(); } major = read_uword(f); minor = read_uword(f); if (major != 2) { throw CBadVer(); } sf2->MinorVersion = minor; } static void ParseLIST(SFFile *sf2, FileReader &f, uint32_t chunklen, ListHandler *handlers) { ListHandler *handler; uint32_t id; uint32_t len; chunklen -= 4; while (chunklen > 0) { id = read_id(f); len = read_dword(f); if (len + 8 > chunklen) { throw CBadForm(); } chunklen -= len + (len & 1) + 8; for (handler = handlers; handler->ID != 0; ++handler) { if (handler->ID == id && handler->Parser != NULL) { handler->Parser(sf2, f, id, len); break; } } if (handler->ID == 0) { // Skip unknown chunks skip_chunk(f, len); } } } static void ParseINFO(SFFile *sf2, FileReader &f, uint32_t chunklen) { sf2->MinorVersion = -1; ParseLIST(sf2, f, chunklen, INFOHandlers); if (sf2->MinorVersion < 0) { // The ifil chunk must be present. throw CBadForm(); } } static void ParseSdta(SFFile *sf2, FileReader &f, uint32_t chunklen) { ParseLIST(sf2, f, chunklen, SdtaHandlers); if (sf2->SampleDataOffset == 0) { throw CBadForm(); } // Section 6.2, page 20: It is not clear if the extra pad byte for an // odd chunk is supposed to be included in the chunk length field. if (sf2->SizeSampleDataLSB != sf2->SizeSampleData && sf2->SizeSampleDataLSB != sf2->SizeSampleData + (sf2->SizeSampleData & 1)) { sf2->SampleDataLSBOffset = 0; sf2->SizeSampleDataLSB = 0; } } static void ParseSmpl(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen) { // Only use the first smpl chunk. (Or should we reject files with more than one?) if (sf2->SampleDataOffset == 0) { if (chunklen & 1) { // Chunk must be an even number of bytes. throw CBadForm(); } sf2->SampleDataOffset = (uint32_t)f.Tell(); sf2->SizeSampleData = chunklen >> 1; } skip_chunk(f, chunklen); } static void ParseSm24(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen) { // The sm24 chunk is ignored if the file version is < 2.04 if (sf2->MinorVersion >= 4) { // Only use the first sm24 chunk. (Or should we reject files with more than one?) if (sf2->SampleDataLSBOffset == 0) { sf2->SampleDataLSBOffset = (uint32_t)f.Tell(); sf2->SizeSampleDataLSB = chunklen; } } skip_chunk(f, chunklen); } static void ParsePdta(SFFile *sf2, FileReader &f, uint32_t chunklen) { ParseLIST(sf2, f, chunklen, PdtaHandlers); } static void ParsePhdr(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen) { SFPreset *preset; // Section 7.2, page 22: // If the PHDR sub-chunk is missing, or contains fewer than two records, // or its size is not a multiple of 38 bytes, the file should be rejected // as structurally unsound. if (chunklen < 38*2 || chunklen % 38 != 0) { throw CBadForm(); } sf2->NumPresets = chunklen / 38; sf2->Presets = new SFPreset[sf2->NumPresets]; preset = sf2->Presets; for (int i = sf2->NumPresets; i != 0; --i, ++preset) { read_name(f, preset->Name); preset->Program = read_uword(f); preset->Bank = read_uword(f); preset->BagIndex = read_uword(f); skip_chunk(f, 4*3); // Skip library, genre, and morphology // Section 7.2, page 22: // The preset bag indices will be monotonically increasing with // increasing preset headers. if (preset != sf2->Presets) { if (preset->BagIndex < (preset - 1)->BagIndex) { throw CBadForm(); } } } } static void ParseBag(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen) { SFBag *bags, *bag; uint16_t prev_mod = 0; int numbags; int i; // Section 7.3, page 22: // It is always a multiple of four bytes in length, and contains one // record for each preset zone plus one record for a terminal zone. ... // If the PBAG sub-chunk is missing, or its size is not a multiple of // four bytes, the file should be rejected as structurally unsound. // Section 7.7: IBAG is the same, but substitute "instrument" for "preset". if (chunklen & 3) { throw CBadForm(); } numbags = chunklen >> 2; if (chunkid == ID_pbag) { if (numbags != sf2->Presets[sf2->NumPresets - 1].BagIndex + 1) { throw CBadForm(); } sf2->PresetBags = bags = new SFBag[numbags]; sf2->NumPresetBags = numbags; } else { assert(chunkid == ID_ibag); if (numbags != sf2->Instruments[sf2->NumInstruments - 1].BagIndex + 1) { throw CBadForm(); } sf2->InstrBags = bags = new SFBag[numbags]; sf2->NumInstrBags = numbags; } for (bag = bags, i = numbags; i != 0; --i, ++bag) { bag->GenIndex = read_uword(f); uint16_t mod = read_uword(f); // Section 7.3, page 22: // If the generator or modulator indices are non-monotonic or do not // match the size of the respective PGEN or PMOD sub-chunks, the file // is structurally defective and should be rejected at load time. if (bag != bags) { if (bag->GenIndex < (bag - 1)->GenIndex || mod < prev_mod) { throw CBadForm(); } } prev_mod = mod; bag->KeyRange.Lo = bag->VelRange.Lo = 0; bag->KeyRange.Hi = bag->VelRange.Hi = 127; bag->Target = -1; } } static void ParseMod(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen) { // Section 7.4, page 23: // It [the PMOD sub-chunk] is always a multiple of ten bytes in length, // and contains zero or more modulators plus a terminal record if (chunklen % 10 != 0) { throw CBadForm(); } // We've checked the length, now ignore the chunk. skip_chunk(f, chunklen); } static void ParseGen(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen) { SFGenList *gens, *gen; int numgens; int i; // Section 7.5, page 24: // If the PGEN sub-chunk is missing, or its size is not a multiple of // four bytes, the file should be rejected as structurally unsound. if (chunklen & 3) { throw CBadForm(); } numgens = chunklen >> 2; if (chunkid == ID_pgen) { // Section 7.3, page 22: // the size of the PGEN sub-chunk in bytes will be equal to four // times the terminal preset’s wGenNdx plus four. if (numgens != sf2->PresetBags[sf2->NumPresetBags - 1].GenIndex + 1) { throw CBadForm(); } sf2->PresetGenerators = gens = new SFGenList[numgens]; sf2->NumPresetGenerators = numgens; } else { assert(chunkid == ID_igen); if (numgens != sf2->InstrBags[sf2->NumInstrBags - 1].GenIndex + 1) { throw CBadForm(); } sf2->InstrGenerators = gens = new SFGenList[numgens]; sf2->NumInstrGenerators = numgens; } for (i = numgens, gen = gens; i != 0; --i, ++gen) { gen->Oper = read_uword(f); gen->uAmount = read_uword(f); #ifdef __BIG_ENDIAN__ if (gen->Oper == GEN_keyRange || gen->Oper == GEN_velRange) { // Reswap range generators gen->uAmount = LittleShort(gen->uAmount); } #endif } } static void ParseInst(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen) { int i; SFInst *inst; // Section 7.6, page 25: // If the INST sub-chunk is missing, contains fewer than two records, or its // size is not a multiple of 22 bytes, the file should be rejected as // structurally unsound. if (chunklen < 22*2 || chunklen % 22 != 0) { throw CBadForm(); } sf2->NumInstruments = chunklen / 22; sf2->Instruments = inst = new SFInst[sf2->NumInstruments]; for (i = sf2->NumInstruments; i != 0; --i, ++inst) { read_name(f, inst->Name); inst->BagIndex = read_uword(f); // Section 7.6, page 25: // If the instrument bag indices are non-monotonic or if the terminal // instrument’s wInstBagNdx does not match the IBAG sub-chunk size, the // file is structurally defective and should be rejected at load time. if (inst != sf2->Instruments) { if (inst->BagIndex < (inst - 1)->BagIndex) { throw CBadForm(); } } } } static void ParseShdr(SFFile *sf2, FileReader &f, uint32_t chunkid, uint32_t chunklen) { int i; SFSample *sample; // Section 7.10, page 29: // If the SHDR sub-chunk is missing, or its is size is not a multiple of 46 // bytes the file should be rejected as structurally unsound. if (chunklen % 46 != 0) { throw CBadForm(); } sf2->NumSamples = chunklen / 46; sf2->Samples = sample = new SFSample[sf2->NumSamples]; for (i = sf2->NumSamples; i != 0; --i, ++sample) { sample->InMemoryData = NULL; read_name(f, sample->Name); sample->Start = read_dword(f); sample->End = read_dword(f); sample->StartLoop = read_dword(f); sample->EndLoop = read_dword(f); sample->SampleRate = read_dword(f); sample->OriginalPitch = read_byte(f); sample->PitchCorrection = read_char(f); sample->SampleLink = read_uword(f); sample->SampleType = read_uword(f); if (sample->SampleRate == 0) { // Section 7.10, page 29: // A value of zero is illegal. If an illegal or impractical value is // encountered, the nearest practical value should be used. sample->SampleRate = 400; } if (sample->OriginalPitch > 127) { // Section 7.10, page 29: // For unpitched sounds, a conventional value of 255 should be used // Values between 128 and 254 are illegal. Whenever an illegal value // or a value of 255 is encountered, the value 60 should be used. sample->OriginalPitch = 60; } // Clamp sample positions to the available sample data. sample->Start = MIN(sample->Start, sf2->SizeSampleData - 1); sample->End = MIN(sample->End, sf2->SizeSampleData - 1); sample->StartLoop = MIN(sample->StartLoop, sf2->SizeSampleData - 1); sample->EndLoop = MIN(sample->EndLoop, sf2->SizeSampleData - 1); if (sample->Start >= sample->End) { sample->SampleType |= SFST_Bad; } } } SFFile *ReadSF2(const char *filename, FileReader &f) { SFFile *sf2 = NULL; uint32_t filelen; uint32_t chunklen; try { // Read RIFF sfbk header if (read_id(f) != ID_RIFF) { return NULL; } filelen = read_dword(f); if (read_id(f) != ID_sfbk) { return NULL; } filelen -= 4; // First chunk must be an INFO LIST check_list(f, ID_INFO, filelen, chunklen); sf2 = new SFFile(filename); ParseINFO(sf2, f, chunklen); filelen -= chunklen + 8; // Second chunk must be a sdta LIST check_list(f, ID_sdta, filelen, chunklen); ParseSdta(sf2, f, chunklen); // Third chunk must be a pdta LIST check_list(f, ID_pdta, filelen, chunklen); ParsePdta(sf2, f, chunklen); // There should be no more chunks. If there are, we'll just ignore them rather than reject the file. if (!sf2->FinalStructureTest()) { throw CBadForm(); } sf2->CheckBags(); sf2->TranslatePercussions(); return sf2; } catch (CIOErr) { Printf("Error reading %s: %s\n", filename, strerror(errno)); } catch (CBadForm) { Printf("%s is corrupted.\n", filename); } catch (CBadVer) { Printf("%s is not a SoundFont version 2 file.\n", filename); } if (sf2 != NULL) { delete sf2; } return NULL; } SFFile::SFFile(const char *filename) : FontFile(filename) { Presets = NULL; PresetBags = NULL; PresetGenerators = NULL; Instruments = NULL; InstrBags = NULL; InstrGenerators = NULL; Samples = NULL; MinorVersion = 0; SampleDataOffset = 0; SampleDataLSBOffset = 0; SizeSampleData = 0; SizeSampleDataLSB = 0; NumPresets = 0; NumPresetBags = 0; NumPresetGenerators = 0; NumInstruments = 0; NumInstrBags = 0; NumInstrGenerators = 0; NumSamples = 0; } SFFile::~SFFile() { if (Presets != NULL) { delete[] Presets; } if (PresetBags != NULL) { delete[] PresetBags; } if (PresetGenerators != NULL) { delete[] PresetGenerators; } if (Instruments != NULL) { delete[] Instruments; } if (InstrBags != NULL) { delete[] InstrBags; } if (InstrGenerators != NULL) { delete[] InstrGenerators; } if (Samples != NULL) { for (int i = 0; i < NumSamples; ++i) { if (Samples[i].InMemoryData != NULL) { delete[] Samples[i].InMemoryData; } } delete[] Samples; } } bool SFFile::FinalStructureTest() { // All required chunks must be present. if (Presets == NULL || PresetBags == NULL || PresetGenerators == NULL || Instruments == NULL || InstrBags == NULL || InstrGenerators == NULL || Samples == NULL) { return false; } // What good is it if it has no sample data? if (SizeSampleData == 0) { return false; } return true; } void SFFile::SetOrder(int order, int drum, int bank, int program) { if (drum) { for (int i = 0; i < NumPresets; ++i) { if (Percussion[i].Generators.drumset == bank && Percussion[i].Generators.key == program) { Percussion[i].LoadOrder = order; } } } else { for (int i = 0; i < NumPresets; ++i) { if (Presets[i].Program == program && Presets[i].Bank == bank) { Presets[i].LoadOrder = order; } } } } void SFFile::SetAllOrders(int order) { for (int i = 0; i < NumPresets; ++i) { Presets[i].LoadOrder = order; } for (unsigned int i = 0; i < Percussion.Size(); ++i) { Percussion[i].LoadOrder = order; } } Instrument *SFFile::LoadInstrument(Renderer *song, int drum, int bank, int program) { return LoadInstrumentOrder(song, -1, drum, bank, program); } Instrument *SFFile::LoadInstrumentOrder(Renderer *song, int order, int drum, int bank, int program) { if (drum) { for (unsigned int i = 0; i < Percussion.Size(); ++i) { if ((order < 0 || Percussion[i].LoadOrder == order) && Percussion[i].Generators.drumset == bank && Percussion[i].Generators.key == program) { return LoadPercussion(song, &Percussion[i]); } } } else { for (int i = 0; i < NumPresets - 1; ++i) { if ((order < 0 || Presets[i].LoadOrder == order) && Presets[i].Bank == bank && Presets[i].Program == program) { return LoadPreset(song, &Presets[i]); } } } return NULL; } //=========================================================================== // // SFFile :: CheckBags // // For all preset and instrument zones, extract the velocity and key ranges // and instrument and sample targets. // //=========================================================================== void SFFile::CheckBags() { int i; for (i = 0; i < NumPresets - 1; ++i) { if (Presets[i].BagIndex >= Presets[i + 1].BagIndex) { // Preset is empty. Presets[i].Bank = ~0; } else { CheckZones(Presets[i].BagIndex, Presets[i + 1].BagIndex, 0); Presets[i].bHasGlobalZone = PresetBags[Presets[i].BagIndex].Target < 0; } } for (i = 0; i < NumInstruments - 1; ++i) { if (Instruments[i].BagIndex >= Instruments[i + 1].BagIndex) { // Instrument is empty. } else { CheckZones(Instruments[i].BagIndex, Instruments[i + 1].BagIndex, 1); Instruments[i].bHasGlobalZone = InstrBags[Instruments[i].BagIndex].Target < 0; } } } //=========================================================================== // // SFFile :: CheckZones // // For every zone in the bag, extract the velocity and key ranges and // instrument and sample targets. // //=========================================================================== void SFFile::CheckZones(int start, int stop, bool instr) { SFBag *bag; SFGenList *gens; SFGenerator terminal_gen; int zone_start, zone_stop; int i, j; if (!instr) { bag = PresetBags; gens = PresetGenerators; terminal_gen = GEN_instrument; } else { bag = InstrBags; gens = InstrGenerators; terminal_gen = GEN_sampleID; } for (i = start; i < stop; ++i) { zone_start = bag[i].GenIndex; zone_stop = bag[i + 1].GenIndex; if (zone_start > zone_stop) { // Skip empty zones, and mark them inaccessible. bag[i].KeyRange.Lo = 255; bag[i].KeyRange.Hi = 255; bag[i].VelRange.Lo = 255; bag[i].VelRange.Hi = 255; continue; } // According to the specs, if keyRange is present, it must be the first generator. // If velRange is present, it may only be preceded by keyRange. In real life, there // exist Soundfonts that violate this rule, so we need to scan every generator. // Preload ranges from the global zone. if (i != start && bag[start].Target < 0) { bag[i].KeyRange = bag[start].KeyRange; bag[i].VelRange = bag[start].VelRange; } for (j = zone_start; j < zone_stop; ++j) { if (gens[j].Oper == GEN_keyRange) { bag[i].KeyRange = gens[j].Range; } else if (gens[j].Oper == GEN_velRange) { bag[i].VelRange = gens[j].Range; } else if (gens[j].Oper == terminal_gen) { if (terminal_gen == GEN_instrument && gens[j].uAmount < NumInstruments - 1) { bag[i].Target = gens[j].uAmount; } else if (terminal_gen == GEN_sampleID && gens[j].uAmount < NumSamples - 1) { bag[i].Target = gens[j].uAmount; } break; } } if (bag[i].Target < 0 && i != start) { // Only the first zone may be targetless. If any other zones are, // make them inaccessible. bag[i].KeyRange.Lo = 255; bag[i].KeyRange.Hi = 255; bag[i].VelRange.Lo = 255; bag[i].VelRange.Hi = 255; } // Check for swapped ranges. (Should we fix them or ignore them?) if (bag[i].KeyRange.Lo > bag[i].KeyRange.Hi) { swapvalues(bag[i].KeyRange.Lo, bag[i].KeyRange.Hi); } if (bag[i].VelRange.Lo > bag[i].VelRange.Hi) { swapvalues(bag[i].VelRange.Lo, bag[i].VelRange.Hi); } } } //=========================================================================== // // SFFile :: TranslatePercussions // // For every percussion instrument, compile a set of composite generators // for each key, to make creating TiMidity instruments for individual // percussion parts easier. // //=========================================================================== void SFFile::TranslatePercussions() { for (int i = 0; i < NumPresets - 1; ++i) { if (Presets[i].Bank == 128 && Presets[i].Program < 128) { TranslatePercussionPreset(&Presets[i]); } } } //=========================================================================== // // SFFile :: TranslatePercussionPreset // // Compile a set of composite generators for each key of this percussion // instrument. Note that one instrument is actually an entire drumset. // //=========================================================================== void SFFile::TranslatePercussionPreset(SFPreset *preset) { SFPerc perc; int i; bool has_global; perc.LoadOrder = preset->LoadOrder; i = preset->BagIndex; has_global = false; for (i = preset->BagIndex; i < (preset + 1)->BagIndex; ++i) { if (PresetBags[i].Target < 0) { // This preset zone has no instrument. continue; } if (PresetBags[i].KeyRange.Lo > 127 || PresetBags[i].VelRange.Lo > 127) { // This preset zone is inaccesible. continue; } TranslatePercussionPresetZone(preset, &PresetBags[i]); } } //=========================================================================== // // SFFile :: TranslatePercussionPresetZone // // Create a composite generator set for all keys and velocity ranges in this // preset zone that intersect with this zone's instrument. // //=========================================================================== void SFFile::TranslatePercussionPresetZone(SFPreset *preset, SFBag *pzone) { int key, i; for (key = pzone->KeyRange.Lo; key <= pzone->KeyRange.Hi; ++key) { SFInst *inst = &Instruments[pzone->Target]; for (i = inst->BagIndex; i < (inst + 1)->BagIndex; ++i) { if (InstrBags[i].Target < 0) { // This instrument zone has no sample. continue; } if (InstrBags[i].KeyRange.Lo > key || InstrBags[i].KeyRange.Hi < key) { // This instrument zone does not contain the key we want. continue; } if (InstrBags[i].VelRange.Lo > pzone->VelRange.Hi || InstrBags[i].VelRange.Hi < pzone->VelRange.Lo) { // This instrument zone does not intersect the current velocity range. continue; } // An intersection! Add the composite generator for this key and velocity range. SFPerc perc; perc.LoadOrder = preset->LoadOrder; perc.Preset = preset; perc.Generators = DefaultGenerators; if (inst->bHasGlobalZone) { SetInstrumentGenerators(&perc.Generators, InstrBags[inst->BagIndex].GenIndex, InstrBags[inst->BagIndex + 1].GenIndex); } SetInstrumentGenerators(&perc.Generators, InstrBags[i].GenIndex, InstrBags[i + 1].GenIndex); AddPresetGenerators(&perc.Generators, pzone->GenIndex, (pzone + 1)->GenIndex, preset); perc.Generators.drumset = (uint8_t)preset->Program; perc.Generators.key = key; perc.Generators.velRange.Lo = MAX(pzone->VelRange.Lo, InstrBags[i].VelRange.Lo); perc.Generators.velRange.Hi = MIN(pzone->VelRange.Hi, InstrBags[i].VelRange.Hi); perc.Generators.sampleID = InstrBags[i].Target; Percussion.Push(perc); } } } void SFFile::SetInstrumentGenerators(SFGenComposite *composite, int start, int stop) { // Proceed from first to last; later generators override earlier ones. SFGenList *gen = &InstrGenerators[start]; for (int i = stop - start; i != 0; --i, ++gen) { if (gen->Oper >= GEN_NumGenerators) { // Unknown generator. continue; } if (GenDefs[gen->Oper].StructIndex >= sizeof(SFGenComposite)/2) { // Generator is either unused or ignored. continue; } // Set the generator ((uint16_t *)composite)[GenDefs[gen->Oper].StructIndex] = gen->uAmount; if (gen->Oper == GEN_sampleID) { // Anything past sampleID is ignored. break; } } } void SFFile::AddPresetGenerators(SFGenComposite *composite, int start, int stop, SFPreset *preset) { bool gen_set[GEN_NumGenerators] = { false, }; AddPresetGenerators(composite, start, stop, gen_set); if (preset->bHasGlobalZone) { AddPresetGenerators(composite, PresetBags[preset->BagIndex].GenIndex, PresetBags[preset->BagIndex + 1].GenIndex, gen_set); } } void SFFile::AddPresetGenerators(SFGenComposite *composite, int start, int stop, bool gen_set[GEN_NumGenerators]) { // Proceed from last to first; later generators override earlier ones. SFGenList *gen = &PresetGenerators[stop - 1]; const GenDef *def; for (int i = stop - start; i != 0; --i, --gen) { if (gen->Oper >= GEN_NumGenerators) { // Unknown generator. continue; } if (gen_set[gen->Oper]) { // Generator was already set. continue; } def = &GenDefs[gen->Oper]; if (def->StructIndex >= sizeof(SFGenComposite)/2) { // Generator is either unused or ignored. continue; } if (def->Flags & GENF_InstrOnly) { // Generator is not valid at the preset level. continue; } // Add to instrument/default generator. int added = ((int16_t *)composite)[def->StructIndex] + gen->Amount; // Clamp to proper range. if (added <= -32768 && def->Flags & GENF_32768_Ok) { added = -32768; } else { added = clamp(added, def->Min, def->Max); } ((int16_t *)composite)[def->StructIndex] = added; gen_set[gen->Oper] = true; if (gen->Oper == GEN_instrument) { // Anything past the instrument generator is ignored. break; } } } Instrument *SFFile::LoadPercussion(Renderer *song, SFPerc *perc) { unsigned int i; int drumkey; int drumset; int j; Instrument *ip = new Instrument; ip->samples = 0; drumkey = perc->Generators.key; drumset = perc->Generators.drumset; // Count all percussion composites that match this one's key and set. for (i = 0; i < Percussion.Size(); ++i) { if (Percussion[i].Generators.key == drumkey && Percussion[i].Generators.drumset == drumset && Percussion[i].Generators.sampleID < NumSamples) { SFSample *sfsamp = &Samples[Percussion[i].Generators.sampleID]; if (sfsamp->InMemoryData == NULL) { LoadSample(sfsamp); } if (sfsamp->InMemoryData != NULL) { ip->samples++; } } } if (ip->samples == 0) { // Nothing here to play. delete ip; return NULL; } ip->sample = (Sample *)safe_malloc(sizeof(Sample) * ip->samples); memset(ip->sample, 0, sizeof(Sample) * ip->samples); // Fill in Sample structure for each composite. for (j = 0, i = 0; i < Percussion.Size(); ++i) { SFPerc *zone = &Percussion[i]; SFGenComposite *gen = &zone->Generators; if (gen->key != drumkey || gen->drumset != drumset || gen->sampleID >= NumSamples) { continue; } SFSample *sfsamp = &Samples[gen->sampleID]; if (sfsamp->InMemoryData == NULL) { continue; } Sample *sp = ip->sample + j++; // Set velocity range sp->low_vel = gen->velRange.Lo; sp->high_vel = gen->velRange.Hi; // Set frequency range sp->low_freq = note_to_freq(gen->key); sp->high_freq = sp->low_freq; ApplyGeneratorsToRegion(gen, sfsamp, song, sp); } assert(j == ip->samples); return ip; } //=========================================================================== // // SFFile :: LoadPreset // //=========================================================================== Instrument *SFFile::LoadPreset(Renderer *song, SFPreset *preset) { SFInst *inst; SFSample *sfsamp; SFGenComposite gen; int i, j, k; Instrument *ip = new Instrument; ip->samples = 0; // Count the number of regions we'll need. for (i = preset->BagIndex; i < (preset + 1)->BagIndex; ++i) { if (PresetBags[i].Target < 0) { // Preset zone has no instrument. continue; } inst = &Instruments[PresetBags[i].Target]; for (j = inst->BagIndex; j < (inst + 1)->BagIndex; ++j) { if (InstrBags[j].Target < 0) { // Instrument zone has no sample. continue; } if (InstrBags[j].KeyRange.Lo <= PresetBags[i].KeyRange.Hi && InstrBags[j].KeyRange.Hi >= PresetBags[i].KeyRange.Lo && InstrBags[j].VelRange.Lo <= PresetBags[i].VelRange.Hi && InstrBags[j].VelRange.Hi >= PresetBags[i].VelRange.Lo) { // The preset and instrument zones intersect! sfsamp = &Samples[InstrBags[j].Target]; if (sfsamp->InMemoryData == NULL) { LoadSample(sfsamp); } if (sfsamp->InMemoryData != NULL) { ip->samples++; } } } } if (ip->samples == 0) { // Nothing here to play. delete ip; return NULL; } // Allocate the regions and define them ip->sample = (Sample *)safe_malloc(sizeof(Sample) * ip->samples); memset(ip->sample, 0, sizeof(Sample) * ip->samples); k = 0; for (i = preset->BagIndex; i < (preset + 1)->BagIndex; ++i) { if (PresetBags[i].Target < 0) { // Preset zone has no instrument. continue; } inst = &Instruments[PresetBags[i].Target]; for (j = inst->BagIndex; j < (inst + 1)->BagIndex; ++j) { if (InstrBags[j].Target < 0) { // Instrument zone has no sample. continue; } if (InstrBags[j].KeyRange.Lo <= PresetBags[i].KeyRange.Hi && InstrBags[j].KeyRange.Hi >= PresetBags[i].KeyRange.Lo && InstrBags[j].VelRange.Lo <= PresetBags[i].VelRange.Hi && InstrBags[j].VelRange.Hi >= PresetBags[i].VelRange.Lo) { // The preset and instrument zones intersect! sfsamp = &Samples[InstrBags[j].Target]; if (sfsamp->InMemoryData == NULL) { continue; } Sample *sp = ip->sample + k++; // Set velocity range sp->low_vel = MAX(InstrBags[j].VelRange.Lo, PresetBags[i].VelRange.Lo); sp->high_vel = MIN(InstrBags[j].VelRange.Hi, PresetBags[i].VelRange.Hi); // Set frequency range sp->low_freq = note_to_freq(MAX(InstrBags[j].KeyRange.Lo, PresetBags[i].KeyRange.Lo)); sp->high_freq = note_to_freq(MIN(InstrBags[j].KeyRange.Hi, PresetBags[i].KeyRange.Hi)); gen = DefaultGenerators; if (inst->bHasGlobalZone) { SetInstrumentGenerators(&gen, InstrBags[inst->BagIndex].GenIndex, InstrBags[inst->BagIndex + 1].GenIndex); } SetInstrumentGenerators(&gen, InstrBags[j].GenIndex, InstrBags[j + 1].GenIndex); AddPresetGenerators(&gen, PresetBags[i].GenIndex, PresetBags[i + 1].GenIndex, preset); ApplyGeneratorsToRegion(&gen, sfsamp, song, sp); } } } assert(k == ip->samples); return ip; } //=========================================================================== // // SFFile :: ApplyGeneratorsToRegion // // The caller must set the key and velocity ranges. Other information for // the TiMidity sample will be filled in using the generators given. // // FIXME: At least try to do something useful with every parameter. // //=========================================================================== void SFFile::ApplyGeneratorsToRegion(SFGenComposite *gen, SFSample *sfsamp, Renderer *song, Sample *sp) { sp->type = INST_SF2; // Set loop and sample points int start, end; start = gen->startAddrsOffset + gen->startAddrsCoarseOffset * 32768; end = gen->endAddrsOffset + gen->endAddrsCoarseOffset * 32768; start = MAX(sfsamp->Start, sfsamp->Start + start); end = MIN(sfsamp->End, sfsamp->End + end); sp->loop_start = MAX(start, sfsamp->StartLoop + gen->startLoopAddrsOffset + gen->startLoopAddrsCoarseOffset * 32768); sp->loop_end = MIN(end, sfsamp->EndLoop + gen->endLoopAddrsOffset + gen->endLoopAddrsCoarseOffset * 32768); sp->loop_start = (sp->loop_start - start) << FRACTION_BITS; sp->loop_end = (sp->loop_end - start) << FRACTION_BITS; sp->data_length = (end - start) << FRACTION_BITS; sp->data = sfsamp->InMemoryData + start - sfsamp->Start; if (gen->overridingRootKey >= 0 && gen->overridingRootKey <= 127) { sp->scale_note = gen->overridingRootKey; } else { sp->scale_note = sfsamp->OriginalPitch; } sp->root_freq = note_to_freq(sp->scale_note); sp->sample_rate = sfsamp->SampleRate; sp->key_group = gen->exclusiveClass; // Set key scaling if (gen->keynum >= 0 && gen->keynum <= 127) { sp->scale_note = gen->keynum; sp->scale_factor = 0; } else if (gen->scaleTuning >= 0) { sp->scale_factor = gen->scaleTuning * 1024 / 100; // Does the root key also serve as the scale key? Assuming it does here. } else { sp->scale_factor = 1024; sp->scale_note = 60; } // Set panning sp->panning = gen->pan; // Set volume envelope sp->envelope.sf2.delay_vol = gen->delayVolEnv; sp->envelope.sf2.attack_vol = gen->attackVolEnv; sp->envelope.sf2.hold_vol = gen->holdVolEnv; sp->envelope.sf2.decay_vol = gen->decayVolEnv; sp->envelope.sf2.sustain_vol = gen->sustainVolEnv; sp->envelope.sf2.release_vol = gen->releaseVolEnv; // Set sample modes if (gen->sampleModes == 1) { sp->modes = PATCH_LOOPEN | PATCH_SUSTAIN | PATCH_NO_SRELEASE; } else if (gen->sampleModes == 3) { sp->modes = PATCH_LOOPEN | PATCH_SUSTAIN; } else { sp->modes = PATCH_SUSTAIN; } // Set tuning (in cents) sp->tune = gen->coarseTune * 100 + gen->fineTune; sp->velocity = (int8_t)gen->velocity; sp->initial_attenuation = gen->initialAttenuation; } //=========================================================================== // // SFFile :: LoadSample // // Loads a sample's data and converts it from 16/24-bit to floating point. // //=========================================================================== void SFFile::LoadSample(SFSample *sample) { FileReader fp = gus_sfreader->LookupFile(Filename.c_str()).first; uint32_t i; if (!fp.isOpen()) { return; } sample->InMemoryData = new float[sample->End - sample->Start + 1]; fp.Seek(SampleDataOffset + sample->Start * 2, FileReader::SeekSet); // Load 16-bit sample data. for (i = 0; i < sample->End - sample->Start; ++i) { int16_t samp = fp.ReadInt16(); sample->InMemoryData[i] = samp / 32768.f; } if (SampleDataLSBOffset != 0) { // Load lower 8 bits of 24-bit sample data. fp.Seek(SampleDataLSBOffset + sample->Start, FileReader::SeekSet); for (i = 0; i < sample->End - sample->Start; ++i) { uint8_t samp = fp.ReadUInt8(); sample->InMemoryData[i] = ((((int32_t(sample->InMemoryData[i] * 32768) << 8) | samp) << 8) >> 8) / 8388608.f; } } // Final 0 byte is for interpolation. sample->InMemoryData[i] = 0; }