github Quixotic7/ChoralRootFM1 v0.19
ChoralRoot 0.19

4 hours ago

ChoralRoot 0.19 is a sound release: a cleaner output stage that you can compare with the old one (Options > Output),
a stereo chorus, a CZ-1 that no longer pops and sits level with the other engines, and USB recording at 48 kHz as an
option. The Options no longer take the keys away, and GLO with PRESETS or ALGORITHM picks the engine in the layers
too. It also fixes a speaker setting that another firmware could leave behind, and MASTER right after switching on.

Updating from 0.18 keeps your settings, sounds and loops. Three things sound different after the update without your
doing anything: CZ-1 is 6 dB quieter, single notes are a little quieter and full chords cleaner (Output starts on
Clean), and the chorus is stereo. What changes for you has the list.

The output: Options > Output

A cleaner output stage. The last stage before the headphones, the speaker and the USB recording has been
rebuilt. Until now a full chord with the bass kept the output limiter working all of the time at full MASTER, which
added distortion you could hear as roughness in the mids and highs, and turning MASTER down cost quiet sounds their
detail. The new stage, Clean:

  • leaves the sound untouched up to just under full level;
  • uses the limiter as a safety that holds still instead of moving with every bass note, and that looks ahead: the
    sound waits 0.7 ms, so the limiter has turned a loud peak down before it arrives instead of just after. A hard
    attack is no longer bent on its first millisecond;
  • keeps the level it has set for a fifth of a second, and for as long as the music stays near it, instead of letting
    go and catching again with every beat of a loud chord. A single loud hit is still let go quickly, so nothing stays
    turned down after it;
  • applies MASTER last, so the knob changes the level and nothing else, and a low setting keeps the full resolution.

Options > Output has two values: Clean (the new stage, and the default) and Classic (the output stage of
0.18 and before, kept so that you can compare). You can switch while you play: the sound fades out and in again
within a fortieth of a second, without a click. The setting is saved by itself, like the other Options.

Measured, at full MASTER (on the computer, with the firmware's own code; not yet on an FM-1): on a six-note chord
with the bass the distortion drops from about 4 % to between 1 % and nothing measurable, depending on the sound; on
a single note from about 0.5 % to under 0.02 %. What is left on the loudest chords is the limiter adjusting the
level, not a bent wave: the output stage itself adds less than 0.003 %. Most full chords with the bass come out a
little louder than before (up to 1.3 dB), a single note a little quieter (2.4 dB).

Where you hear it most is with the effects turned far up, both sends high and a long reverb: the loudest moments are
steadier there, with less of the roughness that a busy limiter leaves.

Clean's sound is 0.7 ms late (the limiter's look-ahead; Classic has none). Everything is late together: notes, the
arp, the looper, the metronome's click and the count-in, and in a USB recording all three stereo pairs, which line up
to the sample.

Over USB the recording's master pair follows the stage you chose: with Clean it peaks 2 dB under full level, where
Classic stayed about 3.5 dB under it. The chord and bass pairs are the same sound as before; with Clean they are
0.7 ms later, with the master pair. Do not switch Output during a take: the chord and bass pairs jump by those
0.7 ms at the switch.

The chorus is stereo

Until now the chorus came back as one signal, the same in both ears. It now comes back as two, one for each side,
moved by the same slow sweep a quarter turn apart: on headphones the chorus spreads out instead of sitting in the
middle, and its swirl is no longer the same in both ears. Rate, Depth and the send do what they did. The two sides
are a quarter turn apart and not opposite so that the chorus still moves when both are added together on one
speaker. This is in both Output settings, so a sound with a chorus send is the one thing that Classic does not play
exactly as 0.18 did.

CZ-1

