Files
sointu/include/sointu.h
Veikko Sariola 8183c698da Separate Synth and SynthState: SynthState is the part that Render changes.
This should make testing easier, as Synth can be assumed to stay the same
during each call. Synth is also the part that we can parse from .asm/.json file
and a Patch can be compiled into a synth. Synth can be eventually made
quite opaque to the user. The user should not need to worry about opcodes
etc.
2020-10-28 19:47:59 +02:00

150 lines
5.2 KiB
C

#ifndef _SOINTU_H
#define _SOINTU_H
#pragma pack(push,4) // this should be fine for both Go and assembly
typedef struct Unit {
float State[8];
float Ports[8];
} Unit;
typedef struct Voice {
int Note;
int Release;
float Inputs[8];
float Reserved[6];
struct Unit Units[63];
} Voice;
typedef struct DelayWorkspace {
float Buffer[65536];
float Dcin;
float Dcout;
float Filtstate;
} DelayWorkspace;
typedef struct SynthWorkspace {
unsigned char Curvoices[32];
float Left;
float Right;
float Aux[6];
struct Voice Voices[32];
} SynthWorkspace;
typedef struct SynthState {
struct SynthWorkspace SynthWrk;
struct DelayWorkspace DelayWrks[64]; // let's keep this as 64 for now, so the delays take 16 meg. If that's too little or too much, we can change this in future.
unsigned int RandSeed;
unsigned int GlobalTick;
} SynthState;
typedef struct Synth {
unsigned char Commands[32 * 64];
unsigned char Values[32 * 64 * 8];
unsigned int Polyphony;
unsigned int NumVoices;
} Synth;
#pragma pack(pop)
#if UINTPTR_MAX == 0xffffffff // are we 32-bit?
#if defined(__clang__) || defined(__GNUC__)
#define CALLCONV __attribute__ ((stdcall))
#elif defined(_WIN32)
#define CALLCONV __stdcall // on 32-bit platforms, we just use stdcall, as all know it
#endif
#else // 64-bit
#define CALLCONV // the asm will use honor honor correct x64 ABI on all 64-bit platforms
#endif
#ifdef INCLUDE_GMDLS
extern void CALLCONV su_load_gmdls(void);
#endif
// int su_render(Synth* synth,SynthState* synthState, float* buffer, int samples):
// Renders 'samples' number of 'samples' to the 'buffer', using 'synth'.
// Modifies 'synthState' and fills the 'buffer'.
//
// Parameters:
// synth pointer to the synthesizer used. Won't get modified by the call.
// synthState pointer to current synthState. RandSeed should be > 0 e.g. 1
// buffer audio sample buffer, L R L R ...
// samples maximum number of samples to be rendered. WARNING: buffer
// should have a length of 2 * samples as the audio is stereo.
//
// Returns error code:
// 0 everything ok
// (returns always 0 as no errors are implemented yet)
int CALLCONV su_render(Synth* synth,SynthState* synthState, float* buffer, int samples);
// int su_render_time(Synth* synth,SynthState* synthState, float* buffer, int* samples, int* time):
// Renders samples until 'samples' number of samples are reached or 'time' number of
// modulated time ticks are reached, whichever happens first. 'samples' and 'time' are
// are passed by reference as the function modifies to tell how many samples were
// actually rendered and how many time ticks were actually advanced.
//
// Parameters:
// synth pointer to the synthesizer used. Won't get modified by the call.
// synthState pointer to current synthState. RandSeed should be > 0 e.g. 1
// Also synthState->SamplesPerRow cannot be 0 or nothing will be
// rendered; either set it to INT32_MAX to always render full
// buffer, or something like SAMPLE_RATE * 60 / (BPM * 4) for
// having 4 rows per beat.
// buffer audio sample buffer, L R L R ...
// samples pointer to the maximum number of samples to be rendered.
// buffer should have a length of 2 * maxsamples as the audio
// is stereo.
// time maximum modulated time rendered.
//
// The value referred by samples is changed to contain the actual number of samples rendered
// Similarly, the value referred by time is changed to contain the number of time ticks advanced.
// If samples_out == samples_in, then is must be that time_in <= time_out.
// If samples_out < samples_in, then time_out >= time_in. Note that it could happen that
// time_out > time_in, as it is modulated and the time could advance by 2 or more, so the loop
// exit condition would fire when the current time is already past time_in
//
// Returns error code:
// 0 everything ok
// (no actual errors implemented yet)
int CALLCONV su_render_time(Synth* synth,SynthState* synthState, float* buffer, int* samples, int* time);
// Arithmetic opcode ids
extern const int su_add_id;
extern const int su_addp_id;
extern const int su_pop_id;
extern const int su_loadnote_id;
extern const int su_mul_id;
extern const int su_mulp_id;
extern const int su_push_id;
extern const int su_xch_id;
// Effect opcode ids
extern const int su_distort_id;
extern const int su_hold_id;
extern const int su_crush_id;
extern const int su_gain_id;
extern const int su_invgain_id;
extern const int su_filter_id;
extern const int su_clip_id;
extern const int su_pan_id;
extern const int su_delay_id;
extern const int su_compres_id;
// Flowcontrol opcode ids
extern const int su_advance_id;
extern const int su_speed_id;
// Sink opcode ids
extern const int su_out_id;
extern const int su_outaux_id;
extern const int su_aux_id;
extern const int su_send_id;
// Source opcode ids
extern const int su_envelope_id;
extern const int su_noise_id;
extern const int su_oscillat_id;
extern const int su_loadval_id;
extern const int su_receive_id;
extern const int su_in_id;
#endif // _SOINTU_H