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>
142 lines
3.8 KiB
C
142 lines
3.8 KiB
C
#ifndef SYNTH_H
|
|
#define SYNTH_H
|
|
|
|
#include <stdint.h>
|
|
|
|
#define VOICE_COUNT 8
|
|
#define OSC_COUNT 2
|
|
#define LFO_COUNT 2
|
|
#define OSC_MAX_GAIN 4.0f
|
|
|
|
typedef enum {
|
|
WAVE_SINE,
|
|
WAVE_TRIANGLE,
|
|
WAVE_SAW,
|
|
WAVE_RAMP,
|
|
WAVE_PULSE,
|
|
WAVE_NOISE,
|
|
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 {
|
|
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 pitchBend;
|
|
float pitchBendRange;
|
|
int lastStolenVoice;
|
|
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);
|
|
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
|