From 8d17aa29baf0b33229dbdd82d8d5f6cbe3fe0240 Mon Sep 17 00:00:00 2001 From: gramanas Date: Sun, 26 Nov 2023 16:29:00 +0200 Subject: Many changes! --- src/synth_gui.c | 317 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 271 insertions(+), 46 deletions(-) (limited to 'src/synth_gui.c') 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, ¬e_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); */ -- cgit v1.2.3