Support N MIDI inputs, each with its own queue#3
Open
jeudine wants to merge 11 commits into
Open
Conversation
Replace the single optional MIDI input with a list of inputs. Each input gets its own queue, condvar and midir connection, plus a dedicated consumer thread that drains only that queue. - run() now takes Vec<MidiInParam> (empty = standalone/no input) - Conductor::handle_input and Context::handle_input gain an input_id (0-based index of the source input) - Slave mode: the first input marked slave is the clock/transport source; the others are message-only inputs - Per-input port prompt names the input being selected - input_test now drives multiple independent inputs and asserts per-input routing via input-dependent transposition - Patch mseq_core/mseq_tracks to local paths so the workspace builds against the new (breaking) core API during development Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a test asserting that in slave mode with multiple inputs, only the clock of the slave input (input 0) advances the step: clocks arriving on a non-slave input are dropped at routing and never reach the conductor or move the step. Extract the transport/channel classification into MidiMessage::is_transport so the production input callback and the test share the same routing logic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Only one clock/transport source is supported, so log a warning when more than one input has `slave: true`; the first one is used and the rest become message-only inputs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Trigger the CI workflow on push to any branch instead of only main. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Declare mseq_core and mseq_tracks with both a version and a local path so the workspace builds against the local sources during development while still publishing proper version requirements. Removes the patch.crates-io section. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Consumer threads: park on the queue's own mutex/condvar (the pair the midir callback notifies) with a predicate loop, so messages that arrive before parking can't be lost; drain via an O(1) swap so the real-time callback isn't blocked while the conductor runs. - run_slave: drain the transport queue, release it, then apply transport under the run lock alone, removing the only place these locks nested. - Replace the (Arc<Mutex<InputQueue>>, Arc<Condvar>) tuple with a named NotifyQueue struct (queue/condvar fields, new()/push() helpers). - Remove the unused Context::pause field and its dead branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Update the README to match the new multi-input API: the multiple-inputs feature, an "input_id" note on handle_input, a "MIDI Inputs" section, and the corrected usage example (handle_input gains input_id, midi_in is a Vec). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Previously process_pre_tick and handle_input called the conductor while paused but discarded the returned instructions. Always execute them now; pausing only freezes the step counter (step-driven tracks hold position), it no longer silences update/handle_input output. Update the Conductor::update, Conductor::handle_input and Context::pause docs to describe the new pause semantics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conductor::handle_input now returns an InputResponse with two channels: - instructions: processed by the MIDI controller (buffered/step-scheduled), executed only while running and dropped while paused. - messages: forwarded straight to the MIDI output, bypassing the controller, sent always including while paused. This lets a conductor echo/transform incoming MIDI immediately and even while paused, without the controller's clock-quantized note buffering. Also revert update's pause behavior to dropping its instructions while paused (undoing the previous "keep instructions while paused" change for update), and update the related docs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Generalizes MIDI input from a single optional port to N independent inputs, each with its own message queue and consumer thread, and gives
handle_inputa direct MIDI forwarding channel.run()now takesmidi_in: Vec<MidiInParam>instead ofOption<MidiInParam>(empty vec = standalone/no input).Conductor::handle_input/Context::handle_inputgain a 0-basedinput_id(matching the input's position in theVec) so messages can be routed per source.slave. Extraslaveinputs are treated as message-only, with a warning.Dual-channel
handle_inputConductor::handle_inputnow returns anInputResponsewith two independent channels:instructions— processed by theMidiController(buffered / step-scheduled), executed only while running and dropped while paused.messages— forwarded straight to the MIDI output, bypassing the controller, sent always (including while paused).This lets a conductor echo/transform incoming MIDI immediately and even while paused, without the controller's clock-quantized note buffering.
Correctness / cleanup
mem::takeso the real-time midir callback is never blocked while the conductor runs).NotifyQueuestruct to replace the(Arc<Mutex<..>>, Arc<Condvar>)tuple.pausefield fromContext.MidiMessage::is_transport; added slave-mode multi-input and pause-forwarding tests.Breaking changes
run()signature:Option<MidiInParam>->Vec<MidiInParam>.Conductor::handle_inputgains aninput_id: usizeparameter and now returnsInputResponseinstead ofVec<Instruction>.Testing
cargo fmt --all --check,cargo clippy --workspace(all-features + no-default-features,-D warnings)RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --all-featurescargo test --workspace --all-features(unit, integration, doctests)🤖 Generated with Claude Code