Skip to content
Open
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
53 changes: 32 additions & 21 deletions src/amy.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,8 @@ struct event amy_default_event() {
AMY_UNSET(e.portamento_ms);
AMY_UNSET(e.filter_type);
AMY_UNSET(e.chained_osc);
AMY_UNSET(e.mod_source);
AMY_UNSET(e.mod0_source);
AMY_UNSET(e.mod1_source);
AMY_UNSET(e.eq_l);
AMY_UNSET(e.eq_m);
AMY_UNSET(e.eq_h);
Expand Down Expand Up @@ -527,7 +528,8 @@ void amy_add_event_internal(struct event e, uint16_t base_osc) {
if(AMY_IS_SET(e.portamento_ms)) { d.param=PORTAMENTO; d.data = *(uint32_t *)&e.portamento_ms; add_delta_to_queue(d); }
if(AMY_IS_SET(e.chained_osc)) { e.chained_osc += base_osc; d.param=CHAINED_OSC; d.data = *(uint32_t *)&e.chained_osc; add_delta_to_queue(d); }
if(AMY_IS_SET(e.reset_osc)) { e.reset_osc += base_osc; d.param=RESET_OSC; d.data = *(uint32_t *)&e.reset_osc; add_delta_to_queue(d); }
if(AMY_IS_SET(e.mod_source)) { e.mod_source += base_osc; d.param=MOD_SOURCE; d.data = *(uint32_t *)&e.mod_source; add_delta_to_queue(d); }
if(AMY_IS_SET(e.mod0_source)) { e.mod0_source += base_osc; d.param=MOD0_SOURCE; d.data = *(uint32_t *)&e.mod0_source; add_delta_to_queue(d); }
if(AMY_IS_SET(e.mod1_source)) { e.mod1_source += base_osc; d.param=MOD1_SOURCE; d.data = *(uint32_t *)&e.mod1_source; add_delta_to_queue(d); }
if(AMY_IS_SET(e.filter_type)) { d.param=FILTER_TYPE; d.data = *(uint32_t *)&e.filter_type; add_delta_to_queue(d); }
if(AMY_IS_SET(e.algorithm)) { d.param=ALGORITHM; d.data = *(uint32_t *)&e.algorithm; add_delta_to_queue(d); }
if(AMY_IS_SET(e.eq_l)) { d.param=EQ_L; d.data = *(uint32_t *)&e.eq_l; add_delta_to_queue(d); }
Expand Down Expand Up @@ -636,7 +638,8 @@ void reset_osc(uint16_t i ) {
synth[i].substep = 0;
synth[i].status = SYNTH_OFF;
AMY_UNSET(synth[i].chained_osc);
AMY_UNSET(synth[i].mod_source);
AMY_UNSET(synth[i].mod_source[0]);
AMY_UNSET(synth[i].mod_source[1]);
AMY_UNSET(synth[i].render_clock);
AMY_UNSET(synth[i].note_on_clock);
synth[i].note_off_clock = 0; // Used to check that last event seen by note was off.
Expand Down Expand Up @@ -790,8 +793,8 @@ void show_debug(uint8_t type) {
fprintf(stderr,"global: volume %f bend %f eq: %f %f %f \n", amy_global.volume, amy_global.pitch_bend, S2F(amy_global.eq[0]), S2F(amy_global.eq[1]), S2F(amy_global.eq[2]));
//printf("mod global: filter %f resonance %f\n", mglobal.filter_freq, mglobal.resonance);
for(uint16_t i=0;i<10 /* AMY_OSCS */;i++) {
fprintf(stderr,"osc %d: status %d wave %d mod_source %d velocity %f logratio %f feedback %f filtype %d resonance %f portamento_alpha %f step %f chained %d algo %d source %d,%d,%d,%d,%d,%d \n",
i, synth[i].status, synth[i].wave, synth[i].mod_source,
fprintf(stderr,"osc %d: status %d wave %d mod0_source %d mod1_source %d velocity %f logratio %f feedback %f filtype %d resonance %f portamento_alpha %f step %f chained %d algo %d source %d,%d,%d,%d,%d,%d \n",
i, synth[i].status, synth[i].wave, synth[i].mod_source[0], synth[i].mod_source[1],
synth[i].velocity, synth[i].logratio, synth[i].feedback, synth[i].filter_type, synth[i].resonance, synth[i].portamento_alpha, P2F(synth[i].step), synth[i].chained_osc,
synth[i].algorithm,
synth[i].algo_source[0], synth[i].algo_source[1], synth[i].algo_source[2], synth[i].algo_source[3], synth[i].algo_source[4], synth[i].algo_source[5] );
Expand Down Expand Up @@ -903,6 +906,19 @@ float portamento_ms_to_alpha(uint16_t portamento_ms) {
return 1.0f - 1.0f / (1 + portamento_ms * AMY_SAMPLE_RATE / 1000 / AMY_BLOCK_SIZE);
}

void trigger_mod_source(uint16_t source) {
synth[source].phase = synth[source].trigger_phase;

synth[source].note_on_clock = total_samples; // Need a note_on_clock to have envelope work correctly.
if(synth[source].wave==SINE) sine_mod_trigger(source);
if(synth[source].wave==SAW_DOWN) saw_up_mod_trigger(source);
if(synth[source].wave==SAW_UP) saw_down_mod_trigger(source);
if(synth[source].wave==TRIANGLE) triangle_mod_trigger(source);
if(synth[source].wave==PULSE) pulse_mod_trigger(source);
if(synth[source].wave==PCM) pcm_mod_trigger(source);
if(synth[source].wave==CUSTOM) custom_mod_trigger(source);
}

// play an event, now -- tell the audio loop to start making noise
void play_event(struct delta d) {
AMY_PROFILE_START(PLAY_EVENT)
Expand Down Expand Up @@ -992,9 +1008,10 @@ void play_event(struct delta d) {
reset_osc(*(int16_t *)&d.data);
}
}
if(d.param == MOD_SOURCE) {
if(d.param == MOD0_SOURCE || d.param == MOD1_SOURCE) {
uint16_t mod_osc = *(uint16_t *)&d.data;
synth[d.osc].mod_source = mod_osc;
if (d.param == MOD0_SOURCE) synth[d.osc].mod_source[0] = mod_osc;
else synth[d.osc].mod_source[1] = mod_osc;
// NOTE: These are event-only side effects. A purist would strive to remove them.
// When an oscillator is named as a modulator, we change its state.
synth[mod_osc].status = SYNTH_IS_MOD_SOURCE;
Expand Down Expand Up @@ -1065,18 +1082,11 @@ void play_event(struct delta d) {
float initial_freq = freq_of_logfreq(initial_logfreq);
osc_note_on(d.osc, initial_freq);
// trigger the mod source, if we have one
if(AMY_IS_SET(synth[d.osc].mod_source)) {
synth[synth[d.osc].mod_source].phase = synth[synth[d.osc].mod_source].trigger_phase;

synth[synth[d.osc].mod_source].note_on_clock = total_samples; // Need a note_on_clock to have envelope work correctly.
if(synth[synth[d.osc].mod_source].wave==SINE) sine_mod_trigger(synth[d.osc].mod_source);
if(synth[synth[d.osc].mod_source].wave==SAW_DOWN) saw_up_mod_trigger(synth[d.osc].mod_source);
if(synth[synth[d.osc].mod_source].wave==SAW_UP) saw_down_mod_trigger(synth[d.osc].mod_source);
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);
}
if(AMY_IS_SET(synth[d.osc].mod_source[0]))
trigger_mod_source(synth[d.osc].mod_source[0]);
if(AMY_IS_SET(synth[d.osc].mod_source[1]))
trigger_mod_source(synth[d.osc].mod_source[1]);

} else if(synth[d.osc].velocity > 0 && *(float *)&d.data == 0) { // new note off
// DON'T clear velocity, we still need to reference it in decay.
//synth[d.osc].velocity = 0;
Expand Down Expand Up @@ -1149,7 +1159,8 @@ void hold_and_modify(uint16_t osc) {
ctrl_inputs[COEF_VEL] = synth[osc].velocity;
ctrl_inputs[COEF_EG0] = S2F(compute_breakpoint_scale(osc, 0, 0));
ctrl_inputs[COEF_EG1] = S2F(compute_breakpoint_scale(osc, 1, 0));
ctrl_inputs[COEF_MOD] = S2F(compute_mod_scale(osc));
ctrl_inputs[COEF_MOD0] = S2F(compute_mod_scale(0, osc));
ctrl_inputs[COEF_MOD1] = S2F(compute_mod_scale(1, osc));
ctrl_inputs[COEF_BEND] = amy_global.pitch_bend;

msynth[osc].last_pan = msynth[osc].pan;
Expand Down Expand Up @@ -1830,7 +1841,7 @@ struct event amy_parse_message(char * message) {
break;
case 'K': e.load_patch = atoi(message+start); break;
case 'l': e.velocity=atoff(message + start); break;
case 'L': e.mod_source=atoi(message + start); break;
case 'L': e.mod1_source=atoi(message + start); break;
case 'm': e.portamento_ms=atoi(message + start); break;
case 'M': if (AMY_HAS_ECHO) {
float echo_params[5] = {AMY_UNSET_FLOAT, AMY_UNSET_FLOAT, AMY_UNSET_FLOAT, AMY_UNSET_FLOAT, AMY_UNSET_FLOAT};
Expand Down
21 changes: 12 additions & 9 deletions src/amy.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,16 @@ typedef int16_t output_sample_type;
#define ZERO_LOGFREQ_IN_HZ 261.63
#define ZERO_MIDI_NOTE 60

#define NUM_COMBO_COEFS 7 // 7 control-mixing params: const, note, velocity, env1, env2, mod, pitchbend
#define NUM_COMBO_COEFS 8 // 8 control-mixing params: const, note, velocity, env1, env2, mod1, mod2, pitchbend
enum coefs{
COEF_CONST = 0,
COEF_NOTE = 1,
COEF_VEL = 2,
COEF_EG0 = 3,
COEF_EG1 = 4,
COEF_MOD = 5,
COEF_BEND = 6,
COEF_MOD0 = 5,
COEF_MOD1 = 6,
COEF_BEND = 7,
};

#define MAX_MESSAGE_LEN 1024
Expand Down Expand Up @@ -162,9 +163,10 @@ enum params{
FILTER_FREQ=PAN + NUM_COMBO_COEFS, // 37..43
RATIO=FILTER_FREQ + NUM_COMBO_COEFS, // 44
RESONANCE, PORTAMENTO, CHAINED_OSC, // 45, 46, 47
MOD_SOURCE, FILTER_TYPE, // 48, 49
EQ_L, EQ_M, EQ_H, // 50, 51, 52
ALGORITHM, LATENCY, TEMPO, // 53, 54, 55
MOD0_SOURCE, MOD1_SOURCE, // 48, 49
FILTER_TYPE, // 50
EQ_L, EQ_M, EQ_H, // 51, 52, 53
ALGORITHM, LATENCY, TEMPO, // 54, 55, 56
ALGO_SOURCE_START=100, // 100..105
ALGO_SOURCE_END=100+MAX_ALGO_OPS, // 106
BP_START=ALGO_SOURCE_END + 1, // 107..138
Expand Down Expand Up @@ -294,7 +296,8 @@ struct event {
float resonance;
uint16_t portamento_ms;
uint16_t chained_osc;
uint16_t mod_source;
uint16_t mod0_source;
uint16_t mod1_source;
uint8_t algorithm;
uint8_t filter_type;
float eq_l;
Expand Down Expand Up @@ -336,7 +339,7 @@ struct synthinfo {
float resonance;
float portamento_alpha;
uint16_t chained_osc;
uint16_t mod_source;
uint16_t mod_source[2];
uint8_t algorithm;
uint8_t filter_type;
// algo_source remains int16 because users can add -1 to indicate no osc
Expand Down Expand Up @@ -588,7 +591,7 @@ esp_err_t dsps_biquad_f32_ae32(const float *input, float *output, int len, float

// envelopes
extern SAMPLE compute_breakpoint_scale(uint16_t osc, uint8_t bp_set, uint16_t sample_offset);
extern SAMPLE compute_mod_scale(uint16_t osc);
extern SAMPLE compute_mod_scale(uint16_t mod, uint16_t osc);
extern SAMPLE compute_mod_value(uint16_t mod_osc);
extern void retrigger_mod_source(uint16_t osc);

Expand Down
4 changes: 2 additions & 2 deletions src/envelope.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ SAMPLE compute_mod_value(uint16_t mod_osc) {
return value;
}

SAMPLE compute_mod_scale(uint16_t osc) {
uint16_t source = synth[osc].mod_source;
SAMPLE compute_mod_scale(uint16_t mod, uint16_t osc) {
uint16_t source = synth[osc].mod_source[mod];
if(AMY_IS_SET(source)) {
if(source != osc) { // that would be weird
hold_and_modify(source);
Expand Down