CZ-1 no longer pops when a chord is struck again or changed, and its bass no longer clicks. Three things in the
engine made a step in the sound, and all three are gone.

  • A note played again while it still sounded (the same chord struck twice, a chord change that keeps a note, the arp,
    the bass moving from note to note) was cut to silence in an instant and then started over: a pop, a short hole,
    and the attack again. It now carries on from where it was. You will hear the difference beyond the pop: a
    sustained CZ-1 chord struck again swells from its level instead of starting from nothing.
  • The timbre envelope (DCW) moved in small jumps 1400 times a second. On a low note with a fast timbre attack each
    jump was audible as a click a few milliseconds after the note began, most of all on CZ BASS and on low piano
    and mallet tones. It now moves smoothly.
  • A sounding note played again at a different velocity jumped to its new level in an instant: a tick on every such
    note, worst on the string and lead tones. Loop steps with their own velocities, a MIDI keyboard or a pattern's
    accents do that; the FM-1's own keys, which have one velocity, do not. The level now glides to the new velocity.

Held chords themselves were not at fault in our renders, and nothing inside the engine clips. If you still hear
pops or drop-outs on held CZ-1 chords after this build, it is a different cause (the processor finishing a moment of
sound late) and the unit can say so: run python3 tools/cz_pop_check.py --read after playing, or
--play to have it play the heaviest CZ-1 chords for 45 seconds and report.

Every CZ-1 tone keeps its timbre; the first milliseconds of a note are not sample for sample what 0.18 played, and
the level is 6 dB lower (next). Saved CZ-1 sounds are untouched. The other engines are unchanged, sample for sample.
The cost is under 1 % for a held chord and about 9 % while a tone's timbre is moving.

CZ-1 is 6 dB quieter, level with the other engines. For the same chord CZ-1's peaks were 5 to 6 dB above the VA's
and FM TONE's and 7 dB above FM6's; two thirds of its sounds pushed a six-note chord past full level into the output
limiter. The whole engine now plays 6 dB lower: exactly half the level, the tone itself untouched.

  • Your own saved CZ-1 sounds get 6 dB quieter too, like every factory tone. That is intended: the engine was
    too loud as a whole. Nothing in a saved sound or a CZ-1 bank is rewritten; turn MASTER or the part's level up if
    you had balanced a set around the old level.
  • BRASS 1, STRINGS 2 and PIPE ORGAN 1 used to carry their own cut (5, 3 and 6 dB) to tame them; that is gone, so
    they end up 1 dB, 3 dB and not at all quieter than before.
  • An FM-1 that was last saved with one of those three on the chord or bass part comes back at the new level too:
    the old cut is not brought back with the saved session.
  • A CZ-1 part with DIST on is driven 6 dB less: turn DIST up for the same grit.

Looked at and left alone: BRASS 1 (and other tones whose brightness lingers after the volume has gone) comes back
bright at once when a chord is let go and struck again within a few hundredths of a second, instead of opening up
as on a fresh strike. A measuring tool counted that as a click; it is the tone carrying on from where it was, which
is what a CZ's sound chip does. A few percussive tones (AFRO PERC, SYN DRUMS 2, CONGA) have a hard edge on very low
notes just after the attack: that is their waveform, not a fault.

USB recording at 48 kHz, as an option

