Skip to content

cv_trigger: a host-sized input count, and MIDI notes reach a CV note output - #1179

Merged
dpwe merged 4 commits into
mainfrom
cv-trigger-host-inputs
Sep 24, 2026
Merged

dpwe merged 4 commits into
mainfrom
cv-trigger-host-inputs

Conversation

@bwhitman

Copy link
Copy Markdown
Collaborator

Three things found bringing a MIDI->CV converter (a Kenton Pro Solo) to a board whose gate input is a GPIO, not an ADC channel:

  • AMY_MAX_CV_IN is #ifndef now, and update_external_cv_in loops over it, so a host can build with -DAMY_MAX_CV_IN=3 and answer amy_external_coef_hook for a channel that is not analog at all -- a gate pin read as 0 or 5 V. A cv_trigger can then fire off a gate jack with no ADC input spent on it. Default and web behaviour unchanged.
  • A MIDI note now reaches a CV_GATE note output. note_output_handle_event refused any note carrying note_source_channel unless forward_midi_in, a flag only MIDI_OUT mode can set. The feedback-loop guard is right for a MIDI port and has no loop to guard against on a jack, so a MIDI keyboard never moved a CV/gate output. The guard applies to MIDI_OUT only now.
  • An empty velocity field stays unset. note_output='1,0,2,,12,24' (the doc's own example) cast the unset arg to channel 0, so every note-on wrote its velocity over the pitch DAC.

Docs: cv_trigger's scale and offset are octaves with 0 at ZERO_MIDI_NOTE (midi_note_for_logfreq), not semitones, which api.md now says; cv_pitch_for's comment claimed to be that formula's exact inverse in semitones and now says how the two convert.

🤖 Generated with Claude Code

bwhitman and others added 2 commits September 22, 2026 15:11
…output

Three things a MIDI->CV bench on a Tulip found in an afternoon:

- AMY_MAX_CV_IN is #ifndef now and update_external_cv_in loops over it,
  so a host can offer amy_external_coef_hook a channel that is not an
  ADC at all -- a gate GPIO read as 0 or 5 V -- and a cv_trigger can
  fire off a gate jack with no analog input spent on it.
- note_output_handle_event refused any note carrying note_source_channel
  unless forward_midi_in, a flag only MIDI_OUT mode can set. The
  feedback-loop guard is right for a MIDI port and has no loop to guard
  against on a jack, so a MIDI keyboard never moved a CV/gate output.
  The guard is MIDI_OUT only now.
- An empty velocity field in the CV_GATE config ("1,0,2,,12,24", the
  doc's own example) was cast to channel 0, so every note-on wrote its
  velocity over the pitch DAC. An unset arg stays unset.

And the doc: cv_trigger's scale and offset are OCTAVES with 0 at
ZERO_MIDI_NOTE (midi_note_for_logfreq), not semitones, and note_output's
comment claimed to be its exact inverse in semitones. api.md says so and
cv_pitch_for says how to convert.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The same #ifndef as AMY_MAX_CV_IN, for the same reason: a host with
more control outputs than four -- here two DACs, two gates and two that
only exist to be stamped -- names the count at build time, and the
default stays what it was.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Comment thread src/amy.h Outdated
Comment on lines +141 to +142
// build time (-DAMY_MAX_CV_IN=3) to offer amy_external_coef_hook a
// channel that is not an ADC at all -- a gate GPIO read as 0 or 5 V, so

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is confusing. I can imagine a situation where ext_coef_in(2) actually reads a gate input, but AMY knows nothing about how that function interprets its arg. The comment seems to imply something more specific.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed -- that comment described what one host does with channel 2, and AMY has no view of that. Reworded to what AMY actually knows: the count of channels update_external_cv_in asks the hook for and the range a cv_trigger may name, with what a channel physically is left to the host.

@github-actions

Copy link
Copy Markdown
Contributor

🎛️ AMY HW CI (AMYboard bench)

Flashed this PR's AMY (LoadTestChord: 6-voice Juno patch=1, one held note every 2 s) onto the physical AMYboard and measured the smoothed render load as the chord grows — back-to-back with the same sketch built at the PR's merge base, so Δ is this PR's own cost.

✅ PASS — the bench ran the test to completion.

notes held main @ 29cd07c this PR Δ
1 987 976 -11
2 1142 1131 -11
3 1714 1700 -14
4 1883 1872 -11
5 2472 2457 -15
6 2609 2596 -13

Full chord settled render μs: 2597 (was 2608, Δ -0.4%) (peak 2602, 39 samples)

⬇️ Artifacts: serial log · load trace · report

Self-hosted bench (amyboardci). FAIL means only that the test could not run — the load values are informational, with no threshold and no audio compare. See tools/arduino_loadsweep/.

@dpwe
dpwe merged commit 6d9c790 into main Sep 24, 2026
12 checks passed
@bwhitman

Copy link
Copy Markdown
Collaborator Author

⛓️ tulipcc integration PR opened

This merge was pinned into tulipcc for full-system CI: shorepine/tulipcc#1372

Test it there and merge that PR to move tulipcc onto this AMY.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants