#include "synth_gui.h"
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"
//#include "raylib.h"
void
draw_text(synth_t * synth, Font f, int x, int y)
{
char buf[64];
int count = 0;
float text_size = 10;
int offset = 12;
snprintf(buf, sizeof buf, "lfo freq %.4f", synth->cc_lfo_freq.value);
DrawText(buf, x, y + offset * count++, text_size, GRAY);
snprintf(buf, sizeof buf, "lfo amp %.4f", synth->cc_lfo_amp.value);
DrawText(buf, x, y + offset * count++, text_size, GRAY);
snprintf(buf, sizeof buf, "filter reso %.4f", synth->cc_resonance.value);
DrawText(buf, x, y + offset * count++, text_size, GRAY);
snprintf(buf, sizeof buf, "filter cutoff %.1f", synth->cc_cutoff.value);
DrawText(buf, x, y + offset * count++, text_size, GRAY);
snprintf(buf, sizeof buf, "extra pitch %.4f", synth->cc_pitch.value);
DrawText(buf, x, y + offset * count++, text_size, GRAY);
DrawRectangleLines(x - 6, y - 3, 120, 3 + (count * offset) + 3, GRAY);
}
void
mouse(synth_t *synth, PaStream *stream)
{
float m = GetMouseWheelMove();
int x = 0;
if (m < 0) x = -1;
else if (m > 0) x = 1;
int y = GetMouseY();
if (x) {
if (y > 20 && y <= 32) {
cc_step(&synth->cc_lfo_freq, x);
} else if (y > 32 && y <= 44) {
cc_step(&synth->cc_lfo_amp, x);
} else if (y > 44 && y <= 56) {
cc_step(&synth->cc_resonance, x);
} else if (y > 56 && y <= 68) {
cc_step(&synth->cc_cutoff, x);
} else if (y > 68 && y <= 80) {
cc_step(&synth->cc_pitch, x);
}
}
}
void
keyboard(synth_t * synth, PaStream *stream)
{
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;
for (int i = 0; keys[i]; i++) {
if (IsKeyPressed(keys[i])) {
//printf("Note On : %s[%d] %fHz\n", int_to_note(i % 12), (synth->octave + (i / 12)) % 8, note);
synth->midi_note[i].freq = 16.35160 * pow(2, (synth->octave + i / 12.0));
//synth->midi_note[i].freq = notes[i % 12][(synth->octave + (i / 12)) % 8];
synth->midi_note[i].channel = -1;
synth->midi_note[i].noteOn = Pa_GetStreamTime(synth->stream);
synth->midi_note[i].noteOff = 0;
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;
}
}
for (int i = 0; keys[i]; i++) {
if (IsKeyReleased(keys[i])) {
synth->midi_note[i].noteOff = Pa_GetStreamTime(synth->stream);
synth->midi_note[i].noteOffSample = synth->midi_note[i].elapsed;
note = notes[i % 12][(synth->octave + (i / 12)) % 8];
}
}
int mods[] = {KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, 0};
synth->modi = 0;
for (int i = 0; mods[i]; i++) {
if (IsKeyDown(mods[i])) {
synth->modifiers[synth->modi++] = mods[i];
}
}
if (IsKeyDown(265)) { // up
for (int i = 0; i < synth->modi; i++) {
if (synth->modifiers[i] == KEY_Z) {
cc_step(&synth->cc_cutoff, 1);
}
if (synth->modifiers[i] == KEY_X) {
cc_step(&synth->cc_resonance, 1);
}
if (synth->modifiers[i] == KEY_C) {
cc_step(&synth->cc_pitch, 1);
}
if (synth->modifiers[i] == KEY_V) {
cc_step(&synth->cc_lfo_freq, 1);
}
if (synth->modifiers[i] == KEY_B) {
cc_step(&synth->cc_lfo_amp, 1);
}
}
}
if (IsKeyDown(264)) { // down
for (int i = 0; i < synth->modi; i++) {
if (synth->modifiers[i] == KEY_Z) {
cc_step(&synth->cc_cutoff, -1);
}
if (synth->modifiers[i] == KEY_X) {
cc_step(&synth->cc_resonance, -1);
}
if (synth->modifiers[i] == KEY_C) {
cc_step(&synth->cc_pitch, -1);
}
if (synth->modifiers[i] == KEY_V) {
cc_step(&synth->cc_lfo_freq, -1);
}
if (synth->modifiers[i] == KEY_B) {
cc_step(&synth->cc_lfo_amp, -1);
}
}
}
if (IsKeyDown(KEY_ENTER)) { // down
for (int i = 0; i < synth->modi; i++) {
if (synth->modifiers[i] == KEY_Z) {
cc_reset(&synth->cc_cutoff);
}
if (synth->modifiers[i] == KEY_X) {
cc_reset(&synth->cc_resonance);
}
if (synth->modifiers[i] == KEY_C) {
cc_reset(&synth->cc_pitch);
}
if (synth->modifiers[i] == KEY_V) {
cc_reset(&synth->cc_lfo_freq);
}
if (synth->modifiers[i] == KEY_B) {
cc_reset(&synth->cc_lfo_amp);
}
}
}
}
void
draw_bars(synth_t * synth, int x, int y, int width, int height, int offset)
{
char buf[64];
int count = 0;
snprintf(buf, sizeof buf, "%.3f", synth->adsr.a);
synth->adsr.a = GuiSlider((Rectangle){ x, y + count++ * (height + offset), width, height }, "A: ", buf, synth->adsr.a , 0.00001f, 2.0f);
snprintf(buf, sizeof buf, "%.3f", synth->adsr.peak);
synth->adsr.peak = GuiSlider((Rectangle){ x, y + count++ * (height + offset), width, height }, "P: ", buf, synth->adsr.peak , 0.0f, 1.0f);
snprintf(buf, sizeof buf, "%.3f", synth->adsr.d);
synth->adsr.d = GuiSlider((Rectangle){ x, y + count++ * (height + offset), width, height }, "D: ", buf, synth->adsr.d , 0.001f, 2.0f);
snprintf(buf, sizeof buf, "%.3f", synth->adsr.s);
synth->adsr.s = GuiSlider((Rectangle){ x, y + count++ * (height + offset), width, height }, "S: ", buf, synth->adsr.s , 0.0f, 1.0f);
snprintf(buf, sizeof buf, "%.3f", synth->adsr.r);
synth->adsr.r = GuiSlider((Rectangle){ x, y + count++ * (height + offset), width, height }, "R: ", buf, synth->adsr.r , 0.001f, 3.0f);
snprintf(buf, sizeof buf, "%.3f", synth->gain);
synth->gain = GuiSlider((Rectangle){ x, y + count++ * (height + offset), width, height }, "gain: ", buf, synth->gain , 0.0f, 2.0f);
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.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_adsr(synth_t *synth, int x, int y, int width, int height)
{
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 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;
}
}
int freeze;
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);
}
fftwf_free(output);
}
void
draw_osc(synth_t * synth, int x, int y, int width, int height)
{
DrawRectangle(x, y, width, height, BLACK);
DrawRectangleLines(x, y, width, height, WHITE);
float osc_wave[width];
for (int i = 0; i < width; i++) {
osc_wave[i] = 0;
}
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 = 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;
}
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 - 80, y + height - 80, 80, 80);
}
}
void
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
while (!WindowShouldClose()) {
keyboard(synth, synth->stream);
mouse(synth, synth->stream);
int prec = 100000;
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(BLACK);
// GUI
char buf[64];
osc_sound(0);
snprintf(buf, sizeof buf, "%d", len);
len = GuiSlider((Rectangle){WIDTH / 2 - 108, 150 + 42 + 42, 216, 24 }, "", buf, len , 0, 157);
set_sound_start(len*2048);
set_sound_len(len*2048 + 2048);
draw_bars(synth, 33, 20, 256, 20, 4);
draw_text(synth, f, WIDTH / 2 - 108, 20);
if ( GuiButton((Rectangle){ WIDTH / 2 - 108, 150 - 6 - 6 + 42, 216, 6 }, "")) {
synth->x = 1;
}
snprintf(buf, sizeof buf, "%.1f", synth->x);
synth->x = GuiSlider((Rectangle){ WIDTH / 2 - 108, 150 + 42, 216, 24 }, "x", buf, synth->x , 0.0f, 2.0f);
synth->filter = GuiToggle((Rectangle){ WIDTH - 100 - 50 - 100 - 50 , 50 , 100, 24 }, "FILTER", synth->filter);
GuiSpinner((Rectangle){ WIDTH - 100 - 50 , 50, 100, 24 }, "oct: ", &(synth->octave), 0, 7, 0);
snprintf(buf, sizeof buf, "generator %d --> ", synth->geni);
GuiSpinner((Rectangle){ WIDTH - 100 - 50 , 50 + 24 + 6, 100, 24 }, buf, &(synth->geni), 0, 6, 0);
synth->clamp = GuiToggle((Rectangle){ WIDTH - 100 - 50, 50 + 24 + 6 + 24 + 6, 100, 24 }, "clamp", synth->clamp);
synth->delay = GuiToggle((Rectangle){ WIDTH - 100 - 50, 50 + 24 + 6 + 24 + 6 + 24 + 6, 100, 24 }, "delay", synth->delay);
// 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); */
/* snprintf(buf, sizeof buf, "stream time: %f", Pa_GetStreamTime(synth->stream)); */
/* DrawText(buf, WIDTH / 2 -300, HEIGHT - 300, 11, LIGHTGRAY); */
EndDrawing();
//----------------------------------------------------------------------------------
current_time = Pa_GetStreamTime(synth->stream);
//printf("%f :: %ld\n", current_time - prev_time, phase);
prev_time = current_time;
}
CloseWindow();
}