ChoralRoot In can now also run at 48 kHz, and you switch that on: Options > USB Rate. Many phones, tablets and
apps only take 48 kHz, and a DAW project at 48 kHz no longer has to convert. This is SLOOP 2.5's feature, which came
from Felucca 1.1.5, made for ChoralRoot's six channels.

  • It is off until you ask for it. Options > USB Rate (after USB Level): 44.1 kHz (the default) or
    44.1 + 48 kHz. At the default the FM-1 presents exactly the audio device 0.18 did, one rate, and the recording is
    bit for bit what 0.18 sent: updating changes nothing about USB audio. Set to 44.1 + 48 kHz, the computer, phone
    or app chooses (on a Mac: ChoralRoot In's Format in Audio MIDI Setup; on Windows: the device's Advanced
    properties; a DAW from its project).
  • Why an option: the one Mac it was tried on picked 48 kHz by itself as soon as it was offered, and then every
    recording pays for the conversion, needed or not.
  • Changing it reconnects the FM-1, as USB Record does: USB RECONNECTING, about a second off USB, the MIDI port
    with it. It waits for a backup, a restore or an update that is running (and so does USB Record now). Not during a
    take.
  • At 44.1 kHz nothing changed, in either setting: the FM-1 itself runs at 44.1 kHz, and so does the headphone
    output.
  • At 48 kHz the FM-1 converts all six channels on the way out. The treble's top is a little softer (flat to 12
    kHz, 1 dB down at 16 kHz, 2.7 dB at 18 kHz). The conversion takes about 6 % of the processor: in the very heaviest
    sounds (CZ-1 chords with both effects) a voice may drop sooner while a 48 kHz recording runs. Not measured on the
    unit yet, and not yet tried with a phone, a tablet, Windows or a hub.
  • With USB Rate at 44.1 + 48 kHz the computer sees ChoralRoot In as another version of the device, so that it
    reads the two rates instead of remembering one: a computer may set the device up again the first time.

The Options leave the keys alone

The keys keep playing while the Options are open. Until now the white keys jumped to the first eleven settings
there and nothing sounded, which made it awkward to hear what a setting does: you had to hold a chord before opening
the menu. Now the keyboard is exactly what it was on the view you came from: chords, LOCK, Key Mode, the split, the
arpeggio, LATCH, and a loop being recorded takes what you play. The key lights show what plays; OPT is lit while the
Options are open.

  • To compare Classic and Clean: OPT, SELECT to Output, play, and turn KNOB 1 while the chord sounds.
  • The menu is SELECT, KNOB 1 and the buttons. SELECT moves through the settings (every one of them; the keys
    only ever reached the first eleven), KNOB 1 sets the value, OPT, HOME, OCT- or OCT+ leaves. The screen's bottom
    line says so.
  • No key confirms or cancels anything. Calibrate still starts with OCT+, Reset Settings with OCT+ twice; playing
    while Reset Settings asks OCT+ again: reset neither does it nor withdraws the question.
  • KNOB 2 to 4, PRESETS and ALGORITHM keep their view jobs in the Options, as before. OCT- and OCT+ close the Options
    there, so the octave is set before you open them.
  • Unchanged: OPT in the PLAY menu and in the KEY layer still switches what their keys do and does not open the
    Options; OPT held with a knob or a button is the same second function as before.

GLO + ALGORITHM and GLO + PRESETS: the engine, in the layers too

Hold GLO (OPT) and turn ALGORITHM: the bass engine. Hold GLO and turn PRESETS: the chord engine. Now wherever
the knob alone changes the sound
, not only on the main screen.

  • In 0.18 both worked on the main screen (and in the Options) only. In a layer, GLO + ALGORITHM just changed the
    bass sound, as ALGORITHM alone does; before 0.18 it had been the bass level there. Now it is the bass engine in
    the KEY, PERF, FX, BASS, SEQ and LOCK layers, in the view menu, and in the sound editor, with the same picker,
    landing on the sound you last used in that engine. A bass that is off stays off.
  • GLO + PRESETS follows the same rule: the chord engine in those layers too.
  • Where a knob has another job, GLO leaves it alone: in the PLAY menu PRESETS is the loop slot and ALGORITHM the
    record mode; in the loop editor GLO + PRESETS jumps 8 steps and GLO + ALGORITHM sets the Voicing to Follow /
    0; in the sound editor PRESETS opens the editor's own preset picker.
  • In the editor's picker (EDIT held) and on the save dialog GLO changes nothing about the two knobs: both are busy
    with one sound.
  • The read-out's bottom line, Hold OPT to change engine, now shows wherever that is true.
  • The bass level is still BASS held + ALGORITHM. A knob turned while a layer's own button is held past the hold
    time is still that layer's.

Fixes

