Created
September 28, 2025 13:29
-
-
Save jamesb93/d5662bb757dc1d678cf3b874d9e48d86 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "ext.h" | |
| #include "ext_obex.h" | |
| #include "ext_time.h" | |
| typedef struct _note_state t_note_state; | |
| typedef struct _note_debounce { | |
| t_object x_obj; | |
| double threshold; | |
| void *out_midi; | |
| t_note_state *note_states[128]; | |
| } t_note_debounce; | |
| struct _note_state { | |
| t_note_debounce *owner; | |
| long pitch; | |
| long velocity; | |
| t_clock *clock; | |
| char is_pending; | |
| }; | |
| void *note_debounce_new(t_symbol *s, long argc, t_atom *argv); | |
| void note_debounce_free(t_note_debounce *x); | |
| void note_debounce_assist(t_note_debounce *x, void *b, long m, long a, char *s); | |
| void note_debounce_list(t_note_debounce *x, t_symbol *s, long argc, t_atom *argv); | |
| void note_debounce_clock_tick(t_note_state *note_state); | |
| static t_class *s_note_debounce_class; | |
| void ext_main(void *r) { | |
| t_class *c = class_new("note.debounce", (method)note_debounce_new, (method)note_debounce_free, | |
| (long)sizeof(t_note_debounce), 0L, A_GIMME, 0); | |
| class_addmethod(c, (method)note_debounce_list, "list", A_GIMME, 0); | |
| class_addmethod(c, (method)note_debounce_assist, "assist", A_CANT, 0); | |
| CLASS_ATTR_DOUBLE(c, "threshold", 0, t_note_debounce, threshold); | |
| CLASS_ATTR_LABEL(c, "threshold", 0, "Debounce Threshold (ms)"); | |
| CLASS_ATTR_FILTER_MIN(c, "threshold", 0.0); | |
| CLASS_ATTR_DEFAULT(c, "threshold", 0, "50.0"); | |
| CLASS_ATTR_STYLE_LABEL(c, "threshold", 0, "text", "Threshold:"); | |
| class_register(CLASS_BOX, c); | |
| s_note_debounce_class = c; | |
| } | |
| void *note_debounce_new(t_symbol *s, long argc, t_atom *argv) { | |
| t_note_debounce *x = (t_note_debounce *)object_alloc(s_note_debounce_class); | |
| if (!x) { | |
| return NULL; | |
| } | |
| x->out_midi = listout((t_object *)x); | |
| for (int i = 0; i < 128; i++) { | |
| t_note_state *ns = (t_note_state *)sysmem_newptr(sizeof(t_note_state)); | |
| if (ns) { | |
| ns->owner = x; | |
| ns->pitch = i; | |
| ns->velocity = 0; | |
| ns->is_pending = 0; | |
| ns->clock = clock_new(ns, (method)note_debounce_clock_tick); | |
| x->note_states[i] = ns; | |
| } else { | |
| object_error((t_object *)x, "failed to allocate memory for note state %d", i); | |
| x->note_states[i] = NULL; | |
| } | |
| } | |
| attr_args_process(x, argc, argv); | |
| return x; | |
| } | |
| void note_debounce_free(t_note_debounce *x) { | |
| for (int i = 0; i < 128; i++) { | |
| if (x->note_states[i]) { | |
| clock_free(x->note_states[i]->clock); | |
| sysmem_freeptr(x->note_states[i]); | |
| } | |
| } | |
| } | |
| void note_debounce_assist(t_note_debounce *x, void *b, long m, long a, char *s) { | |
| if (m == ASSIST_INLET) { | |
| switch (a) { | |
| case 0: snprintf(s, 256, "(list) MIDI Note (pitch, velocity)"); break; | |
| } | |
| } else { | |
| snprintf(s, 256, "(list) Debounced MIDI Note"); | |
| } | |
| } | |
| void note_debounce_list(t_note_debounce *x, t_symbol *s, long argc, t_atom *argv) { | |
| if (argc < 2) { | |
| object_warn((t_object *)x, "list must contain at least pitch and velocity"); | |
| return; | |
| } | |
| long pitch = atom_getlong(argv); | |
| long velocity = atom_getlong(argv + 1); | |
| if (pitch < 0 || pitch > 127) { | |
| object_warn((t_object *)x, "pitch %ld is out of MIDI range (0-127)", pitch); | |
| return; | |
| } | |
| t_note_state *note = x->note_states[pitch]; | |
| if (!note) return; | |
| if (velocity > 0) { | |
| if (note->is_pending) { | |
| clock_unset(note->clock); | |
| } | |
| note->velocity = velocity; | |
| note->is_pending = 1; | |
| clock_delay(note->clock, x->threshold); | |
| } else { | |
| if (note->is_pending) { | |
| clock_unset(note->clock); | |
| note->is_pending = 0; | |
| note->velocity = 0; | |
| } else { | |
| t_atom out_atoms[2]; | |
| atom_setlong(&out_atoms[0], pitch); | |
| atom_setlong(&out_atoms[1], 0); | |
| outlet_list(x->out_midi, NULL, 2, out_atoms); | |
| note->velocity = 0; | |
| } | |
| } | |
| } | |
| void note_debounce_clock_tick(t_note_state *note_state) { | |
| if (!note_state) return; | |
| if (note_state->is_pending) { | |
| note_state->is_pending = 0; | |
| t_note_debounce *x = note_state->owner; | |
| t_atom out_atoms[2]; | |
| atom_setlong(&out_atoms[0], note_state->pitch); | |
| atom_setlong(&out_atoms[1], note_state->velocity); | |
| outlet_list(x->out_midi, NULL, 2, out_atoms); | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment