diff options
author | gramanas <anastasis.gramm2@gmail.com> | 2023-04-20 18:24:49 +0300 |
---|---|---|
committer | gramanas <anastasis.gramm2@gmail.com> | 2023-04-20 18:24:49 +0300 |
commit | c161e1016a04566a56b4858ea43502fed88dcc7f (patch) | |
tree | b4b3086f000df6b63229dae6057f1aa253811b30 | |
parent | 1e374b426b4182c7b73b35219aadaca267b9623c (diff) | |
download | synth-project-c161e1016a04566a56b4858ea43502fed88dcc7f.tar.gz synth-project-c161e1016a04566a56b4858ea43502fed88dcc7f.tar.bz2 synth-project-c161e1016a04566a56b4858ea43502fed88dcc7f.zip |
Add midi
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | src/Makefile.am | 6 | ||||
-rw-r--r-- | src/midi.c | 91 | ||||
-rw-r--r-- | src/midi.h | 17 | ||||
-rw-r--r-- | src/synth.c | 10 | ||||
-rw-r--r-- | src/synth_engine.c | 43 | ||||
-rw-r--r-- | src/synth_engine.h | 7 | ||||
-rw-r--r-- | src/synth_gui.c | 36 |
8 files changed, 151 insertions, 60 deletions
diff --git a/configure.ac b/configure.ac index 7df60c7..ebf1d63 100644 --- a/configure.ac +++ b/configure.ac @@ -27,6 +27,7 @@ AC_PROG_CC dnl AC_CHECK_HEADERS([stdlib.h]) AC_CHECK_HEADERS([raylib.h]) AC_CHECK_HEADERS([portaudio.h]) +AC_CHECK_HEADERS([portmidi.h]) dnl AC_CHECK_FUNCS([memset]) dnl AC_CHECK_FUNCS([strcasecmp]) diff --git a/src/Makefile.am b/src/Makefile.am index 91e1be5..3aff1d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,6 +6,8 @@ common_sources = filter.c \ lowpass.c \ lowpass.h \ Makefile.am \ + midi.c \ + midi.h \ notes.h \ raygui.h \ synth_engine.c \ @@ -24,10 +26,10 @@ common_sources = filter.c \ # -fno-math-errno and stuff like that, without enabling # -funsafe-math-optimizations. Some FP code can get big speedups from # fast-math, like auto-vectorization. -AM_CFLAGS = -O3 -march=native -fno-math-errno -funroll-loops -flto +AM_CFLAGS = -O3 -march=native -fno-math-errno -funroll-loops -flto -pthread synth_SOURCES = synth.c $(common_sources) -synth_LDADD = -lportaudio -lrt -lm -lasound -lraylib +synth_LDADD = -lportaudio -lrt -lm -lasound -lraylib -lportmidi #cookbook_SOURCES = cookbook.c $(common_sources) #cookbook_SOURCES = cookbook.c $(common_sources) #cook_SOURCES = cook.c $(common_sources) diff --git a/src/midi.c b/src/midi.c new file mode 100644 index 0000000..46608cb --- /dev/null +++ b/src/midi.c @@ -0,0 +1,91 @@ +#include "midi.h" + +#include <string.h> + +void midi_decode(uint32_t msg) { + // printf("MIDI message: 0x%X\n", msg); + uint8_t status = msg; + uint8_t data1 = (msg >> 8) & 0xFF; + uint8_t data2 = (msg >> 16) & 0xFF; + uint8_t channel = (status & 0x0F) + 1; // convert to human + uint8_t message = status >> 4; + + switch (message) { + case 0x08: + printf("Note Off: channel=%d, note=%d, velocity=%d\n", channel, data1, data2); + break; + case 0x09: + printf("Note On: channel=%d, note=%d, velocity=%d\n", channel, data1, data2); + break; + case 0x0A: + printf("Aftertouch: channel=%d, note=%d, pressure=%d\n", channel, data1, data2); + break; + case 0x0B: + printf("Control Change: channel=%d, controller=%d, value=%d\n", channel, data1, data2); + break; + case 0x0C: + printf("Program Change: channel=%d, program=%d\n", channel, data1); + break; + case 0x0D: + printf("Channel Pressure: channel=%d, pressure=%d\n", channel, data1); + break; + case 0x0E: + printf("Pitch Bend: channel=%d, value=%d\n", channel, ((data2 << 7) | data1) - 8192); + break; + default: + printf("Unknown MIDI message\n"); + break; + } +} + + +void +midiCallback(PtTimestamp timestamp, void *userData) { + midi_t * m = (midi_t *)userData; + + if (!m->stream) return; + + if (!Pm_Poll(m->stream)) return; + + PmEvent buf; + Pm_Read(m->stream, &buf, 1); + + //printf("%d\n", buf.message); + midi_decode(buf.message); + +} + +void +init_midi(midi_t *m, synth_t *synth) +{ + m->stream = NULL; + + Pm_Initialize(); + + printf("midi devs: %d\n", Pm_CountDevices()); + const PmDeviceInfo *info; + int i; + for (i = 0; i < Pm_CountDevices(); i++) { + info = Pm_GetDeviceInfo(i); + printf("%d: %s [input: %d output: %d opened: %d is_virt:%d] (interf: %s) -- %d\n", i, info->name, info->input, info->output, info->opened, info->is_virtual, info->interf, Pm_GetDefaultInputDeviceID()); + //if (!strcmp("MPK225 MIDI", info->name) && !info->input) break; + if (!strcmp("MPK225 Port A", info->name) && info->input == 1) break; + } + + Pt_Start(1, midiCallback, m); + + Pm_OpenInput(&(m->stream), + i, //Pm_GetDefaultInputDeviceID(), + NULL, + 128, + NULL, + NULL); +} + +void +terminate_midi(midi_t *m) +{ + Pm_Close(m->stream); + Pt_Stop(); + Pm_Terminate(); +} diff --git a/src/midi.h b/src/midi.h new file mode 100644 index 0000000..fd9b749 --- /dev/null +++ b/src/midi.h @@ -0,0 +1,17 @@ +#ifndef MIDI_H +#define MIDI_H + +#include <portmidi.h> +#include <porttime.h> + +#include "synth_engine.h" + +typedef struct midi_t { + PortMidiStream * stream; + synth_t * synth; +} midi_t; + +void init_midi(midi_t *midi, synth_t *synth); +void terminate_midi(midi_t *midi); + +#endif /* MIDI_H */ diff --git a/src/synth.c b/src/synth.c index 5ce932f..29c44e7 100644 --- a/src/synth.c +++ b/src/synth.c @@ -27,16 +27,20 @@ #include <string.h> #include <portaudio.h> +#include <portmidi.h> + /* graphics */ /* synth */ #include "synth_engine.h" #include "synth_gui.h" +#include "midi.h" #define NUM_SECONDS (1) #define WAVE_SIZE (44100) + static void StreamFinished( void* synthData ) { @@ -44,6 +48,7 @@ StreamFinished( void* synthData ) printf( "Stream Completed\n"); } + int main(void) { PaStreamParameters outputParameters; @@ -51,7 +56,10 @@ main(void) { PaError err; int i; synth_t synth; + midi_t midi; + init_synth(&synth); + init_midi(&midi, &synth); err = Pa_Initialize(); @@ -107,6 +115,8 @@ main(void) { return err; error: Pa_Terminate(); + free_synth(&synth); + terminate_midi(&midi); fprintf( stderr, "An error occurred while using the portaudio stream\n" ); fprintf( stderr, "Error number: %d\n", err ); fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); diff --git a/src/synth_engine.c b/src/synth_engine.c index 6bd6bf6..56ab771 100644 --- a/src/synth_engine.c +++ b/src/synth_engine.c @@ -152,14 +152,11 @@ make_sample(unsigned long long phase, void *synthData, unsigned int sample_rate, //LFO! //if (synth->adsr.elapsed > SAMPLE_RATE / 2) synth->adsr.elapsed = 0; - if (synth->poly) { - int n = synth->notes_active; - for (int i = 0; i < n; i++) { - sample += (1.0 / n) * synth->gen[synth->geni](synth->freq[i] + synth->freq_offset, synth->freq_count[i], synth->x, sample_rate); - } - } else { - sample = synth->gen[synth->geni](synth->n.freq + synth->freq_offset, phase, synth->x, sample_rate); - } + /* int n = synth->actual_notes_active; */ + /* for (int i = 0; i < n; i++) { */ + /* sample += (1.0 / n) * synth->gen[synth->geni](synth->actual_freq[i] + synth->freq_offset, synth->actual_freq_count[i], synth->x, sample_rate); */ + /* } */ + sample = synth->gen[synth->geni](synth->n.freq + synth->freq_offset, phase, synth->x, sample_rate); if (!viz && synth->filter) { // ALLL THE FILTERS @@ -197,7 +194,7 @@ sound_gen(const void *inputBuffer, void *outputBuffer, (void) statusFlags; (void) inputBuffer; - float s; + float s = 0.0f; for( unsigned long i=0; i<framesPerBuffer; i++ ) { //get_portaudio_frame(outputBuffer, synth); if (!synth->active) { @@ -205,29 +202,29 @@ sound_gen(const void *inputBuffer, void *outputBuffer, *out++ = 0.0f; continue; } + if (adsr_amplitude(synth, synth->adsr.elapsed) == 0 && synth->n.noteOff != 0) { - //printf("SYNTH OPFF\n"); synth->active = 0; *out++ = 0.0f; *out++ = 0.0f; continue; } + s = make_sample(synth->n.elapsed, synth, SAMPLE_RATE, 0); *out++ = s; *out++ = s; + synth->adsr.elapsed++; synth->n.elapsed++; - for (int j = 0; j < synth->notes_active; j++) { - synth->freq_count[j]++; - } if (!synth->multi) { - for (int j = 0; j < synth->notes_active; j++) { - if (synth->freq_count[j] >= (1.0 / synth->freq[i]) * SAMPLE_RATE) synth->freq_count[j] = 0; - } if (synth->n.elapsed >= (1.0 / synth->n.freq) * SAMPLE_RATE) synth->n.elapsed = 0; } else { } + synth->viz.wi >= 1000199 ? synth->viz.wi = 0 : synth->viz.wi++; + synth->viz.wave[synth->viz.wi] = s; + synth->viz.wi >= 1000199 ? synth->viz.wi = 0 : synth->viz.wi++; + synth->viz.wave[synth->viz.wi] = s; } return paContinue; @@ -239,12 +236,6 @@ init_synth(synth_t * synth) synth->freq_offset = 0; synth->gain = 1; synth->x = 1; - - synth->notes_active = 0; - for (int i = 0; i<100;i++) { - synth->freq[i] = 0; - synth->freq_count[i] = 0; - } synth->n.freq = 0; synth->n.noteOn = 0; @@ -277,8 +268,16 @@ init_synth(synth_t * synth) synth->active = 0; synth->viz.sample_rate_divider = 1; + synth->viz.wi = 0; LowPass_Init(); synth->fff = create_bw_low_pass_filter(2, SAMPLE_RATE, 400); synth->fff2 = create_bw_band_stop_filter(8, SAMPLE_RATE, 15000, 22000); } + +void +free_synth(synth_t * synth) +{ + free_bw_low_pass(synth->fff); + free_bw_band_stop(synth->fff2); +} diff --git a/src/synth_engine.h b/src/synth_engine.h index 1191892..86cfe1a 100644 --- a/src/synth_engine.h +++ b/src/synth_engine.h @@ -40,6 +40,8 @@ typedef struct { typedef struct { int sample_rate_divider; + float wave[1000200]; + int wi; } viz_t; typedef struct { @@ -47,10 +49,6 @@ typedef struct { float gain; float x; - int notes_active; - float freq[100]; - unsigned long long freq_count[100]; - note_t n; adsr_t adsr; @@ -78,6 +76,7 @@ typedef struct { float adsr_amplitude(void *synthData, unsigned long long elapsed); float make_sample(unsigned long long phase, void *synthData, unsigned int sample_rate, int viz); void init_synth(synth_t * synth); +void free_synth(synth_t * synth); int sound_gen(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, diff --git a/src/synth_gui.c b/src/synth_gui.c index d09a9b6..6b44595 100644 --- a/src/synth_gui.c +++ b/src/synth_gui.c @@ -14,16 +14,9 @@ set_note(void *synthData, float note, PaTime time, int key) synth->n.noteOn = time; synth->n.noteOff = 0; synth->n.elapsed = 0; - if (synth->poly) { - if (!synth->notes_active) synth->adsr.elapsed = 0; - } else { - synth->adsr.elapsed = 0; - } + synth->adsr.elapsed = 0; synth->active = 1; - - synth->freq[synth->notes_active] = note; - synth->freq_count[synth->notes_active++] = 0; } @@ -43,35 +36,11 @@ keyboard(void *synthData, PaStream *stream) set_note(synth, note, Pa_GetStreamTime(stream), keys[i]); //printf("Note On : %s[%d] %fHz\n", int_to_note(i % 12), (synth->octave + (i / 12)) % 8, note); } - printf("["); - for (int j = 0; j < synth->notes_active; j++) { - printf("%f ", synth->freq[j]); - } - printf("]\n"); } } for (int i = 0; keys[i]; i++) { if (IsKeyReleased(keys[i])) { note = notes[i % 12][(synth->octave + (i / 12)) % 8]; - int j; - for (j = 0; j < synth->notes_active; j++) { - // printf("Comparing freq(j): %f to note: %f\n", synth->freq[j], note); - if (synth->freq[j] == note) { - synth->freq[j] = 0; - synth->notes_active--; - //printf("Note down %f\n", note); - break; - } - } - for (int k = j; k < synth->notes_active + 1; k++) { - //printf("moving %f <-- %f\n", synth->freq[k], synth->freq[k + 1]); - synth->freq[k] = synth->freq[k + 1]; - } - printf("["); - for (int j = 0; j < synth->notes_active; j++) { - printf("%f ", synth->freq[j]); - } - printf("]\n"); if (synth->n.key == keys[i]) { synth->n.noteOff = Pa_GetStreamTime(stream); //printf("Note Off: %s[%d]\n", int_to_note(i % 12), (synth->octave + (i / 12)) % 8); @@ -112,6 +81,9 @@ draw_signals(synth_t * synth, int x, int y, int width, int height) GuiSpinner((Rectangle){ x+ 100, y - 24 - 6, 100, 24 }, "rate divider: ", &(synth->viz.sample_rate_divider), 1, 10, 0); int period = (1 / (synth->n.freq + 1)) * SAMPLE_RATE / synth->viz.sample_rate_divider; + for (int i = x; i < WIDTH - x; i++) { + DrawCircle(i , y + height / 2 + floor(50 * synth->viz.wave[x * (SAMPLE_RATE / 2)]), point_radius, MAGENTA); + } if (synth->multi) { for (int i = x; i < WIDTH - x; i++) { DrawCircle(i , y + height / 2 + floor(50 * make_sample(i - x, synth, SAMPLE_RATE / synth->viz.sample_rate_divider, 1)), point_radius, RED); |