A speaker setting left behind by Felucca. If your FM-1 ran Felucca with MENU > SPEAKER set to LOWCUT or BASS+
before you installed ChoralRoot, that setting stayed active: every sound lost its low end (17 dB and more under
100 Hz), and with BASS+ gained added harmonics. ChoralRoot has no entry for it and Reset Settings did not clear it.
0.19 switches it off for good at the first start and stores that with the next save. A new FM-1, or one that never
ran Felucca with that setting, was not affected.

On 0.17 or 0.18 the only ways to clear it are to restore a backup that does not have it, or to install Felucca, set
MENU > SPEAKER to OFF there, and install ChoralRoot again.

MASTER is right from the first note. After switching on, the first half second used to play at a middle level
whatever the knob said, then slide to it: loud with the knob turned down. The knob is now read before any sound.

Smaller things.

  • Changing Options > USB Record now waits for a backup, a restore or an update that is running, as USB Rate does.
  • The sound read-out's bottom line Hold OPT to change engine shows wherever holding OPT does that, and nowhere
    else.

What changes for you

  • CZ-1 is 6 dB quieter, your own CZ-1 sounds included (BRASS 1 by 1 dB, STRINGS 2 by 3 dB, PIPE ORGAN 1 not at
    all). Raise the part's level or MASTER if a set was balanced around the old level; a CZ-1 part with DIST wants
    more DIST.
  • With Output on Clean, the default, a single note is about 2.4 dB quieter than in 0.18 and most full chords
    with the bass up to 1.3 dB louder. Classic has 0.18's levels.
  • With Clean everything you hear is 0.73 ms later than in Classic or 0.18 (32 samples). A key's own delay to its
    sound was 0.7 to 2.9 ms already. Classic adds none.
  • A sound with a chorus send is stereo now, in both Output settings. With no chorus send Classic plays what 0.18
    played, sample for sample (CZ-1 apart, which changed by itself).
  • In the Options the white keys no longer jump to a setting: they play. Use SELECT.
  • GLO + ALGORITHM in a layer is the bass engine, no longer the bass sound; GLO + PRESETS in a layer is the chord
    engine.
  • Your saved state comes back as it was. Settings, sounds, loops and the session need no conversion. One
    exception by design: a session saved on CZ-1 BRASS 1, STRINGS 2 or PIPE ORGAN 1 comes back without the old cut
    those sounds carried, at the engine's new level. Nothing is rewritten until your next save.
  • USB recording is unchanged unless you set Options > USB Rate to 44.1 + 48 kHz: the same device, the same
    rate, the same bytes. With Clean the recorded master pair has Clean's levels, and the chord and bass pairs are
    0.73 ms later with it.

Going back to 0.18

0.18 and older read 0.19's settings as their own. They do not know Options > Output or USB Rate: if Output is on
Classic or USB Rate on 44.1 + 48 kHz when you go back, the older firmware takes its default for one retired
setting and nothing else changes. Their chorus is the mono one, CZ-1 is at its old level, ChoralRoot In is 44.1 kHz
only, and the white keys are the Options' shortcuts again.

For developers

