summaryrefslogtreecommitdiffstats
path: root/src/synth_gui.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/synth_gui.c')
-rw-r--r--src/synth_gui.c317
1 files changed, 271 insertions, 46 deletions
diff --git a/src/synth_gui.c b/src/synth_gui.c
index c210f2b..bb394c7 100644
--- a/src/synth_gui.c
+++ b/src/synth_gui.c
@@ -26,9 +26,8 @@ draw_text(synth_t * synth, Font f, int x, int y)
}
void
-mouse(void *synthData, PaStream *stream)
+mouse(synth_t *synth, PaStream *stream)
{
- synth_t * synth = (synth_t *)synthData;
float m = GetMouseWheelMove();
int x = 0;
if (m < 0) x = -1;
@@ -51,10 +50,8 @@ mouse(void *synthData, PaStream *stream)
}
void
-keyboard(void *synthData, PaStream *stream)
+keyboard(synth_t * synth, PaStream *stream)
{
- synth_t * synth = (synth_t *)synthData;
-
int keys[] = {KEY_Q, KEY_TWO, KEY_W, KEY_THREE, KEY_E, KEY_R, KEY_FIVE, KEY_T, KEY_SIX, KEY_Y, KEY_SEVEN, KEY_U, KEY_I, KEY_NINE, KEY_O, KEY_ZERO, KEY_P, KEY_LEFT_BRACKET, KEY_EQUAL, KEY_RIGHT_BRACKET, 0};
float note;
@@ -70,7 +67,16 @@ keyboard(void *synthData, PaStream *stream)
synth->midi_note[i].velocity = 1.0;
synth->midi_note[i].elapsed = 0;
synth->midi_note[i].active = 1;
-
+ int flag = 1;
+ for (int j = 0; j < synth->midi_active_n; j++) {
+ if (synth->midi_active[j] == &synth->midi_note[i]) {
+ flag = 0;
+ }
+ }
+ if (flag) {
+ synth->midi_active[synth->midi_active_n++] = &synth->midi_note[i];
+ }
+
//synth->adsr.elapsed = 0;
synth->active = 1;
}
@@ -171,66 +177,281 @@ draw_bars(synth_t * synth, int x, int y, int width, int height, int offset)
snprintf(buf, sizeof buf, "%.3f", synth->del_feedback);
synth->del_feedback = GuiSlider((Rectangle){ x, y + count++ * (height + offset), width, height }, "fb: ", buf, synth->del_feedback , 0.0f, 1.0f);
snprintf(buf, sizeof buf, "%.3f", synth->del_time);
- synth->del_time = GuiSlider((Rectangle){ x, y + count++ * (height + offset), width, height }, "del_time: ", buf, synth->del_time , 0.0f, 0.1f);
+ synth->del_time = GuiSlider((Rectangle){ x, y + count++ * (height + offset), width, height }, "del_time: ", buf, synth->del_time , 0.0f, 0.3f);
+}
+
+void frequencyToColor(float frequency, int *red, int *green, int *blue) {
+ // Assuming frequency ranges from 20 to 20000 Hz
+ float minFrequency = 20.0;
+ float maxFrequency = 20000.0;
+
+ // Map the frequency to a value between 0 and 1
+ float normalizedFrequency = (frequency - minFrequency) / (maxFrequency - minFrequency);
+
+ // Define colors for the rainbow spectrum
+ float hue = normalizedFrequency * 360.0;
+
+ // Convert HSV to RGB
+ float c = 1.0;
+ float x = c * (1.0 - fabs(fmod(hue / 60.0, 2.0) - 1.0));
+ float m = 0.0;
+
+ float r, g, b;
+
+ if (hue >= 0 && hue < 60) {
+ r = c;
+ g = x;
+ b = 0;
+ } else if (hue >= 60 && hue < 120) {
+ r = x;
+ g = c;
+ b = 0;
+ } else if (hue >= 120 && hue < 180) {
+ r = 0;
+ g = c;
+ b = x;
+ } else if (hue >= 180 && hue < 240) {
+ r = 0;
+ g = x;
+ b = c;
+ } else if (hue >= 240 && hue < 300) {
+ r = x;
+ g = 0;
+ b = c;
+ } else {
+ r = c;
+ g = 0;
+ b = x;
+ }
+
+ *red = (int)((r + m) * 255);
+ *green = (int)((g + m) * 255);
+ *blue = (int)((b + m) * 255);
+}
+
+#include "fftw3.h"
+
+float
+amp(float re, float im) {
+ float a = fabsf(re);
+ float b = fabsf(im);
+ if (a < b) return b;
+ return a;
}
void
-draw_signals(synth_t * synth, int x, int y, int width, int height)
+draw_adsr(synth_t *synth, int x, int y, int width, int height)
{
- DrawRectangleLines(x, y, width, height, BLACK);
+ int x_prev = x;
+ for (int i = 0; i < synth->midi_active_n; i++) {
+ int rec_y = y + height - 1 +
+ 4.5 * floor( (WIDTH / 24) *
+ - fix_adsr(&synth->adsr,
+ synth->midi_active[i]->noteOn,
+ synth->midi_active[i]->noteOff,
+ synth->midi_active[i]->elapsed,
+ synth->midi_active[i]->noteOffSample));
+
+ int rec_width = (WIDTH - x - x) / synth->midi_active_n;
+ int rec_height = HEIGHT - rec_y;
- int point_radius = 1.5;
+ int red, green, blue;
+ frequencyToColor(synth->midi_active[i]->freq * 25, &red, &green, &blue);
+ DrawRectangleGradientV(x_prev, rec_y - 10, rec_width, rec_height, ColorAlpha(BLUE, .2) ,ColorAlpha((Color) {red, green, blue}, .2));
+ //DrawRectangleGradientV(x_prev, rec_y - 10, rec_width, rec_height, ColorAlpha(GREEN, .2) ,ColorAlpha(RED, .2));
+ //DrawRectangleLines(x_prev, rec_y - 10, rec_width, rec_height, GRAY);
+ x_prev += rec_width;
+ }
+}
- //GuiSpinner((Rectangle){ x+ 100, y - 24 - 6, 100, 24 }, "rate divider: ", &(synth->viz.sample_rate_divider), 1, 10, 0);
+int freeze;
- for (int i = x; i < WIDTH - x; i++) {
- if (synth->viz.wave[i - x] > 1 || synth->viz.wave[i - x] < -1)
- DrawCircle(i , y + height / 2 + floor(50 * synth->viz.wave[i - x]), point_radius, RED);
- else if (synth->viz.wave[i - x] >= 0.99 || synth->viz.wave[i - x] <= -0.99)
- DrawCircle(i , y + height / 2 + floor(50 * synth->viz.wave[i - x]), point_radius, MAGENTA);
- else
- DrawCircle(i , y + height / 2 + floor(50 * synth->viz.wave[i - x]), point_radius, RAYWHITE);
- }
- for (int i = x; i < WIDTH - x; i++) {
- DrawCircle(i , y + height / 2 + floor(5 * synth->fftviz.wave[i - x]) * -1, point_radius, RED);
+void
+draw_wave(synth_t *synth, int x, int y, int width, int height)
+{
+ Color col;
+ for (int i = 0; i < width; i++) {
+ for (int j = 0; j < synth->viz.rate_divider; j++) {
+ int ii = synth->viz.rate_divider * (i) + j;
+ if (synth->viz.wave_viz_buffer[ii] > 1 || synth->viz.wave_viz_buffer[ii] < -1)
+ col = RED;
+ else if (synth->viz.wave_viz_buffer[ii] >= 0.99 || synth->viz.wave_viz_buffer[ii] <= -0.99)
+ col = MAGENTA;
+ else
+ col = WHITE;
+ DrawPixel(i + x , y + height / 2 + floor(50 * synth->viz.wave_viz_buffer[ii + j]), col);
+ }
}
+ /* for (int j = 0; j < 100; j++) { */
+ /* DrawPixel(synth->viz.tmp_index / synth->viz.rate_divider + 20, j + y + height / 2, BLUE); */
+ /* DrawPixel(synth->viz.tmp_index / synth->viz.rate_divider + 20, -j + y + height / 2, BLUE); */
+ /* } */
+ //EndScissorMode();
+}
+
+void
+draw_fft(synth_t *synth, int x, int y, int width, int height)
+{
+ int viz_size = width * synth->viz.rate_divider;
+ int fft_output_len = viz_size / 2 + 1;
+
+ fftwf_complex* output = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * fft_output_len);
+
+ fftwf_plan forward_plan = fftwf_plan_dft_r2c_1d(fft_output_len, synth->viz.fft_input_buffer, output, FFTW_ESTIMATE);
+
+ fftwf_execute(forward_plan);
+
+ fftwf_destroy_plan(forward_plan);
+
+ // "Squash" into the Logarithmic Scale
+ float step = 1.06;
+ float lowf = 1.0f;
+ size_t m = 0;
+ float max_amp_tso = 1.0f;
+ for (float f = lowf; (size_t) f < fft_output_len/2; f = ceilf(f*step)) {
+ float f1 = ceilf(f*step);
+ float a = 0.0f;
+ for (size_t q = (size_t) f; q < fft_output_len/2 && q < (size_t) f1; ++q) {
+ float b = log(sqrt(output[q][0]*output[q][0] + output[q][1]*output[q][1]));
+ if (b > a) a = b;
+ }
+ if (max_amp_tso < a) max_amp_tso = a;
+ synth->viz.fft_output_buffer[m++] = a;
+ }
+
+ // Normalize Frequencies to 0..1 range
+ for (size_t i = 0; i < m; ++i) {
+ synth->viz.fft_output_buffer[i] /= max_amp_tso;
+ }
+
+ // Smooth out and smear the values
+ for (size_t i = 0; i < m; ++i) {
+ float smoothness = 4;
+ synth->viz.fft_smooth_buffer[i] += (synth->viz.fft_output_buffer[i] - synth->viz.fft_smooth_buffer[i])*smoothness*((float)1/30);
+ /* float smearness = 3; */
+ /* ss_smear[i] += (synth->viz.fft_smooth_buffer[i] - ss_smear[i])*smearness*((float)1/30); */
+ }
+ // Display the Bars
+ float cell_width = (float)width/m;
+ for (size_t i = 0; i < m; ++i) {
+ float t = synth->viz.fft_smooth_buffer[i];
+ float saturation = 0.75f;
+ float value = 1.0f;
+ float hue = (float)i/m;
+ Color color = ColorFromHSV(hue*360, saturation, value);
+ Vector2 startPos = {
+ x + i*cell_width + cell_width/2,
+ y + height - height*2/3*t,
+ };
+ Vector2 endPos = {
+ x + i*cell_width + cell_width/2,
+ y + height,
+ };
+ float thick = cell_width/3*sqrtf(t);
+ DrawLineEx(startPos, endPos, thick, BLUE);
+ }
- int c = 0;
- for (int i = 0; i < MIDI_NOTES; i++) {
- if (!synth->midi_note[i].active) continue;
- c++;
+ fftwf_free(output);
+}
+
+void
+draw_osc(synth_t * synth, int x, int y, int width, int height)
+{
+ DrawRectangleLines(x - 3, y - 3, width + 6, height + 6, GREEN);
+ float osc_wave[width];
+ for (int i = 0; i < width; i++) {
+ osc_wave[i] = 0;
}
- int x_prev = x;
- for (int i = 0; i < MIDI_NOTES; i++) {
- if (!synth->midi_note[i].active)
- continue;
+ for (int i = 0; i < synth->midi_active_n; i++) {
+ midi_note_t * note = synth->midi_active[i];
+ midi_note_t note_dup;
+ note_dup.freq = note->freq;
+ note_dup.channel = note->channel;
+ note_dup.noteOn = note->noteOn;
+ note_dup.noteOff = note->noteOff;
+ note_dup.velocity = note->velocity;
+ note_dup.wvt_index = synth->geni == 4 ? note->wvt_index : 0;
+ note_dup.lfo_index = note->lfo_index;
+ note_dup.elapsed = note->elapsed;
+ note_dup.noteOffSample = note->noteOffSample;
+ note_dup.adsr = note->adsr;
+ note_dup.active = note->active;
+ for (int i = 0; i < width; i++) {
+ float freq = SAMPLE_RATE/(float)width;
+ osc_wave[i] += synth->gen[synth->geni](freq, &note_dup, synth->x, SAMPLE_RATE);
+ }
+ break;
+ }
- int rec_y = y + height - 1 +
- 4.5 * floor( (WIDTH / 24) *
- - fix_adsr(&synth->adsr,
- synth->midi_note[i].noteOn,
- synth->midi_note[i].noteOff,
- synth->midi_note[i].elapsed,
- synth->midi_note[i].noteOffSample));
-
- int rec_width = (WIDTH - x - x) / c;
- int rec_height = HEIGHT - rec_y;
-
- //DrawLine(x_prev, v, x_prev + (WIDTH - x) / c, v , WHITE);
- DrawRectangleGradientV(x_prev, rec_y, rec_width, rec_height, ColorAlpha(GREEN, .2) ,ColorAlpha(RED, .2));
- DrawRectangleLines(x_prev, rec_y, rec_width, rec_height, GRAY);
- x_prev += rec_width;
+ float max = 0;
+ for (int i = 0; i < width; i++) {
+ if (fabs(osc_wave[i]) > max) max = fabs(osc_wave[i]);
}
+
+ if (synth->midi_active_n) {
+ for (int i = 0; i < width; i++) {
+ DrawPixel(i + x , y + height / 2 + floor(height/2 * osc_wave[i] / max), RED);
+ }
+ }
+
}
+void
+draw_signals(synth_t * synth, int x, int y, int width, int height)
+{
+ DrawRectangleLines(x, y, width, height, WHITE);
+
+ if (synth->viz.wave_enabled || synth->viz.spectrum_enabled)
+ synth->viz.rate_divider = GuiSlider((Rectangle){ x + (width / 2) / 2, y - 12, width / 2, 12 }, "", NULL, synth->viz.rate_divider , 1, 150);
+
+ int viz_size = width * synth->viz.rate_divider;
+
+ float samples[RING_SIZE];
+ int rc = PaUtil_ReadRingBuffer( &synth->viz.wave_buffer, &samples, RING_SIZE);
+
+ synth->viz.freeze = GuiCheckBox((Rectangle){ x + width - 16, y, 16, 16 }, "", synth->viz.freeze);
+ if (!synth->viz.freeze) {
+ for (int i = 0; i < rc; i++) {
+ synth->viz.fft_input_buffer[synth->viz.tmp_index] = samples[i];
+ synth->viz.tmp_buffer[synth->viz.tmp_index++] = samples[i];
+ if (synth->viz.tmp_index >= viz_size) {
+ synth->viz.tmp_index = 0;
+ }
+ }
+ for (int i = synth->viz.tmp_index; i < viz_size + synth->viz.tmp_index; i++) {
+ synth->viz.wave_viz_buffer[i - synth->viz.tmp_index] = synth->viz.tmp_buffer[i % (viz_size)];
+ }
+ } else {
+ }
+
+ synth->viz.spectrum_enabled = GuiCheckBox((Rectangle){ x, y, 16, 16 }, "spectrum", synth->viz.spectrum_enabled);
+ if (synth->viz.spectrum_enabled) {
+ draw_fft(synth, x, y, width, height);
+ }
+
+ synth->viz.wave_enabled = GuiCheckBox((Rectangle){ x + 100, y, 16, 16 }, "wave", synth->viz.wave_enabled);
+ if (synth->viz.wave_enabled) {
+ draw_wave(synth, x, y, width, height);
+ }
+
+ synth->viz.adsr_enabled = GuiCheckBox((Rectangle){ x + 170, y, 16, 16 }, "adsr", synth->viz.adsr_enabled);
+ if (synth->viz.adsr_enabled) {
+ draw_adsr(synth, x, y, width, height);
+ }
+
+ synth->viz.osc_enabled = GuiCheckBox((Rectangle){ x + width - 170, y, 16, 16 }, "osc", synth->viz.osc_enabled);
+ if (synth->viz.osc_enabled) {
+ draw_osc(synth, x + width - 170 - 40, y + 80, 80, 80);
+ }
+}
void
-rayrun(void *synthData){
- synth_t * synth = (synth_t *)synthData;
+rayrun(synth_t *synth){
PaTime current_time = 0;
PaTime prev_time = 0;
+ int len;
InitWindow(WIDTH, HEIGHT, "Raylib synth");
Font f = LoadFont("/usr/share/fonts/TTF/DejaVuSans.ttf");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
@@ -248,6 +469,9 @@ rayrun(void *synthData){
// GUI
char buf[64];
+ snprintf(buf, sizeof buf, "%d", len);
+ len = GuiSlider((Rectangle){WIDTH / 2 - 108, 150 + 42 + 42, 216, 24 }, "", buf, len , 0, 99);
+
draw_bars(synth, 33, 20, 256, 20, 4);
draw_text(synth, f, WIDTH / 2 - 108, 20);
@@ -268,6 +492,7 @@ rayrun(void *synthData){
// signals
draw_signals(synth, 20, 390, WIDTH - 2*20, 200);
+ //draw_signals(synth, 300, 390, WIDTH - 2*300, 200);
//DrawText("THE SYNTH!!!!!!!!!!!!!!!!!!1", WIDTH / 2 - 100, 50, 20, LIGHTGRAY);
/* DrawText("KEYBOARD: Q .. ]", WIDTH / 2 -300, HEIGHT - 300 - 50, 20, LIGHTGRAY); */