from io import BytesIO from typing import Optional, Union, BinaryIO, TypeVar, Type, List, Dict from chipchune._util import read_byte, read_short, read_int, read_str from .data_types import ( InsFeatureAbstract, InsFeatureMacro, InsMeta, InstrumentType, InsFeatureName, InsFeatureFM, InsFeatureOpr1Macro, InsFeatureOpr2Macro, InsFeatureOpr3Macro, InsFeatureOpr4Macro, InsFeatureC64, InsFeatureGB, GBHwSeq, SingleMacro, InsFeatureAmiga, InsFeatureOPLDrums, InsFeatureSNES, GainMode, InsFeatureN163, InsFeatureFDS, InsFeatureWaveSynth, _InsFeaturePointerAbstract, InsFeatureSampleList, InsFeatureWaveList, InsFeatureMultiPCM, InsFeatureSoundUnit, InsFeatureES5506, InsFeatureX1010, GenericADSR, InsFeatureDPCMMap, InsFeaturePowerNoise, InsFeatureSID2 ) from .enums import ( _FurInsImportType, MacroCode, OpMacroCode, MacroItem, MacroType, GBHwCommand, SNESSusMode, WaveFX, ESFilterMode, MacroSize ) FILE_MAGIC_STR = b'-Furnace instr.-' DEV127_FILE_MAGIC_STR = b'FINS' EMBED_MAGIC_STR = b'INST' DEV127_EMBED_MAGIC_STR = b'INS2' T_MACRO = TypeVar('T_MACRO', bound=InsFeatureMacro) # T_MACRO must be subclass of InsFeatureMacro T_POINTERS = TypeVar('T_POINTERS', bound=_InsFeaturePointerAbstract) class FurnaceInstrument: def __init__(self, file_name: Optional[str] = None, protocol_version: Optional[int] = 1) -> None: """ Creates or opens a new Furnace instrument as a Python object. :param file_name: (Optional) If specified, then it will parse a file as a FurnaceInstrument. If file name (str) is given, it will load that file. Defaults to None. :param protocol_version: (Optional) If specified, it will determine which format the instrument is to be serialized (exported) to. It is ignored if loading up a file. Defaults to 2 (dev127+ ins. format) """ self.file_name: Optional[str] = None """ Original file name, if the object was initialized with one. """ self.protocol_version: Optional[int] = protocol_version """ Instrument file "protocol" version. Currently: - 0: The "unified" instrument format up to Furnace version 126. - 1: The new "featural" instrument format introduced in version 127. """ self.features: List[InsFeatureAbstract] = [] """ List of features, regardless of protocol version. """ self.meta: InsMeta = InsMeta() """ Instrument metadata. """ # self.wavetables: list[] = [] # self.samples: list[] = [] self.__map_to_fn = { b'NA': self.__load_na_block, b'FM': self.__load_fm_block, b'MA': self.__load_ma_block, b'64': self.__load_c64_block, b'GB': self.__load_gb_block, b'SM': self.__load_sm_block, b'O1': self.__load_o1_block, b'O2': self.__load_o2_block, b'O3': self.__load_o3_block, b'O4': self.__load_o4_block, b'LD': self.__load_ld_block, b'SN': self.__load_sn_block, b'N1': self.__load_n1_block, b'FD': self.__load_fd_block, b'WS': self.__load_ws_block, b'SL': self.__load_sl_block, b'WL': self.__load_wl_block, b'MP': self.__load_mp_block, b'SU': self.__load_su_block, b'ES': self.__load_es_block, b'X1': self.__load_x1_block, b'NE': self.__load_ne_block, # TODO: No documentation? #b'EF': self.__load_ef_block, b'PN': self.__load_pn_block, b'S2': self.__load_s2_block, } if isinstance(file_name, str): self.load_from_file(file_name) def load_from_file(self, file_name: Optional[str] = None) -> None: if isinstance(file_name, str): self.file_name = file_name if self.file_name is None: raise RuntimeError('No file name set, either set self.file_name or pass file_name to the function') # since we're loading from an uncompressed file, we can just check the file magic number with open(self.file_name, 'rb') as f: detect_magic = f.peek(len(FILE_MAGIC_STR))[:len(FILE_MAGIC_STR)] if detect_magic == FILE_MAGIC_STR: return self.load_from_stream(f, _FurInsImportType.FORMAT_0_FILE) elif detect_magic[:len(DEV127_FILE_MAGIC_STR)] == DEV127_FILE_MAGIC_STR: return self.load_from_stream(f, _FurInsImportType.FORMAT_1_FILE) else: # uncompressed for sure raise ValueError('No recognized file type magic') def load_from_bytes(self, data: bytes, import_as: Union[int, _FurInsImportType]) -> None: """ Load an instrument from a series of bytes. :param data: Bytes :param import_as: int see :method:`FurnaceInstrument.load_from_stream` """ return self.load_from_stream( BytesIO(data), import_as ) def load_from_stream(self, stream: BinaryIO, import_as: Union[int, _FurInsImportType]) -> None: """ Load a module from an **uncompressed** stream. :param stream: File-like object containing the uncompressed module. :param import_as: int - 0 = old format instrument file - 1 = old format, embedded in module - 2 = new format instrument file - 3 = new format, embedded in module """ if import_as == _FurInsImportType.FORMAT_0_FILE: if stream.read(len(FILE_MAGIC_STR)) != FILE_MAGIC_STR: raise ValueError('Bad magic value for a format 1 file') self.protocol_version = 0 self.meta.version = read_short(stream) read_short(stream) # reserved ins_data_ptr = read_int(stream) num_waves = read_short(stream) num_samples = read_short(stream) read_int(stream) # reserved # these don't exist for format 1 instrs. self.__wavetable_ptr = [ read_int(stream) for _ in range(num_waves) ] self.__sample_ptr = [ read_int(stream) for _ in range(num_samples) ] stream.seek(ins_data_ptr) self.__load_format_0_embed(stream) # TODO: load wavetables and samples elif import_as == _FurInsImportType.FORMAT_0_EMBED: self.protocol_version = 0 return self.__load_format_0_embed(stream) elif import_as == _FurInsImportType.FORMAT_1_FILE: if stream.read(len(DEV127_FILE_MAGIC_STR)) != DEV127_FILE_MAGIC_STR: raise ValueError('Bad magic value for a format 1 file') self.protocol_version = 1 self.__load_format_1(stream) # TODO: load wavetables and samples elif import_as == _FurInsImportType.FORMAT_1_EMBED: if stream.read(len(DEV127_EMBED_MAGIC_STR)) != DEV127_EMBED_MAGIC_STR: raise ValueError('Bad magic value for a format 1 embed') self.protocol_version = 1 ins_data = BytesIO(stream.read(read_int(stream))) return self.__load_format_1(ins_data) else: raise ValueError('Invalid import type') def __str__(self) -> str: return '' % ( self.get_name(), self.meta.type ) def __load_format_1(self, stream: BinaryIO) -> None: # skip headers and magic self.meta.version = read_short(stream) self.meta.type = InstrumentType(read_short(stream)) self.features.clear() # add all the features feat = self.__read_format_1_feature(stream) while isinstance(feat, InsFeatureAbstract): self.features.append(feat) feat = self.__read_format_1_feature(stream) def __read_format_1_feature(self, stream: BinaryIO) -> Optional[object]: # subclass InsFeatureAbstract code = stream.read(2) if code == b'EN' or code == b'': # eof return None len_block = read_short(stream) feature_block = BytesIO(stream.read(len_block)) # if this fails it might be a malformed file return self.__map_to_fn[code](feature_block) def get_name(self) -> str: """ Shortcut to fetch the instrument name. :return: Instrument name """ name = '' for i in self.features: if isinstance(i, InsFeatureName): name = i # InsFeatureName also subclasses 'str' so it's fine return name # format 1 features def __load_na_block(self, stream: BytesIO) -> InsFeatureName: return InsFeatureName( read_str(stream) ) def __load_fm_block(self, stream: BytesIO) -> InsFeatureFM: fm = InsFeatureFM() # read base data data = [read_byte(stream) for _ in range(4)] current = data.pop(0) ops = current & 0b1111 fm.op_list[0].enable = bool(current & 16) fm.op_list[1].enable = bool(current & 32) fm.op_list[2].enable = bool(current & 64) fm.op_list[3].enable = bool(current & 128) current = data.pop(0) fm.alg = (current >> 4) & 0b111 fm.fb = current & 0b111 current = data.pop(0) fm.fms2 = (current >> 5) & 0b111 fm.ams = (current >> 3) & 0b11 fm.fms = current & 0b111 current = data.pop(0) fm.ams2 = (current >> 6) & 0b11 if current & 32: fm.ops = 4 else: fm.ops = 2 fm.opll_preset = current & 31 # read operators for op in range(ops): data = [read_byte(stream) for _ in range(8)] current = data.pop(0) fm.op_list[op].ksr = bool(current & 128) fm.op_list[op].dt = (current >> 4) & 7 fm.op_list[op].mult = current & 15 current = data.pop(0) fm.op_list[op].sus = bool(current & 128) fm.op_list[op].tl = current & 127 current = data.pop(0) fm.op_list[op].rs = (current >> 6) & 3 fm.op_list[op].vib = bool(current & 32) fm.op_list[op].ar = current & 31 current = data.pop(0) fm.op_list[op].am = bool(current & 128) fm.op_list[op].ksl = (current >> 5) & 3 fm.op_list[op].dr = current & 31 current = data.pop(0) fm.op_list[op].egt = bool(current & 128) fm.op_list[op].kvs = (current >> 5) & 3 fm.op_list[op].d2r = current & 31 current = data.pop(0) fm.op_list[op].sl = (current >> 4) & 15 fm.op_list[op].rr = current & 15 current = data.pop(0) fm.op_list[op].dvb = (current >> 4) & 15 fm.op_list[op].ssg_env = current & 15 current = data.pop(0) fm.op_list[op].dam = (current >> 5) & 7 fm.op_list[op].dt2 = (current >> 3) & 3 fm.op_list[op].ws = current & 7 return fm def __common_ma_block(self, stream: BytesIO, macro_class: Type[T_MACRO]) -> T_MACRO: ma = macro_class() ma.macros.clear() read_short(stream) # header size target_code: Union[MacroCode, OpMacroCode] if macro_class in [InsFeatureOpr1Macro, InsFeatureOpr2Macro, InsFeatureOpr3Macro, InsFeatureOpr4Macro]: target_code = OpMacroCode(read_byte(stream)) else: target_code = MacroCode(read_byte(stream)) while target_code != MacroCode.STOP: new_macro = SingleMacro(kind=target_code) length = read_byte(stream) loop = read_byte(stream) release = read_byte(stream) new_macro.mode = read_byte(stream) flags = read_byte(stream) word_size = MacroSize(flags >> 6 & 0b11) # type: ignore new_macro.type = MacroType(flags >> 1 & 0b11) new_macro.open = bool(flags & 1) new_macro.delay = read_byte(stream) new_macro.speed = read_byte(stream) # adsr and lfo will simply be kept as a list macro_content: List[Union[int, MacroItem]] = [ int.from_bytes( stream.read(word_size.num_bytes), byteorder='little', signed=word_size.signed ) for _ in range(length) ] if loop != 0xff: # hard limit in new macro macro_content.insert(loop, MacroItem.LOOP) if release != 0xff: # ^ macro_content.insert(release, MacroItem.RELEASE) new_macro.data = macro_content ma.macros.append(new_macro) if macro_class in [InsFeatureOpr1Macro, InsFeatureOpr2Macro, InsFeatureOpr3Macro, InsFeatureOpr4Macro]: target_code = OpMacroCode(read_byte(stream)) else: target_code = MacroCode(read_byte(stream)) return ma def __load_ma_block(self, stream: BytesIO) -> InsFeatureMacro: return self.__common_ma_block(stream, InsFeatureMacro) def __load_o1_block(self, stream: BytesIO) -> InsFeatureOpr1Macro: return self.__common_ma_block(stream, InsFeatureOpr1Macro) def __load_o2_block(self, stream: BytesIO) -> InsFeatureOpr2Macro: return self.__common_ma_block(stream, InsFeatureOpr2Macro) def __load_o3_block(self, stream: BytesIO) -> InsFeatureOpr3Macro: return self.__common_ma_block(stream, InsFeatureOpr3Macro) def __load_o4_block(self, stream: BytesIO) -> InsFeatureOpr4Macro: return self.__common_ma_block(stream, InsFeatureOpr4Macro) def __load_c64_block(self, stream: BytesIO) -> InsFeatureC64: c64 = InsFeatureC64() data = [read_byte(stream) for _ in range(4)] current = data.pop(0) c64.duty_is_abs = bool((current >> 7) & 1) c64.init_filter = bool((current >> 6) & 1) c64.vol_is_cutoff = bool((current >> 5) & 1) c64.to_filter = bool((current >> 4) & 1) c64.noise_on = bool((current >> 3) & 1) c64.pulse_on = bool((current >> 2) & 1) c64.saw_on = bool((current >> 1) & 1) c64.tri_on = bool(current & 1) current = data.pop(0) c64.osc_sync = bool((current >> 7) & 1) c64.ring_mod = bool((current >> 6) & 1) c64.no_test = bool((current >> 5) & 1) c64.filter_is_abs = bool((current >> 4) & 1) c64.ch3_off = bool((current >> 3) & 1) c64.bp = bool((current >> 2) & 1) c64.hp = bool((current >> 1) & 1) c64.lp = bool(current & 1) current = data.pop(0) c64.envelope.a = (current >> 4) & 0b1111 c64.envelope.d = current & 0b1111 current = data.pop(0) c64.envelope.s = (current >> 4) & 0b1111 c64.envelope.r = current & 0b1111 c64.duty = read_short(stream) c_r = read_short(stream) c64.cut = c_r & 0b11111111111 c64.res = (c_r >> 12) & 0b1111 return c64 def __load_gb_block(self, stream: BytesIO) -> InsFeatureGB: gb = InsFeatureGB() data = [read_byte(stream) for _ in range(4)] current = data.pop(0) gb.env_vol = current & 0b1111 gb.env_dir = (current >> 4) & 1 gb.env_len = (current >> 5) & 0b111 gb.sound_len = data.pop(0) current = data.pop(0) gb.soft_env = bool(current & 1) gb.always_init = bool((current >> 1) & 1) hw_seq_len = data.pop(0) for i in range(hw_seq_len): seq_entry = GBHwSeq( GBHwCommand(read_byte(stream)) ) seq_entry.data = [ read_byte(stream), read_byte(stream) ] gb.hw_seq.append(seq_entry) return gb def __load_sm_block(self, stream: BytesIO) -> InsFeatureAmiga: sm = InsFeatureAmiga() sm.init_sample = read_short(stream) current = read_byte(stream) sm.use_wave = bool((current >> 2) & 1) sm.use_sample = bool((current >> 1) & 1) sm.use_note_map = bool(current & 1) sm.wave_len = read_byte(stream) if sm.use_note_map: for i in range(len(sm.sample_map)): sm.sample_map[i].freq = read_short(stream) sm.sample_map[i].sample_index = read_short(stream) return sm def __load_ld_block(self, stream: BytesIO) -> InsFeatureOPLDrums: return InsFeatureOPLDrums( fixed_drums=bool(read_byte(stream) & 1), kick_freq=read_short(stream), snare_hat_freq=read_short(stream), tom_top_freq=read_short(stream) ) def __load_sn_block(self, stream: BytesIO) -> InsFeatureSNES: sn = InsFeatureSNES() data = [read_byte(stream) for _ in range(4)] current = data.pop(0) sn.envelope.d = (current >> 4) & 0b1111 sn.envelope.a = current & 0b1111 current = data.pop(0) sn.envelope.s = (current >> 4) & 0b1111 sn.envelope.r = current & 0b1111 current = data.pop(0) sn.use_env = bool((current >> 4) & 1) sn.sus = SNESSusMode((current >> 3) & 1) gain_mode = current & 0b111 if current < 4: gain_mode = 0 sn.gain_mode = GainMode(gain_mode) sn.gain = data.pop(0) if self.meta.version >= 131: d2s = read_byte(stream) sn.sus = SNESSusMode((d2s >> 5 & 0b11)) sn.d2 = d2s & 31 return sn def __load_n1_block(self, stream: BytesIO) -> InsFeatureN163: return InsFeatureN163( wave=read_int(stream), wave_pos=read_byte(stream), wave_len=read_byte(stream), wave_mode=read_byte(stream) ) def __load_fd_block(self, stream: BytesIO) -> InsFeatureFDS: fd = InsFeatureFDS( mod_speed=read_int(stream), mod_depth=read_int(stream), init_table_with_first_wave=bool(read_byte(stream)) ) for i in range(32): fd.mod_table[i] = read_byte(stream) return fd def __load_ws_block(self, stream: BytesIO) -> InsFeatureWaveSynth: return InsFeatureWaveSynth( wave_indices=[ read_int(stream), read_int(stream) ], rate_divider=read_byte(stream), effect=WaveFX(read_byte(stream)), enabled=bool(read_byte(stream) & 1), global_effect=bool(read_byte(stream) & 1), speed=read_byte(stream), params=[ read_byte(stream), read_byte(stream), read_byte(stream), read_byte(stream) ] ) def __common_pointers_block(self, stream: BytesIO, ptr_class: Type[T_POINTERS]) -> T_POINTERS: pt = ptr_class() num_entries = read_byte(stream) for _ in range(num_entries): pt.pointers[read_byte(stream)] = -1 for i in pt.pointers: pt.pointers[i] = read_int(stream) return pt def __load_sl_block(self, stream: BytesIO) -> InsFeatureSampleList: return self.__common_pointers_block(stream, InsFeatureSampleList) def __load_wl_block(self, stream: BytesIO) -> InsFeatureWaveList: return self.__common_pointers_block(stream, InsFeatureWaveList) def __load_mp_block(self, stream: BytesIO) -> InsFeatureMultiPCM: return InsFeatureMultiPCM( ar=read_byte(stream), d1r=read_byte(stream), dl=read_byte(stream), d2r=read_byte(stream), rr=read_byte(stream), rc=read_byte(stream), lfo=read_byte(stream), vib=read_byte(stream), am=read_byte(stream), ) def __load_su_block(self, stream: BytesIO) -> InsFeatureSoundUnit: return InsFeatureSoundUnit( switch_roles=bool(read_byte(stream)) ) def __load_es_block(self, stream: BytesIO) -> InsFeatureES5506: return InsFeatureES5506( filter_mode=ESFilterMode(read_byte(stream)), k1=read_short(stream), k2=read_short(stream), env_count=read_short(stream), left_volume_ramp=read_byte(stream), right_volume_ramp=read_byte(stream), k1_ramp=read_byte(stream), k2_ramp=read_byte(stream), k1_slow=read_byte(stream), k2_slow=read_byte(stream) ) def __load_x1_block(self, stream: BytesIO) -> InsFeatureX1010: return InsFeatureX1010( bank_slot=read_int(stream) ) def __load_ne_block(self, stream: BytesIO) -> InsFeatureDPCMMap: sm = InsFeatureDPCMMap() sm.use_map = bool(read_byte(stream) & 1) if sm.use_map: for i in range(len(sm.sample_map)): sm.sample_map[i].pitch = read_byte(stream) sm.sample_map[i].delta = read_byte(stream) return sm # TODO: No documentation? #def __load_ef_block(self, stream: BytesIO) -> InsFeatureESFM: # pass def __load_pn_block(self, stream: BytesIO) -> InsFeaturePowerNoise: return InsFeaturePowerNoise( octave=read_byte(stream) ) def __load_s2_block(self, stream: BytesIO) -> InsFeatureSID2: current_byte = read_byte(stream) return InsFeatureSID2( volume=current_byte & 0b1111, wave_mix=(current_byte >> 4) & 0b11, noise_mode=(current_byte >> 6) & 0b11 ) # format 0; also used for file because it includes the "INST" header too def __load_format_0_embed(self, stream: BinaryIO) -> None: # load format 0 as a series of format 1 feature blocks # aux function... def add_to_macro_data(macro: List[Union[int, MacroItem]], loop: Optional[int] = 0xffffffff, release: Optional[int] = 0xffffffff, data: Optional[List[int]] = None) -> None: if data is not None: macro.extend(data) if loop is not None and loop != 0xffffffff: # old macros have a 4-byte length macro.insert(loop, MacroItem.LOOP) if release is not None and release != 0xffffffff: macro.insert(release, MacroItem.RELEASE) # we check the header here if stream.read(len(EMBED_MAGIC_STR)) != EMBED_MAGIC_STR: raise RuntimeError('Bad magic value for a format 0 embed') blk_size = read_int(stream) if blk_size > 0: ins_data = BytesIO(stream.read(blk_size)) else: ins_data = stream self.meta.version = read_short(ins_data) # overwrites the file header version self.meta.type = InstrumentType(read_byte(ins_data)) read_byte(ins_data) # read all features in one go! self.features.clear() # name, insert immediately self.features.append( InsFeatureName(read_str(ins_data)) ) # fm if True: fm = InsFeatureFM( alg=read_byte(ins_data), fb=read_byte(ins_data), fms=read_byte(ins_data), ams=read_byte(ins_data), ops=read_byte(ins_data), opll_preset=read_byte(ins_data) ) read_short(ins_data) for i in range(4): fm.op_list[i].am = bool(read_byte(ins_data)) fm.op_list[i].ar = read_byte(ins_data) fm.op_list[i].dr = read_byte(ins_data) fm.op_list[i].mult = read_byte(ins_data) fm.op_list[i].rr = read_byte(ins_data) fm.op_list[i].sl = read_byte(ins_data) fm.op_list[i].tl = read_byte(ins_data) fm.op_list[i].dt2 = read_byte(ins_data) fm.op_list[i].rs = read_byte(ins_data) fm.op_list[i].dt = read_byte(ins_data) fm.op_list[i].d2r = read_byte(ins_data) fm.op_list[i].ssg_env = read_byte(ins_data) fm.op_list[i].dam = read_byte(ins_data) fm.op_list[i].dvb = read_byte(ins_data) fm.op_list[i].egt = bool(read_byte(ins_data)) fm.op_list[i].ksl = read_byte(ins_data) fm.op_list[i].sus = bool(read_byte(ins_data)) fm.op_list[i].vib = bool(read_byte(ins_data)) fm.op_list[i].ws = read_byte(ins_data) fm.op_list[i].ksr = bool(read_byte(ins_data)) en = read_byte(ins_data) if self.meta.version >= 114: fm.op_list[i].enable = bool(en) kvs = read_byte(ins_data) if self.meta.version >= 115: fm.op_list[i].kvs = kvs ins_data.read(10) self.features.append(fm) # gameboy if True: gb = InsFeatureGB( env_vol=read_byte(ins_data), env_dir=read_byte(ins_data), env_len=read_byte(ins_data), sound_len=read_byte(ins_data) ) self.features.append(gb) # c64 if True: c64 = InsFeatureC64( tri_on=bool(read_byte(ins_data)), saw_on=bool(read_byte(ins_data)), pulse_on=bool(read_byte(ins_data)), noise_on=bool(read_byte(ins_data)), duty=read_short(ins_data), ring_mod=read_byte(ins_data), osc_sync=read_byte(ins_data), to_filter=bool(read_byte(ins_data)), init_filter=bool(read_byte(ins_data)), vol_is_cutoff=bool(read_byte(ins_data)), res=read_byte(ins_data), lp=bool(read_byte(ins_data)), bp=bool(read_byte(ins_data)), hp=bool(read_byte(ins_data)), ch3_off=bool(read_byte(ins_data)), cut=read_short(ins_data), duty_is_abs=bool(read_byte(ins_data)), filter_is_abs=bool(read_byte(ins_data)) ) c64.envelope = GenericADSR( a=read_byte(ins_data), d=read_byte(ins_data), s=read_byte(ins_data), r=read_byte(ins_data), ) self.features.append(c64) # amiga if True: amiga = InsFeatureAmiga( init_sample=read_short(ins_data) ) wave = read_byte(ins_data) wavelen = read_byte(ins_data) if self.meta.version >= 82: amiga.use_wave = bool(wave) amiga.wave_len = wavelen for _ in range(12): read_byte(ins_data) # reserved self.features.append(amiga) # standard if True: mac = InsFeatureMacro() vol_mac = SingleMacro(kind=MacroCode.VOL) arp_mac = SingleMacro(kind=MacroCode.ARP) duty_mac = SingleMacro(kind=MacroCode.DUTY) wave_mac = SingleMacro(kind=MacroCode.WAVE) vol_mac.data.clear() arp_mac.data.clear() duty_mac.data.clear() wave_mac.data.clear() mac_list: List[SingleMacro] = [vol_mac, arp_mac, duty_mac, wave_mac] mac.macros = mac_list vol_mac_len = read_int(ins_data) arp_mac_len = read_int(ins_data) duty_mac_len = read_int(ins_data) wave_mac_len = read_int(ins_data) if self.meta.version >= 17: pitch_mac = SingleMacro(kind=MacroCode.PITCH) x1_mac = SingleMacro(kind=MacroCode.EX1) x2_mac = SingleMacro(kind=MacroCode.EX2) x3_mac = SingleMacro(kind=MacroCode.EX3) pitch_mac.data.clear() x1_mac.data.clear() x2_mac.data.clear() x3_mac.data.clear() mac_list.extend([pitch_mac, x1_mac, x2_mac, x3_mac]) pitch_mac_len = read_int(ins_data) x1_mac_len = read_int(ins_data) x2_mac_len = read_int(ins_data) x3_mac_len = read_int(ins_data) vol_mac_loop = read_int(ins_data) arp_mac_loop = read_int(ins_data) duty_mac_loop = read_int(ins_data) wave_mac_loop = read_int(ins_data) if self.meta.version >= 17: pitch_mac_loop = read_int(ins_data) x1_mac_loop = read_int(ins_data) x2_mac_loop = read_int(ins_data) x3_mac_loop = read_int(ins_data) arp_mac_mode = read_byte(ins_data) old_vol_height = read_byte(ins_data) old_duty_height = read_byte(ins_data) read_byte(ins_data) add_to_macro_data(vol_mac.data, loop=vol_mac_loop, release=None, data=[read_int(ins_data) for _ in range(vol_mac_len)]) add_to_macro_data(arp_mac.data, loop=arp_mac_loop, release=None, data=[read_int(ins_data) for _ in range(arp_mac_len)]) add_to_macro_data(duty_mac.data, loop=duty_mac_loop, release=None, data=[read_int(ins_data) for _ in range(duty_mac_len)]) add_to_macro_data(wave_mac.data, loop=wave_mac_loop, release=None, data=[read_int(ins_data) for _ in range(wave_mac_len)]) # adjust values if self.meta.version < 31: if arp_mac_mode == 0: for j in range(len(arp_mac.data)): if isinstance(arp_mac.data[j], int): arp_mac.data[j] -= 12 if self.meta.version < 87: if c64.vol_is_cutoff and not c64.filter_is_abs: for j in range(len(vol_mac.data)): if isinstance(vol_mac.data[j], int): vol_mac.data[j] -= 18 if c64.duty_is_abs: # TODO for j in range(len(duty_mac.data)): if isinstance(duty_mac.data[j], int): duty_mac.data[j] -= 12 if self.meta.version < 112: if arp_mac_mode == 1: # fixed arp! for i in range(len(arp_mac.data)): if isinstance(arp_mac.data[i], int): arp_mac.data[i] |= (1 << 30) if len(arp_mac.data) > 0: if arp_mac_loop != 0xffffffff: if arp_mac_loop == arp_mac_len+1: arp_mac.data[-1] = 0 arp_mac.data.append(MacroItem.LOOP) elif arp_mac_loop == arp_mac_len: arp_mac.data.append(0) else: arp_mac.data.append(0) # read more macros if self.meta.version >= 17: add_to_macro_data(pitch_mac.data, loop=pitch_mac_loop, release=None, data=[read_int(ins_data) for _ in range(pitch_mac_len)]) add_to_macro_data(x1_mac.data, loop=x1_mac_loop, release=None, data=[read_int(ins_data) for _ in range(x1_mac_len)]) add_to_macro_data(x2_mac.data, loop=x2_mac_loop, release=None, data=[read_int(ins_data) for _ in range(x2_mac_len)]) add_to_macro_data(x3_mac.data, loop=x3_mac_loop, release=None, data=[read_int(ins_data) for _ in range(x3_mac_len)]) else: if self.meta.type == InstrumentType.STANDARD: if old_vol_height == 31: self.meta.type = InstrumentType.PCE elif old_duty_height == 31: self.meta.type = InstrumentType.SSG self.features.append(mac) # fm macros if True: if self.meta.version >= 29: alg_mac = SingleMacro(kind=MacroCode.ALG) fb_mac = SingleMacro(kind=MacroCode.FB) fms_mac = SingleMacro(kind=MacroCode.FMS) ams_mac = SingleMacro(kind=MacroCode.AMS) mac_list.extend([alg_mac, fb_mac, fms_mac, ams_mac]) alg_mac.data.clear() fb_mac.data.clear() fms_mac.data.clear() ams_mac.data.clear() alg_mac_len = read_int(ins_data) fb_mac_len = read_int(ins_data) fms_mac_len = read_int(ins_data) ams_mac_len = read_int(ins_data) alg_mac_loop = read_int(ins_data) fb_mac_loop = read_int(ins_data) fms_mac_loop = read_int(ins_data) ams_mac_loop = read_int(ins_data) vol_mac.open = bool(read_byte(ins_data)) arp_mac.open = bool(read_byte(ins_data)) duty_mac.open = bool(read_byte(ins_data)) wave_mac.open = bool(read_byte(ins_data)) pitch_mac.open = bool(read_byte(ins_data)) x1_mac.open = bool(read_byte(ins_data)) x2_mac.open = bool(read_byte(ins_data)) x3_mac.open = bool(read_byte(ins_data)) alg_mac.open = bool(read_byte(ins_data)) fb_mac.open = bool(read_byte(ins_data)) fms_mac.open = bool(read_byte(ins_data)) ams_mac.open = bool(read_byte(ins_data)) add_to_macro_data(alg_mac.data, loop=alg_mac_loop, release=None, data=[read_int(ins_data) for _ in range(alg_mac_len)]) add_to_macro_data(fb_mac.data, loop=fb_mac_loop, release=None, data=[read_int(ins_data) for _ in range(fb_mac_len)]) add_to_macro_data(fms_mac.data, loop=fms_mac_loop, release=None, data=[read_int(ins_data) for _ in range(fms_mac_len)]) add_to_macro_data(ams_mac.data, loop=ams_mac_loop, release=None, data=[read_int(ins_data) for _ in range(ams_mac_len)]) # fm op macros if True: if self.meta.version >= 29: new_ops: Dict[int, InsFeatureMacro] = {} # actual ops ops_types: Dict[int, Type[InsFeatureMacro]] = { # classes 0: InsFeatureOpr1Macro, 1: InsFeatureOpr2Macro, 2: InsFeatureOpr3Macro, 3: InsFeatureOpr4Macro, } ops: Dict[int, Dict[str, Union[int, bool]]] = { # params 0: {}, 1: {}, 2: {}, 3: {} } for opi in ops: ops[opi]["am_mac_len"] = read_int(ins_data) ops[opi]["ar_mac_len"] = read_int(ins_data) ops[opi]["dr_mac_len"] = read_int(ins_data) ops[opi]["mult_mac_len"] = read_int(ins_data) ops[opi]["rr_mac_len"] = read_int(ins_data) ops[opi]["sl_mac_len"] = read_int(ins_data) ops[opi]["tl_mac_len"] = read_int(ins_data) ops[opi]["dt2_mac_len"] = read_int(ins_data) ops[opi]["rs_mac_len"] = read_int(ins_data) ops[opi]["dt_mac_len"] = read_int(ins_data) ops[opi]["d2r_mac_len"] = read_int(ins_data) ops[opi]["ssg_mac_len"] = read_int(ins_data) ops[opi]["am_mac_loop"] = read_int(ins_data) ops[opi]["ar_mac_loop"] = read_int(ins_data) ops[opi]["dr_mac_loop"] = read_int(ins_data) ops[opi]["mult_mac_loop"] = read_int(ins_data) ops[opi]["rr_mac_loop"] = read_int(ins_data) ops[opi]["sl_mac_loop"] = read_int(ins_data) ops[opi]["tl_mac_loop"] = read_int(ins_data) ops[opi]["dt2_mac_loop"] = read_int(ins_data) ops[opi]["rs_mac_loop"] = read_int(ins_data) ops[opi]["dt_mac_loop"] = read_int(ins_data) ops[opi]["d2r_mac_loop"] = read_int(ins_data) ops[opi]["ssg_mac_loop"] = read_int(ins_data) ops[opi]["am_mac_open"] = read_byte(ins_data) ops[opi]["ar_mac_open"] = read_byte(ins_data) ops[opi]["dr_mac_open"] = read_byte(ins_data) ops[opi]["mult_mac_open"] = read_byte(ins_data) ops[opi]["rr_mac_open"] = read_byte(ins_data) ops[opi]["sl_mac_open"] = read_byte(ins_data) ops[opi]["tl_mac_open"] = read_byte(ins_data) ops[opi]["dt2_mac_open"] = read_byte(ins_data) ops[opi]["rs_mac_open"] = read_byte(ins_data) ops[opi]["dt_mac_open"] = read_byte(ins_data) ops[opi]["d2r_mac_open"] = read_byte(ins_data) ops[opi]["ssg_mac_open"] = read_byte(ins_data) for opi in ops: new_op = ops_types[opi]() new_op.macros = [] am_mac = SingleMacro(kind=OpMacroCode.AM) am_mac.open = bool(ops[opi]["am_mac_open"]) am_mac.data.clear() add_to_macro_data(am_mac.data, loop=ops[opi]["am_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["am_mac_len"])]) ar_mac = SingleMacro(kind=OpMacroCode.AR) ar_mac.open = bool(ops[opi]["ar_mac_open"]) ar_mac.data.clear() add_to_macro_data(ar_mac.data, loop=ops[opi]["ar_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["ar_mac_len"])]) dr_mac = SingleMacro(kind=OpMacroCode.DR) dr_mac.open = bool(ops[opi]["dr_mac_open"]) dr_mac.data.clear() add_to_macro_data(dr_mac.data, loop=ops[opi]["dr_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["dr_mac_len"])]) mult_mac = SingleMacro(kind=OpMacroCode.MULT) mult_mac.open = bool(ops[opi]["mult_mac_open"]) mult_mac.data.clear() add_to_macro_data(mult_mac.data, loop=ops[opi]["mult_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["mult_mac_len"])]) rr_mac = SingleMacro(kind=OpMacroCode.RR) rr_mac.open = bool(ops[opi]["rr_mac_open"]) rr_mac.data.clear() add_to_macro_data(rr_mac.data, loop=ops[opi]["rr_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["rr_mac_len"])]) sl_mac = SingleMacro(kind=OpMacroCode.SL) sl_mac.open = bool(ops[opi]["sl_mac_open"]) sl_mac.data.clear() add_to_macro_data(sl_mac.data, loop=ops[opi]["sl_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["sl_mac_len"])]) tl_mac = SingleMacro(kind=OpMacroCode.TL) tl_mac.open = bool(ops[opi]["tl_mac_open"]) tl_mac.data.clear() add_to_macro_data(tl_mac.data, loop=ops[opi]["tl_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["tl_mac_len"])]) dt2_mac = SingleMacro(kind=OpMacroCode.DT2) dt2_mac.open = bool(ops[opi]["dt2_mac_open"]) dt2_mac.data.clear() add_to_macro_data(dt2_mac.data, loop=ops[opi]["dt2_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["dt2_mac_len"])]) rs_mac = SingleMacro(kind=OpMacroCode.RS) rs_mac.open = bool(ops[opi]["rs_mac_open"]) rs_mac.data.clear() add_to_macro_data(rs_mac.data, loop=ops[opi]["rs_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["rs_mac_len"])]) dt_mac = SingleMacro(kind=OpMacroCode.DT) dt_mac.open = bool(ops[opi]["dt_mac_open"]) dt_mac.data.clear() add_to_macro_data(dt_mac.data, loop=ops[opi]["dt_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["dt_mac_len"])]) d2r_mac = SingleMacro(kind=OpMacroCode.D2R) d2r_mac.open = bool(ops[opi]["d2r_mac_open"]) d2r_mac.data.clear() add_to_macro_data(d2r_mac.data, loop=ops[opi]["d2r_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["d2r_mac_len"])]) ssg_mac = SingleMacro(kind=OpMacroCode.SSG_EG) ssg_mac.open = bool(ops[opi]["ssg_mac_open"]) ssg_mac.data.clear() add_to_macro_data(ssg_mac.data, loop=ops[opi]["ssg_mac_loop"], release=None, data=[read_int(ins_data) for _ in range(ops[opi]["ssg_mac_len"])]) new_op.macros.extend([ am_mac, ar_mac, dr_mac, mult_mac, rr_mac, sl_mac, tl_mac, dt2_mac, rs_mac, dt_mac, d2r_mac, ssg_mac ]) # must be in order!! new_ops[opi] = new_op # release points if True: if self.meta.version >= 44: add_to_macro_data(vol_mac.data, None, read_int(ins_data), None) add_to_macro_data(arp_mac.data, None, read_int(ins_data), None) add_to_macro_data(duty_mac.data, None, read_int(ins_data), None) add_to_macro_data(wave_mac.data, None, read_int(ins_data), None) add_to_macro_data(pitch_mac.data, None, read_int(ins_data), None) add_to_macro_data(x1_mac.data, None, read_int(ins_data), None) add_to_macro_data(x2_mac.data, None, read_int(ins_data), None) add_to_macro_data(x3_mac.data, None, read_int(ins_data), None) add_to_macro_data(alg_mac.data, None, read_int(ins_data), None) add_to_macro_data(fb_mac.data, None, read_int(ins_data), None) add_to_macro_data(fms_mac.data, None, read_int(ins_data), None) add_to_macro_data(ams_mac.data, None, read_int(ins_data), None) for opi in new_ops: for i in range(12): add_to_macro_data(new_ops[opi].macros[i].data, None, read_int(ins_data), None) # extended op macros if True: if self.meta.version >= 61: for op in new_ops: dam_mac = SingleMacro(kind=OpMacroCode.DAM) dvb_mac = SingleMacro(kind=OpMacroCode.DVB) egt_mac = SingleMacro(kind=OpMacroCode.EGT) ksl_mac = SingleMacro(kind=OpMacroCode.KSL) sus_mac = SingleMacro(kind=OpMacroCode.SUS) vib_mac = SingleMacro(kind=OpMacroCode.VIB) ws_mac = SingleMacro(kind=OpMacroCode.WS) ksr_mac = SingleMacro(kind=OpMacroCode.KSR) dam_mac_len = read_int(ins_data) dvb_mac_len = read_int(ins_data) egt_mac_len = read_int(ins_data) ksl_mac_len = read_int(ins_data) sus_mac_len = read_int(ins_data) vib_mac_len = read_int(ins_data) ws_mac_len = read_int(ins_data) ksr_mac_len = read_int(ins_data) dam_mac_loop = read_int(ins_data) dvb_mac_loop = read_int(ins_data) egt_mac_loop = read_int(ins_data) ksl_mac_loop = read_int(ins_data) sus_mac_loop = read_int(ins_data) vib_mac_loop = read_int(ins_data) ws_mac_loop = read_int(ins_data) ksr_mac_loop = read_int(ins_data) dam_mac_rel = read_int(ins_data) dvb_mac_rel = read_int(ins_data) egt_mac_rel = read_int(ins_data) ksl_mac_rel = read_int(ins_data) sus_mac_rel = read_int(ins_data) vib_mac_rel = read_int(ins_data) ws_mac_rel = read_int(ins_data) ksr_mac_rel = read_int(ins_data) dam_mac.open = bool(read_byte(ins_data)) dvb_mac.open = bool(read_byte(ins_data)) egt_mac.open = bool(read_byte(ins_data)) ksl_mac.open = bool(read_byte(ins_data)) sus_mac.open = bool(read_byte(ins_data)) vib_mac.open = bool(read_byte(ins_data)) ws_mac.open = bool(read_byte(ins_data)) ksr_mac.open = bool(read_byte(ins_data)) dam_mac.data.clear() dvb_mac.data.clear() egt_mac.data.clear() ksl_mac.data.clear() sus_mac.data.clear() vib_mac.data.clear() ws_mac.data.clear() ksr_mac.data.clear() add_to_macro_data(dam_mac.data, dam_mac_loop, dam_mac_rel, [ read_byte(ins_data) for _ in range(dam_mac_len) ]) add_to_macro_data(dvb_mac.data, dvb_mac_loop, dvb_mac_rel, [ read_byte(ins_data) for _ in range(dvb_mac_len) ]) add_to_macro_data(egt_mac.data, egt_mac_loop, egt_mac_rel, [ read_byte(ins_data) for _ in range(egt_mac_len) ]) add_to_macro_data(ksl_mac.data, ksl_mac_loop, ksl_mac_rel, [ read_byte(ins_data) for _ in range(ksl_mac_len) ]) add_to_macro_data(sus_mac.data, sus_mac_loop, sus_mac_rel, [ read_byte(ins_data) for _ in range(sus_mac_len) ]) add_to_macro_data(vib_mac.data, vib_mac_loop, vib_mac_rel, [ read_byte(ins_data) for _ in range(vib_mac_len) ]) add_to_macro_data(ws_mac.data, ws_mac_loop, ws_mac_rel, [ read_byte(ins_data) for _ in range(ws_mac_len) ]) add_to_macro_data(ksr_mac.data, ksr_mac_loop, ksr_mac_rel, [ read_byte(ins_data) for _ in range(ksr_mac_len) ]) new_ops[op].macros.extend([ dam_mac, dvb_mac, egt_mac, ksl_mac, sus_mac, vib_mac, ws_mac, ksr_mac ]) # opl drum data if True: if self.meta.version >= 63: opl_drum = InsFeatureOPLDrums( fixed_drums = bool(read_byte(ins_data)) ) read_byte(ins_data) opl_drum.kick_freq = read_short(ins_data) opl_drum.snare_hat_freq = read_short(ins_data) opl_drum.tom_top_freq = read_short(ins_data) self.features.append(opl_drum) # clear macros if True: if self.meta.version < 63 and self.meta.type == InstrumentType.PCE: duty_mac.data.clear() if self.meta.version < 70 and self.meta.type == InstrumentType.FM_OPLL: wave_mac.data.clear() # sample map if True: if self.meta.version >= 67: note_map = InsFeatureAmiga() note_map.use_note_map = bool(read_byte(ins_data)) if note_map.use_note_map: for i in range(len(note_map.sample_map)): note_map.sample_map[i].freq = read_int(ins_data) for i in range(len(note_map.sample_map)): note_map.sample_map[i].sample_index = read_short(ins_data) self.features.append(note_map) # n163 if True: if self.meta.version >= 73: n163 = InsFeatureN163( wave=read_int(ins_data), wave_pos=read_byte(ins_data), wave_len=read_byte(ins_data), wave_mode=read_byte(ins_data) ) read_byte(ins_data) # reserved self.features.append(n163) # moar macroes if True: if self.meta.version >= 76: pan_l_mac = SingleMacro(kind=MacroCode.PAN_L) pan_r_mac = SingleMacro(kind=MacroCode.PAN_R) phase_res_mac = SingleMacro(kind=MacroCode.PHASE_RESET) x4_mac = SingleMacro(kind=MacroCode.EX4) x5_mac = SingleMacro(kind=MacroCode.EX5) x6_mac = SingleMacro(kind=MacroCode.EX6) x7_mac = SingleMacro(kind=MacroCode.EX7) x8_mac = SingleMacro(kind=MacroCode.EX8) pan_l_mac.data.clear() pan_r_mac.data.clear() phase_res_mac.data.clear() x4_mac.data.clear() x5_mac.data.clear() x6_mac.data.clear() x7_mac.data.clear() x8_mac.data.clear() pan_l_mac_len = read_int(ins_data) pan_r_mac_len = read_int(ins_data) phase_res_mac_len = read_int(ins_data) x4_mac_len = read_int(ins_data) x5_mac_len = read_int(ins_data) x6_mac_len = read_int(ins_data) x7_mac_len = read_int(ins_data) x8_mac_len = read_int(ins_data) pan_l_mac_loop = read_int(ins_data) pan_r_mac_loop = read_int(ins_data) phase_res_mac_loop = read_int(ins_data) x4_mac_loop = read_int(ins_data) x5_mac_loop = read_int(ins_data) x6_mac_loop = read_int(ins_data) x7_mac_loop = read_int(ins_data) x8_mac_loop = read_int(ins_data) pan_l_mac_rel = read_int(ins_data) pan_r_mac_rel = read_int(ins_data) phase_res_mac_rel = read_int(ins_data) x4_mac_rel = read_int(ins_data) x5_mac_rel = read_int(ins_data) x6_mac_rel = read_int(ins_data) x7_mac_rel = read_int(ins_data) x8_mac_rel = read_int(ins_data) pan_l_mac.open = bool(read_byte(ins_data)) pan_r_mac.open = bool(read_byte(ins_data)) phase_res_mac.open = bool(read_byte(ins_data)) x4_mac.open = bool(read_byte(ins_data)) x5_mac.open = bool(read_byte(ins_data)) x6_mac.open = bool(read_byte(ins_data)) x7_mac.open = bool(read_byte(ins_data)) x8_mac.open = bool(read_byte(ins_data)) add_to_macro_data(pan_l_mac.data, pan_l_mac_loop, pan_l_mac_rel, [ read_int(ins_data) for _ in range(pan_l_mac_len) ]) add_to_macro_data(pan_r_mac.data, pan_r_mac_loop, pan_r_mac_rel, [ read_int(ins_data) for _ in range(pan_r_mac_len) ]) add_to_macro_data(phase_res_mac.data, phase_res_mac_loop, phase_res_mac_rel, [ read_int(ins_data) for _ in range(phase_res_mac_len) ]) add_to_macro_data(x4_mac.data, x4_mac_loop, x4_mac_rel, [ read_int(ins_data) for _ in range(x4_mac_len) ]) add_to_macro_data(x5_mac.data, x5_mac_loop, x5_mac_rel, [ read_int(ins_data) for _ in range(x5_mac_len) ]) add_to_macro_data(x6_mac.data, x6_mac_loop, x6_mac_rel, [ read_int(ins_data) for _ in range(x6_mac_len) ]) add_to_macro_data(x7_mac.data, x7_mac_loop, x7_mac_rel, [ read_int(ins_data) for _ in range(x7_mac_len) ]) add_to_macro_data(x8_mac.data, x8_mac_loop, x8_mac_rel, [ read_int(ins_data) for _ in range(x8_mac_len) ]) mac_list.extend([ pan_l_mac, pan_r_mac, phase_res_mac, x4_mac, x5_mac, x6_mac, x7_mac, x8_mac ]) # fds if True: if self.meta.version >= 76: fds = InsFeatureFDS( mod_speed=read_int(ins_data), mod_depth=read_int(ins_data), init_table_with_first_wave=bool(read_byte(ins_data)) ) read_byte(ins_data) # reserved read_byte(ins_data) read_byte(ins_data) fds.mod_table = [read_byte(ins_data) for _ in range(32)] self.features.append(fds) # opz if True: if self.meta.version >= 77: fm.fms2 = read_byte(ins_data) fm.ams2 = read_byte(ins_data) # wave synth if True: if self.meta.version >= 79: ws = InsFeatureWaveSynth( wave_indices=[read_int(ins_data), read_int(ins_data)], rate_divider=read_byte(ins_data), effect=WaveFX(read_byte(ins_data)), enabled=bool(read_byte(ins_data)), global_effect=bool(read_byte(ins_data)), speed=read_byte(ins_data), params=[read_byte(ins_data) for _ in range(4)] ) self.features.append(ws) # macro moads if True: if self.meta.version >= 84: vol_mac.mode = read_byte(ins_data) duty_mac.mode = read_byte(ins_data) wave_mac.mode = read_byte(ins_data) pitch_mac.mode = read_byte(ins_data) x1_mac.mode = read_byte(ins_data) x2_mac.mode = read_byte(ins_data) x3_mac.mode = read_byte(ins_data) alg_mac.mode = read_byte(ins_data) fb_mac.mode = read_byte(ins_data) fms_mac.mode = read_byte(ins_data) ams_mac.mode = read_byte(ins_data) pan_l_mac.mode = read_byte(ins_data) pan_r_mac.mode = read_byte(ins_data) phase_res_mac.mode = read_byte(ins_data) x4_mac.mode = read_byte(ins_data) x5_mac.mode = read_byte(ins_data) x6_mac.mode = read_byte(ins_data) x7_mac.mode = read_byte(ins_data) x8_mac.mode = read_byte(ins_data) # c64 no test if True: if self.meta.version >= 89: c64.no_test = bool(read_byte(ins_data)) # multipcm if True: if self.meta.version >= 93: mp = InsFeatureMultiPCM( ar=read_byte(ins_data), d1r=read_byte(ins_data), dl=read_byte(ins_data), d2r=read_byte(ins_data), rr=read_byte(ins_data), rc=read_byte(ins_data), lfo=read_byte(ins_data), vib=read_byte(ins_data), am=read_byte(ins_data) ) for _ in range(23): # reserved read_byte(ins_data) self.features.append(mp) # sound unit if True: if self.meta.version >= 104: amiga.use_sample = bool(read_byte(ins_data)) su = InsFeatureSoundUnit( switch_roles=bool(read_byte(ins_data)) ) self.features.append(su) # gb hw seq if True: if self.meta.version >= 105: gb_hwseq_len = read_byte(ins_data) gb.hw_seq.clear() for i in range(gb_hwseq_len): gb.hw_seq.append( GBHwSeq( command=GBHwCommand(read_byte(ins_data)), data=[read_byte(ins_data), read_byte(ins_data)] ) ) # additional gb if True: if self.meta.version >= 106: gb.soft_env = bool(read_byte(ins_data)) gb.always_init = bool(read_byte(ins_data)) # es5506 if True: if self.meta.version >= 107: es = InsFeatureES5506( filter_mode=ESFilterMode(read_byte(ins_data)), k1=read_short(ins_data), k2=read_short(ins_data), env_count=read_short(ins_data), left_volume_ramp=read_byte(ins_data), right_volume_ramp=read_byte(ins_data), k1_ramp=read_byte(ins_data), k2_ramp=read_byte(ins_data), k1_slow=read_byte(ins_data), k2_slow=read_byte(ins_data) ) self.features.append(es) # snes if True: if self.meta.version >= 109: snes = InsFeatureSNES() snes.use_env = bool(read_byte(ins_data)) if self.meta.version >= 118: snes.gain_mode = GainMode(read_byte(ins_data)) snes.gain = read_byte(ins_data) else: read_byte(ins_data) read_byte(ins_data) snes.envelope.a = read_byte(ins_data) snes.envelope.d = read_byte(ins_data) snes_env_s = read_byte(ins_data) snes.envelope.s = snes_env_s & 0b111 snes.envelope.r = read_byte(ins_data) snes.sus = SNESSusMode((snes_env_s >> 3) & 1) # ??? self.features.append(snes) # macro speed delay if True: if self.meta.version >= 111: vol_mac.speed = read_byte(ins_data) arp_mac.speed = read_byte(ins_data) duty_mac.speed = read_byte(ins_data) wave_mac.speed = read_byte(ins_data) pitch_mac.speed = read_byte(ins_data) x1_mac.speed = read_byte(ins_data) x2_mac.speed = read_byte(ins_data) x3_mac.speed = read_byte(ins_data) alg_mac.speed = read_byte(ins_data) fb_mac.speed = read_byte(ins_data) fms_mac.speed = read_byte(ins_data) ams_mac.speed = read_byte(ins_data) pan_l_mac.speed = read_byte(ins_data) pan_r_mac.speed = read_byte(ins_data) phase_res_mac.speed = read_byte(ins_data) x4_mac.speed = read_byte(ins_data) x5_mac.speed = read_byte(ins_data) x6_mac.speed = read_byte(ins_data) x7_mac.speed = read_byte(ins_data) x8_mac.speed = read_byte(ins_data) vol_mac.delay = read_byte(ins_data) arp_mac.delay = read_byte(ins_data) duty_mac.delay = read_byte(ins_data) wave_mac.delay = read_byte(ins_data) pitch_mac.delay = read_byte(ins_data) x1_mac.delay = read_byte(ins_data) x2_mac.delay = read_byte(ins_data) x3_mac.delay = read_byte(ins_data) alg_mac.delay = read_byte(ins_data) fb_mac.delay = read_byte(ins_data) fms_mac.delay = read_byte(ins_data) ams_mac.delay = read_byte(ins_data) pan_l_mac.delay = read_byte(ins_data) pan_r_mac.delay = read_byte(ins_data) phase_res_mac.delay = read_byte(ins_data) x4_mac.delay = read_byte(ins_data) x5_mac.delay = read_byte(ins_data) x6_mac.delay = read_byte(ins_data) x7_mac.delay = read_byte(ins_data) x8_mac.delay = read_byte(ins_data) for op in ops: for i in range(20): new_ops[op].macros[i].speed = read_byte(ins_data) for i in range(20): new_ops[op].macros[i].delay = read_byte(ins_data) # old arp mac format if True: if self.meta.version < 112: if arp_mac.mode != 0: arp_mac.mode = 0 for i in range(len(arp_mac.data)): if isinstance(arp_mac.data[i], int): arp_mac.data[i] ^= 0x40000000 # add ops macros at the end if True: if self.meta.version >= 29: for _, op_contents in new_ops.items(): self.features.append(op_contents)