Haiku BMidiText: Timestamped MIDI Diagnostics Without Hiding Latency
Use Haiku BMidiText as a human-readable MIDI diagnostic sink while accounting for millisecond timing, stdout blocking, pacing, and SysEx volume.
BMidiText is a concrete BMidi implementation that prints incoming MIDI events as readable text. It is useful for checking whether notes, controller values, and system messages reach a test process. It is not a binary recorder, a structured event log, a MIDI protocol validator, or a low-latency monitoring tool. Its behavior includes timestamp pacing and printf() output, both of which affect the path being observed.
The class implements callbacks for note-on/off, pressure, control change, program change, pitch bend, system exclusive, system common, and real-time messages. For each event, the implementation waits until its timestamp and prints a line to standard output. This makes it convenient in a terminal during bring-up, but potentially disruptive when attached to a live endpoint that has a strict timing budget.
Connect it as a diagnostic consumer
BMidi objects can connect to other BMidi objects through Connect(). A BMidiText instance can therefore serve as a human-readable destination in a test graph. Keep ownership and disconnection explicit so no producer can deliver an event after the sink has been destroyed.
BMidiText trace;
source.Connect(&trace);
// Exercise the MIDI source while observing the terminal output.
source.Disconnect(&trace);
This sketch shows the connection boundary. For an asynchronous producer, stop or quiesce it before disconnecting and destroying the text sink. Avoid holding a MIDI, device, or UI lock while waiting for output. printf() can block on a full pipe or slow terminal, so a diagnostic sink can perturb timing or stall the producer path depending on the calling architecture.
Interpret timestamps and timer reset correctly
The BMidi API uses uint32 timestamps, and B_NOW is defined as system time in milliseconds. BMidiText::ResetTimer() controls the start point used for the printed relative offset. When its start time is zero, the implementation initializes the start point from the first event timestamp. If ResetTimer(true) is used, it captures B_NOW as the start point. Interpret the printed prefix as a relative diagnostic value, not a high-resolution hardware timestamp.
The underlying timing granularity is milliseconds, and a 32-bit value can wrap in a sufficiently long-running system. Do not compare this text output directly to microsecond media times or BTimeCode labels. If precise synchronization matters, capture event timestamps into a structured record with a clearly defined time base and use a monotonic high-resolution source.
The implementation calls SnoozeUntil(time) before printing each event. That is useful when replaying scheduled events into a human-readable trace, but it means the sink intentionally waits. A test that connects this sink to a live producer may therefore observe behavior different from a sink that simply records without pacing. Choose the diagnostic topology to match the question you are asking.
Read the printed fields without inferring more than they say
The output labels channel, note, velocity, controller, program, pressure, pitch-bend bytes, and system status fields. Those lines help answer whether an event callback fired and what values reached it. They are not a canonical serialization format. Do not parse the human-readable lines as a stable interchange protocol; the spelling and formatting are implementation details.
Pitch bend is printed as least-significant and most-significant bytes, not necessarily as one normalized signed value. If your analysis needs a centered numeric bend, combine and scale the bytes according to MIDI semantics in your own parser. Similarly, the trace’s channel numbering and raw controller values should be mapped explicitly in the UI rather than mistaken for descriptive names.
System exclusive messages are printed as hexadecimal bytes. A large payload can generate substantial stdout volume and block for a noticeable period. Truncate only in a separate diagnostic layer and clearly report truncation; never silently assume the entire payload was represented by a short line.
The text sink is best used to answer narrow questions: did the event arrive, what raw values did it carry, and in what approximate order did the callbacks occur? It does not show device identity, endpoint configuration, or whether the sound-producing synth rendered the note. Pair it with roster or endpoint diagnostics when investigating routing, and with a capture file when the exact sequence must be replayed.
For a malformed-looking trace, verify the producer first. MIDI channel values, note numbers, and controller values are raw numeric data; a human-readable label is not automatically a semantic interpretation. Pitch bend is split into two bytes by this class, while the combined 14-bit representation requires a separate calculation. Keep the raw trace to preserve evidence and derive normalized values in a separate analysis stage.
Keep diagnostics from changing the system under test
Do not use BMidiText as the only evidence in a real-time performance test. Its wait-and-print behavior adds scheduling and I/O work. For timing analysis, connect a bounded recorder that timestamps and buffers events, then write the trace from a non-critical thread. Compare the result with a separate run that has no observer attached to determine how much the diagnostic sink changes throughput.
Protect sensitive data in logs. SysEx messages can contain device-specific or proprietary payloads; a raw hex dump may expose more than the operator expects. Provide an explicit opt-in for full payload logging and a bounded default summary with message length and status.
If stdout is consumed by a script, isolate MIDI trace output from normal command output or route the process through a dedicated logging pipe. A terminal display can reorder the apparent visual timing due to buffering and scheduling. Preserve event timestamps in the data model if you need to reconstruct ordering offline.
Verify the diagnostic path
Test one event of every supported callback family, a burst of same-time messages, events scheduled in the future, a stream with out-of-order timestamps, a large SysEx payload, and sink disconnection during shutdown. Check that output includes the values expected from the source and that a consumer slow-down is visible rather than silently dropping events.
Compare a BMidiText trace with a BMidiStore capture or a hardware monitor. Any disagreement may come from different time bases, connection topology, event ordering, or the text sink’s pacing. Record the Haiku revision and whether the source was live, imported, or synthesized.
Use a bounded test stream to check that the text output remains manageable under dense controller automation. Count lines and bytes, and exercise a producer with bursts rather than only one note at a time. If the terminal or pipe falls behind, the measurement itself is saturated; move detailed traces to a buffered recorder and keep the live console summary sampled.
Validate the sink’s lifecycle as a small graph: producer creation, connection, event delivery, producer stop, disconnection, then object destruction. If the source can invoke callbacks concurrently, determine whether the output interleaves and whether that is acceptable for diagnosis. The printed format does not add sequence numbers, so simultaneous callbacks may be difficult to reconstruct after the fact. Add an outer recorder with its own sequence counter when precise ordering is part of the incident evidence.
When reporting a bug, include a short trace of the smallest event sequence that reproduces it and note whether the text sink was pacing events. Do not attach an unbounded SysEx dump by default. A compact reproduction is easier to compare across Haiku revisions and avoids turning stdout logs into a second source of timing pressure.
Acceptance criteria
Accept BMidiText for interactive diagnosis when producers are quiesced before teardown, output is treated as human-readable, timestamps are interpreted as millisecond diagnostics, and the system under test tolerates pacing and stdout I/O. Use a separate bounded recorder for performance or loss-sensitive evidence.
BMidiText makes MIDI callbacks visible. It does not provide a stable log schema or a non-perturbing measurement path.
Related:
- Haiku MIDI Kit: Roster-Based Endpoints, Connections, and Timestamped Events
- Haiku BMidiStore: Sequence Capture, Event Ordering, and MIDI File Limits
Sources: