mirror of
https://github.com/jwetzell/JakAudioTools.git
synced 2026-08-25 00:09:02 +00:00
update musparser to use namedtuple
This commit is contained in:
@@ -2,3 +2,4 @@ JAK1/
|
|||||||
JAK2/
|
JAK2/
|
||||||
JAK3/
|
JAK3/
|
||||||
OUT/
|
OUT/
|
||||||
|
*.mid
|
||||||
+83
-148
@@ -8,6 +8,55 @@ import struct
|
|||||||
#yes two midi libraries...
|
#yes two midi libraries...
|
||||||
import music21
|
import music21
|
||||||
import mido
|
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):
|
def is_valid_file(parser, arg):
|
||||||
if not os.path.exists(arg):
|
if not os.path.exists(arg):
|
||||||
@@ -32,126 +81,57 @@ sounds_length = 20
|
|||||||
|
|
||||||
with open(args.filepath, "rb") as f:
|
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 = MUS._make(struct.unpack(MUSStructString,f.read(32)))
|
||||||
'''
|
|
||||||
MUSinfo = struct.unpack("IIIIIIII",f.read(32))
|
print(f"SoundBank Start: {MUSInfo.soundBankStart}")
|
||||||
soundbankStart = MUSinfo[4]
|
print(f"Sequence Start: {MUSInfo.sequenceDataStart}")
|
||||||
print(f"SoundBank Start: {soundbankStart}")
|
|
||||||
sequenceStart = MUSinfo[6]
|
|
||||||
print(f"Sequence Start: {sequenceStart}")
|
|
||||||
|
|
||||||
|
|
||||||
sbv2Start = f.tell()
|
sbv2Start = f.tell()
|
||||||
|
|
||||||
'''
|
sbv2Info = SBV2._make(struct.unpack(SBV2StructString,f.read(80)))
|
||||||
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))
|
|
||||||
|
|
||||||
instrumentStart = sbv2[10] + sbv2Start
|
instrumentStart = sbv2Info.instrumentOffset + sbv2Start
|
||||||
regionStart = sbv2[11] + sbv2Start
|
regionStart = sbv2Info.regionOffset + sbv2Start
|
||||||
|
|
||||||
print(MUSinfo)
|
print(MUSInfo)
|
||||||
print(sbv2)
|
print(sbv2Info)
|
||||||
f.seek(instrumentStart)
|
f.seek(instrumentStart)
|
||||||
instruments = []
|
instruments = []
|
||||||
|
|
||||||
|
|
||||||
while(f.tell() < regionStart):
|
while(f.tell() < regionStart):
|
||||||
'''
|
instrument = Instrument._make(struct.unpack(InstrumentStructString,f.read(8)))
|
||||||
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))
|
|
||||||
instruments.append(instrument)
|
instruments.append(instrument)
|
||||||
|
|
||||||
print("INSTRUMENTS")
|
print("INSTRUMENTS")
|
||||||
for instrument in instruments:
|
for instrument in instruments:
|
||||||
print(instrument)
|
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 = []
|
regions = []
|
||||||
while(f.tell()<soundbankStart):
|
while(f.tell()<MUSInfo.soundBankStart):
|
||||||
region = struct.unpack("=BBBBhIHBBHII", f.read(24))
|
regionInstance = Region._make(struct.unpack(RegionStructString, f.read(24)))
|
||||||
regions.append(region)
|
regions.append(regionInstance)
|
||||||
|
|
||||||
print("REGIONS")
|
print("REGIONS")
|
||||||
sampleOffsets = []
|
sampleOffsets = []
|
||||||
for region in regions:
|
for region in regions:
|
||||||
print(region)
|
print(region)
|
||||||
sampleIndex = region[11]
|
sampleIndex = region.sampleId
|
||||||
sampleOffset = soundbankStart + region[10]
|
sampleOffset = MUSInfo.soundBankStart + region.sampleOffset
|
||||||
if sampleOffset not in sampleOffsets:
|
if sampleOffset not in sampleOffsets:
|
||||||
sampleOffsets.append(sampleOffset)
|
sampleOffsets.append(sampleOffset)
|
||||||
#print(f"Region Sample Audio offset: {soundbankStart + region[10]}")
|
#print(f"Region Sample Audio offset: {MUSInfo.soundBankStart + region[10]}")
|
||||||
# this should align the index of the sample offset with it's sample ID
|
# this should align the index of the sample offset with it's sample ID
|
||||||
sampleOffsets.sort()
|
sampleOffsets.sort()
|
||||||
# the sample data lies between here and the next processed section 22050 Hz sample rate mono adpcm
|
# the sample data lies between here and the next processed section 22050 Hz sample rate mono adpcm
|
||||||
|
|
||||||
'''
|
|
||||||
struct seq {
|
|
||||||
u32 version; // 2
|
|
||||||
u32 subversion; // 1
|
|
||||||
u32 number; // 16
|
|
||||||
u32 size; // Size of sequence data following this struct
|
|
||||||
}
|
|
||||||
|
|
||||||
'''
|
f.seek(MUSInfo.sequenceDataStart)
|
||||||
f.seek(sequenceStart)
|
|
||||||
|
|
||||||
seq = struct.unpack("4I",f.read(16))
|
seqInstance = SEQ._make(struct.unpack(SEQStructString,f.read(16)))
|
||||||
|
|
||||||
|
|
||||||
midBlockType = f.read(4)
|
midBlockType = f.read(4)
|
||||||
@@ -163,72 +143,27 @@ with open(args.filepath, "rb") as f:
|
|||||||
midBlockDataOffsets = []
|
midBlockDataOffsets = []
|
||||||
|
|
||||||
if(midBlockType == str.encode("MID ")):
|
if(midBlockType == str.encode("MID ")):
|
||||||
'''
|
|
||||||
struct MID {
|
midBlock = MID._make(struct.unpack(MIDStructString, f.read(44)))
|
||||||
char signature[4]; // "MID "
|
|
||||||
u16 a;
|
|
||||||
u16 b;
|
|
||||||
u32 c;
|
|
||||||
u32 d;
|
|
||||||
char name[4]; // e.g. "V1RA"
|
|
||||||
u32 e;
|
|
||||||
u32 f; // offset from start of struct to midi data
|
|
||||||
u32 g;
|
|
||||||
u32 tempo; // microseconds per quarter note?
|
|
||||||
u32 division; // or not...
|
|
||||||
u16 unknown;
|
|
||||||
u8 iTrack; // track index 0 - 2
|
|
||||||
u8 nTrack; // number of tracks (3)
|
|
||||||
}
|
|
||||||
'''
|
|
||||||
midBlock = struct.unpack("4sHHII4sIIIIIHBB", f.read(44))
|
|
||||||
midBlocks.append(midBlock)
|
midBlocks.append(midBlock)
|
||||||
midBlockDataOffsets.append(int(midBlock[7] + mmidBlockStart))
|
midBlockDataOffsets.append(int(midBlock.midiDataOffset + mmidBlockStart))
|
||||||
midBlockOffsets.append(mmidBlockStart)
|
midBlockOffsets.append(mmidBlockStart)
|
||||||
print(midBlock)
|
print(midBlock)
|
||||||
elif midBlockType == str.encode("MMID"):
|
elif midBlockType == str.encode("MMID"):
|
||||||
'''
|
mmidBlock = MMID._make(struct.unpack(MMIDStructString, f.read(16)))
|
||||||
struct MMID {
|
|
||||||
char signature[4]; // "MMID"
|
|
||||||
u16 type; //?
|
|
||||||
u8 a; //?
|
|
||||||
u8 nTrack; // maybe... it's always 3 lol
|
|
||||||
char name[4]; // e.g. "V1RA"
|
|
||||||
u32 blank; //?
|
|
||||||
}
|
|
||||||
'''
|
|
||||||
|
|
||||||
mmidBlock = struct.unpack("4sHBB4sI", f.read(16))
|
|
||||||
print("MMIDBLOCK")
|
print("MMIDBLOCK")
|
||||||
print(mmidBlock)
|
print(mmidBlock)
|
||||||
for i in range(mmidBlock[3]):
|
for i in range(mmidBlock.trackCount):
|
||||||
blockOffset = struct.unpack("I",f.read(4))[0] + mmidBlockStart
|
blockOffset = struct.unpack("I",f.read(4))[0] + mmidBlockStart
|
||||||
midBlockOffsets.append(blockOffset)
|
midBlockOffsets.append(blockOffset)
|
||||||
|
|
||||||
for offset in midBlockOffsets:
|
for offset in midBlockOffsets:
|
||||||
f.seek(offset)
|
f.seek(offset)
|
||||||
'''
|
|
||||||
struct MID {
|
midBlock = MID._make(struct.unpack(MIDStructString, f.read(44)))
|
||||||
char signature[4]; // "MID "
|
|
||||||
u16 a;
|
|
||||||
u16 b;
|
|
||||||
u32 c;
|
|
||||||
u32 d;
|
|
||||||
char name[4]; // e.g. "V1RA"
|
|
||||||
u32 e;
|
|
||||||
u32 oMIDI; // offset from start of struct to midi data
|
|
||||||
u32 g;
|
|
||||||
u32 tempo; // microseconds per quarter note?
|
|
||||||
u32 division; // or not...
|
|
||||||
u16 unknown;
|
|
||||||
u8 iTrack; // track index 0 - 2
|
|
||||||
u8 nTrack; // number of tracks (3)
|
|
||||||
}
|
|
||||||
'''
|
|
||||||
midBlock = struct.unpack("4sHHII4sIIIIIHBB", f.read(44))
|
|
||||||
midBlocks.append(midBlock)
|
midBlocks.append(midBlock)
|
||||||
for i in range(len(midBlockOffsets)):
|
for i in range(len(midBlockOffsets)):
|
||||||
midBlockDataOffsets.append(int(midBlocks[i][7] + midBlockOffsets[i]))
|
midBlockDataOffsets.append(int(midBlocks[i].midiDataOffset + midBlockOffsets[i]))
|
||||||
|
|
||||||
print("MIDBLOCKS")
|
print("MIDBLOCKS")
|
||||||
for block in midBlocks:
|
for block in midBlocks:
|
||||||
@@ -261,10 +196,10 @@ with open(args.filepath, "rb") as f:
|
|||||||
|
|
||||||
trackEvents = []
|
trackEvents = []
|
||||||
for block in midBlocks:
|
for block in midBlocks:
|
||||||
mt = music21.midi.MidiTrack(block[12])
|
mt = music21.midi.MidiTrack(block.trackIndex)
|
||||||
midiData = midBlockData[block[12]]
|
midiData = midBlockData[block.trackIndex]
|
||||||
mt.read(midiData)
|
mt.read(midiData)
|
||||||
cleanTrack = music21.midi.MidiTrack(block[12])
|
cleanTrack = music21.midi.MidiTrack(block.trackIndex)
|
||||||
|
|
||||||
for event in mt.events:
|
for event in mt.events:
|
||||||
if(event.type in eventsToOutput or event.isDeltaTime()):
|
if(event.type in eventsToOutput or event.isDeltaTime()):
|
||||||
@@ -274,7 +209,7 @@ with open(args.filepath, "rb") as f:
|
|||||||
track = mido.MidiTrack()
|
track = mido.MidiTrack()
|
||||||
mid.tracks.append(track)
|
mid.tracks.append(track)
|
||||||
|
|
||||||
track.append(mido.MetaMessage('set_tempo',tempo=block[9]))
|
track.append(mido.MetaMessage('set_tempo',tempo=block.midiTempo))
|
||||||
|
|
||||||
delayTime = 0
|
delayTime = 0
|
||||||
for event in trackEvents:
|
for event in trackEvents:
|
||||||
@@ -301,4 +236,4 @@ with open(args.filepath, "rb") as f:
|
|||||||
delayTime = 0
|
delayTime = 0
|
||||||
track.append(messageToAdd)
|
track.append(messageToAdd)
|
||||||
|
|
||||||
mid.save(f"{sbv2[3].decode('utf-8')}.mid")
|
mid.save(f"{sbv2Info.name.decode('utf-8')}.mid")
|
||||||
|
|||||||
Reference in New Issue
Block a user