diff options
| author | grm <grm@eyesin.space> | 2025-10-19 12:45:07 +0300 |
|---|---|---|
| committer | grm <grm@eyesin.space> | 2025-10-19 12:45:07 +0300 |
| commit | 2875bd24390d48d2af44071869757b6f1ba35498 (patch) | |
| tree | 35cc617dba5cbae9ac63cd72b064c690a1b22338 | |
| parent | bb13b4a409578cf1e9fbca727196f88249fc4b0b (diff) | |
| download | synth-project-2875bd24390d48d2af44071869757b6f1ba35498.tar.gz synth-project-2875bd24390d48d2af44071869757b6f1ba35498.tar.bz2 synth-project-2875bd24390d48d2af44071869757b6f1ba35498.zip | |
| -rw-r--r-- | b.c | 24 | ||||
| -rw-r--r-- | src/sequencer.c | 171 |
2 files changed, 195 insertions, 0 deletions
@@ -52,6 +52,14 @@ synth_libs(B_Cmd *cmd) inlcude_dirs(cmd); } +void +sequencer_libs(B_Cmd *cmd) +{ + b_cmd_append(cmd, "-lportaudio", "-lportmidi", "-lrt", "-lraylib", "-lpthread"); + + inlcude_dirs(cmd); +} + void cc(B_Cmd *cmd) { b_cmd_append(cmd, "clang"); @@ -84,6 +92,8 @@ build_c(B_Cmd* cmd, b_da_append_many(cmd, input_paths, input_paths_len); if (0 == strcmp(output_path, BUILD_DIR"synth")) synth_libs(cmd); + if (0 == strcmp(output_path, BUILD_DIR"sequencer")) + sequencer_libs(cmd); return b_cmd_run_sync(*cmd); } @@ -215,6 +225,16 @@ main(int argc, char *argv[]) "src/os.c", }; + const char *sequencer_deps[] = { + "src/midi.h", + "src/synth_common.h", + }; + + const char* sequencer_paths[] = { + "src/midi.c", + "src/sequencer.c", + }; + const char *templates[] = { "tmpl/index.html" }; @@ -235,6 +255,10 @@ main(int argc, char *argv[]) B_ARRAY_LEN(synth_deps), BUILD_DIR "synth")) return 1; + if (!build_c(&cmd, sequencer_paths, B_ARRAY_LEN(sequencer_paths), sequencer_deps, + B_ARRAY_LEN(sequencer_deps), BUILD_DIR "sequencer")) + return 1; + if (tests && !build_tests(&cmd, BUILD_DIR "test")) return 1; 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 <stdlib.h> +#include <portmidi.h> +#include <porttime.h> +#include <stdio.h> +#include <string.h> +#include <raylib.h> +#include <pthread.h> +#include <math.h> + +#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; +} |
