/* * Brickworks * * Copyright (C) 2023, 2024 Orastron Srl unipersonale * * Brickworks is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * Brickworks is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Brickworks. If not, see . * * File author: Stefano D'Angelo */ /*! * module_type {{{ utility }}} * version {{{ 1.0.1 }}} * requires {{{ bw_common }}} * description {{{ * Simple data structure that helps keeping track of note on/off events and * pressed key status. * * It is not concerned with timing. * }}} * changelog {{{ * * }}} */ #ifndef BW_NOTE_QUEUE_H #define BW_NOTE_QUEUE_H #include #ifdef __cplusplus extern "C" { #endif /*** Public API ***/ /*! api {{{ * #### bw_note_queue_status * ```>>> */ typedef struct { char pressed; float velocity; } bw_note_queue_status; /*! <<<``` * Note status: * * `pressed`: whether the note is pressed (non-`0`) or not (`0`); * * `velocity`: velocity in [`0.f`, `1.f`] or otherwise negative to * indicate unknown / not available. * * #### bw_note_queue_event * ```>>> */ typedef struct { unsigned char note; char went_off; } bw_note_queue_event; /*! <<<``` * Note on/off event: * * `note`: note number in [`0`, `127`]; * * `went_off`: whether a note off event fired on the same note (non-`0`) * or not (`0`) -- see `bw_note_queue`. * * #### bw_note_queue * ```>>> */ typedef struct { bw_note_queue_event events[128]; bw_note_queue_status status[128]; unsigned char n_events; unsigned char n_pressed; } bw_note_queue; /*! <<<``` * Note on/off event queue and pressed key status: * * `events`: events since the reset/clear -- the order is not meaningful * and it contains maximum one event per note number, so that the last * event added for a given note overwrites the previous if it exists; * `went_off` is set to non-`0` in case of a note off event or when * overwriting an event whose `went_off` was already non-`0`; * * `status`: current status of all notes; * * `n_events`: number of elements in `events`; * * `n_pressed`: number of currently pressed keys. * * #### bw_note_queue_reset() * ```>>> */ static inline void bw_note_queue_reset( bw_note_queue * BW_RESTRICT queue); /*! <<<``` * Clear both the event queue (no events) and the note statuses (all notes * off, all velocities `0.f`) in `queue`. * * #### bw_note_queue_clear() * ```>>> */ static inline void bw_note_queue_clear( bw_note_queue * BW_RESTRICT queue); /*! <<<``` * Clears the event queue (no events) in `queue` without affecting the note * statuses. * * #### bw_note_queue_add() * ```>>> */ static inline void bw_note_queue_add( bw_note_queue * BW_RESTRICT queue, unsigned char note, char pressed, float velocity, char force_went_off); /*! <<<``` * Adds a new event to `queue` with the specified `note` number, `pressed` * value, and `velocity`. * * If `force_went_off` is set to non-`0`, `went_off` is always set to * non-`0`. * * #### bw_note_queue_is_valid() * ```>>> */ static inline char bw_note_queue_is_valid( const bw_note_queue * BW_RESTRICT queue); /*! <<<``` * Tries to determine whether `queue` is valid and returns non-`0` if it * seems to be the case and `0` if it is certainly not. False positives are * possible, false negatives are not. * * `queue` must at least point to a readable memory block of size greater * than or equal to that of `bw_note_queue`. * }}} */ #ifdef __cplusplus } #endif /*** Implementation ***/ /* WARNING: This part of the file is not part of the public API. Its content may * change at any time in future versions. Please, do not use it directly. */ #ifdef __cplusplus extern "C" { #endif static inline void bw_note_queue_reset( bw_note_queue * BW_RESTRICT queue) { BW_ASSERT(queue != BW_NULL); for (int i = 0; i < 128; i++) { queue->status[i].pressed = 0; queue->status[i].velocity = 0.f; } queue->n_events = 0; queue->n_pressed = 0; BW_ASSERT_DEEP(bw_note_queue_is_valid(queue)); } static inline void bw_note_queue_clear( bw_note_queue * BW_RESTRICT queue) { BW_ASSERT(queue != BW_NULL); BW_ASSERT_DEEP(bw_note_queue_is_valid(queue)); queue->n_events = 0; } static inline void bw_note_queue_add( bw_note_queue * BW_RESTRICT queue, unsigned char note, char pressed, float velocity, char force_went_off) { BW_ASSERT(queue != BW_NULL); BW_ASSERT_DEEP(bw_note_queue_is_valid(queue)); BW_ASSERT(note < 128); BW_ASSERT(bw_is_finite(velocity) && velocity <= 1.f); if (!pressed && !queue->status[note].pressed) return; unsigned char i; for (i = 0; i < queue->n_events; i++) if (queue->events[i].note == note) break; char went_off = 0; if (i == queue->n_events) queue->n_events++; else went_off = queue->events[i].went_off || !queue->status[note].pressed; queue->events[i].note = note; queue->events[i].went_off = went_off || force_went_off; if (pressed && !queue->status[note].pressed) queue->n_pressed++; else if (!pressed && queue->status[note].pressed) queue->n_pressed--; queue->status[note].pressed = pressed; queue->status[note].velocity = velocity; BW_ASSERT_DEEP(bw_note_queue_is_valid(queue)); } static inline char bw_note_queue_is_valid( const bw_note_queue * BW_RESTRICT queue) { BW_ASSERT(queue != BW_NULL); if (queue->n_events >= 128 || queue->n_pressed >= 128) return 0; for (unsigned char i = 0; i < queue->n_events; i++) { const bw_note_queue_event *ev = queue->events + i; if (ev->note >= 128) return 0; for (unsigned char j = 0; j < i; j++) { const bw_note_queue_event *ev2 = queue->events + j; if (ev2->note == ev->note) return 0; } } unsigned char cnt = 0; for (unsigned char i = 0; i < 128; i++) { const bw_note_queue_status *st = queue->status + i; if (st->pressed) cnt++; if (!bw_is_finite(st->velocity) || st->velocity > 1.f) return 0; } return cnt == queue->n_pressed; } #ifdef __cplusplus } /*** Public C++ API ***/ namespace Brickworks { /*! api_cpp {{{ * ##### Brickworks::NoteQueue * ```>>> */ class NoteQueue { public: NoteQueue(); void clear(); void add( unsigned char note, bool pressed, float velocity, bool forceWentOff); bw_note_queue queue; }; /*! <<<``` * }}} */ /*** Implementation ***/ /* WARNING: This part of the file is not part of the public API. Its content may * change at any time in future versions. Please, do not use it directly. */ inline NoteQueue::NoteQueue() { bw_note_queue_reset(&queue); } inline void NoteQueue::clear() { bw_note_queue_clear(&queue); } inline void NoteQueue::add( unsigned char note, bool pressed, float velocity, bool forceWentOff) { bw_note_queue_add(&queue, note, pressed, velocity, forceWentOff); } } #endif #endif