diff --git a/.gitignore b/.gitignore index 9104364..27af1b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ JAK1/ JAK2/ JAK3/ -OUT/ \ No newline at end of file +OUT/ +*.mid \ No newline at end of file diff --git a/musparser.py b/musparser.py index 66c5021..aa7c36b 100644 --- a/musparser.py +++ b/musparser.py @@ -8,6 +8,55 @@ import struct #yes two midi libraries... import music21 import mido +from collections import namedtuple + + +#named tuple definitions + +#struct type I I I I I I I I +MUS = namedtuple('MUS','version subversion minorversion something soundBankStart soundBankSize sequenceDataStart s3') +MUSStructString= "8I" + +#struct type 4s I I 4s I H H H H I I I I I I I 12s H H H H I +SBV2 = namedtuple('SBV2', 'signature version hversion name a b nMap nSMap nSample f instrumentOffset regionOffset i soundBankSize k l extname m n o p q') +SBV2StructString = "4s2I4sI4H7I12s4HI" + +#struct type B B H I +Instrument = namedtuple('Instrument','regionCount volume zero regionOffset') +InstrumentStructString = "2BHI" + +''' + struct Region { // 0x18 B + uint8_t type; //? always 0 + uint8_t marker1; // usually 7f, not always, maybe another volume? + uint8_t a; // not same for same sample data + uint8_t b; // not same for same sample data + int16_t c; // signed in range +/- 64? Tuning? intruments with 2 regions seem to have these in a plus minus couple, maybe panorama? + uint32_t keymap; //? LSB is only set for programs with multiple entries? + uint16_t marker2; // 0x80ff, bt sometimes LSB is dX + uint8_t type2; // in range c9 - d1? can be different for same sample data + uint8_t marker3; // always 9f, or 0x80 | 0x1f? + uint16_t version; //? 1 or 0, maybe looped flag? not same sample data + uint32_t oSample; // offset to sample from start of sample bank + uint32_t sampleID; // I think it's the index in the sample bank... + } + +''' +#struct type B B B B h B B H B B B B H I I +Region = namedtuple('Region','type volume1 a b pan c volume2 zero d e f g loopFlag sampleOffset sampleId') +RegionStructString = "=BBBBhBBHBBBBHII" + +#struct type I I I I +SEQ = namedtuple('SEQ','version subversion minorversion sequenceDataSize') +SEQStructString = "4I" + +#struct type 4s H H I I 4s I I I I I H B B +MID = namedtuple('MID','signature a b c d name e midiDataOffset g midiTempo division zero trackIndex trackCount') +MIDStructString = "4s2H2I4s5IH2B" + +#struct type 4s H B B 4s I +MMID = namedtuple('MMID','signature type a trackCount name zero') +MMIDStructString = "4sH2B4sI" def is_valid_file(parser, arg): if not os.path.exists(arg): @@ -32,126 +81,57 @@ sounds_length = 20 with open(args.filepath, "rb") as f: - ''' - struct MUS { // 2 * 16 - uint32_t version; // 1 version? - uint32_t subversion; // 3 subversion? - uint32_t minorversion; // 32 minor version? - uint32_t oSomething; // points to start of soundbank data, -32? or is it SBv2 struct data size? - uint32_t oBank; // this points to the start of the soundbank sample data - uint32_t sBank; // size of sound bank - uint32_t oMID; // pointer to sequenced music data - uint32_t s3; - } - - E.g.: Version 1.3.32, oSomething 704, Bank offset 736 B, Bank Size 534256 B, MID offset 534992, ? 22772 - ''' - MUSinfo = struct.unpack("IIIIIIII",f.read(32)) - soundbankStart = MUSinfo[4] - print(f"SoundBank Start: {soundbankStart}") - sequenceStart = MUSinfo[6] - print(f"Sequence Start: {sequenceStart}") + + MUSInfo = MUS._make(struct.unpack(MUSStructString,f.read(32))) + + print(f"SoundBank Start: {MUSInfo.soundBankStart}") + print(f"Sequence Start: {MUSInfo.sequenceDataStart}") sbv2Start = f.tell() - ''' - struct SBv2 { // 5x16 B - char signature[4]; // "SBv2" - uint32_t version; - uint32_t hversion; - char name[4]; //eg "V1RA" or "BOSS" - uint32_t a; // 0xa - uint16_t b; //1 - uint16_t nMap; - uint16_t nSMap; - uint16_t nSample; // same as nMap? no it can be different... - uint32_t f; //0x34 another offset, to end of extname from start of SBv2? - uint32_t oInstrument; // from start of SBv2 - uint32_t oRegions; // from start of SBv2 - uint32_t i; //0x5040 - uint32_t sizeofsamples; // same as in file header - uint32_t k; //0 - uint32_t l; //4 or 5? - char extname[12]; // e.g. "V1RAV1RAV1RA" - uint16_t m; - uint16_t n; - uint16_t o; - uint16_t p; - uint32_t q; - } - ''' - sbv2 = struct.unpack("4sII4sIHHHHIIIIIII12sHHHHI",f.read(80)) + sbv2Info = SBV2._make(struct.unpack(SBV2StructString,f.read(80))) - instrumentStart = sbv2[10] + sbv2Start - regionStart = sbv2[11] + sbv2Start + instrumentStart = sbv2Info.instrumentOffset + sbv2Start + regionStart = sbv2Info.regionOffset + sbv2Start - print(MUSinfo) - print(sbv2) + print(MUSInfo) + print(sbv2Info) f.seek(instrumentStart) instruments = [] + + while(f.tell() < regionStart): - ''' - struct Instrument { - uint8_t nRegion; // usually 1 - uint8_t volume; // ? maybe... like 7f is 1.0? - uint16_t something; // always 0 - uint32_t oRegion; // relative to start of SBv2 block I think, points to the first region for instrument - } - ''' - instrument = struct.unpack("BBHI",f.read(8)) + instrument = Instrument._make(struct.unpack(InstrumentStructString,f.read(8))) instruments.append(instrument) print("INSTRUMENTS") for instrument in instruments: print(instrument) - ''' - struct Region { // 0x18 B - uint8_t type; //? always 0 - uint8_t marker1; // usually 7f, not always, maybe another volume? - uint8_t a; // not same for same sample data - uint8_t b; // not same for same sample data - int16_t c; // signed in range +/- 64? Tuning? intruments with 2 regions seem to have these in a plus minus couple, maybe panorama? - uint32_t keymap; //? LSB is only set for programs with multiple entries? - uint16_t marker2; // 0x80ff, bt sometimes LSB is dX - uint8_t type2; // in range c9 - d1? can be different for same sample data - uint8_t marker3; // always 9f, or 0x80 | 0x1f? - uint16_t version; //? 1 or 0, maybe looped flag? not same sample data - uint32_t oSample; // offset to sample from start of sample bank - uint32_t sampleID; // I think it's the index in the sample bank... - } - - ''' + regions = [] - while(f.tell()