2023-06-05 09:30:38 +00:00
|
|
|
/*
|
|
|
|
* Brickworks
|
|
|
|
*
|
|
|
|
* Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* File author: Stefano D'Angelo
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* module_type {{{ utility }}}
|
2023-07-21 06:56:27 +00:00
|
|
|
* version {{{ 0.6.0 }}}
|
|
|
|
* requires {{{ bw_common }}}
|
2023-06-05 09:30:38 +00:00
|
|
|
* description {{{
|
2023-07-12 08:52:16 +00:00
|
|
|
* Simple data structure that helps keeping track of note on/off events and
|
|
|
|
* pressed key status.
|
2023-06-05 09:30:38 +00:00
|
|
|
*
|
2023-07-12 08:52:16 +00:00
|
|
|
* It is not concerned with timing.
|
2023-06-05 09:30:38 +00:00
|
|
|
* }}}
|
|
|
|
* changelog {{{
|
|
|
|
* <ul>
|
2023-07-21 06:56:27 +00:00
|
|
|
* <li>Version <strong>0.6.0</strong>:
|
|
|
|
* <ul>
|
2023-07-25 06:52:01 +00:00
|
|
|
* <li>Added debugging code.</li>
|
2023-07-21 06:56:27 +00:00
|
|
|
* <li>Removed dependency on bw_config.</li>
|
|
|
|
* </ul>
|
|
|
|
* </li>
|
2023-06-05 09:30:38 +00:00
|
|
|
* <li>Version <strong>0.5.0</strong>:
|
|
|
|
* <ul>
|
|
|
|
* <li>First release.</li>
|
|
|
|
* </ul>
|
|
|
|
* </li>
|
|
|
|
* </ul>
|
|
|
|
* }}}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef _BW_NOTE_QUEUE_H
|
|
|
|
#define _BW_NOTE_QUEUE_H
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C" {
|
|
|
|
#endif
|
|
|
|
|
2023-06-05 16:41:14 +00:00
|
|
|
#include <bw_common.h>
|
|
|
|
|
2023-06-05 09:30:38 +00:00
|
|
|
/*! api {{{
|
|
|
|
* #### bw_note_queue_status
|
|
|
|
* ```>>> */
|
2023-06-05 16:41:14 +00:00
|
|
|
typedef struct {
|
2023-06-05 09:30:38 +00:00
|
|
|
char pressed;
|
2023-06-05 16:41:14 +00:00
|
|
|
float velocity; // negative = unknown / not available
|
|
|
|
} bw_note_queue_status;
|
2023-06-05 09:30:38 +00:00
|
|
|
/*! <<<```
|
2023-07-12 08:52:16 +00:00
|
|
|
* Note status:
|
|
|
|
* * `pressed`: whether the note is pressed (non-`0`) or not (`0`);
|
|
|
|
* * `velocity`: velocity in [`0.f`, `1.f`].
|
|
|
|
*
|
2023-06-05 09:30:38 +00:00
|
|
|
* #### bw_note_queue_event
|
|
|
|
* ```>>> */
|
2023-06-05 16:41:14 +00:00
|
|
|
typedef struct {
|
2023-06-05 09:30:38 +00:00
|
|
|
unsigned char note;
|
|
|
|
bw_note_queue_status status;
|
|
|
|
char went_off;
|
2023-06-05 16:41:14 +00:00
|
|
|
} bw_note_queue_event;
|
2023-06-05 09:30:38 +00:00
|
|
|
/*! <<<```
|
2023-07-12 08:52:16 +00:00
|
|
|
* Note on/off event:
|
|
|
|
* * `note`: note number in [`0`, `127`];
|
|
|
|
* * `status`: note status;
|
|
|
|
* * `went_off`: whether a note off event fired on the same note (non-`0`)
|
|
|
|
* or not (`0`) -- see `bw_note_queue`.
|
|
|
|
*
|
2023-06-05 09:30:38 +00:00
|
|
|
* #### bw_note_queue
|
|
|
|
* ```>>> */
|
2023-06-05 16:41:14 +00:00
|
|
|
typedef struct {
|
2023-06-05 09:30:38 +00:00
|
|
|
bw_note_queue_event events[128];
|
|
|
|
unsigned char n_events;
|
|
|
|
bw_note_queue_status status[128];
|
|
|
|
unsigned char n_pressed;
|
2023-06-05 16:41:14 +00:00
|
|
|
} bw_note_queue;
|
2023-06-05 09:30:38 +00:00
|
|
|
/*! <<<```
|
2023-07-12 08:52:16 +00:00
|
|
|
* 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`;
|
|
|
|
* * `n_events`: number of elements in `events`;
|
|
|
|
* * `status`: current status of all notes;
|
|
|
|
* * `n_pressed`: number of currently pressed keys.
|
|
|
|
*
|
2023-06-05 09:30:38 +00:00
|
|
|
* #### bw_note_queue_reset()
|
|
|
|
* ```>>> */
|
2023-06-05 16:41:14 +00:00
|
|
|
static inline void bw_note_queue_reset(bw_note_queue *BW_RESTRICT queue);
|
2023-06-05 09:30:38 +00:00
|
|
|
/*! <<<```
|
2023-07-12 08:52:16 +00:00
|
|
|
* Clear both the event queue (no events) and the note statuses (all notes
|
|
|
|
* off, all velocities `0.f`) in `queue`.
|
|
|
|
*
|
2023-06-05 09:30:38 +00:00
|
|
|
* #### bw_note_queue_clear()
|
|
|
|
* ```>>> */
|
2023-06-05 16:41:14 +00:00
|
|
|
static inline void bw_note_queue_clear(bw_note_queue *BW_RESTRICT queue);
|
2023-06-05 09:30:38 +00:00
|
|
|
/*! <<<```
|
2023-07-12 08:52:16 +00:00
|
|
|
* Clears the event queue (no events) in `queue` without affecting the note
|
|
|
|
* statuses.
|
|
|
|
*
|
2023-06-05 09:30:38 +00:00
|
|
|
* #### bw_note_queue_add()
|
|
|
|
* ```>>> */
|
2023-06-05 16:41:14 +00:00
|
|
|
static inline void bw_note_queue_add(bw_note_queue *BW_RESTRICT queue, unsigned char note, char pressed, float velocity, char force_went_off);
|
2023-06-06 06:08:39 +00:00
|
|
|
/*! <<<```
|
2023-07-12 08:52:16 +00:00
|
|
|
* 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`.
|
2023-07-25 06:52:01 +00:00
|
|
|
*
|
|
|
|
* #### bw_note_queue_add()
|
|
|
|
* ```>>> */
|
2023-07-25 13:03:29 +00:00
|
|
|
static inline char bw_note_queue_is_valid(const bw_note_queue *BW_RESTRICT queue);
|
2023-07-25 06:52:01 +00:00
|
|
|
/*! <<<```
|
|
|
|
* WRITEME
|
2023-06-06 06:08:39 +00:00
|
|
|
* }}} */
|
2023-06-05 09:30:38 +00:00
|
|
|
|
|
|
|
/*** 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. */
|
|
|
|
|
2023-06-05 16:41:14 +00:00
|
|
|
static inline void bw_note_queue_reset(bw_note_queue *BW_RESTRICT queue) {
|
2023-07-25 06:52:01 +00:00
|
|
|
BW_ASSERT(queue != NULL);
|
2023-06-08 07:35:36 +00:00
|
|
|
for (int i = 0; i < 128; i++)
|
|
|
|
queue->status[i] = (bw_note_queue_status){ .pressed = 0, .velocity = 0.f };
|
2023-06-05 09:30:38 +00:00
|
|
|
queue->n_pressed = 0;
|
2023-06-08 07:35:36 +00:00
|
|
|
queue->n_events = 0;
|
2023-07-25 06:52:01 +00:00
|
|
|
BW_ASSERT_DEEP(bw_note_queue_is_valid(queue));
|
2023-06-05 09:30:38 +00:00
|
|
|
}
|
|
|
|
|
2023-06-05 16:41:14 +00:00
|
|
|
static inline void bw_note_queue_clear(bw_note_queue *BW_RESTRICT queue) {
|
2023-07-25 06:52:01 +00:00
|
|
|
BW_ASSERT(queue != NULL);
|
|
|
|
BW_ASSERT_DEEP(bw_note_queue_is_valid(queue));
|
2023-06-05 09:30:38 +00:00
|
|
|
queue->n_events = 0;
|
|
|
|
}
|
|
|
|
|
2023-06-05 16:41:14 +00:00
|
|
|
static inline void bw_note_queue_add(bw_note_queue *BW_RESTRICT queue, unsigned char note, char pressed, float velocity, char force_went_off) {
|
2023-07-25 06:52:01 +00:00
|
|
|
BW_ASSERT(queue != NULL);
|
|
|
|
BW_ASSERT_DEEP(bw_note_queue_is_valid(queue));
|
|
|
|
BW_ASSERT(note < 128);
|
|
|
|
BW_ASSERT(bw_is_finite(velocity) && velocity <= 1.f);
|
|
|
|
|
2023-06-05 09:30:38 +00:00
|
|
|
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
|
2023-06-08 07:35:36 +00:00
|
|
|
went_off = queue->events[i].went_off || !queue->events[i].status.pressed;
|
2023-06-05 09:30:38 +00:00
|
|
|
|
2023-06-08 07:35:36 +00:00
|
|
|
queue->events[i] = (bw_note_queue_event){ .note = note, .status = { pressed, velocity }, .went_off = went_off || force_went_off };
|
2023-06-05 09:30:38 +00:00
|
|
|
if (pressed && !queue->status[note].pressed)
|
|
|
|
queue->n_pressed++;
|
|
|
|
else if (!pressed && queue->status[note].pressed)
|
|
|
|
queue->n_pressed--;
|
2023-06-08 07:35:36 +00:00
|
|
|
queue->status[note] = (bw_note_queue_status){ .pressed = pressed, .velocity = velocity };
|
2023-07-25 06:52:01 +00:00
|
|
|
|
|
|
|
BW_ASSERT_DEEP(bw_note_queue_is_valid(queue));
|
|
|
|
}
|
|
|
|
|
2023-07-25 13:03:29 +00:00
|
|
|
static inline char bw_note_queue_is_valid(const bw_note_queue *BW_RESTRICT queue) {
|
2023-07-25 06:52:01 +00:00
|
|
|
BW_ASSERT(queue != NULL);
|
|
|
|
|
|
|
|
if (queue->n_events >= 128 || queue->n_pressed >= 128)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < (int)queue->n_events; i++) {
|
2023-07-30 07:48:56 +00:00
|
|
|
const bw_note_queue_event *ev = queue->events + i;
|
2023-07-25 06:52:01 +00:00
|
|
|
if (ev->note >= 128 || !bw_is_finite(ev->status.velocity) || ev->status.velocity > 1.f)
|
|
|
|
return 0;
|
|
|
|
for (int j = 0; j < i; j++) {
|
2023-07-30 07:48:56 +00:00
|
|
|
const bw_note_queue_event *ev2 = queue->events + j;
|
2023-07-25 06:52:01 +00:00
|
|
|
if (ev2->note == ev->note)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int cnt = 0;
|
|
|
|
for (int i = 0; i < 128; i++) {
|
2023-07-30 07:48:56 +00:00
|
|
|
const bw_note_queue_status *st = queue->status + i;
|
2023-07-25 06:52:01 +00:00
|
|
|
if (st->pressed)
|
|
|
|
cnt++;
|
|
|
|
if (!bw_is_finite(st->velocity) || st->velocity > 1.f)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cnt == queue->n_pressed;
|
2023-06-05 09:30:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#endif
|