summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--src/Makefile.am6
-rw-r--r--src/midi.c91
-rw-r--r--src/midi.h17
-rw-r--r--src/synth.c10
-rw-r--r--src/synth_engine.c43
-rw-r--r--src/synth_engine.h7
-rw-r--r--src/synth_gui.c36
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);