The output chain (docs/AUDIO-QUALITY.md, docs/FX.md "The master, and Options > Output").

  • docs/AUDIO-QUALITY.md: what was measured against Sloop 2.2 and 2.6 and against 0.17 (GitHub issue 3), every
    change to Felucca's audio path from 1.0.1 to 1.5.1, the design of the output chain, everything that processes the
    sound of a freshly reset unit, and how to measure. All of it on the host and in the emulator: nothing in it was
    measured on a unit.
  • Clean: the mix x 0.75 in Q17, the DC blocker, a delay of 32 samples, the limiter (CLN_T 26000; CLN_HOLD
    200 ms, CLN_HOLD_HIT 40 ms, CLN_REHOLD 1.2 dB, release 186 ms), a knee that is linear to the threshold, MASTER
    last into the 24-bit word (fx.c clean_block). It looks one block ahead with a linear glide between blocks, after
    Sloop 2.6's (LICENSING.md): no sample leaves the limiter over its threshold (tests/cr_output_test.c).
    -DCLN_LA_K=2u builds it with 1.45 ms instead of 0.73.
  • The hold, against the limiter of 0.19's first two rounds (40 ms, 0.3 dB): on very wet passages the residue is 4 to
    11 dB lower (two thirds to under a third of what it was) for a level at most 1 dB lower; a single hit's recovery
    is as it was (docs/AUDIO-QUALITY.md "The hold"; tools/master_rhythm.py, tools/master_listen_hold.py).
  • The switch: the chain's input faded to nothing over 16 blocks, one silent block through the old chain, the other
    chain from rest, and up again: 24 ms (fx.c out_fade).
  • The measurement harness is in the tree: tests/master_measure.c, tools/master_analyze.py,
    tools/master_measure_run.sh, tools/master_measure_tables.sh, tools/master_listen.py; for the look-ahead
    tools/master_transient.py (a kick on a bass, as a mix for master_measure chain), tools/master_listen_la.py,
    tools/emu/wavlag.py.
  • tools/emu/classic_exact.sh: Output = Classic against a 0.18 build, every emulator script, bit for bit, with the
    chorus's return held at 0.18's (EMU_CHORUS=mono, a test switch of the emulator); and with the stereo chorus as
    built for the scripts that send nothing to it (the scripts that play CZ-1 differ by CZ-1's own changes, and are
    named); REF_CLASSIC=1 compares with a 0.19 tree held at Classic. EMU_OUTPUT=classic holds the emulator at
    Classic. The script's head says how the 0.18 reference is built.
  • The click and the USB part pairs are delayed with Clean's look-ahead: the click starts 32 samples late
    (cr_out.c cr_click_mix, cr_ck_wait), the part pairs wait in a line of their own (fx.c ua_parts_late, 256
    bytes of RAM, only while the computer records through Clean; the line is emptied at a change of the chain, which
    is the jump named above). tests/cr_align_test.c.
  • MASTER is read before the audio starts (firmware/src/master.c, Felucca 1.1.5's fix; EMU_MASTER=ADC sets the
    emulator's pot at power-on).
  • Felucca's settings.lowcut is held at 0 by cr_settings_boot, by a restore and by Reset Settings
    (tools/emu/test_persist.sh boots on seeded flash images; tools/emu/seed_lowcut.py).
  • CR_OUT_MAKEUP=1 ./build.sh: Clean with its trim given back in the DAC word's upper 6 dB. Not a release build and
    not finished: USB's master pair and the SCOPE view are not handled above the Q15 sample's full scale.
  • Options > Output is bit 1 of the settings block's pick_roots, Options > USB Rate bit 2: no record version, older
    records read as the defaults (docs/SETTINGS.md).

The chorus (docs/FX.md "The chorus, stereo"): cr_fx.c xc_block, two taps on the one line, after SLOOP 2.6's
(LICENSING.md; SLOOP's are half a turn apart); tools/chorus_response.py, tools/chorus_listen.sh,
tests/cr_fx_test.c. xfp[XP_CWIDTH] (0 = both sides the left tap, 127 = a quarter turn) has no knob yet; the lines
a Width knob needs are in docs/FX.md. With nothing sent and the line empty the chorus does no work.

CZ-1 (docs/CZ1.md "Pops (0.19)", "The second round", "Level"): a sounding voice keeps its levels over a
note-on; the DCW depth is ramped across a block (cz_native_line_mv); a line's gain runs on from its last block
(cz_voice_t.ag); CZ_VOICE_GAIN 2 (a voice adds * 2, not * 4: every sample exactly half); CB_TRIM's three
rows are gone. tests/cz_pops.c (33 280 runs), tools/cz_pops_report.py, tools/cz_pops_why.py,
tools/cz_pop_check.py for the unit. All 65 CZ-1 goldens of tests/regress.c were rewritten. A part put back from
the session takes its trim from the running firmware's table, not from the record (cr_settings.h cr_session_part_trim; the record is unchanged).

USB recording at 48 kHz (docs/USB-AUDIO.md "48 kHz"): with the option on, one alternate setting with two
discrete rates and the endpoint's SET_CUR (SLOOP's and Felucca's form), bcdDevice 3.23, wMaxPacketSize 688;
with it off, 0.18's descriptors byte for byte (bcdDevice 3.22, one rate, 640; tests/cr_usb018_desc.h), SET_CUR
48000 taken and without effect, and no 48 kHz code run. The two states differ in bcdDevice so that hosts re-read.
A 12-tap polyphase FIR x 160 / 147 in the audio ISR (SLOOP's table), about 185 us a half by the unit's listing; SysEx
79 USB has the rate, the 48 kHz counters and the option's state, fm1_install.py --debug and tools/uac_check.py --rate read them (--rate 48000 against a unit that does not offer it says so); tests/cr_usbaudio48_test.c,
tests/cr_usbrate_test.c. The 48 kHz code: flash +4896 B, POOL +724 B, RAM +128 B. docs/USB-AUDIO.md has the
measurements, the cost counted from the unit's listing, and the test plan for real hosts in both states (one
observation so far: macOS chose 48 kHz unasked).

Does recording change the sound? No: the capture is a tap (tests/cr_usbtap_test.c; docs/USB-AUDIO.md "Does
USB recording affect the sound?"). It costs time: at 44.1 kHz about 126 us a half by the unit's listing and the
SIE's waits, four times the emulator's estimate; not measured on the unit.

GLO and the two sound knobs: one predicate, cr_ui.c cu_epk_ok(part), for the gesture and for the read-out's
hint (cu_pool_home is gone); tools/emu/scripts/cr_glo_knobs.txt (32 dumps: every place, the three kept ones, the
held-layer rule, the bass off). The table of places before and after: docs/PRESETS.md.

The Options and the keys: cr_ui.c cu_key_press has no Options case, cr_ui_pump hands the keys to the
engine during a redraw of the Options page, cr_leds draws the playing state; the Options screen has a footer.
Emulator test tools/emu/scripts/cr_opt_keys.txt.

TIMER5 and the static budget (tests/target_budget.py): usb_poll and what it does on a rare event (a control
transfer, a bus reset, a MIDI packet, the console's bytes) are functions of their own, out of the 100 us interrupt's
line (usb.c USB_OUTLINE, USB_COLD). Nothing a tick runs changed; a poll (2 kHz) has one call more. Inlined, that
code's branches were counted as loops around the key scan and the figure moved by a quarter between builds with the
compiler's block order. The budget now has a line for the scan itself (4511) and lines for usb_poll, ep1_rx,
ua_service_paced and ua_transmit; the audio interrupt's line is its present figure (16415; it was 44152, from
before PHYS was retired). The update loader is byte for byte what it was.

The browser emulator builds with Emscripten (sh tools/emu/web/build_web.sh, which finds the SDK in ~/emsdk
when emcc is not on the PATH); its test is node tools/emu/web/test_web_emu.mjs.


ChoralRoot 0.19 for the M-VAVE FM-1 (identity FM-1_920).

Install from the browser at https://quixotic7.github.io/ChoralRootFM1/ (Chrome or Edge, the FM-1 connected by USB), or with the command-line installer: python3 tools/fm1_install.py choralroot-0.19.fwsc.

SHA256SUMS lists the package and the app. The package holds three JieLi AC79 SDK files (Apache-2.0); LICENSE, LICENSING.md and LICENSES.zip carry every licence. Installing firmware is at your own risk.

Full Changelog: v0.18...v0.19

Don't miss a new ChoralRootFM1 release

NewReleases is sending notifications on new releases.