Add local emulators for v1/v2/v3 hardware#142
Conversation
Account for blit elapsed time when computing SPI sleep deficit, and skip sleeps below macOS's ~1 ms timer granularity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
# Conflicts: # pyproject.toml # uv.lock
# Conflicts: # uilib/lcd_ili9341.py
# Conflicts: # modalapi/modhandler.py # uv.lock
Extracted from release/patch ef3d349 ("Patch to make everything work together"). The Modhandler/Mod base classes spin up an AsyncWebSocketBridge pointed at :80 (assumed for on-device MOD-UI); on the emulator MOD Desktop listens at :18181. Replace the bridge after super().__init__() so emulator runs talk to the right port. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
# Conflicts: # modalapi/modhandler.py
The new WifiManager (now on pistomp-v3 via PR TreeFallSound#140) is command-queue-based: callers invoke the queue with Cmd objects, poll() drains via on_status_change callback, saved profiles surface through get_cached_saved(), and connect_scanned/saved gained extra args (security, wait, reconnect). StubWifiManager still used the pre-queue interface and crashed the emulator on first wifi tick. Reuse the real CommandQueue against the stub (its run() methods match the new shape), add the missing methods, and drop the orphaned configure_wifi path which no caller invokes anymore. Relocated from release/patch bridge 54c590a now that feat/multi-wifi has landed on base.
Settings hardcoded /home/pistomp/data/config — on the emulator, set_setting() either silently lost the write or scribbled at a non-emulator path, so LCD speed and other persistent choices never survived restart. Make Settings accept a data_dir parameter (default unchanged) and pass the emulator's data dir from EmulatorModhandler, alongside banks.json and last.json. Also mkdir -p the emu data dir so first-run writes succeed. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
# Conflicts: # pyproject.toml # uv.lock
rreichenbach
left a comment
There was a problem hiding this comment.
I finally got this to work.
On a Mac, I had to do this to create the correct env:
uv venv --python 3.13
uv sync --extra emulator
Could be good to document that in README.md
Then I had to make these changes. Probably due to not being updated to include the recent Footswitch change from enabled to toggled.
--- a/emulator/controls.py
+++ b/emulator/controls.py
@@ -101,11 +101,11 @@ class MockFootswitch(footswitch.Footswitch):
pass
def press(self):
-
self.enabled = not self.enabled
-
self.toggled = not self.toggled if self.midiout and self.midi_CC is not None and _rtmidi_available: self.midiout.send_message( [CONTROL_CHANGE | (self.midi_channel & 0x0F),
-
self.midi_CC, 127 if self.enabled else 0])
-
self.midi_CC, 127 if self.toggled else 0]) self.refresh_callback(footswitch=self)
diff --git a/emulator/window.py b/emulator/window.py
index 3db8e2c..788bb3a 100644
--- a/emulator/window.py
+++ b/emulator/window.py
@@ -252,7 +252,7 @@ class EmulatorWindow:
# Footswitch state colours
for btn, idx in self._fs_btns:
fs = self.hw.footswitches[idx]
-
color = FS_ON if fs.enabled else FS_OFF
-
color = FS_ON if fs.toggled else FS_OFF
|
@rreichenbach nice catch -- I suppose it would be a good idea to add some minimal tests for the emulator. Also, any particular reason for 3.13? If it doesn't work with 3.14 I can take a look. |
|
@rreichenbach Ready for re-review. I added some tests, fixed the bug you found, and got rid of the |
pi-stomp Emulator
Run locally against a MOD Desktop instance.
Requires MOD Desktop running at
http://127.0.0.1:18181/as well aslilv. See the updated README.md for more details.Hardware versions
emulator_v3EmulatorModhandler+EmulatorHardwareV3emulator_v2EmulatorModhandler+EmulatorHardwareV2emulator_v1EmulatorMod+EmulatorHardwareV1Key classes
EmulatorHardwareBaseemulator/hardware_base.pyEmulatorHardwareV1/V2/V3emulator/hardware_v*.pyEmulatorModhandleremulator/modhandler.pyModhandlersubclass — ownsVirtualAudiocard,StubWifiManager,StubRelay; drives render loopEmulatorModemulator/mod.pyModsubclassEmulatorWindowemulator/window.pyLcdPygameemulator/lcd_pygame.pyLcdBaseimplementation — receives PIL frames, blits to pygameSurfaceVirtualAudiocardemulator/stubs.pyStubWifiManageremulator/stubs.pyRender loop
poll_controlsruns every ~10 ms:EmulatorWindow.process_events()— drains pygame events, fires hardware callbackssuper().poll_controls()— polls encoders/footswitcheslcd.poll_updates()— flushes dirty PIL→pygame blitEmulatorWindow.render()— scales LCD surface and blits to screenNotes
pygame._freetypeC extension directly we can get around it*
lilvis installed outside the venv, so there's arun_emulator.shshim that uses pkg-config / Homebrew to find the prefix and exportsDYLD_LIBRARY_PATH/PYTHONPATHfor itemulator/__init__.pypatchesImageFont.truetypeto resolve bare names fromfonts/time.sleepbased on the number of pixels blittedImages