Skip to main content

c_src/midiio_recv.h

/*
 * midiio_recv.h — the raw receive seam (arc3/slice1).
 *
 * THE INBOUND SEAM, mirror of midiio_send.h. midiio_msg_to_bytes is the single C
 * entry point the recv callback funnels a parsed mm_message through: message in,
 * exact wire bytes out. It is the one symbol re-pointed at native mm_in_open_raw
 * when upstream ships it — at which point this interim adapter is deleted and the
 * recv callback above the seam is unchanged.
 *
 * SysEx is NOT handled here: msg->sysex points into a callback-lifetime buffer
 * (Finding A.3 / NIF-LEARNINGS L04), so the caller memcpys those bytes into the
 * Erlang binary *during* the callback. This function reconstructs every
 * fixed-shape message from the parsed fields and returns its length (1..3);
 * MM_SYSEX (and any unknown type) returns 0, signalling "use msg->sysex".
 *
 * Derived by reading mm_out_send's switch (minimidio.h:903) backwards — the exact
 * inverse of midiio_send.h's adapter. Include AFTER minimidio.h.
 */
#ifndef MIDIIO_RECV_H
#define MIDIIO_RECV_H

static inline size_t midiio_msg_to_bytes(const mm_message *msg, uint8_t buf[3])
{
    switch (msg->type) {
        /* Channel voice with two data bytes: status = (type<<4)|channel. */
        case MM_NOTE_OFF:
        case MM_NOTE_ON:
        case MM_POLY_PRESSURE:
        case MM_CONTROL_CHANGE:
        case MM_PITCH_BEND:
            buf[0] = (uint8_t)(((uint8_t)msg->type << 4) | (msg->channel & 0x0F));
            buf[1] = msg->data[0];
            buf[2] = msg->data[1];
            return 3;

        /* Channel voice with one data byte. */
        case MM_PROGRAM_CHANGE:
        case MM_CHANNEL_PRESSURE:
            buf[0] = (uint8_t)(((uint8_t)msg->type << 4) | (msg->channel & 0x0F));
            buf[1] = msg->data[0];
            return 2;

        /* System common — unique status bytes (not a nibble shift). */
        case MM_MTC_QUARTER_FRAME:
            buf[0] = 0xF1; buf[1] = msg->data[0]; return 2;
        case MM_SONG_POSITION:
            buf[0] = 0xF2;
            buf[1] = (uint8_t)(msg->song_position & 0x7F);
            buf[2] = (uint8_t)((msg->song_position >> 7) & 0x7F);
            return 3;
        case MM_SONG_SELECT:
            buf[0] = 0xF3; buf[1] = msg->data[0]; return 2;
        case MM_TUNE_REQUEST:
            buf[0] = 0xF6; return 1;

        /* System real-time — single status byte. */
        case MM_CLOCK:        buf[0] = 0xF8; return 1;
        case MM_START:        buf[0] = 0xFA; return 1;
        case MM_CONTINUE:     buf[0] = 0xFB; return 1;
        case MM_STOP:         buf[0] = 0xFC; return 1;
        case MM_ACTIVE_SENSE: buf[0] = 0xFE; return 1;
        case MM_RESET:        buf[0] = 0xFF; return 1;

        /* SysEx (and anything unrecognised): the caller uses msg->sysex. */
        case MM_SYSEX:
        default:
            return 0;
    }
}

#endif /* MIDIIO_RECV_H */