class Flanger(Module): name = mtype = "Flanger" mgroup = "Effect" flags = 0x000451 behaviors = {B.receives_audio, B.sends_audio} class LfoWaveform(Enum): hsin = 0 sin = 1 class LfoFreqUnit(Enum): hz_0_05 = 0 # hz * 0.05 ms = 1 hz = 2 tick = 3 line = 4 line_2 = 5 # line / 2 line_3 = 6 # line / 3 dry = Controller((0, 256), 256) wet = Controller((0, 256), 128) feedback = Controller((0, 256), 128) delay = Controller((0, 1000), 200) response = Controller((0, 256), 2) lfo_freq = Controller((0, 512), 8) lfo_amp = Controller((0, 256), 32) lfo_waveform = Controller(LfoWaveform, LfoWaveform.hsin) set_lfo_phase = Controller((0, 256), 0) # used to reset module lfo_freq_unit = Controller(LfoFreqUnit, LfoFreqUnit.hz_0_05)
class Generator(Module): name = mtype = "Generator" mgroup = "Synth" chnk = 1 flags = 0x000059 behaviors = {B.receives_notes, B.receives_modulator, B.sends_audio} class Waveform(Enum): triangle = 0 saw = 1 square = 2 noise = 3 dirty = 4 sin = 5 hsin = 6 asin = 7 psin = 8 class Mode(Enum): stereo = 0 mono = 1 class DirtyWaveform(DirtyWaveformChunk): chnm = 0 volume = Controller((0, 256), 128) waveform = Controller(Waveform, Waveform.triangle) panning = Controller((-128, 128), 0) attack = Controller((0, 512), 0) release = Controller((0, 512), 0) polyphony_ch = Controller((1, 16), 8) mode = Controller(Mode, Mode.stereo) sustain = Controller(bool, True) freq_modulation_input = Controller((0, 256), 0) duty_cycle = Controller((0, 1022), 511) def __init__(self, **kwargs): samples = kwargs.pop("samples", None) super(Generator, self).__init__(**kwargs) self.dirty_waveform = self.DirtyWaveform() if samples is not None: self.dirty_waveform.samples = samples def specialized_iff_chunks(self): for chunk in self.dirty_waveform.chunks(): yield chunk for chunk in super(Generator, self).specialized_iff_chunks(): yield chunk def load_chunk(self, chunk): if chunk.chnm == 0: self.load_dirty_waveform(chunk) def load_dirty_waveform(self, chunk): self.dirty_waveform.samples = chunk.chdt self.dirty_waveform.format = self.dirty_waveform.Format(chunk.chff or 1) self.dirty_waveform.freq = chunk.chfr
class Lfo(Module): name = mtype = "LFO" mgroup = "Effect" flags = 0x000451 behaviors = {B.sends_audio} class Type(Enum): amplitude = 0 panning = 1 class Waveform(Enum): sin = 0 square = 1 sin2 = 2 saw = 3 saw2 = 4 random = 5 triangle = 6 random_interpolated = 7 class Channels(Enum): stereo = 0 mono = 1 class FrequencyUnit(Enum): hz_64 = 0 # hz / 64 ms = 1 hz = 2 tick = 3 line = 4 line_2 = 5 # line / 2 line_3 = 6 # line / 3 freq_ranges = { FrequencyUnit.hz_64: WarnOnlyRange(1, 2048), FrequencyUnit.ms: WarnOnlyRange(1, 4000), FrequencyUnit.hz: WarnOnlyRange(1, 16384), FrequencyUnit.tick: WarnOnlyRange(1, 256), FrequencyUnit.line: WarnOnlyRange(1, 256), FrequencyUnit.line_2: WarnOnlyRange(1, 256), FrequencyUnit.line_3: WarnOnlyRange(1, 256), } volume = Controller((0, 512), 256) type = Controller(Type, Type.amplitude) amplitude = Controller((0, 256), 256) freq = Controller( DependentRange("frequency_unit", freq_ranges, Range(1, 2048)), 256) waveform = Controller(Waveform, Waveform.sin) set_phase = Controller((0, 256), 0) # used to reset module channels = Controller(Channels, Channels.stereo) frequency_unit = Controller(FrequencyUnit, FrequencyUnit.hz_64) duty_cycle = Controller((0, 256), 128) generator = Controller(bool, False)
class Sound2Ctl(Module): name = mtype = "Sound2Ctl" mgroup = "Misc" chnk = 1 options_chnm = 0 flags = 0x600051 class Channels(Enum): mono = 0 stereo = 1 class Mode(Enum): lq = 0 hq = 1 behaviors = {B.receives_audio, B.sends_controls} sample_rate_hz = Controller((1, 32768), 50) channels = Controller(Channels, Channels.mono) absolute = Controller(bool, True) gain = Controller((0, 1024), 256) smooth = Controller((0, 256), 128) mode = Controller(Mode, Mode.hq) out_min = Controller((0, 32768), 0) out_max = Controller((0, 32768), 32768) out_controller = Controller((0, 32), 0) record_values = Option(False) def load_chunk(self, chunk): if chunk.chnm == self.options_chnm: self.load_options(chunk)
class Delay(Module): name = mtype = "Delay" mgroup = "Effect" flags = 0x000451 behaviors = {B.receives_audio, B.sends_audio} class Channels(Enum): stereo = 0 mono = 1 class DelayUnits(Enum): sec_16384 = 0 # sec/16384 ms = 1 hz = 2 tick = 3 line = 4 line_2 = 5 # line/2 line_3 = 6 # line/3 dry = Controller((0, 512), 256) wet = Controller((0, 512), 256) delay_l = Controller((0, 256), 128) delay_r = Controller((0, 256), 160) volume_l = Controller((0, 256), 256) volume_r = Controller((0, 256), 256) channels = Controller(Channels, Channels.stereo) inverse = Controller(bool, False) delay_units = Controller(DelayUnits, DelayUnits.sec_16384)
class Input(Module): name = mtype = "Input" mgroup = "Synth" flags = 0x000049 behaviors = {B.sends_audio} class Channels(Enum): mono = 0 stereo = 1 volume = Controller((0, 1024), 256) channels = Controller(Channels, Channels.mono)
class Eq(Module): name = mtype = "EQ" mgroup = "Effect" flags = 0x000051 behaviors = {B.receives_audio, B.sends_audio} class Channels(Enum): stereo = 0 mono = 1 low = Controller((0, 512), 256) middle = Controller((0, 512), 256) high = Controller((0, 512), 256) channels = Controller(Channels, Channels.stereo)
class Feedback(Module): name = mtype = "Feedback" mgroup = "Misc" flags = 0x600051 behaviors = { B.receives_audio, B.receives_feedback, B.sends_audio, B.sends_feedback } class Channels(Enum): stereo = 0 mono = 1 volume = Controller((0, 10000), 1000) channels = Controller(Channels, Channels.stereo)
class VocalFilter(Module): name = mtype = "Vocal filter" mgroup = "Effect" flags = 0x000051 behaviors = {B.receives_audio, B.sends_audio} class VoiceType(Enum): soprano = 0 alto = 1 tenor = 2 bass = 3 class Channels(Enum): stereo = 0 mono = 1 volume = Controller((0, 512), 256) formant_width_hz = Controller((0, 256), 128) intensity = Controller((0, 256), 128) formants = Controller((1, 5), 5) vowel = Controller((0, 256), 0) voice_type = Controller(VoiceType, VoiceType.soprano) channels = Controller(Channels, Channels.stereo)
class VorbisPlayer(Module): name = mtype = "Vorbis player" mgroup = "Synth" chnk = 1 flags = 0x008049 behaviors = {B.sends_audio} data = None volume = Controller((0, 512), 256) original_speed = Controller(bool, True) finetune = Controller((-128, 128), 0) transpose = Controller((-128, 128), 0) interpolation = Controller(bool, True) polyphony_ch = Controller((1, 4), 1) repeat = Controller(bool, False) def __init__(self, **kwargs): data = kwargs.pop("data", None) super(VorbisPlayer, self).__init__(**kwargs) self.data = data def specialized_iff_chunks(self): yield (b"CHNM", pack("<I", 0)) yield (b"CHDT", self.data or b"") yield from super(VorbisPlayer, self).specialized_iff_chunks() def load_chunk(self, chunk): if chunk.chnm == 0: self.data = chunk.chdt
class Velocity2Ctl(Module): name = mtype = "Velocity2Ctl" mgroup = "Misc" flags = 0x020049 behaviors = {B.receives_notes, B.sends_controls} class NoteOffAction(Enum): do_nothing = 0 vel_down = 1 vel_up = 2 note_off_action = Controller(NoteOffAction, NoteOffAction.do_nothing) out_min = Controller((0, 32768), 0) out_max = Controller((0, 32768), 32768) out_offset = Controller((-16384, 16384), 0) out_controller = Controller((0, 32), 0)
class Modulator(Module): name = mtype = "Modulator" mgroup = "Effect" flags = 0x002051 behaviors = {B.receives_audio, B.receives_modulator, B.sends_audio} class ModulationType(Enum): amplitude = 0 phase = 1 phase_abs = 2 class Channels(Enum): stereo = 0 mono = 1 volume = Controller((0, 512), 256) modulation_type = Controller(ModulationType, ModulationType.amplitude) channels = Controller(Channels, Channels.stereo)
class Loop(Module): name = mtype = "Loop" mgroup = "Effect" flags = 0x000451 behaviors = {B.receives_audio, B.sends_audio} class Channels(Enum): mono = 0 stereo = 1 class Mode(Enum): normal = 0 ping_pong = 1 volume = Controller((0, 256), 256) delay = Controller((0, 256), 256) # line / 128 channels = Controller(Channels, Channels.stereo) repeats = Controller((0, 64), 0) mode = Controller(Mode, Mode.normal)
class Echo(Module): name = mtype = "Echo" mgroup = "Effect" flags = 0x000451 behaviors = {B.receives_audio, B.sends_audio} class Channels(Enum): mono = 0 stereo = 1 class DelayUnits(Enum): sec_256 = 0 # sec/256 ms = 1 hz = 2 tick = 3 line = 4 line_2 = 5 # line/2 line_3 = 6 # line/3 dry = Controller((0, 256), 256) wet = Controller((0, 256), 128) feedback = Controller((0, 256), 128) delay = Controller((0, 256), 256) channels = Controller(Channels, Channels.stereo) delay_units = Controller(DelayUnits, DelayUnits.sec_256)
class DcBlocker(Module): name = mtype = "DC Blocker" mgroup = "Effect" flags = 0x000051 behaviors = {B.receives_audio, B.sends_audio} class Channels(Enum): stereo = 0 mono = 1 channels = Controller(Channels, Channels.stereo)
class Reverb(Module): name = mtype = "Reverb" mgroup = "Effect" flags = 0x000051 behaviors = {B.receives_audio, B.sends_audio} class Mode(Enum): hq = 0 hq_mono = 1 lq = 2 lq_mono = 3 dry = Controller((0, 256), 256) wet = Controller((0, 256), 64) feedback = Controller((0, 256), 256) damp = Controller((0, 256), 128) stereo_width = Controller((0, 256), 256) freeze = Controller(bool, False) mode = Controller(Mode, Mode.hq) all_pass_filter = Controller(bool, True) room_size = Controller((0, 128), 16) random_seed = Controller((0, 32768), 0)
class Kicker(Module): name = mtype = "Kicker" mgroup = "Synth" flags = 0x000049 behaviors = {B.receives_notes, B.sends_audio} class Waveform(Enum): triangle = 0 square = 1 sin = 2 volume = Controller((0, 256), 256) waveform = Controller(Waveform, Waveform.triangle) panning = Controller((-128, 128), 0) attack = Controller((0, 512), 0) release = Controller((0, 512), 32) boost = Controller((0, 1024), 0) env_acceleration = Controller((0, 1024), 256) polyphony_ch = Controller((1, 4), 1) anticlick = Controller(bool, False)
class Gpio(Module): name = mtype = "GPIO" mgroup = "Misc" flags = 0x000051 behaviors = {B.receives_audio, B.sends_audio} out = Controller(bool, False) out_pin = Controller((0, 256), 0) out_threshold = Controller((0, 100), 50) in_ = Controller(bool, False) in_pin = Controller((0, 256), 0) in_note = Controller((0, 128), 0) in_amplitude = Controller((0, 100), 100)
class Glide(Module): name = mtype = "Glide" mgroup = "Misc" flags = 0x021049 behaviors = {B.receives_notes, B.sends_notes} response = Controller((0, 1000), 500) sample_rate_hz = Controller((1, 32768), 150) offset_on_1st_note = Controller(bool, False) polyphony = Controller(bool, True) pitch = Controller((-600, 600), 0) pitch_scale = Controller((0, 200), 100) reset = Controller(bool, False) # used to reset module
class Amplifier(Module): name = mtype = "Amplifier" mgroup = "Effect" flags = 0x000051 behaviors = {B.receives_audio, B.sends_audio} receives_audio = True sends_audio = True volume = Controller((0, 1024), 256) panning = Controller((-128, 128), 0) dc_offset = Controller((-128, 128), 0) inverse = Controller(bool, False) stereo_width = Controller((0, 256), 128) absolute = Controller(bool, False) fine_volume = Controller((0, 32768), 32768) gain = Controller((0, 5000), 1)
class WaveShaper(Module): name = mtype = "WaveShaper" mgroup = "Effect" chnk = 1 flags = 0x000051 behaviors = {B.receives_audio, B.sends_audio} class Mode(Enum): hq = 0 hq_mono = 1 lq = 2 lq_mono = 3 class Curve(ArrayChunk): chnm = 0 length = 256 type = "H" element_size = 2 min_value = 0 max_value = 0xFFFF def default(self, x): return x * 0x100 input_volume = Controller((0, 512), 256) mix = Controller((0, 256), 256) output_volume = Controller((0, 512), 256) symmetric = Controller(bool, True) mode = Controller(Mode, Mode.hq) dc_blocker = Controller(bool, True) def __init__(self, **kwargs): values = kwargs.pop("values", None) super(WaveShaper, self).__init__(**kwargs) self.curve = self.Curve() if values is not None: self.curve.values = values def specialized_iff_chunks(self): for chunk in self.curve.chunks(): yield chunk for chunk in super(WaveShaper, self).specialized_iff_chunks(): yield chunk def load_chunk(self, chunk): if chunk.chnm == 0: self.curve.bytes = chunk.chdt
class Compressor(Module): name = mtype = "Compressor" mgroup = "Effect" flags = 0x002051 behaviors = {B.receives_audio, B.sends_audio} class Mode(Enum): peak = 0 rms = 1 peak_zero_latency = 2 volume = Controller((0, 512), 256) threshold = Controller((0, 512), 256) slope_pct = Controller((0, 200), 100) attack_ms = Controller((0, 500), 1) release_ms = Controller((1, 1000), 300) mode = Controller(Mode, Mode.peak) sidechain_input = Controller((0, 32), 0)
class Vibrato(Module): name = mtype = "Vibrato" mgroup = "Effect" flags = 0x000451 behaviors = {B.receives_audio, B.sends_audio} class Channels(Enum): stereo = 0 mono = 1 class FrequencyUnit(Enum): hz_64 = 0 # hz / 64 ms = 1 hz = 2 tick = 3 line = 4 line_2 = 5 # line / 2 line_3 = 6 # line / 3 freq_ranges = { FrequencyUnit.hz_64: WarnOnlyRange(1, 2048), FrequencyUnit.ms: WarnOnlyRange(1, 4000), FrequencyUnit.hz: WarnOnlyRange(1, 16384), FrequencyUnit.tick: WarnOnlyRange(1, 2048), FrequencyUnit.line: WarnOnlyRange(1, 2048), FrequencyUnit.line_2: WarnOnlyRange(1, 2048), FrequencyUnit.line_3: WarnOnlyRange(1, 2048), } volume = Controller((0, 256), 256) amplitude = Controller((0, 256), 16) freq = Controller( DependentRange("frequency_unit", freq_ranges, Range(1, 2048)), 256) channels = Controller(Channels, Channels.stereo) set_phase = Controller((0, 256), 0) # used to reset module frequency_unit = Controller(FrequencyUnit, FrequencyUnit.hz_64) exponential_amplitude = Controller(bool, False)
class PitchShifter(Module): name = mtype = "Pitch shifter" mgroup = "Effect" flags = 0x000051 behaviors = {B.receives_audio, B.sends_audio} class Mode(Enum): hq = 0 hq_mono = 1 lq = 2 lq_mono = 3 volume = Controller((0, 512), 256) pitch = Controller((-600, 600), 0) pitch_scale = Controller((0, 200), 100) feedback = Controller((0, 256), 0) grain_size = Controller((0, 256), 64) mode = Controller(Mode, Mode.hq)
class Pitch2Ctl(Module): name = mtype = "Pitch2Ctl" mgroup = "Misc" flags = 0x020049 behaviors = {B.receives_notes, B.sends_controls} class Mode(Enum): frequency_hz = 0 pitch = 1 class NoteOffAction(Enum): do_nothing = 0 pitch_down = 1 pitch_up = 2 mode = Controller(Mode, Mode.frequency_hz) note_off_action = Controller(NoteOffAction, NoteOffAction.do_nothing) first_note = Controller((0, 256), 0) number_of_semitones = Controller((0, 256), 120) out_min = Controller((0, 32768), 0) out_max = Controller((0, 32768), 32768) out_controller = Controller((0, 32), 0)
class Distortion(Module): name = mtype = "Distortion" mgroup = "Effect" flags = 0x000051 behaviors = {B.receives_audio, B.sends_audio} class Type(Enum): lim = 0 clipping = 0 sat = 1 foldback = 1 foldback2 = 2 foldback3 = 3 overflow = 4 volume = Controller((0, 256), 128) type = Controller(Type, Type.lim) power = Controller((0, 256), 0) bit_depth = Controller((1, 16), 16) freq_hz = Controller((0, 44100), 44100) noise = Controller((0, 256), 0)
class MultiCtl(Module): name = mtype = "MultiCtl" mgroup = "Misc" chnk = 2 flags = 0x020051 behaviors = {B.sends_controls} class Mapping: def __init__(self, value): self.min, self.max, self.controller = value[:3] class MappingArray(ArrayChunk): chnm = 0 length = 16 type = "IIIIIIII" element_size = 4 * 8 def default(self, _): return MultiCtl.Mapping((0, 0x8000, 0)) @property def encoded_values(self): return list( chain.from_iterable((x.min, x.max, x.controller, 0, 0, 0, 0, 0) for x in self.values)) @property def python_type(self): return MultiCtl.Mapping class Curve(ArrayChunk): chnm = 1 length = 257 type = "H" element_size = 2 min_value = 0 max_value = 0x8000 def default(self, x): return x * 0x80 value = Controller((0, 32768), 0) gain = Controller((0, 1024), 256) quantization = Controller((0, 32768), 32768) out_offset = Controller((-16384, 16384), 0) response = Controller((0, 1000), 1000) sample_rate_hz = Controller((1, 32768), 150) def __init__(self, **kwargs): curve = kwargs.pop("curve", None) mappings = kwargs.pop("mappings", []) super(MultiCtl, self).__init__(**kwargs) self.curve = self.Curve() if curve is not None: self.curve.values = curve self.mappings = self.MappingArray() for i, mapping in enumerate(mappings): self.mappings.values[i] = self.Mapping(mapping) def on_value_changed(self, value, down, up): if self.parent is None or not down: return downstream_mods = [] for to_mod in range(256): from_mods = self.parent.module_connections[to_mod] if self.index in from_mods: downstream_mods.append(to_mod) for i, to_mod in enumerate(downstream_mods): mapping = self.mappings.values[i] mod = self.parent.modules[to_mod] ctl = list(mod.controllers.values())[mapping.controller - 1] vt = ctl.value_type if isinstance(vt, Range): vmax = None if isinstance(vt, CompactRange) else vt.max - vt.min smin, smax = mapping.min, mapping.max dmin, dmax = 0, vt.max - vt.min if smin > smax: smin, smax = smax, smin dmin, dmax = dmax, dmin converted = convert_value( self.gain, self.quantization, smin, smax, dmin, dmax, vmax, self.value, self.curve.values, ) final_value = converted + vt.min setattr(mod, ctl.name, final_value) # TODO: apply out_offset # TODO: what should we do if it's not a range? def reflect(self, index=0, propagate=True): """Reflect the value of the controller mapped at the given index; inverse of setting value""" downstream_mods = [] for to_mod in range(256): from_mods = self.parent.module_connections[to_mod] if self.index in from_mods: downstream_mods.append(to_mod) if len(downstream_mods) == index + 1: break else: raise IndexError( "No destination module mapped at index {}".format(index)) mapping = self.mappings.values[index] if mapping.controller == 0: raise IndexError( "No destination controller mapped at index {}".format(index)) reflect_mod = self.parent.modules[downstream_mods[-1]] reflect_ctl_name = list(reflect_mod.controllers)[mapping.controller - 1] reflect_ctl = reflect_mod.controllers[reflect_ctl_name] reflect_value = getattr(reflect_mod, reflect_ctl_name) if hasattr(reflect_value, "value"): reflect_value = reflect_value.value t = reflect_ctl.value_type if isinstance(t, Range): dmin = t.min dmax = t.max elif t is bool: dmin = 0 dmax = 1 elif isinstance(t, type) and issubclass(t, Enum): dmin = 0 dmax = len(t) else: dmin = 0 dmax = 32768 inverted = invert_value( gain=self.gain, smin=mapping.min, smax=mapping.max, dmin=dmin, dmax=dmax, vmax=dmax - dmin if dmax > dmin else dmin - dmax, value=reflect_value, ) if propagate: self.value = inverted else: self.controller_values["value"] = inverted def specialized_iff_chunks(self): yield from self.mappings.chunks() yield from self.curve.chunks() yield from super(MultiCtl, self).specialized_iff_chunks() def load_chunk(self, chunk): if chunk.chnm == 0: self.mappings.bytes = chunk.chdt elif chunk.chnm == 1: self.curve.bytes = chunk.chdt @staticmethod def macro(project, *mod_ctl_pairs, name=None, layer=0, x=0, y=0, initial=None): if len(mod_ctl_pairs) > 16: raise MappingError("MultiCtl supports max of 16 destinations") mappings = [] mods = [] gains = set() for mod, ctl in mod_ctl_pairs: if not isinstance(ctl, Controller): ctl = mod.controllers[ctl] t = ctl.instance_value_type(mod) if isinstance(t, type) and issubclass(t, Enum): mapmin, mapmax = 0, len(t) - 1 gains.add(256 + int(256 / mapmax)) elif t is bool: mapmin, mapmax = 0, 1 gains.add(512) elif t.min == 1: mapmin, mapmax = t.min, t.max gains.add(256 + int(256 / mapmax)) elif isinstance(t, CompactRange): mapmin, mapmax = 0, (t.max - t.min) gains.add(256) else: mapmin, mapmax = 0, 0x8000 gains.add(256) mappings.append((mapmin, mapmax, ctl.number)) mods.append(project.modules[mod.index]) if len(mods) != len(set(mods)): raise MappingError( "Only one MultiCtl mapping per destination module allowed") gain = list(gains).pop() if gains and len(gains) == 1 else 256 bundle = project.new_module(MultiCtl, name=name, layer=layer, x=x, y=y, gain=gain, mappings=mappings) bundle >> mods if initial is not None: bundle.value = initial return bundle
class Fm(Module): name = mtype = "FM" mgroup = "Synth" flags = 0x000049 behaviors = {B.receives_notes, B.sends_audio} class Mode(Enum): hq = 0 hq_mono = 1 lq = 2 lq_mono = 3 c_volume = Controller((0, 256), 128) m_volume = Controller((0, 256), 48) panning = Controller((-128, 128), 0) c_freq_ratio = Controller((0, 16), 1) m_freq_ratio = Controller((0, 16), 1) m_feedback = Controller((0, 256), 0) c_attack = Controller((0, 512), 32) c_decay = Controller((0, 512), 32) c_sustain = Controller((0, 256), 128) c_release = Controller((0, 512), 64) m_attack = Controller((0, 512), 32) m_decay = Controller((0, 512), 32) m_sustain = Controller((0, 256), 128) m_release = Controller((0, 512), 64) m_scaling_per_key = Controller((0, 4), 0) polyphony_ch = Controller((1, 16), 4) mode = Controller(Mode, Mode.hq)
class SpectraVoice(Module): name = mtype = "SpectraVoice" mgroup = "Synth" chnk = 4 flags = 0x000049 behaviors = {B.receives_notes, B.sends_audio} class Mode(Enum): hq = 0 hq_mono = 1 lq = 2 lq_mono = 3 hq_spline = 4 class HarmonicType(Enum): hsin = 0 rect = 1 org1 = 2 org2 = 3 org3 = 4 org4 = 5 sin = 6 random = 7 triangle1 = 8 triangle2 = 9 overtones1 = 10 overtones2 = 11 overtones3 = 12 overtones4 = 13 class HarmonicValueArray(ArrayChunk): length = 16 class HarmonicFreqArray(HarmonicValueArray): chnm = 0 type = "H" default = [1098] + [0] * 15 element_size = 2 min_value = 0 max_value = 0x8000 class HarmonicVolumeArray(HarmonicValueArray): chnm = 1 type = "B" default = [255] + [0] * 15 element_size = 1 min_value = 0 max_value = 0xFF class HarmonicWidthArray(HarmonicValueArray): chnm = 2 type = "B" default = [3] + [0] * 15 element_size = 1 min_value = 0 max_value = 0xFF class HarmonicTypeArray(HarmonicValueArray): chnm = 3 type = "B" element_size = 1 @property def default(self): return [SpectraVoice.HarmonicType.hsin] * 16 @property def encoded_values(self): return [x.value for x in self.values] @property def python_type(self): return SpectraVoice.HarmonicType volume = Controller((0, 256), 128) panning = Controller((-128, 128), 0) attack = Controller((0, 512), 10) release = Controller((0, 512), 512) polyphony_ch = Controller((1, 32), 8) mode = Controller(Mode, Mode.hq_spline) sustain = Controller(bool, True) spectrum_resolution = Controller((0, 5), 1) # Note: These are controllers used to program the module while it's loaded. # When scripting, use the objects in `self.harmonics` instead. harmonic = Controller((0, 15), 0) h_freq_hz = Controller((0, 22050), 1098) h_volume = Controller((0, 255), 255) h_width = Controller((0, 255), 3) h_type = Controller(HarmonicType, HarmonicType.hsin) def __init__(self, **kwargs): harmonics = kwargs.pop("harmonics", []) super(SpectraVoice, self).__init__(**kwargs) self.harmonic_freqs = self.HarmonicFreqArray() self.harmonic_volumes = self.HarmonicVolumeArray() self.harmonic_widths = self.HarmonicWidthArray() self.harmonic_types = self.HarmonicTypeArray() # Initialize harmonics from 'harmonics' kwarg. self.harmonics = [Harmonic(self, index) for index in range(16)] for i, (freq, volume, width, type) in enumerate(harmonics): h = self.harmonics[i] h.freq_hz, h.volume, h.width, h.type = freq, volume, width, type h = self.harmonics[self.harmonic] self.h_freq_hz = h.freq_hz self.h_volume = h.volume self.h_width = h.width self.h_type = h.type def specialized_iff_chunks(self): for chunk in self.harmonic_freqs.chunks(): yield chunk for chunk in self.harmonic_volumes.chunks(): yield chunk for chunk in self.harmonic_widths.chunks(): yield chunk for chunk in self.harmonic_types.chunks(): yield chunk for chunk in super(SpectraVoice, self).specialized_iff_chunks(): yield chunk def load_chunk(self, chunk): if chunk.chnm == 0: self.harmonic_freqs.bytes = chunk.chdt for h, freq in zip(self.harmonics, self.harmonic_freqs.values): h.freq_hz = freq elif chunk.chnm == 1: self.harmonic_volumes.bytes = chunk.chdt for h, volume in zip(self.harmonics, self.harmonic_volumes.values): h.volume = volume elif chunk.chnm == 2: self.harmonic_widths.bytes = chunk.chdt for h, width in zip(self.harmonics, self.harmonic_widths.values): h.width = width elif chunk.chnm == 3: self.harmonic_types.bytes = chunk.chdt for h, type in zip(self.harmonics, self.harmonic_types.values): h.type = type
class Filter(Module): name = mtype = "Filter" mgroup = "Effect" flags = 0x000451 behaviors = {B.receives_audio, B.sends_audio} class Type(Enum): lp = 0 hp = 1 bp = 2 notch = 3 class Mode(Enum): hq = 0 hq_mono = 1 lq = 2 lq_mono = 3 class RollOff(Enum): db_12 = 0 db_24 = 1 db_36 = 2 db_48 = 3 class LfoFreqUnit(Enum): hz_0_02 = 0 # hz * 0.02 ms = 1 hz = 2 tick = 3 line = 4 line_2 = 5 # line / 2 line_3 = 6 # line / 3 class LfoWaveform(Enum): sin = 0 saw = 1 saw2 = 2 square = 3 random = 4 volume = Controller((0, 256), 256) freq = Controller((0, 14000), 14000) resonance = Controller((0, 1530), 0) type = Controller(Type, Type.lp) response = Controller((0, 256), 8) mode = Controller(Mode, Mode.hq) impulse = Controller((0, 14000), 0) mix = Controller((0, 256), 256) lfo_freq = Controller((0, 1024), 8) lfo_amp = Controller((0, 256), 0) set_lfo_phase = Controller((0, 256), 0) # used to reset module exponential_freq = Controller(bool, False) roll_off = Controller(RollOff, RollOff.db_12) lfo_freq_unit = Controller(LfoFreqUnit, LfoFreqUnit.hz_0_02) lfo_waveform = Controller(LfoWaveform, LfoWaveform.sin)