#define _POSIX_C_SOURCE 200809L #include "raylib.h" #include #include #include #include #include "synth.h" #include "midi.h" #include "ui.h" #include "config.h" #include "icon_data.h" #include "platform.h" #define LOGICAL_WIDTH 1020 #define LOGICAL_HEIGHT 710 #define BORDER_WIDTH 8 atomic_int gRunning = 1; static Synth gSynth = {0}; static MidiState gMidi = {0}; static UIState gUI = {0}; static void AudioOutputCallback(void *buffer, unsigned int frames) { synthFillBuffer(&gSynth, (int16_t *)buffer, (int)frames); } int main(void) { chdirToExeDir(); const int sampleRate = 48000; if (!midiInit(&gMidi, &gSynth)) return 1; midiScanInputs(&gMidi); pthread_t midiTid; pthread_create(&midiTid, NULL, midiThread, &gMidi); synthInit(&gSynth, (float)sampleRate); // Window setup SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(LOGICAL_WIDTH + 2 * BORDER_WIDTH, LOGICAL_HEIGHT + 2 * BORDER_WIDTH, "SoundThing"); { Image appIcon = LoadImageFromMemory(".png", icon_png, (int)icon_png_len); SetWindowIcon(appIcon); UnloadImage(appIcon); } // Camera2D setup Camera2D ui_cam = { 0 }; ui_cam.zoom = 1.0f; recomputeViewPort(&ui_cam, LOGICAL_WIDTH, LOGICAL_HEIGHT, BORDER_WIDTH); // Audio device setup InitAudioDevice(); SetAudioStreamBufferSizeDefault(1024); AudioStream stream = LoadAudioStream(sampleRate, 16, 1); SetAudioStreamCallback(stream, AudioOutputCallback); PlayAudioStream(stream); SetTargetFPS(120); uiInit(&gUI, &gMidi, &ui_cam); // Auto-connect to last used MIDI device if still available { char lastDev[CONFIG_NAME_LEN]; if (configLoadLastDevice(lastDev, sizeof(lastDev))) { for (int i = 0; i < gMidi.inputCount; i++) { if (strcmp(gMidi.inputs[i].clientName, lastDev) == 0) { midiDevConnect(&gMidi, gMidi.inputs[i].client, gMidi.inputs[i].port); uiLoadCcMappingsForDevice(&gUI, lastDev); break; } } } } // Layout constants const float pad = 10.0f; const float oscW = 220.0f; const float oscH = 340.0f; const float envW = 180.0f; const float envH = 280.0f; const float masterW = 180.0f; const float masterH = 360.0f; const float lfoW = 180.0f; const float lfoH = 155.0f; const float filterW = 200.0f; const float filterH = 300.0f; while (!WindowShouldClose()) { // Sync voice settings from voice 0 synthSyncVoices(&gSynth); BeginDrawing(); BeginMode2D(ui_cam); if(IsWindowResized()) { recomputeViewPort(&ui_cam, LOGICAL_WIDTH, LOGICAL_HEIGHT, BORDER_WIDTH); } ClearBackground((Color){ 189, 168, 131, 255 }); // tan background // // Oscillator panels — left side, stacked vertically // uiOscillatorPanel(&gUI, 0, // padding, padding, // oscW, oscH, // &gSynth.voices[0].oscillators[0], "Oscillator 0"); // // uiOscillatorPanel(&gUI, 100, // padding, padding + oscH + padding, // oscW, oscH, // &gSynth.voices[0].oscillators[1], "Oscillator 1"); // // // Envelope panels — to the right of oscillators // float envX = padding + oscW + padding; // uiEnvelopePanel(&gUI, 200, // envX, padding, // envW, envH, // &gSynth.voices[0].ampEnv, "Amp Envelope"); // // uiEnvelopePanel(&gUI, 250, // envX, padding + envH + padding, // envW, envH, // &gSynth.voices[0].modEnv, "Mod Envelope"); // // // Master panel — to the right of envelopes // float masterX = envX + envW + padding; // uiMasterPanel(&gUI, // masterX, padding, // masterW, masterH, // &gSynth); // Left column — oscillator panels stacked vertically float oscX = pad; uiOscillatorPanel(&gUI, 0, oscX, pad, oscW, oscH, &gSynth.voices[0].oscillators[0], "Oscillator 0"); uiOscillatorPanel(&gUI, 100, oscX, pad + oscH + pad, oscW, oscH, &gSynth.voices[0].oscillators[1], "Oscillator 1"); // Middle column — envelope panels stacked vertically float envX = pad + oscW + pad; uiEnvelopePanel(&gUI, 200, envX, pad, envW, envH, &gSynth.voices[0].ampEnv, "Envelope 0 - Amp"); uiEnvelopePanel(&gUI, 250, envX, pad + envH + pad, envW, envH, &gSynth.voices[0].modEnv, "Envelope 1 - Mod"); // LFO column float lfoX = pad + oscW + pad + envW + pad; uiLfoPanel(&gUI, 500, lfoX, pad, lfoW, lfoH, &gSynth.lfos[0], "LFO 0"); uiLfoPanel(&gUI, 600, lfoX, pad + lfoH + pad, lfoW, lfoH, &gSynth.lfos[1], "LFO 1"); // Filter panel float filterX = lfoX + lfoW + pad; uiFilterPanel(&gUI, filterX, pad, filterW, filterH, &gSynth.voices[0].filter, "Filter"); // Master panel — rightmost float masterX = filterX + filterW + pad; uiMasterPanel(&gUI, masterX, pad, masterW, masterH, &gSynth); // Floating overlays — drawn last, each one on top of the previous uiMidiMenu(&gUI, (LOGICAL_WIDTH - 420) * 0.5f, 100.0f, 420.0f, &gMidi); uiPatchMenu(&gUI, (LOGICAL_WIDTH - 420) * 0.5f, 80.0f, 420.0f, &gSynth); uiAboutMenu(&gUI, (LOGICAL_WIDTH - 420) * 0.5f, 160.0f, 420.0f); EndDrawing(); } gRunning = 0; StopAudioStream(stream); UnloadAudioStream(stream); CloseAudioDevice(); pthread_join(midiTid, NULL); uiClose(&gUI); CloseWindow(); midiClose(&gMidi); return 0; }