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
7 changes: 6 additions & 1 deletion src/amy.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ struct event amy_default_event() {
AMY_UNSET(e.filter_type);
AMY_UNSET(e.chained_osc);
AMY_UNSET(e.mod_source);
AMY_UNSET(e.sync_source);
AMY_UNSET(e.eq_l);
AMY_UNSET(e.eq_m);
AMY_UNSET(e.eq_h);
Expand Down Expand Up @@ -528,6 +529,7 @@ void amy_add_event_internal(struct event e, uint16_t base_osc) {
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.sync_source)) { e.sync_source += base_osc; d.param=SYNC_SOURCE; d.data = *(uint32_t *)&e.sync_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 @@ -637,6 +639,7 @@ void reset_osc(uint16_t i ) {
synth[i].status = SYNTH_OFF;
AMY_UNSET(synth[i].chained_osc);
AMY_UNSET(synth[i].mod_source);
AMY_UNSET(synth[i].sync_source);
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 @@ -1003,6 +1006,7 @@ void play_event(struct delta d) {
// Remove default amplitude dependence on velocity when an oscillator is made a modulator.
synth[mod_osc].amp_coefs[COEF_VEL] = 0;
}
if(d.param == SYNC_SOURCE) synth[d.osc].sync_source = *(uint16_t*)&d.data;

if(d.param == RATIO) synth[d.osc].logratio = *(float *)&d.data;

Expand Down Expand Up @@ -1878,6 +1882,7 @@ struct event amy_parse_message(char * message) {
e.eq_h = eq[2];
}
break;
case 'y': e.sync_source=atoi(message + start); break;
case 'z': {
uint32_t sm[6]; // patch, length, SR, midinote, loopstart, loopend
parse_list_uint32_t(message+start, sm, 6, 0);
Expand All @@ -1889,7 +1894,7 @@ struct event amy_parse_message(char * message) {
}
break;
}
/* Y,y available */
/* Y available */
/* Z used for end of message */
default:
break;
Expand Down
3 changes: 3 additions & 0 deletions src/amy.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ enum params{
MOD_SOURCE, FILTER_TYPE, // 48, 49
EQ_L, EQ_M, EQ_H, // 50, 51, 52
ALGORITHM, LATENCY, TEMPO, // 53, 54, 55
SYNC_SOURCE, // 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 @@ -295,6 +296,7 @@ struct event {
uint16_t portamento_ms;
uint16_t chained_osc;
uint16_t mod_source;
uint16_t sync_source;
uint8_t algorithm;
uint8_t filter_type;
float eq_l;
Expand Down Expand Up @@ -337,6 +339,7 @@ struct synthinfo {
float portamento_alpha;
uint16_t chained_osc;
uint16_t mod_source;
uint16_t sync_source;
uint8_t algorithm;
uint8_t filter_type;
// algo_source remains int16 because users can add -1 to indicate no osc
Expand Down
97 changes: 87 additions & 10 deletions src/oscillators.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ PHASOR render_lut_fm(SAMPLE* buf,
}

PHASOR render_lut(SAMPLE* buf,
PHASOR phase,
PHASOR phase,
PHASOR step,
SAMPLE incoming_amp, SAMPLE ending_amp,
const LUT* lut,
Expand All @@ -205,6 +205,7 @@ PHASOR render_lut(SAMPLE* buf,
return phase;
}


PHASOR render_lut_cub(SAMPLE* buf,
PHASOR phase,
PHASOR step,
Expand All @@ -225,6 +226,54 @@ PHASOR render_lut_cub(SAMPLE* buf,
return phase;
}

//accepting some repetition to not add more branching to the normal render loop
PHASOR render_synced_lut(SAMPLE* buf,
PHASOR phase, PHASOR step,
PHASOR sync, PHASOR sync_step,
SAMPLE incoming_amp, SAMPLE ending_amp,
const LUT* lut,
SAMPLE* pmax_value) {
AMY_PROFILE_START(RENDER_LUT)
RENDER_LUT_PREAMBLE
for(uint16_t i = 0; i < AMY_BLOCK_SIZE; i++) {
PHASOR total_phase = phase;

RENDER_LUT_GUTS(NOTHING, NOTHING, INTERP_LINEAR)

RENDER_LUT_LOOP_END

if (sync + sync_step > 1.0f) phase = 0;
sync = P_WRAPPED_SUM(sync, sync_step);

}
*pmax_value = max_value;
AMY_PROFILE_STOP(RENDER_LUT)
return phase;
}

PHASOR render_synced_lut_cub(SAMPLE* buf,
PHASOR phase, PHASOR step,
PHASOR sync, PHASOR sync_step, PHASOR sync_start,
SAMPLE incoming_amp, SAMPLE ending_amp,
const LUT* lut,
SAMPLE *pmax_value) {
AMY_PROFILE_START(RENDER_LUT_CUB)
RENDER_LUT_PREAMBLE
for(uint16_t i = 0; i < AMY_BLOCK_SIZE; i++) {
PHASOR total_phase = phase;

RENDER_LUT_GUTS(NOTHING, NOTHING, INTERP_CUBIC)

RENDER_LUT_LOOP_END

if (sync + sync_step > 1.0f) phase = sync_start;
sync = P_WRAPPED_SUM(sync, sync_step);
}
*pmax_value = max_value;
AMY_PROFILE_STOP(RENDER_LUT_CUB)
return phase;
}

/* Pulse wave */
void pulse_note_on(uint16_t osc, float freq) {
//printf("pulse_note_on: time %lld osc %d logfreq %f amp %f last_amp %f\n", total_samples, osc, synth[osc].logfreq, msynth[osc].amp, msynth[osc].last_amp);
Expand All @@ -241,15 +290,29 @@ SAMPLE render_lpf_lut(SAMPLE* buf, uint16_t osc, int8_t is_square, int8_t direct
SAMPLE last_amp = direction * F2S(msynth[osc].last_amp);
PHASOR pwm_phase = synth[osc].phase;
SAMPLE max_value;
synth[osc].phase = render_lut_cub(buf, synth[osc].phase, step, last_amp, amp, synth[osc].lut, &max_value);
int8_t is_synced = AMY_IS_SET(synth[osc].sync_source) && synth[osc].sync_source != osc;
PHASOR sync_phase, sync_step;
if (is_synced) {
sync_step = F2P(freq_of_logfreq(msynth[synth[osc].sync_source].logfreq) / (float) AMY_SAMPLE_RATE);
sync_phase = synth[synth[osc].sync_source].phase;
synth[osc].phase = render_synced_lut_cub(buf, synth[osc].phase, step, sync_phase, sync_step, F2P(0), last_amp, amp, synth[osc].lut, &max_value);
} else
synth[osc].phase = render_lut_cub(buf, synth[osc].phase, step, last_amp, amp, synth[osc].lut, &max_value);

if (is_square) { // For pulse only, add a second delayed negative LUT wave.
float duty = msynth[osc].duty;
if (duty < 0.01f) duty = 0.01f;
if (duty > 0.99f) duty = 0.99f;
pwm_phase = P_WRAPPED_SUM(pwm_phase, F2P(msynth[osc].last_duty));
// Second pulse is given some blockwise-constant FM to maintain phase continuity across blocks.
PHASOR delta_phase_per_sample = F2P((duty - msynth[osc].last_duty) / AMY_BLOCK_SIZE);
render_lut_cub(buf, pwm_phase, step + delta_phase_per_sample, -last_amp, -amp, synth[osc].lut, &max_value);
if (is_synced) {
//we aren't as worried about phase continuity when syncing
pwm_phase = P_WRAPPED_SUM(pwm_phase, F2P(duty));
render_synced_lut_cub(buf, pwm_phase, step, sync_phase, sync_step, F2P(duty), -last_amp, -amp, synth[osc].lut, &max_value);
} else {
// Second pulse is given some blockwise-constant FM to maintain phase continuity across blocks.
if (duty < 0.01f) duty = 0.01f;
if (duty > 0.99f) duty = 0.99f;
pwm_phase = P_WRAPPED_SUM(pwm_phase, F2P(msynth[osc].last_duty));
PHASOR delta_phase_per_sample = F2P((duty - msynth[osc].last_duty) / AMY_BLOCK_SIZE);
render_lut_cub(buf, pwm_phase, step + delta_phase_per_sample, -last_amp, -amp, synth[osc].lut, &max_value);
}
msynth[osc].last_duty = duty;
}
// Remember last_amp.
Expand Down Expand Up @@ -361,7 +424,13 @@ SAMPLE render_triangle(SAMPLE* buf, uint16_t osc) {
SAMPLE amp = F2S(msynth[osc].amp);
SAMPLE last_amp = F2S(msynth[osc].last_amp);
SAMPLE max_value;
synth[osc].phase = render_lut(buf, synth[osc].phase, step, last_amp, amp, synth[osc].lut, &max_value);

if (AMY_IS_SET(synth[osc].sync_source) && synth[osc].sync_source != osc) {
PHASOR sync_step = F2P(freq_of_logfreq(msynth[synth[osc].sync_source].logfreq) / (float) AMY_SAMPLE_RATE);
PHASOR sync_phase = synth[synth[osc].sync_source].phase;
synth[osc].phase = render_synced_lut(buf, synth[osc].phase, step, sync_phase, sync_step, last_amp, amp, synth[osc].lut, &max_value);
} else
synth[osc].phase = render_lut(buf, synth[osc].phase, step, last_amp, amp, synth[osc].lut, &max_value);
msynth[osc].last_amp = msynth[osc].amp;
return max_value;
}
Expand Down Expand Up @@ -447,7 +516,13 @@ SAMPLE render_sine(SAMPLE* buf, uint16_t osc) {
SAMPLE last_amp = F2S(msynth[osc].last_amp);
//fprintf(stderr, "render_sine: time %f osc %d freq %f last_amp %f amp %f\n", total_samples / (float)AMY_SAMPLE_RATE, osc, AMY_SAMPLE_RATE * P2F(step), S2F(last_amp), S2F(amp));
SAMPLE max_value;
synth[osc].phase = render_lut(buf, synth[osc].phase, step, last_amp, amp, synth[osc].lut, &max_value);
//check if we are syncing
if (AMY_IS_SET(synth[osc].sync_source) && synth[osc].sync_source != osc) {
PHASOR sync_step = F2P(freq_of_logfreq(msynth[synth[osc].sync_source].logfreq) / (float) AMY_SAMPLE_RATE);
PHASOR sync_phase = synth[synth[osc].sync_source].phase;
synth[osc].phase = render_synced_lut(buf, synth[osc].phase, step, sync_phase, sync_step, last_amp, amp, synth[osc].lut, &max_value);
} else
synth[osc].phase = render_lut(buf, synth[osc].phase, step, last_amp, amp, synth[osc].lut, &max_value);
msynth[osc].last_amp = msynth[osc].amp;
return max_value;
}
Expand Down Expand Up @@ -633,3 +708,5 @@ void ks_deinit(void) {
for(int i=0;i<AMY_KS_OSCS;i++) free(ks_buffer[i]);
free(ks_buffer);
}