v0.9.5 beta — full feature build ready for distribution

Core additions since initial commit:
- Platform-split MIDI (midi_linux.c / midi_windows.c)
- Patch system: save/load/scan, subdirectory navigation, favourites
- Config system: last MIDI device and CC mappings persisted to disk
- Custom embedded pixel font and sprite sheet (no loose asset files at runtime)
- Window icon embedded at runtime (SetWindowIcon) and in Windows .exe (windres)
- chdirToExeDir() in platform.c so double-click launch finds patches/ correctly
- About screen accessible from Master panel

UI polish:
- TITLE_BAR_H constant (20 px) — sprite buttons now fit inside title bars
- All title bar text, button overlay text, and close/active labels vertically centred
- Full-screen dim overlay computed from camera transform (no more partial coverage)
- Patch browser: folder navigation, breadcrumb title, 256-slot limit lifted

Build:
- Makefile auto-discovers sources; embeds sprites/font/icon via xxd rules
- Windows cross-compile: make PLATFORM=windows (mingw-w64 + windres)
- windows.h isolated in platform.c to avoid Rectangle/CloseWindow conflicts with raylib.h
- WIN_RES uses lazy = assignment so OBJ_DIR expands correctly for windres output path

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Anachronaut
2026-07-07 19:18:34 -04:00
parent 801ea22d3e
commit 75e0894ff8
70 changed files with 5910 additions and 237 deletions

View File

@@ -3,7 +3,10 @@
#include <stdint.h>
#define VOICE_COUNT 16
#define VOICE_COUNT 8
#define OSC_COUNT 2
#define LFO_COUNT 2
#define OSC_MAX_GAIN 4.0f
typedef enum {
WAVE_SINE,
@@ -15,32 +18,124 @@ typedef enum {
WAVE_COUNT // handy for the modulo wrap on waveform switching
} Waveform;
// Envelope structures:
typedef enum {
ENV_IDLE,
ENV_ATTACK,
ENV_DECAY,
ENV_SUSTAIN,
ENV_RELEASE
} EnvStage;
typedef enum {
MOD_SOURCE_NONE = 0,
MOD_SOURCE_AMP_ENV = 1,
MOD_SOURCE_MOD_ENV = 2,
MOD_SOURCE_LFO = 3,
MOD_SOURCE_LFO2 = 4
} ModSource;
typedef struct {
float phase;
float freqHz;
float amp;
float targetAmp;
int active;
int midiNote;
EnvStage stage;
float value; // current output value, 0.0 to 1.0
float attackSec;
float decaySec;
float sustainLevel;
float releaseSec;
} Envelope;
typedef struct {
float phase;
float rate; // Hz
Waveform waveform;
int active;
float noiseHeld;
float noisePhase;
} LFO;
typedef enum {
FILTER_LOWPASS,
FILTER_HIGHPASS,
FILTER_BANDPASS,
FILTER_COUNT
} FilterType;
typedef struct {
float cutoff; // Hz
float resonance; // 0.0 (flat) to 0.99 (near self-oscillation)
FilterType type;
int active;
float low, band; // TPT integrator states s1, s2
int modRouting;
float modDepth; // Hz
int resModRouting;
float resModDepth; // resonance units (-0.99..0.99)
} Filter;
typedef struct {
float phase;
float dutyCycle;
Waveform waveform;
float detune; // cents, 0 = no detune
float noiseHeld; // last drawn random value for clocked noise
float noisePhase; // tracks when to draw a new noise value
float gain;
int active; // whether this oscillator contributes to output
int octave; // transposition in octaves, -2 to +2
// Modulation routing: one source per destination by design.
// Each parameter (pwm, detune, gain) has exactly one mod source and one depth.
int modRouting[3]; // 0=off, 1=env0, 2=env1, 3=lfo0, 4=lfo1
float modDepth[3]; // Depth of modulation parameter
} Oscillator;
typedef struct {
Oscillator oscillators[OSC_COUNT];
float freqHz;
int active;
int midiNote;
Envelope ampEnv;
Envelope modEnv;
Filter filter;
} Voice;
typedef struct {
float sampleRate;
float attackSec;
float releaseSec;
float dutyCycle;
Waveform waveform;
Voice voices[VOICE_COUNT];
float pitchBend;
float pitchBendRange;
int lastStolenVoice;
float pitchBend; // -1.0 to +1.0, normalized from raw MIDI value
float pitchBendRange; // semitones, e.g. 2.0f
Voice voices[VOICE_COUNT];
float volume; // 0.0 to 1.0, master output level
LFO lfos[LFO_COUNT];
} Synth;
// Envelope functions:
void envelopeInit(Envelope *e, float attackSec, float decaySec, float sustainLevel, float releaseSec);
void envelopeNoteOn(Envelope *e);
void envelopeNoteOff(Envelope *e);
float envelopeTick(Envelope *e, float sampleRate);
// Oscillator functions:
void oscillatorInit(Oscillator *o, Waveform waveform, float dutyCycle, float detune, float gain);
float oscillatorTick(Oscillator *o, float freqHz, float bendMultiplier, float sampleRate,
float dutyCycle, float detune, float gain);
// Synth functions:
void synthInit(Synth *s, float sampleRate);
void synthResetPatch(Synth *s);
void synthNoteOn(Synth *s, int midiNote);
void synthNoteOff(Synth *s, int midiNote);
void synthFillBuffer(Synth *s, int16_t *out, int frames);
float waveformSample(Waveform w, float phase, float dutyCycle);
void synthSyncVoices(Synth *s);
// LFO functions:
float lfoTick(LFO *l, float sampleRate);
// Filter functions:
float filterTick(Filter *f, float input, float cutoff, float resonance, float sampleRate);
const char *filterTypeName(FilterType t);
// Waveform functions:
float waveformSample(Waveform w, float phase, float dutyCycle, float noiseHeld);
const char *waveformName(Waveform w);
#endif