diff --git a/docs/badge/usage.rst b/docs/badge/usage.rst index 7eb4192ade5bff550fd58d296341d59546e1b6e9..e982b59151b0f114a970450ae85cdaf2f880a24c 100644 --- a/docs/badge/usage.rst +++ b/docs/badge/usage.rst @@ -88,7 +88,7 @@ We ship some noise-making apps by default: shoegaze ^^^^^^^^ -Electric guitar simulator with fuzz and reverb. Tilt for wiggle stick. App button middle switches between render modes: Lo Fi has lower framerate, default has slower input response time. App button left turns delay on and off. Top petals play notes in chord, bottom petals change chord. +Electric guitar simulator with fuzz and reverb. Tilt for wiggle stick. Top petals play notes in chord, bottom petals change chord. App button left turns delay on and off, app button right checks for chords in the savefile of chord organ and toggles between them if found. Otamatone ^^^^^^^^^ @@ -112,8 +112,8 @@ be reset when passing it, if they're "open" they let the sequencer pass through Your beat is saved in flash at ``/sys/gay_drums.json`` when exiting gay drums! Make sure to wait until the menu screen appears before turning the power off though :D! -harmonic demo -^^^^^^^^^^^^^ +chord organ +^^^^^^^^^^^ A chord organ! The top petals always play the 5 notes of the selected chord. @@ -146,7 +146,7 @@ Samples are saved in flash at ``/sys/samples/tiny_sample_*.wav``. .. _usage_apps: -Applications +Apps ------------ Audio passthrough diff --git a/python_payload/apps/demo_harmonic/__init__.py b/python_payload/apps/demo_harmonic/__init__.py index 160e38bec0f0a82723eea799ccc31b43839c4a83..a995239d357617366725f17709ec6316f078e6be 100644 --- a/python_payload/apps/demo_harmonic/__init__.py +++ b/python_payload/apps/demo_harmonic/__init__.py @@ -305,6 +305,7 @@ class HarmonicApp(Application): chord["nine"] = self.chords[i].nine chord["root"] = self.chords[i].root chord["voicing"] = self.chords[i].voicing + chord["tones_readonly"] = self.chords[i].notes if not file_is_different: user_chord = user_settings["chords"][i] if self._file_settings is None: diff --git a/python_payload/apps/demo_harmonic/flow3r.toml b/python_payload/apps/demo_harmonic/flow3r.toml index 4b20e89b97632a0b1022680de9f4290ece617242..863bc6deddf848ab05d450fcd5e8b6a9126dcba8 100644 --- a/python_payload/apps/demo_harmonic/flow3r.toml +++ b/python_payload/apps/demo_harmonic/flow3r.toml @@ -1,5 +1,5 @@ [app] -name = "harmonic demo" +name = "chord organ" menu = "Music" [entry] diff --git a/python_payload/apps/shoegaze/__init__.py b/python_payload/apps/shoegaze/__init__.py index aac0dfa0ca2d4c429c04185a5a2b862d7647a93d..fb2064d9a6a91170651241bfb32ea76a88a78dfa 100644 --- a/python_payload/apps/shoegaze/__init__.py +++ b/python_payload/apps/shoegaze/__init__.py @@ -5,6 +5,7 @@ from st3m.input import InputState from ctx import Context import json +import errno import math import captouch import bl00mbox @@ -33,7 +34,7 @@ class ShoegazeApp(Application): self.blm: Optional[bl00mbox.Channel] = None self.chord_index = 0 self.chord: List[int] = [] - self._set_chord(3) + self._organ_chords = [None] * 5 self._tilt_bias = 0.0 self._detune_prev = 0.0 self._git_string_tuning = [0] * 4 @@ -43,8 +44,8 @@ class ShoegazeApp(Application): self._rand_limit = 16 self._rand_rot = 0.0 self.delay_on = True - self.fuzz_on = True - self._lofi = False + self.organ_on = False + self._set_chord(3) def _build_synth(self) -> None: if self.blm is None: @@ -95,51 +96,64 @@ class ShoegazeApp(Application): self.main_mixer.signals.gain = 2000 self.main_lp.signals.reso = 2000 + + self.bass_lp.signals.gain = 32767 + self.git_lp.signals.gain = 32767 + self.main_lp.signals.freq = 2500 + self.main_lp.signals.gain = 2000 + self.git_mixer.signals.gain = 4000 + self.main_lp.signals.input = self.main_fuzz.signals.output self._update_connections() def _update_connections(self) -> None: if self.blm is None: return - if self.fuzz_on: - self.bass_lp.signals.gain = 32767 - self.git_lp.signals.gain = 32767 - self.main_lp.signals.freq = 2500 - self.main_lp.signals.gain = 2000 - self.git_mixer.signals.gain = 4000 - self.main_lp.signals.input = self.main_fuzz.signals.output - if self.delay_on: - self.git_delay.signals.input = self.git_fuzz.signals.output - self.main_mixer.signals.input0 = self.git_delay.signals.output - else: - self.main_mixer.signals.input0 = self.git_fuzz.signals.output + if self.delay_on: + self.git_delay.signals.input = self.git_fuzz.signals.output + self.main_mixer.signals.input0 = self.git_delay.signals.output else: - self.bass_lp.signals.gain = 2000 - self.git_lp.signals.gain = 2000 - self.main_lp.signals.freq = 6000 - self.main_lp.signals.gain = 4000 - self.git_mixer.signals.gain = 500 - self.main_lp.signals.input = self.main_mixer.signals.output - if self.delay_on: - self.git_delay.signals.input = self.git_lp.signals.output - self.main_mixer.signals.input0 = self.git_delay.signals.output - else: - self.main_mixer.signals.input0 = self.git_lp.signals.output - - def fuzz_toggle(self) -> None: - self.fuzz_on = not self.fuzz_on - self._update_connections() + self.main_mixer.signals.input0 = self.git_fuzz.signals.output + + def _try_load_settings(self, path): + try: + with open(path, "r") as f: + return json.load(f) + except OSError as e: + if e.errno != errno.ENOENT: + raise # ignore file not found + + def _load_settings(self): + settings_path = "/flash/harmonic_demo.json" + + settings = self._try_load_settings(settings_path) + if settings is not None: + for i, chord in enumerate(settings["chords"]): + if i > 4: + break + self._organ_chords[i] = chord["tones_readonly"] + + def organ_toggle(self) -> None: + self.organ_on = not self.organ_on + if self.organ_on and self._organ_chords[0] is None: + self._load_settings() + if self._organ_chords[0] is None: + self.organ_on = False + self._set_chord(self.chord_index, force_update=True) def delay_toggle(self) -> None: self.delay_on = not self.delay_on self._update_connections() - def _set_chord(self, i: int) -> None: + def _set_chord(self, i: int, force_update=False) -> None: hue = int(54 * (i + 0.5)) % 360 - if i != self.chord_index: + if i != self.chord_index or force_update: self.chord_index = i leds.set_all_hsv(hue, 1, 0.2) leds.update() - self.chord = chords[i] + if self.organ_on and self._organ_chords[i] is not None: + self.chord = self._organ_chords[i] + else: + self.chord = chords[i] def draw(self, ctx: Context) -> None: ctx.text_align = ctx.CENTER @@ -159,38 +173,46 @@ class ShoegazeApp(Application): ctx.text("bass") rot = self._spinny # + self._detune_prev - ctx.rgb(0, 0.5, 0.5) ctx.rotate(rot + self._rand_rot) - ctx.move_to(0, -10) - ctx.text("shoegazeshoegazeshoe") - ctx.move_to(0, 10) - ctx.text("gazeshoegazeshoegaze") + if self.organ_on: + ctx.rgb(0.5, 0, 0.6) + ctx.rotate(0.69) + ctx.move_to(0, -10) + ctx.text("chordorganchordorganchord") + ctx.move_to(0, 10) + ctx.text("organchordorganchordorgan") + ctx.rotate(4.20 + 0.69) + else: + ctx.rgb(0, 0.5, 0.5) + ctx.move_to(0, -10) + ctx.text("shoegazeshoegazeshoe") + ctx.move_to(0, 10) + ctx.text("gazeshoegazeshoegaze") ctx.rotate(-2.2 * (rot + self._rand_rot) - 0.5) + if self.organ_on: + rgb = (0.7, 0.7, 0) + else: + rgb = (0, 0.8, 0) + ctx.move_to(40, 40) + if self.delay_on: - ctx.rgb(0, 0.8, 0) + ctx.rgb(*rgb) ctx.text("delay ON!") else: - ctx.rgb(0, 0.6, 0) + ctx.rgb(*tuple([x * 0.75 for x in rgb])) ctx.text("delay off") + ctx.rgb(*rgb) ctx.rotate(0.2 + self._rand_rot) - ctx.move_to(50, -50) detune = "detune: " + str(int(self._detune_prev * 100)) - ctx.rgb(0, 0.8, 0) + ctx.rgb(*rgb) ctx.text(detune) - ctx.rotate(-2.5 * (rot + 4 * self._rand_rot)) - ctx.move_to(-50, 50) - if self.fuzz_on: - ctx.rgb(0, 0.8, 0) - ctx.text("fuzz ON!") - else: - ctx.rgb(0, 0.6, 0) - ctx.text("fuzz off") + ctx.text("fuzz ON!") def think(self, ins: InputState, delta_ms: int) -> None: super().think(ins, delta_ms) @@ -208,10 +230,7 @@ class ShoegazeApp(Application): if buttons.app.right.pressed: self.delay_toggle() if buttons.app.left.pressed: - pass - # self.fuzz_toggle() - if buttons.app.middle.pressed: - self.lofi = not self.lofi + self.organ_toggle() for i in range(1, 10, 2): if petals[i].whole.pressed: @@ -235,21 +254,8 @@ class ShoegazeApp(Application): self.bass_string.decay = 1000 self.bass_string.signals.trigger.start() - @property - def lofi(self): - return self._lofi - - @lofi.setter - def lofi(self, val): - self._lofi = bool(val) - if self._lofi: - sys_display.set_mode(2313) - else: - sys_display.set_mode(912) - def on_enter(self, vm: Optional[ViewManager]) -> None: super().on_enter(vm) - self.lofi = self.lofi if self.blm is None: self._build_synth() self.blm.foreground = True