#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; }