Repository navigation
Mixer: a console of strips with meters, and the execution reporting back without stopping the audio - #2302
Mixer: a console of strips with meters, and the execution reporting back without stopping the audio#2302jcelerier wants to merge 39 commits into
Conversation
|
The AppImage aarch64 failure on 63e2c52 comes from score-addon-librediffusion: it vendored |
46ff7c5 to
e0dffba
Compare
EXTRA_PRS names PRs of other authors on ossia/score to merge into the build alongside mine, starting with ossia#2302 (jcelerier's mixer console). Each one is merged only while its state is OPEN, so a PR that gets merged or closed drops out of the build without touching the workflow; the run leaves a notice so the number can be cleaned out of the list later. The release body names the author of such PRs (without an @, so the release does not mention them). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lc7tYGHRn6TmaxMEfBTswW
e0dffba to
fb0b153
Compare
|
This is Edu's Claude assistant, posting on Edu's behalf. Rebased onto master ( Submodules
Where master and the PR overlapped
Checked
The pre-rebase head was Since this rebase, master moved avendish to 🤖 Generated with Claude Code |
libossia now applies an interval's gain and pan once, in the outlet, for every interval: the has_gain flag the bus status used to set is gone. Buses set below unity in existing sessions will sound louder, since their gain was applied twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
/out/main's gain is now a post-sum trim over every hardware output, applied after the graph and before the actions, so the library preview stays out of the mix. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
The weights are swapped in, so the previous ones are freed with the closure, off the audio thread. The gain/pan unit test now expects pan to be left alone for signals wider than it: libossia no longer grows it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
Muting an interval goes through a command, from the interval header too,
and is saved in the document ("Muted"). An interval can also be soloed
("Soloed"): as long as one bus is soloed, the document silences every bus
that is not soloed and does not contain, or sit inside, a soloed bus. That
silence is computed, never saved, and reaches the execution as a mute.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
AudioDevice::changed only fires when the whole device is rebuilt. Adding, editing or removing a mapped or virtual port now emits portsChanged, so a view of the ports can follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
Execution::Telemetry is where views ask for meters: what an audio outlet carries after its gain, or every hardware input or output. The handles outlive stops and starts. While the execution runs and the execution-update setting is on, the engine owns an ossia::telemetry::arena, fed by the taps and published at the document's coarse update rate, the same rate every other execution feedback uses. Turning the setting off drops the arena and detaches every tap while running. makeExecutionTick is exported so that a test can drive the real tick. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
It follows libossia, where such a generator made no channel and was silent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
Its inputs are silent and its outputs discarded, so a session without a sound card, a headless test for instance, can still route to the hardware channels and meter them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
It opened at -6 dB without ever sending it, so the master played at unity while the toolbar said otherwise. It now starts from /out/main's gain and follows it at the document's coarse update rate, since the mixer and automation can change it too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
The right-click spin box changed the value but never sent sliderReleased, so whoever commits on release left the command pending. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
score::LevelMeter draws a peak bar per channel in dBFS, with RMS, a peak hold and a clip mark that stays until clicked. Its fall and hold are timed on the wall clock, so it behaves the same at any update rate. It keeps the widest channel count of the last seconds so that a signal whose width changes does not make it jump, and draws channels too narrow for a bar as a heat strip next to a bar of the loudest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
The tabs give way to one scrolling console of sections that can each be shown or hidden: buses, hardware inputs and outputs, mapped and virtual ports. The master, /out/main, stays pinned on the right. A bus strip has a meter of what the bus carries after its gain, a channel count, a dB fader, mute, solo and propagate, a balance and the output address. Everything goes through commands and follows the model, so undo and the interval header keep it in step. Selecting a strip selects its interval and the score's selection highlights the strips. Device ports get a fader and, for the hardware and the main ones, a meter. Strips come in three widths. Meters are read through the telemetry arena, at the document's coarse update rate, and not at all when execution updates are off. The exec test now parks the audio engine, whose actions would otherwise run alongside the tick the test drives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
With the benchmark setting on, Execution::Telemetry gives every process node of the running graph a bench tap, follows nodes as they come and go, and reports each process's share of the real time, measured against the buffer duration rather than against the whole tick, through the process's benchmark signal. The mixer shows the sum for every process in a bus next to its channel count. The benchmark tick, the bench map slot and their metatype are gone: the graph measures on every tick while benchmarking. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
A mapped input or output is a set of the card's channels, so its strip shows those channels of the hardware meter, in the port's order. Strips take the list of channels to show rather than a first channel and a count. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
The VU Meter process drew what its "Levels" control outlet carried: a list of boxed floats built on the audio thread, delivered lossily at the coarse rate, with a peak hold counted in buffers. Its layer now asks the execution's telemetry for a meter on the process's audio inlet and draws it with the same LevelMeterState as the mixer, so peaks between two updates are kept and the ballistics are timed on the wall clock. The Levels outlet still carries the values for whatever is cabled to it. LevelMeterState is the meter without a widget, for graphics items; Execution::Telemetry can meter audio inlets as well as outlets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
An audio outlet now has an upmix: none, repeating the channels, or adding silent ones, up to a channel count. It is saved, set with one undoable step, and an interval hands it to its node. A bus strip offers it in its context menu, which is also how a mono bus becomes pannable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
A bus's pan weights are one gain per channel: "Channel gains..." in its context menu shows a fader for each of them, as many as the bus carries. The balance now only moves the first two weights instead of replacing them all. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
Changing a mapped port's channels or direction, or a virtual port's channel count, took effect only at the next reconnection. The port is now changed in place, as the same object, since running processes may hold a pointer to it. Adding, editing and removing ports all happen while the audio callback skips its tick, instead of under its feet. Turning a mapped port into a virtual one or back still waits for a reconnection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
The Mapped and Virtual sections have a "+" that asks for a new port with the audio protocol's own dialog and adds it, first showing the audio device in the device explorer if the user had not added it there, as one undoable step. A mapped or virtual port's strip can be edited or removed from its context menu. Everything goes through the device explorer's commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
Only the start and the stop of an interval are sent as commands, since whether an interval is executing matters to the model; its position is read from the telemetry at the interface's update rate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
The VU Meter also writes its levels in place instead of allocating a new vector on every tick. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
What these processes show of the execution now comes at the telemetry's update rate and stops with the execution-update setting. Faust reads the values its node publishes instead of the DSP's zones, and the pattern sequencer reads its step from an atomic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
Each tick sent a closure to the edition queue, which then read the control outputs and MIDI buffers of voice 0 on the interface thread while the audio thread wrote them, and after they had been reset. The control outputs now go through a triple buffer read on the telemetry updates. The MIDI for the plug-in's own interface is copied at the end of the tick, into a bounded queue, and only while that interface is open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
…report back The messages an avnd object or a script sends to its interface now follow the execution-update setting, like every other execution feedback. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
Execution::Telemetry::meterVirtualPort hands the port the address of a tap and takes it back before the port goes away. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
Ports kept a pointer to the parameter of a removed node, and the execution state an entry for it: removing a port from the mixer while a process wrote to it crashed. When a device parameter that a port may point to is about to be removed, the ports that point to it and the execution state drop it while the audio callback skips its tick. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
Every strip has the same rows, which keep their place when empty: the titles, channel counts, meters, readouts and controls of all the sections and of the master are at the same heights. Titles share one style and elide; the meter spans the fader's travel; section headers have one height; the scale leaves out labels that would touch and no longer shades its column; the strips keep room for the scroll bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
The row of toggles and the section titles above the strips are replaced by a vertical tab on the left of each section, like the folding sections of the multi-window settings. The width of every strip moves to the tabs' context menu. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
…he other way The ports and the master no longer keep the room of the bus controls: their meters and faders take it. The panel's minimum height follows the strips it shows. The triangle of a tab points down when its section is folded, right when it is open. An empty Buses section is only its tab. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
… the audio Devices announce a subtree before removing it (nodeAboutToBeRemoved). The telemetry lets go of the meters under it while their parameters still exist, and when a process port may point there, the execution drops the ports' addresses between two audio ticks: one wait of about a buffer for the whole removal, and none when no port points there. The audio device is followed in and out of the device list, and across its reconnections. The audio device changes its ports by preparing them and swapping them in between two ticks, instead of skipping audio callbacks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
A run starts at the master gain. The telemetry finds a process's load and a node's bench slot through maps instead of scanning every slot, and a bus looks its processes up once a second instead of at every update. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
- Every audio outlet's gain, pan and upmix reach its running outlet, not only the intervals'; the model offered them on every outlet. - Port faders and the toolbar volume only repaint when the gain changed. - A bus strip updates only what a change shows: a fader drag no longer restyles its title at every move. - Input strips show their levels after the gain they are read with. - A meter tap is only made once it is known where it goes. - Growing the telemetry arena no longer makes every meter flash off. - The edit dialog of a port survives its strip being rebuilt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
…m before reads The gain a port fader or the toolbar volume leaves on release is one undo step (Audio::SetPortGain), kept as the node's audio-gain attribute and restored when the document loads and when the device reconnects. Showing the audio device in the explorer for it no longer reconnects the device, which stopped the audio when it happened during playback: a reconnection with the same engine and channels leaves the tree as it is. Binary data written before mute, solo and upmix existed reads again: the added fields are only read when present (DataStreamWriter::atDelimiterOrEnd). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
The deployment builds of Qt have no style sheets: setStyleSheet is not declared at all there, and the AppImage build failed on it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
GUIApplicationContext was only declared: the precompiled header hid it, a build without one failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
…ings Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX
fb0b153 to
ef2700a
Compare
|
This is Edu's Claude assistant, posting on Edu's behalf. Rebased once more, onto master Full Debug build passes. I reran the telemetry, mixer, avnd, playhead, LV2 and audio tests. Only |
Follows libossia's feature/mixer: an output fader now sets the level of everything its channel plays, sent to /out/main, to a mapped port or straight to /out/N; an input fader trims its channel for every reader. An input meter is scaled per channel by what that channel is read with. The Inputs and Outputs tabs say so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lc7tYGHRn6TmaxMEfBTswW
|
Hi JM, I pushed 08e951e to this branch. It goes with ossia/libossia#950 51236ae45: Edu wanted the Here, the commit:
The audio, port edit, telemetry, routing and outlet tests pass. If you want it split from #2302, tell us. — Edu's Claude assistant |
A rework of the audio mixer panel and of how the execution reports back to the interface. 35 commits, each with its test where one applies. Everything visual was checked in a running score rather than by unit test alone.
Needs ossia/libossia#950 and celtera/avendish#210. The submodule pointers here already reference them. An adversarial review of the whole branch was run before opening this. Its findings are fixed in the last commits, except one that is kept by design (see below).
Mixer panel
/out/mainis pinned on the right. Every row lines up across strips (title, channel count, meter and fader, readout, controls).score::LevelMeteris a multichannel meter with peak hold, an RMS line and clip marks. It keeps the widest channel count seen, so a width change does not make it jump.Model and commands
Audio::SetPortGain; score-plugin-audio now has commands) is one undo step when a fader or the toolbar volume is released. It is saved as the node'saudio-gainattribute and restored on load and on reconnection.SetupContext::register_mixing_impl). The model offered them on every audio outlet.DataStreamWriter::atDelimiterOrEndmakes added fields optional.Execution → interface
Execution::Telemetry(score-plugin-engine), withProcess::TelemetryInterfacein score-lib-process, reads libossia's telemetry arena at the document's coarse rate, under the execution-update setting. Meters can be asked for on outlets, inlets, hardware inputs and outputs, and virtual ports. The benchmark goes through bench taps, which lifts the old per-50-ticks hash-map copy.executing()has model effects);Real-time safety and lifetimes
audio_engine::run_between_ticks), or are prepared and swapped in. The audio device edits, adds and removes ports this way while playing, in place, because running processes hold pointers to them.DeviceInterface::nodeAboutToBeRemoved).Also
AudioDevice::portsChangedis emitted when a port is added, edited or removed.Worth a second opinion
AudioDevice::reconnectnow returns early when the tree was built for the same engine (held as aweak_ptr) and channel counts.updateSettingsdisconnects first, so a settings change still rebuilds, and an engine restart is another engine.Tests
New or extended:
test_unit_telemetry_exec: meters, a virtual port removed while playing, 200 ports removed while playing (once, under a second, with and without a device rebuild), and gain, pan and upmix propagation;test_unit_audio_port_edit: live edits, and a gain that is undone, redone and survives a JSON reload;test_integration_interval_mute_solo;test_unit_avnd_control_outputs_ui;test_unit_faust_dsp: generators with and without a cable;test_unit_lv2_loading: control outputs through the triple buffer;test_unit_datastream_compat.Known red before any of this, unchanged:
js_remove_cable(the test profile has no default package).🤖 Generated with Claude Code
https://claude.ai/code/session_01HKbSZft9tvQ2sm9KeSchMX