Hi there,
I have a general question about code architecture for an API I'm doing.
Basically, it listens to MIDI messages from a native library and I use an FFI binding to forward the messages to the user. A message is represented by an array of bytes, and from that you can describe its data and how to interpret it. I made a layer that converts the raw data to a typed object, but what I want to do is offer a choice to the user so he can retrieve either the raw data or the converted typed object.
What I have so far is like that :
interface InputCallbackParams<T extends Message<MessageData> | number[]> {
message: T;
deltaTime?: number;
}
export interface InputCallbackOptions {
callback?: (params: InputCallbackParams<Message<MessageData>>) => void;
rawCallback?: (params: InputCallbackParams<number[]>) => void;
}
And the method that the user can use to listen to messages :
onMessage(
options: InputCallbackOptions,
): void {
this.callback = Deno.UnsafeCallback.threadSafe(
RtMidiCCallbackCallbackDefinition,
(
deltaTime: number,
message: Deno.PointerValue<unknown>,
messageSize: number | bigint,
) => {
const msg_data = new Uint8Array(
new Deno.UnsafePointerView(message!).getArrayBuffer(
messageSize as number,
),
);
if (options.callback !== undefined) {
options.callback({
message: decodeMessage(msg_data),
deltaTime,
});
}
if (options.rawCallback !== undefined) {
options.rawCallback({
message: Array.from(msg_data),
deltaTime,
});
}
},
);
rtmidi.rtmidi_in_set_callback(this.device, this.callback!.pointer, null);
}
And I wonder if there's a better way to do that and if it looks understandable as an API user. Any thoughts welcome π