Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom oscillator #102

Merged
merged 10 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ default: $(TARGET)
all: default

SOURCES = src/algorithms.c src/amy.c src/envelope.c src/examples.c \
src/filters.c src/oscillators.c src/pcm.c src/partials.c \
src/filters.c src/oscillators.c src/pcm.c src/partials.c src/custom.c \
src/delay.c src/log2_exp2.c src/patches.c

OBJECTS = $(patsubst %.c, %.o, src/algorithms.c src/amy.c src/envelope.c \
src/delay.c src/partials.c src/patches.c \
src/delay.c src/partials.c src/custom.c src/patches.c \
src/examples.c src/filters.c src/oscillators.c src/pcm.c src/log2_exp2.c \
src/libminiaudio-audio.c)

Expand Down
7 changes: 5 additions & 2 deletions src/amy-example.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ int main(int argc, char ** argv) {
}
uint32_t start = amy_sysclock();

#if AMY_HAS_CUSTOM == 1
example_init_custom();
#endif

amy_start(/* cores= */ 1, /* reverb= */ 1, /* chorus= */ 1);

ma_encoder_config config = ma_encoder_config_init(ma_encoding_format_wav, ma_format_s16, AMY_NCHANS, AMY_SAMPLE_RATE);
Expand All @@ -66,15 +70,14 @@ int main(int argc, char ** argv) {


amy_reset_oscs();

example_voice_chord(0);

// Now just spin for 10s
while(amy_sysclock() - start < 5000) {
if (output_filename) {
int16_t * frames = amy_simple_fill_buffer();
int num_frames = AMY_BLOCK_SIZE;
result = ma_encoder_write_pcm_frames(&encoder, frames, num_frames, NULL);
ma_encoder_write_pcm_frames(&encoder, frames, num_frames, NULL);
}
usleep(THREAD_USLEEP);
}
Expand Down
23 changes: 19 additions & 4 deletions src/amy.c
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ void config_chorus(float level, int max_delay, float lfo_freq, float depth) {
alloc_chorus_delay_lines();
}
// if we're turning on for the first time, start the oscillator.
if (synth[CHORUS_MOD_SOURCE].status == OFF) { //chorus.level == 0) {
if (synth[CHORUS_MOD_SOURCE].status == STATUS_OFF) { //chorus.level == 0) {
// Setup chorus oscillator.
synth[CHORUS_MOD_SOURCE].logfreq_coefs[COEF_CONST] = logfreq_of_freq(lfo_freq);
synth[CHORUS_MOD_SOURCE].logfreq_coefs[COEF_NOTE] = 0; // Turn off default.
Expand Down Expand Up @@ -592,7 +592,7 @@ void reset_osc(uint16_t i ) {
synth[i].sample = F2S(0);
synth[i].mod_value = F2S(0);
synth[i].substep = 0;
synth[i].status = OFF;
synth[i].status = STATUS_OFF;
AMY_UNSET(synth[i].chained_osc);
AMY_UNSET(synth[i].mod_source);
synth[i].mod_target = 0;
Expand Down Expand Up @@ -649,6 +649,9 @@ int8_t oscs_init() {
if(pcm_samples) {
pcm_init();
}
if(AMY_HAS_CUSTOM == 1) {
custom_init();
}
events = (struct delta*)malloc_caps(sizeof(struct delta) * AMY_EVENT_FIFO_LEN, EVENTS_RAM_CAPS);
synth = (struct synthinfo*) malloc_caps(sizeof(struct synthinfo) * (AMY_OSCS+1), SYNTH_RAM_CAPS);
msynth = (struct mod_synthinfo*) malloc_caps(sizeof(struct mod_synthinfo) * (AMY_OSCS+1), SYNTH_RAM_CAPS);
Expand Down Expand Up @@ -787,6 +790,9 @@ void osc_note_on(uint16_t osc, float initial_freq) {
if(synth[osc].wave==PARTIAL) partial_note_on(osc);
if(synth[osc].wave==PARTIALS) partials_note_on(osc);
}
if(AMY_HAS_CUSTOM == 1) {
if(synth[osc].wave==CUSTOM) custom_note_on(osc, initial_freq);
}
}

void apply_target_to_coefs(uint16_t osc, int target_val, int which_coef) {
Expand Down Expand Up @@ -954,6 +960,7 @@ void play_event(struct delta d) {
if(synth[synth[d.osc].mod_source].wave==TRIANGLE) triangle_mod_trigger(synth[d.osc].mod_source);
if(synth[synth[d.osc].mod_source].wave==PULSE) pulse_mod_trigger(synth[d.osc].mod_source);
if(synth[synth[d.osc].mod_source].wave==PCM) pcm_mod_trigger(synth[d.osc].mod_source);
if(synth[synth[d.osc].mod_source].wave==CUSTOM) custom_mod_trigger(synth[d.osc].mod_source);
}

}
Expand All @@ -977,6 +984,11 @@ void play_event(struct delta d) {
#endif
}
else if(synth[d.osc].wave==PCM) { pcm_note_off(d.osc); }
else if(synth[d.osc].wave==CUSTOM) {
#if AMY_HAS_CUSTOM == 1
custom_note_off(d.osc);
#endif
}
else {
// osc note off, start release
AMY_UNSET(synth[d.osc].note_on_clock);
Expand Down Expand Up @@ -1051,7 +1063,7 @@ void hold_and_modify(uint16_t osc) {
} else {
if ( (total_samples - synth[osc].zero_amp_clock) >= MIN_ZERO_AMP_TIME_SAMPS) {
//printf("h&m: time %f osc %d OFF\n", total_samples/(float)AMY_SAMPLE_RATE, osc);
synth[osc].status = OFF;
synth[osc].status = STATUS_OFF;
AMY_UNSET(synth[osc].note_off_clock);
AMY_UNSET(synth[osc].zero_amp_clock);
}
Expand Down Expand Up @@ -1125,6 +1137,9 @@ SAMPLE render_osc_wave(uint16_t osc, uint8_t core, SAMPLE* buf) {
if(synth[osc].wave == PARTIALS) max_val = render_partials(buf, osc);
}
}
if(AMY_HAS_CUSTOM == 1) {
if(synth[osc].wave == CUSTOM) max_val = render_custom(buf, osc);
}
AMY_PROFILE_STOP(RENDER_OSC_WAVE)
return max_val;
}
Expand All @@ -1140,7 +1155,7 @@ void amy_render(uint16_t start, uint16_t end, uint8_t core) {
SAMPLE max_val = render_osc_wave(osc, core, per_osc_fb[core]);
if (max_val > max_max) max_max = max_val;
// check it's not off, just in case. todo, why do i care?
if(synth[osc].wave != OFF) {
if(synth[osc].wave != WAVE_OFF) {
// apply filter to osc if set
if(synth[osc].filter_type != FILTER_NONE) filter_process(per_osc_fb[core], osc, max_val);
mix_with_pan(fbl[core], per_osc_fb[core], msynth[osc].last_pan, msynth[osc].pan);
Expand Down
33 changes: 28 additions & 5 deletions src/amy.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@ typedef struct {
uint8_t midinote;
} pcm_map_t;

#ifndef AMY_CONFIG_H
#define AMY_CONFIG_H amy_config.h
#endif

#define QUOTED(x) #x
#define INCLUDE(x) QUOTED(x)

#include "amy_config.h"
#include INCLUDE(AMY_CONFIG_H)

// Rest of amy setup
#define SAMPLE_MAX 32767
Expand Down Expand Up @@ -94,15 +100,16 @@ enum coefs{
#define ALGO 8
#define PARTIAL 9
#define PARTIALS 10
#define CUSTOM 11
#define WAVE_OFF 12
// synth[].status values
#define EMPTY 0
#define SCHEDULED 1
#define PLAYED 2
#define AUDIBLE 3
#define IS_MOD_SOURCE 4
#define IS_ALGO_SOURCE 5
// Is this for .wave or .status?
#define OFF 11
#define STATUS_OFF 6

#define true 1
#define false 0
Expand Down Expand Up @@ -412,6 +419,16 @@ struct state {
int16_t latency_ms;
};

// custom oscillator
struct custom_oscillator {
void (*init)(void);
void (*note_on)(struct synthinfo* osc, float freq);
void (*note_off)(struct synthinfo* osc);
void (*mod_trigger)(struct synthinfo* osc);
SAMPLE (*render)(SAMPLE* buf, struct synthinfo* osc);
SAMPLE (*compute_mod)(struct synthinfo* osc);
};

// Shared structures
extern uint32_t total_samples;
extern struct synthinfo *synth;
Expand Down Expand Up @@ -441,8 +458,8 @@ void amy_stop();
void amy_live_start();
void amy_live_stop();
void amy_reset_oscs();
void amy_print_devices();

void amy_print_devices();
void amy_set_custom(struct custom_oscillator* custom);
extern void reset_osc(uint16_t i );

extern float render_am_lut(float * buf, float step, float skip, float incoming_amp, float ending_amp, const float* lut, int16_t lut_size, float *mod, float bandwidth);
Expand All @@ -451,6 +468,7 @@ extern void ks_deinit();
extern void algo_init();
extern void algo_deinit();
extern void pcm_init();
extern void custom_init();
extern SAMPLE render_ks(SAMPLE * buf, uint16_t osc);
extern SAMPLE render_sine(SAMPLE * buf, uint16_t osc);
extern SAMPLE render_fm_sine(SAMPLE *buf, uint16_t osc, SAMPLE *mod, SAMPLE feedback_level, uint16_t algo_osc, SAMPLE mod_amp);
Expand All @@ -469,6 +487,7 @@ extern void patches_event_has_voices(struct event e);
extern void patches_reset();

extern SAMPLE render_partials(SAMPLE *buf, uint16_t osc);
extern SAMPLE render_custom(SAMPLE *buf, uint16_t osc) ;

extern SAMPLE compute_mod_pulse(uint16_t osc);
extern SAMPLE compute_mod_noise(uint16_t osc);
Expand All @@ -477,6 +496,7 @@ extern SAMPLE compute_mod_saw_up(uint16_t osc);
extern SAMPLE compute_mod_saw_down(uint16_t osc);
extern SAMPLE compute_mod_triangle(uint16_t osc);
extern SAMPLE compute_mod_pcm(uint16_t osc);
extern SAMPLE compute_mod_custom(uint16_t osc);

extern void sine_note_on(uint16_t osc, float initial_freq);
extern void fm_sine_note_on(uint16_t osc, uint16_t algo_osc);
Expand All @@ -486,6 +506,8 @@ extern void triangle_note_on(uint16_t osc, float initial_freq);
extern void pulse_note_on(uint16_t osc, float initial_freq);
extern void pcm_note_on(uint16_t osc);
extern void pcm_note_off(uint16_t osc);
extern void custom_note_on(uint16_t osc, float freq);
extern void custom_note_off(uint16_t osc);
extern void partial_note_on(uint16_t osc);
extern void partial_note_off(uint16_t osc);
extern void algo_note_on(uint16_t osc);
Expand All @@ -498,6 +520,7 @@ extern void saw_up_mod_trigger(uint16_t osc);
extern void triangle_mod_trigger(uint16_t osc);
extern void pulse_mod_trigger(uint16_t osc);
extern void pcm_mod_trigger(uint16_t osc);
extern void custom_mod_trigger(uint16_t osc);
extern SAMPLE amy_get_random();
//extern void algo_custom_setup_patch(uint16_t osc, uint16_t * target_oscs);

Expand Down
2 changes: 2 additions & 0 deletions src/amy_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#define AMY_OSCS 120
#define AMY_SAMPLE_RATE 44100
#define AMY_NCHANS 2
#define AMY_USE_FIXEDPOINT


// These are overriden for you if you include pcm_X.h {tiny, small, large}
Expand All @@ -22,6 +23,7 @@ extern const uint16_t pcm_samples;
#define AMY_MAX_DRIFT_MS 20000 // ms of time you can schedule ahead before synth recomputes time base
#define AMY_KS_OSCS 1 // How many karplus-strong oscillators to keep track of (0 disables KS)
#define AMY_HAS_PARTIALS 1 // 1 = Make partials available
#define AMY_HAS_CUSTOM 0 // 1 = Make custom oscillators available
#define PCM_AMY_SAMPLE_RATE 22050
#define AMY_EVENT_FIFO_LEN 2000

Expand Down
1 change: 0 additions & 1 deletion src/amy_fixedpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
// #define MUL_KK mul4_k12k11(a, b)
// #define MUL_KKS mul4_k16k7(a, b) // the second arg is "sensitive".

#define AMY_USE_FIXEDPOINT

#ifndef AMY_USE_FIXEDPOINT

Expand Down
42 changes: 42 additions & 0 deletions src/custom.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// custom.c

#include "amy.h"
#include <assert.h>

struct custom_oscillator* custom_osc;

void amy_set_custom(struct custom_oscillator* custom) {
assert(custom_osc == NULL);
custom_osc = custom;
}

void custom_init() {
if (custom_osc != NULL) {
custom_osc->init();
}
}

void custom_note_on(uint16_t osc, float freq) {
assert(custom_osc != NULL);
custom_osc->note_on(&synth[osc], freq);
}

void custom_note_off(uint16_t osc) {
assert(custom_osc != NULL);
custom_osc->note_off(&synth[osc]);
}

void custom_mod_trigger(uint16_t osc) {
assert(custom_osc != NULL);
custom_osc->mod_trigger(&synth[osc]);
}

SAMPLE render_custom(SAMPLE* buf, uint16_t osc) {
assert(custom_osc != NULL);
return custom_osc->render(buf, &synth[osc]);
}

SAMPLE compute_mod_custom(uint16_t osc) {
assert(custom_osc != NULL);
return custom_osc->compute_mod(&synth[osc]);
}
3 changes: 3 additions & 0 deletions src/envelope.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ SAMPLE compute_mod_value(uint16_t mod_osc) {
if(synth[mod_osc].wave == SINE) value = compute_mod_sine(mod_osc);
if(pcm_samples)
if(synth[mod_osc].wave == PCM) value = compute_mod_pcm(mod_osc);
if(AMY_HAS_CUSTOM == 1) {
if(synth[mod_osc].wave == CUSTOM) value = compute_mod_custom(mod_osc);
}
synth[mod_osc].mod_value = value;
return value;
}
Expand Down
59 changes: 58 additions & 1 deletion src/examples.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,4 +272,61 @@ void example_drums(uint32_t start, int loops) {
AMY_UNSET(e.midi_note);
}
}
}
}

// Minimal custom oscillator

#if AMY_HAS_CUSTOM == 1

void beeper_init(void) {
printf("Beeper init\n");
}

void beeper_note_on(struct synthinfo* osc, float freq) {
saw_down_note_on(osc->osc, freq);
}

void beeper_note_off(struct synthinfo* osc) {
osc->note_off_clock = total_samples;
}

void beeper_mod_trigger(struct synthinfo* osc) {
saw_down_mod_trigger(osc->osc);
}

SAMPLE beeper_render(SAMPLE* buf, struct synthinfo* osc) {
return render_saw_down(buf, osc->osc);
}

SAMPLE beeper_compute_mod(struct synthinfo* osc) {
return compute_mod_saw_down(osc->osc);
}

struct custom_oscillator beeper = {
beeper_init,
beeper_note_on,
beeper_note_off,
beeper_mod_trigger,
beeper_render,
beeper_compute_mod
};

void example_init_custom() {
amy_set_custom(&beeper);
}

void example_custom_beep() {
struct event e = amy_default_event();
e.osc = 50;
e.time = amy_sysclock();
e.freq_coefs[0] = 880;
e.wave = CUSTOM;
e.velocity = 1;
amy_add_event(e);

e.velocity = 0;
e.time += 500;
amy_add_event(e);
}

#endif
2 changes: 2 additions & 0 deletions src/examples.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ void example_fm(uint32_t start) ;
void example_multimbral_fm();
void example_drums(uint32_t start, int loops);
void example_sine(uint32_t start);
void example_init_custom();
void example_custom_beep();
void example_patches();
void example_voice_chord(uint16_t patch);
void example_voice_alloc();
2 changes: 1 addition & 1 deletion src/oscillators.c
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ void partial_note_off(uint16_t osc) {
AMY_UNSET(synth[osc].note_on_clock);
synth[osc].note_off_clock = total_samples;
synth[osc].last_amp = 0;
synth[osc].status = OFF;
synth[osc].status = STATUS_OFF;
}


Expand Down
Loading
Loading