github leonardoMirabal/frodhes v0.9.7
Frodhes 0.9.7

2 hours ago

Frodhes 0.9.7: a physically modelled electric piano, vibraphone and marimba for the M-VAVE FM-1. Every note is computed live from the mechanics of the instrument; there are no samples.

Download: frodhes.fwsc below (the same file as frodhes-0.9.7.fwsc; the newest is always at https://github.com/leonardoMirabal/frodhes/releases/latest/download/frodhes.fwsc). Try it first in your browser: https://leonardomirabal.github.io/frodhes/

Install

pip install mido python-rtmidi
python3 tools/fm1_install.py frodhes.fwsc

If anything goes wrong, switch the FM-1 off, hold OCT− and switch it on to get the USB rescue, then install again.

New in 0.9.7

  • Install from the browser: the emulator page installs Frodhes on the FM-1 (Chrome or Edge, MIDI with SysEx): Install on your FM-1,
    Find my FM-1, then Install. It runs the same update as tools/fm1_install.py, checks the package and its
    sha256 first, and checks the FM-1's identity after the restart. An install that was cut off gets Resume the
    install
    . Safari and Firefox get the terminal installer's command instead.

Changes

  • The headphone jack keeps the full resolution after MASTER: the volume was applied and then cut to 16 bits, which
    left a tiny rounding error with a small offset (about -95 dBFS, under the FM-1's own hiss). USB audio is
    unchanged.
  • The USB name carries the model (GitHub #21): the FM-1 is Frodhes FM-1 on the computer (it was "Frodhes"), an
    SMK-37 Frodhes SMK-37 Pro (Elite, MKE-P37 likewise). It is the name of the MIDI port, the audio input and the
    USB device. The community firmware flasher now finds the unit for its DOWNLOAD MODE. Each model has a new USB
    product id, so the computer sets the unit up as a new device by itself. A Mac asks "Allow accessory to connect?" once
    (allow it); the DAW then sees a new MIDI device, "Frodhes FM-1", and it has to be selected again (switch its port on
    once where the old one was). The update mode keeps its name, "Felucca Update".

Fixed

  • Recording over USB at 48 kHz: a loud, bright passage no longer reaches full scale. The 44.1 to 48 kHz resampler
    could overshoot a passage the limiter kept at its ceiling and clip one sample; above three quarters of full scale
    it now softens the overshoot instead. 44.1 kHz and the headphone jack are unchanged.
  • SMK-37 (GitHub #17): the CHORD button opens the CHORD page (press again: chord mode on / off) and PARA opens the
    PHASER page (again: on / off). They were the other way round. Their lights and the screen's hints follow.
  • A held note dying away no longer leaves a faint, fixed distortion under it (GitHub #16): the engines' mix is
    dithered where it is cut to 16 bits, so its last few bits fall into a steady noise floor (about -96 dBFS, under
    the headphone jack's own hiss) instead of odd harmonics that stay put while the note fades. Silence is still
    exact digital zero. Every engine (Rhodes, VIBES, MARIMBA).
  • F11 RESO SWEEP is as loud as the other Rhodes presets: it was about 1.7 LU under F01 (over the 1.5 LU the factory
    presets keep). Its LEVEL goes up by 1.5 dB; nothing else in it changes.
  • Recording over USB: when the FM-1's USB controller did not answer one status read in time, the audio input could
    drop one packet of sound (about 1 ms) with no counter showing it. The packet now waits for the next poll instead,
    and the event is counted (CC 116 item 21). Found by reading the code, not heard in testing.

Tools

  • tools/fm1_install.py: "FM-1 not found" now suggests naming the port (--port NAME). An FM-1 running Frodhes
    (its port is called "Frodhes" since 0.9.5, now "Frodhes FM-1") is found without --port; the installer of 0.9.4
    and before does not know those names, so update with this one.
  • The device tools send on MIDI channel 8 by default (FRODHES_MIDI_CH=1..16 to change it), so they work with the
    FM-1's MIDI CHANNEL on 8 or on ALL.
  • tools/hil_crackle.py (the USB repeat check of the monkey and the crackle test): 4 or 5 equal frames where the
    music itself turns slowly are no longer counted as a repeated USB packet; the 44.1 kHz monkey takes that failed on
    them now pass, and injected copies are still found.
  • tools/hil_monkey.py: at 48 kHz one sample at full scale fails the take (--old-48k for firmware before this
    one); a passing take with notes that did not sound is kept (silent-<seed>.npz) to look at.
  • tools/release.py never pushes main (it is protected): the release goes in through a pull request, then
    tools/release.py <version> --after-merge tags it and checks the published files.
  • CI: the emulator's job runs in Playwright's own image, the generators run once, and the host tests run in five
    jobs at once (make pytest SHARD=K/N, tools/pytest_shard.py).
  • tools/final/hil_cpu_fx.py (the CPU check): a take that fails on CoreAudio's error -10863 ("cannot do in current
    context", the first take right after an install) before anything is measured runs once more, on a line of its own.
    The retry is judged like any take; the failed attempt never counts.
  • tools/hil_crackle.py (the USB repeat check of the monkey and the crackle test): a USB copy repeats the whole stereo
    frame, so every channel must show it. One channel's evidence alone (a mono passage, or the other channel at its
    turn) needs 18 dB, and 4 or more equal frames need the wave clearly smoother without them. The host build's own
    music no longer fails the check, and neither does the FM-1 take it failed (a plateau of the music, every counter
    clean).
  • tools/hil_monkey.py: a take that fails an audio check is triaged in the same run. The host build plays the same
    seed and is judged the same way, then one line in the report: HOST TOO (the test or the music), COUNTED (a device
    counter explains it) or DEVICE ONLY (the event and the counters). The host render is kept next to the take.
  • tools/hil_monkey.py, tools/hil_usb_bench.py and each step of tools/final/run_final_rev.sh record the
    computer's load before and after: a busy computer starves the recorder.
  • tools/final/run_final_rev.sh: VIBES and MARIMBA get the key-velocity gate's 5-run follow-up after a 3-run FAIL,
    as the Rhodes does; the 5 runs decide.
  • tools/hil_must.py and tools/hil_cpu.py read the new counter; the gate wants it at 0.

Tested

  • Host: tests/py/test_uac_fir.py (the firmware's resampler bit for bit against its reference with the knee; a loud
    9.5-11 kHz passage at the limiter's ceiling clips without it and not with it), Gate R (every render unchanged).
  • FM-1: tools/hil_monkey.py at 48 kHz, 25 takes: 0 samples at full scale (the gate now fails on one);
    tools/hil_cpu.py --worst --usb 48000, 3 runs per engine: the whole audio interrupt at most 4016 us.
  • Host: tests/py/test_master_click.py (the MASTER code from platform.c at four knob positions, with USB VOL fixed
    and following: within one 24-bit step of the exact product, no offset), tests/py/test_isr_tail.py.
  • FM-1: the jack's idle floor as 0.9.6 (-92.5 dBFS): unchanged where the hiss dominates.
  • Host: tests/py/test_smk_app.py (the buttons' matrix codes, their LEDs and the hints), make test-smk; the FM-1
    build is byte for byte the same with and without the change. Not on an SMK-37 (none here).
  • Host: tests/py/test_dither.py (a held Rhodes A2: its 3rd and 5th harmonics at the noise floor where they stood at
    -117 dBFS before; exact zero before and after a note on every engine, SPREAD on); every render's baseline
    re-saved on purpose (tools/gate_r.py).
  • FM-1: tools/cpu_check.sh, 4 cases of 4 (VIBES at most 4052 us for the whole audio interrupt); the reverb SIZE
    swept under VIBES with every effect on, at most 4120 us (INFINITE) and 4140 us (PLATE).
  • Host: tests/usb_desc_test.c and tests/py/test_usb_desc.py (the FM-1, the FM-1 without USB audio, the three SMK-37
    models and the update loader: product id and name); the installers' tests (terminal and web) find and update a
    unit on either name. The sound is unchanged: every render byte for byte the same (tools/gate_r.py).
  • Host: tools/hil_scan.py --host --only presets (F11 -1.5 LU re F01 before, +0.0 now), tests/py/test_hil_limits.py;
    tests/py/test_cpu_gate.py (the -10863 retry); Gate R (no render uses F11: every render unchanged).
  • Host: tests/uac_seq_test.c and tests/py/test_uac_seq.py run the firmware's own 44.1 kHz USB audio path frame by
    frame for 36 simulated hours (130 million packets, the host's timing jitter, skipped and missing frames, the timer
    interrupting the audio at every point that matters): no frame repeated or lost without a counter. A planted
    one-frame copy is caught; a timed-out status read now loses nothing, where the previous release lost the packet.
  • Host: the repeat check finds 0 events on 800 host takes (7 before); of 1-5-frame copies injected at random it finds
    1498 of 1880 (1492 before) and 1530 of 1878 on takes not used to tune it (1524 before); its self-test finds every
    injected glitch. Every render byte for byte the same (tools/gate_r.py).

Full manual: https://github.com/leonardoMirabal/frodhes/blob/main/docs/MANUAL.md · Built on Dinghy. GPL-3.0.

Don't miss a new frodhes release

NewReleases is sending notifications on new releases.