Skip to content

Possible bugfix: Make Osirus/Ostirus controller dumps address page B by part - #290

Open
eynsai wants to merge 1 commit into
dsp56300:mainfrom
eynsai:virus-ctrldump-pageb-channel
Open

eynsai wants to merge 1 commit into
dsp56300:mainfrom
eynsai:virus-ctrldump-pageb-channel

Conversation

@eynsai

@eynsai eynsai commented Aug 31, 2026 •

Copy link
Copy Markdown

Background

REQUEST_CONTROLLER_DUMP (0x37) asks the device to report a part's edit buffer as a burst of individual controller messages rather than as a sysex dump. buildControllerDumpResponse (virusLib/microcontroller.cpp) builds that reply: page A goes out as control change, page B as poly pressure.

Page A carries the part number in the status byte's low nibble. Page B does not — it always goes out on nibble 0, whichever part was requested.

The inconsistency

Three places in microcontroller.cpp decide how a part is addressed in page A/B controller traffic. Two of them agree with each other and the third does not:

code nibble
incoming page B, sendMIDI singleMode ? SINGLE : channel is the part number
outgoing dump, page A M_CONTROLCHANGE + channel is the part number
outgoing dump, page B M_POLYPRESSURE always 0

The device's own receive path treats the page B nibble as a part index. The dump builder honours that convention on the line for page A and drops it on the line directly below. Nothing in the file explains the asymmetry, and no in-plugin consumer appears to depend on it.

The change

-for (const auto cc : g_pageB)  _responses.emplace_back(MidiEventSource::Device, M_POLYPRESSURE, cc, single[cc + 128], 0);
+for (const auto cc : g_pageB)  _responses.emplace_back(MidiEventSource::Device, M_POLYPRESSURE + channel, cc, single[cc + 128], 0);

channel is the same local the page A line already uses: the requested part, or 0 for the SINGLE slot.

Effect

A controller dump requested for part N now delivers both halves of that part's edit buffer to part N. Before this, page A landed on part N and page B landed on part 0, so a dump for any part other than 0 silently wrote half its payload into the wrong part's parameters.

Requesting a dump for part 0, or for the SINGLE slot in single mode, is unaffected — those already resolve to nibble 0.

Testing

Verified in-situ on live Osirus and OsTIrus instances, against builds with and without this change.

The method relies on a raw sysex reaching the device without refreshing the plugin's parameter cache. A PAGE_B parameter-change message moves a value on the device only, leaving the cache deliberately stale; the controller dump is then the only thing that can resynchronise it, so watching which part catches up reads the addressing decision directly. All parts are seeded with known values first, so a mis-route shows up as the wrong part moving rather than as an ambiguous no-op, and each test asserts the cache did not move before the dump was requested.

Test Without this change With it
PAGE_B write to part 3, dump part 3 value appears on part 0 value appears on part 3
PAGE_B write to part 0, dump part 0 value appears on part 0 unchanged

The second row is the control: part 0 was already the correct destination there, so a change would have indicated the fix overshot. The page A phases of the same battery are byte-identical with and without this change, confirming it does not disturb the half that was already correct.

Interaction between three related PRs

I have submitted three PRs affecting Osirus/Ostirus MIDI channel handling, and they are easiest to reason about together:

The table below considers the effects of various combinations of these three PRs on MIDI handling.

  • Behavior 1: In single mode, if the global MIDI channel is set to channel G != channel 0, and a MIDI-learned mapping is bound to channel G, which part does it resolve to? The same resolution also drives program change routing and the parameter overlay indicators.
  • Behavior 2: In single mode, if the global MIDI channel is set to channel G != channel 0, and CC is sent to that channel, which part is written?
  • Behavior 3: In single mode, if the global MIDI channel is set to channel G != channel 0, and CC is sent to channel 0, which part is written?
  • Behavior 4: In multi mode, if a dump of part N != part 0 is requested, which part do the page B parameters get written to?

Two behaviours are correct in every combination and are left out of the table below: the single-mode controller dump, and the page A half of a multi-mode dump.

Row #285 #290 #291 Behavior 1 Behavior 2 Behavior 3 Behavior 4
1 no no no no part, wrong part G, wrong part 0, wrong part 0, wrong
2 no no yes no part, wrong dropped, wrong part 0, wrong part 0, wrong
3 no yes no no part, wrong part G, wrong part 0, wrong part N, correct
4 no yes yes no part, wrong dropped, wrong part 0, wrong part N, correct
5 yes no no part 0, correct part G, wrong part 0, wrong part 0, wrong
6 yes no yes part 0, correct part 0, correct dropped, correct part 0, wrong
7 yes yes no part 0, correct part G, wrong part 0, wrong part N, correct
8 yes yes yes part 0, correct part 0, correct dropped, correct part N, correct

Row 1 is current behavior, and no row regresses against it, so any subset may be merged in any order. Row 2 is the only one that can look like a regression, since #291 alone drops a control change that is delivered today, but today it is delivered to the wrong part, so neither matches the device.

#290 is independent: it flips Behavior 4 identically in every row. #285 independently fixes Behavior 1. For Behaviors 2 and 3, however, #285 and #291 are complementary rather than independent, and neither alone produces the correct result.

AI Disclosure: This PR was made with the help of an AI coding assistant.

@eynsai eynsai changed the title Make controller dumps address page B by part Possible bugfix: Make controller dumps address page B by part Aug 31, 2026
@eynsai eynsai changed the title Possible bugfix: Make controller dumps address page B by part Possible bugfix: Make Osirus/Ostirus controller dumps address page B by part Aug 31, 2026
@eynsai
eynsai force-pushed the virus-ctrldump-pageb-channel branch from cec5d03 to 341b7a7 Compare September 5, 2026 00:53
@eynsai
eynsai force-pushed the virus-ctrldump-pageb-channel branch from 341b7a7 to 43060a7 Compare September 13, 2026 02:07
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.

1 participant