From 2875bd24390d48d2af44071869757b6f1ba35498 Mon Sep 17 00:00:00 2001 From: grm Date: Sun, 19 Oct 2025 12:45:07 +0300 Subject: Initial sequencer --- src/sequencer.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 src/sequencer.c (limited to 'src') diff --git a/src/sequencer.c b/src/sequencer.c new file mode 100644 index 0000000..5687640 --- /dev/null +++ b/src/sequencer.c @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "synth_common.h" + +#define MIDI_NOTE_ON 0x90 +#define MIDI_NOTE_OFF 0x80 +#define MIDI_CHANNEL 0 +#define SLEEP_INTERVAL_MS 2.0 // main loop sleep + + + +//------------------------------------------------- +// Helper for time math +//------------------------------------------------- + +typedef struct step_t { + PmTimestamp note_on; + PmTimestamp note_off; + float duration; + int note; +} step_t; + +typedef struct pattert_t { + step_t *steps; + uint n; // total + uint c; // current +} pattern_t; + +typedef struct sequencer_t { + PortMidiStream *stream; + int exitted; + + float bpm; + int steps; + pattern_t *pattern; +} sequencer_t; + +sequencer_t * +init_sequencer() { + sequencer_t *seq = (sequencer_t *)malloc(sizeof(sequencer_t)); + + seq->exitted = 0; + seq->bpm = 100.0f; + seq->steps = 16; + + seq->pattern = (pattern_t *)malloc(sizeof(pattern_t)); + seq->pattern->steps = (step_t *)malloc(16 * sizeof(step_t)); + seq->pattern->n = 16; + seq->pattern->c = 0; + + for (uint i = 0; i < seq->pattern->n; i++) { + step_t *step = &seq->pattern->steps[i]; + step->note = 40 + i; + } + + seq->stream = NULL; + PmError err = Pm_Initialize(); + if (err != pmNoError) { + fprintf(stdout, "Pm Error: %s\n", Pm_GetErrorText(err)); + } + + const PmDeviceInfo *info; + int i; + for (i = 0; i < Pm_CountDevices(); i++) { + info = Pm_GetDeviceInfo(i); + if (!info->output) { + continue; + } + printf("%d: %s [input: %d output: %d opened: %d is_virt:%d] (interface: %s)\n", i, info->name, info->input, info->output, info->opened, info->is_virtual, info->interf); + if (0 == strcmp("Midi Through Port-0", info->name)) + break; + } + + printf("Selected device: %s [input: %d output: %d opened: %d is_virt:%d] (interf: %s)\n", info->name, info->input, info->output, info->opened, info->is_virtual, info->interf); + + //no start for output Pt_Start(1, midiCallback, m); + err = Pm_OpenOutput(&(seq->stream), i, NULL, 512, NULL, NULL, 0); + + if (err != pmNoError) { + fprintf(stdout, "Pm Error: %s\n", Pm_GetErrorText(err)); + } + + return seq; +} + +void +free_sequencer(sequencer_t * seq) { + Pm_WriteShort(seq->stream, 0, Pm_Message(0xFC /* MIDI_STOP */, 0, 0)); + + Pm_Close(seq->stream); + Pm_Terminate(); + + free(seq); +} + +static inline double ms_per_beat(sequencer_t * seq) { + return 60000.0 / seq->bpm; +} +static inline double ms_per_step(sequencer_t * seq) { + return ms_per_beat(seq) / seq->steps; +} + +void +rayrun(sequencer_t * seq) { + (void)seq; + SetTraceLogLevel(LOG_ERROR); + InitWindow(WIDTH, HEIGHT, "Raylib sequencer"); + SetTargetFPS(60); + while (!WindowShouldClose()) { + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(BLACK); + + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + seq->exitted = 1; + CloseWindow(); +} + +void * +sequencer_thread(void *arg) +{ + sequencer_t *seq = (sequencer_t *)arg; + + while (!seq->exitted) { + //PmTimestamp current_time = Pt_Time(); + + step_t *step = &seq->pattern->steps[seq->pattern->c++]; + if (seq->pattern->c >= seq->pattern->n) seq->pattern->c = 0; + + Pm_WriteShort(seq->stream, 0, Pm_Message(MIDI_NOTE_ON | MIDI_CHANNEL, step->note, 100)); + + Pt_Sleep(ms_per_step(seq)*4); + Pm_WriteShort(seq->stream, 0, Pm_Message(MIDI_NOTE_OFF | MIDI_CHANNEL, step->note, 0)); + + //Pt_Sleep(SLEEP_INTERVAL_MS); + } + + printf("[Sequencer] Stopping.\n"); + return NULL; +} + + +int +main(void) { + sequencer_t * seq = init_sequencer(); + + pthread_t seq_thread; + + // start sequencer thread + pthread_create(&seq_thread, NULL, sequencer_thread, seq); + + // start gui + rayrun(seq); + + pthread_join(seq_thread, NULL); + + free_sequencer(seq); + + return 0; +} -- cgit v1.2.3