diff --git a/__init__.py b/__init__.py index c6b6f61d7b1f79dd4ca55665284496e0a75db345..c91e43772e26483aa0c05db1e69b29ef6f2c6f9b 100644 --- a/__init__.py +++ b/__init__.py @@ -10,27 +10,92 @@ import random from st3m.ui import colours -# bad hack to figure out which bl00mbox version is running -fm_broken = False -from st3m import processors -if "ProcessorMidldeware" in dir(processors) and \ - len(processors.ProcessorMidldeware.PROCESSORS): - fm_broken = True - - -# stolen from future flow3r 1.4, import from utils instead when released -def mkdir_recursive(path): - if path[0] != "/": - raise ValueError("path must be absolute") - path.rstrip("/") - splt = path.split("/") - for x in range(len(splt)): - subpath = "/".join(splt[: (x + 1)]) - if not os.path.exists(subpath): - os.mkdir(subpath) - elif not os.path.isdir(subpath): - raise FileExistsError +from st3m.utils import mkdir_recursive +class Flute(bl00mbox.Patch): + def __init__(self, chan): + super().__init__(chan) + mp = self.new(bl00mbox.plugins.multipitch, 3) + mp.signals.max_pitch.value = 32767 + mp.signals.min_pitch.value = -32767 + + # noise channel + noise_src = self.new(bl00mbox.plugins.noise) + noise_env = self.new(bl00mbox.plugins.env_adsr) + noise_comb = self.new(bl00mbox.plugins.flanger) + noise_env.signals.input << noise_src.signals.output + noise_env.signals.gain.dB = -34 + noise_env.signals.attack.value = 70 + noise_env.signals.release.value = 100 + noise_env.signals.sustain.value = 20000 + noise_comb.signals.input << noise_env.signals.output + noise_comb.signals.resonance.mult = 3 + noise_comb.signals.mix.value = -16384 + noise_env.signals.trigger << mp.signals.trigger_thru + noise_comb.signals.manual << mp.signals.output[0] + mp.signals.shift[0].tone = 0 + + # osc channel + osc = self.new(bl00mbox.plugins.osc) + osc_noise = self.new(bl00mbox.plugins.noise) + osc_noise_lp = self.new(bl00mbox.plugins.filter) + osc_env = self.new(bl00mbox.plugins.env_adsr) + osc_dist = self.new(bl00mbox.plugins.mixer, 2) + osc_noise_lp.signals.input << osc_noise.signals.output + osc_dist.signals.input[1] << osc_noise_lp.signals.output + osc_dist.signals.input[0] << noise_comb.signals.output + osc_dist.signals.gain.mult = 0.08 + osc.signals.fm << osc_dist.signals.output + osc_noise_lp.signals.cutoff << mp.signals.output[2] + mp.signals.shift[2].tone = -24 + mp.signals.min_pitch = -32767 + osc_noise_lp.signals.reso.mult = 1.5 + osc_noise_lp.signals.gain.dB = -12 + osc_noise_lp.signals.mode.switch.LOWPASS = True + osc.signals.pitch << mp.signals.thru + osc.signals.waveform.switch.TRI = True + osc_env = self.new(bl00mbox.plugins.env_adsr) + osc_env.signals.trigger << mp.signals.trigger_thru + osc_env.signals.attack.value = 150 + osc_env.signals.sustain.value = 32767 + osc_env.signals.release.value = 200 + osc_env.signals.decay.value = 0 + osc_env.signals.input << osc.signals.output + + # main mixer + main_mixer = self.new(bl00mbox.plugins.mixer, 2) + main_lp = self.new(bl00mbox.plugins.filter) + main_mixer.signals.input[0] << osc_env.signals.output + main_mixer.signals.input[1] << noise_comb.signals.output + main_mixer.signals.input_gain[1].dB = 0 + main_mixer.signals.block_dc.switch.ON = True + main_lp.signals.mix.value = 32767 + main_lp.signals.cutoff << mp.signals.output[1] + mp.signals.shift[1].tone = 48 + main_lp.signals.input << main_mixer.signals.output + main_lp.signals.gain.dB = 6 + main_lp.signals.mode.switch.LOWPASS = True + + # eq + eq_bp = self.new(bl00mbox.plugins.filter) + eq_lp = self.new(bl00mbox.plugins.filter) + eq_bp.signals.input << main_lp.signals.output + eq_lp.signals.input << eq_bp.signals.output + eq_bp.signals.cutoff.value = 800 + # bug: introduces DC, bypassing for now + eq_bp.signals.mix.value = 0 + eq_bp.signals.mode.switch.BANDPASS = True + eq_lp.signals.cutoff.value = 4000 + eq_lp.signals.mix.value = 12000 + eq_lp.signals.mode.switch.LOWPASS = True + + self.signals.output = eq_lp.signals.output + self.signals.trigger = mp.signals.trigger_in + self.signals.pitch = mp.signals.input + self.signals.mod_in = mp.signals.mod_in + self.signals.gain = main_mixer.signals.gain + self.signals.fm_noise = osc_dist.signals.input_gain[0] + self.signals.cutoff = mp.signals.shift[1] class BinaryFluteApp(Application): def __init__(self, app_ctx) -> None: @@ -111,91 +176,15 @@ class BinaryFluteApp(Application): settings["code"] = "gray" if self.gray else "bin" utils.save_file_if_changed(self.dirpath + self.settings_file, json.dumps(settings)) - def _build_synth(self): - self.blm = bl00mbox.Channel("flute") - self.blm.volume = 32767 - self.multipitch = self.blm.new(bl00mbox.plugins.multipitch, 10) - self.multipitch.signals.max_pitch = 32767 - self.multipitch.signals.min_pitch = -32767 - - # noise channel - self.noise_src = self.blm.new(bl00mbox.plugins.noise) - self.noise_env = self.blm.new(bl00mbox.plugins.env_adsr) - self.noise_comb = self.blm.new(bl00mbox.plugins.flanger) - self.noise_env.signals.input = self.noise_src.signals.output - self.noise_env.signals.gain.dB = -34 - self.noise_env.signals.attack = 70 - self.noise_env.signals.release = 100 - self.noise_env.signals.sustain = 20000 - self.noise_comb.signals.input = self.noise_env.signals.output - self.noise_comb.signals.resonance = 24000 - self.noise_comb.signals.mix = -16384 - self.noise_env.signals.trigger = self.multipitch.signals.trigger_thru - self.noise_comb.signals.manual = self.multipitch.signals.output[0] - self.multipitch.signals.shift[0].tone = 0 - - # osc channel - self.osc = self.blm.new(bl00mbox.plugins.osc) - self.osc_noise = self.blm.new(bl00mbox.plugins.noise) - self.osc_noise_lp = self.blm.new(bl00mbox.plugins.filter) - self.osc_env = self.blm.new(bl00mbox.plugins.env_adsr) - self.osc_dist = self.blm.new(bl00mbox.plugins.mixer, 2) - self.osc_noise_lp.signals.input = self.osc_noise.signals.output - self.osc_dist.signals.input[1] = self.osc_noise_lp.signals.output - self.osc_dist.signals.input[0] = self.noise_comb.signals.output - self.osc_dist.signals.gain.mult = 0.08 - if fm_broken: - self.osc_dist.signals.gain.mult = 0 - print("skipping fm noise, please update to dev firmware for nicer sound") - self.osc.signals.fm = self.osc_dist.signals.output - self.osc_noise_lp.signals.cutoff = self.multipitch.signals.output[2] - self.multipitch.signals.shift[2].tone = -24 - self.multipitch.signals.min_pitch = -32767 - self.osc_noise_lp.signals.reso = 12000 - self.osc_noise_lp.signals.gain.dB = -12 - self.osc_noise_lp.signals.mode.switch.LOWPASS = True - self.osc.signals.pitch = self.multipitch.signals.thru - self.osc.signals.waveform.switch.TRI = True - self.osc_env = self.blm.new(bl00mbox.plugins.env_adsr) - self.osc_env.signals.trigger = self.multipitch.signals.trigger_thru - self.osc_env.signals.attack = 150 - self.osc_env.signals.sustain = 32767 - self.osc_env.signals.release = 200 - self.osc_env.signals.decay = 0 - self.osc_env.signals.input = self.osc.signals.output - - # main mixer - self.main_mixer = self.blm.new(bl00mbox.plugins.mixer, 2) - self.main_lp = self.blm.new(bl00mbox.plugins.filter) - self.main_mixer.signals.input[0] = self.osc_env.signals.output - self.main_mixer.signals.input[1] = self.noise_comb.signals.output - self.main_mixer.signals.input_gain[1].dB = 0 - self.main_mixer.signals.block_dc.switch.ON = True - self.main_lp.signals.mix = 32767 - self.main_lp.signals.cutoff = self.multipitch.signals.output[1] - self.multipitch.signals.shift[1].tone = 48 - self.main_lp.signals.input = self.main_mixer.signals.output - self.main_lp.signals.gain = 16000 - self.main_lp.signals.mode.switch.LOWPASS = True - - # eq - self.eq_bp = self.blm.new(bl00mbox.plugins.filter) - self.eq_lp = self.blm.new(bl00mbox.plugins.filter) - self.eq_bp.signals.input = self.main_lp.signals.output - self.eq_lp.signals.input = self.eq_bp.signals.output - self.eq_bp.signals.cutoff = 800 - # bug: introduces DC, bypassing for now - self.eq_bp.signals.mix = 0 - self.eq_bp.signals.mode.switch.BANDPASS = True - self.eq_lp.signals.cutoff = 4000 - self.eq_lp.signals.mix = 12000 - self.eq_lp.signals.mode.switch.LOWPASS = True - - self.blm.mixer = self.eq_lp.signals.output + def build_synth(self): + self.blm = bl00mbox.Channel("binary flute") + self.flute = self.blm.new(Flute) + self.blm.gain_dB = 0 + self.blm.signals.line_out << self.flute.signals.output def on_enter(self, vm): super().on_enter(vm) - self._build_synth() + self.build_synth() self.enter_done = False self.full_redraw = True self.sound_active = False @@ -222,10 +211,7 @@ class BinaryFluteApp(Application): def on_exit(self): super().on_exit() - if self.blm is not None: - self.blm.clear() - self.blm.free = True - self.blm = None + self.blm.delete() def draw(self, ctx): if not self.enter_done: @@ -383,10 +369,10 @@ class BinaryFluteApp(Application): if self.sound_active != sound_active: if sound_active: - self.multipitch.signals.trigger_in.start() + self.flute.signals.trigger.start() self.play_note(self.valves, glide = False) else: - self.multipitch.signals.trigger_in.stop() + self.flute.signals.trigger.stop() self.sound_active = sound_active new_captouch_data = False @@ -408,7 +394,7 @@ class BinaryFluteApp(Application): if valves == self.valves_candidate: self.valves = valves if self.sound_active: - self.multipitch.signals.trigger_in.start() + self.flute.signals.trigger.start() self.play_note(self.valves) else: self.valves_candidate = valves @@ -422,21 +408,21 @@ class BinaryFluteApp(Application): vol = 0.5 - vol self.vol += (vol - self.vol) * (1-decay_slow) self.vol = max(0, min(1, self.vol)) - self.main_mixer.signals.gain.dB = -32 + 12 * self.vol - self.multipitch.signals.shift[1].tone = 12 + 12 * self.vol - self.osc_dist.signals.input_gain[0].mult = 1 + 2 * max(self.vol-0.5, 0) + self.flute.signals.gain.dB = -32 + 12 * self.vol + self.flute.signals.cutoff.tone = 12 + 12 * self.vol + self.flute.signals.fm_noise.mult = 1 + 2 * max(self.vol-0.5, 0) else: self.vol = 0 note_delta = self.target_note - self.current_note self.current_note += note_delta * (1 - (0.9**delta_ms)) - self.multipitch.signals.input.tone = self.current_note + self.flute.signals.pitch.tone = self.current_note pitch_mod = ins.imu.acc[2] * 60 if self.pitch_mod_bias is None: self.pitch_mod_bias = pitch_mod self.pitch_mod_bias += (pitch_mod - self.pitch_mod_bias) * (1 - decay_slow) - self.multipitch.signals.mod_in = pitch_mod - self.pitch_mod_bias + self.flute.signals.mod_in = pitch_mod - self.pitch_mod_bias def play_note(self, valves, glide = True): def gray_to_binary(gy): diff --git a/flow3r.toml b/flow3r.toml index b98fc87a8abc9e3dd9427e350b3bb2a7ee62ae67..e8dd244c62eab1c5bb058c6f29cccadae8ee3c8f 100644 --- a/flow3r.toml +++ b/flow3r.toml @@ -11,15 +11,8 @@ author = "moon2" license = "CC0-1.0" description = "flute emulator" url = "https://git.flow3r.garden/moon2/binary-flute" -version = 8 +version = 9 [style] background = "rgb(0.5, 0.5, 1.0)" - -[[style.floaties]] -symbol = "flow3r" -color = "rgba(1.0, 1.0, 1.0, 0.5)" - -[[style.floaties]] -symbol = "cat" color = "rgba(1.0, 1.0, 1.0, 0.5)"