Skip to content
Haiku OSDeep Dive Published Updated 6 min readViews unavailable

Haiku BParameterWeb: Describing Typed Controls on Media Nodes

Model Haiku media-node controls with BParameterWeb groups, stable parameter IDs, typed values, time-stamped updates, and change notifications.

Haiku’s Media Kit has a control model for nodes that expose gain, mute, track selection, bitrate, or other media parameters. BParameterWeb describes the controls as a typed hierarchy. It is a schema for discovery and presentation, not a promise that every value can be changed instantly or that every node implements every advertised control. The node remains responsible for reporting current state, applying requests at the requested performance time, and telling interested clients when state changes.

The web is a description, not a widget tree

A BParameterWeb contains one or more BParameterGroup objects, and groups contain parameter objects or nested groups. BContinuousParameter, BDiscreteParameter, BTextParameter, and BNullParameter represent different kinds of control metadata. The model includes an integer ID, a media type, a human-facing name, a kind string, and type-specific data such as range, step, choices, or text capacity.

The hierarchy lets a control panel build a suitable user interface without hardcoding each add-on’s controls. It should not turn parameter names into permanent identifiers: use the parameter ID and type from the node’s web. IDs need to remain stable for the lifetime of the node instance so a request cannot accidentally address a different setting after a refresh. If an add-on changes its web, clients should re-enumerate rather than reuse stale pointers.

The BParameterWeb header exposes methods to create groups, count groups and parameters, and retrieve objects by index. These returned pointers belong to the web’s object graph; do not delete them independently or retain them after the web is replaced. If a UI reads the web while the node can mutate it, use the BControllable::LockParameterWeb() and UnlockParameterWeb() contract and keep the lock only long enough to copy the metadata needed by the UI model.

Construct a web in the node

An add-on node that implements BControllable supplies its web during construction with the protected SetParameterWeb() method. The header explicitly says to call it from the constructor. Build the full hierarchy first, then register the web. Do not make a visible control panel race a half-constructed model.

BParameterWeb* web = new BParameterWeb;
BParameterGroup* audio = web->MakeGroup("Audio");
audio->MakeContinuousParameter(kGainId, B_MEDIA_RAW_AUDIO,
    "Output gain", B_GAIN, "linear", 0.0f, 1.0f, 0.01f);
audio->MakeDiscreteParameter(kMuteId, B_MEDIA_RAW_AUDIO,
    "Mute", B_MUTE);

status_t status = SetParameterWeb(web);
if (status != B_OK)
    return status;

The code demonstrates the API shape. Use actual IDs owned by the node and the correct media type and unit; the range is illustrative, not a recommendation for all gain controls. Current upstream BControllable::SetParameterWeb() installs the pointer as fWeb and deletes the prior web, so do not delete the new web after handing it to the node. Verify the ownership behavior for the target release before adapting error cleanup, and never free a web that the node still exposes to clients.

Continuous values need an explicit range and step. Discrete controls should expose items that map to valid values, while text parameters require a byte limit and appropriate encoding policy. A value label such as “High” should not be used as the data representation when the API expects an integer choice. Keep UI-friendly labels separate from the underlying ID/value pair.

Read and write values through the node contract

Parameter metadata and parameter state are different things. BControllable declares GetParameterValue() and SetParameterValue() as node hooks. The getter reports a value and the time of its last change; the setter receives an ID, a performance-time argument, bytes, and a size. A request can therefore be scheduled rather than applied at the instant a UI sends it.

Before sending a request, validate the parameter ID against the current web, ensure the value representation matches ValueType(), validate the byte count, and check the range or discrete choices. A control panel should not send arbitrary bytes simply because SetParameterValue() accepts a pointer and size. On the node side, reject unknown IDs and malformed values without changing unrelated state. Copy any bytes that must survive beyond the call’s lifetime.

Use the control protocol’s timing deliberately. A timed gain change should use the media performance-time domain that the node and graph share; a UI wall-clock timestamp is not an equivalent substitute. If the node cannot honor a future time, report that limitation accurately rather than acknowledging a value that never takes effect. The Media Kit headers define control messages and parameter update helpers, but the exact supported scheduling behavior is node-specific.

Keep panels synchronized without flooding the graph

When the actual control changes, BroadcastChangedParameter() is for a structural/control change such as a selector’s available tracks changing. BroadcastNewParameterValue() announces a new value with its performance time. The header cautions not to broadcast too densely because messages can flood the system. Send notifications when state meaningfully changes; do not emit an update for every internal sample or redraw tick unless that is genuinely the control model.

There are two directions to keep straight: a client can request a value, and a node can report when the effective value changes. A request is not itself proof that the hardware or signal path accepted the value. Read back state or wait for the node’s change notification, and present pending/error state where possible. For a physical selector, inserted media may change the available choices independently of the UI; broadcast the change and rebuild the corresponding menu.

The headers also describe control-information buffers. A BControllable that receives parameter data through a BBufferConsumer should call ApplyParameterData() from its BufferReceived() path. This is a graph transport route for timestamped control changes, not the same thing as a slow panel RPC. Keep callback work bounded and avoid blocking on the UI while applying a change.

Flattening and serialization boundaries

BParameterWeb and its child types implement BFlattenable. That makes flattening available for the Media Kit’s transport and storage needs, but it does not mean an application should persist raw flattened bytes as a durable settings format. The schema is tied to the node and its parameter IDs; add-on updates can change controls. For user preferences, store a versioned application-level mapping of known node identity and parameter values, and tolerate a missing or incompatible parameter at restore time.

Do not mistake a flattened web for the live node. It describes controls; it does not preserve an open media connection, a device reservation, or an effective runtime value. A client reconnecting later should rediscover the node, retrieve its web, validate each saved parameter against current metadata, and only then attempt to restore it.

Test the edges, not only the slider

Test empty or unavailable webs, duplicate IDs, unknown IDs, wrong value sizes, values outside a continuous range, a discrete choice disappearing, an update scheduled in the future, a node rejecting a request, and a panel closing while a change is pending. Exercise concurrent web readers and node-side updates using the lock contract. Confirm that a new-value notification does not get interpreted as user input and echoed back in an infinite loop.

Log node ID, parameter ID, type code, request time, payload length, returned status, and observed change time. Avoid logging sensitive text values if a node exposes them. For a control that cannot be verified through readback, state that limitation in the UI instead of showing a false “applied” status.

BParameterWeb is most useful when treated as a typed, refreshable description attached to a live node. Keep metadata and values distinct, validate every request, honor performance-time semantics, and use notifications to keep clients synchronized without confusing intent with effect.

Related:

Sources:

Comments