Skip to content

Commit

Permalink
4 chorus params into one wire-protocol comma-separated list.
Browse files Browse the repository at this point in the history
  • Loading branch information
dpwe committed Aug 31, 2024
1 parent 2a5140d commit f1ab734
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 176 deletions.
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,22 +197,19 @@ Here's the full list:
| `f` | `freq` | float[,float...] | frequency of oscillator, set of ControlCoefficients. Default is 0,1,0,0,0,0,1 (from `note` pitch plus `pitch_bend`) |
| `F` | `filter_freq` | float[,float...] | center frequency for biquad filter, set of ControlCoefficients |
| `G` | `filter_type` | 0-4 | 0 = none (default.) 1 = lowpass, 2 = bandpass, 3 = highpass, 4 = double-order lowpass. |
| `h` | `reverb` | float[,float,float,float] | Reverb parameters: level, liveness, damping, xover: Level is for output mix; liveness controls decay time, 1 = longest, default = 0.85; damping is extra decay of high frequencies, default 0.5; xover is damping crossover frequency, default 3000 Hz. |
| `h` | `reverb` | float[,float,float,float] | Reverb parameters level, liveness, damping, xover: Level is for output mix; liveness controls decay time, 1 = longest, default = 0.85; damping is extra decay of high frequencies, default 0.5; xover is damping crossover frequency, default 3000 Hz. |
| `I` | `ratio` | float | for ALGO types, where the base note frequency controls the modulators, or for the PARTIALS base note, where the ratio controls the speed of the playback |
| `k` | `chorus_level` | float 0-1 | Gain applied to chorus when mixing into output. Set to 0 to turn off chorus. |
| `k` | `chorus` | float[,float,float,float] | Chorus parameters level, delay, freq, depth: Level is for output mix (0 to turn off); delay is max in samples (320); freq is LFO rate in Hz (0.5); depth is proportion of max delay (0.5). |
| `K` | `load_patch` | uint 0-X | Apply a saved patch to start at the selected oscillator |
| `L` | `mod_source` | 0 to OSCS-1 | Which oscillator is used as an modulation/LFO source for this oscillator. Source oscillator will be silent. |
| `l` | `vel` | float 0-1+ | velocity: > 0 to trigger note on, 0 to trigger note off |
| `M` | `chorus_freq` | float | LFO freq of chorus |
| `m` | `chorus_delay` | uint 1-512 | Maximum delay in chorus delay lines, in samples. Default 320 |
| `N` | `latency_ms` | uint | sets latency in ms. default 0 (see LATENCY) |
| `n` | `note` | uint 0-127 | midi note, sets frequency |
| `o` | `algorithm` | uint 1-32 | DX7 algorithm to use for ALGO type |
| `O` | `algo_source` | string | which oscillators to use for the algorithm. list of six (starting with op 6), use empty for not used, e.g 0,1,2 or 0,1,2,,, |
| `p` | `P-patch` | uint | choose a preloaded PCM sample or partial patch. Not for DX7 or Juno, use load_patch for those |
| `P` | `phase` | float 0-1 | where in the oscillator's cycle to start sampling from (also works on the PCM buffer). default 0 |
| `Q` | `pan` | float[,float...] | panning index ControlCoefficients (for stereo output), 0.0=left, 1.0=right. default 0.5. |
| `q` | `chorus_depth` | float | chorus depth |
| `R` | `resonance` | float | q factor of biquad filter. in practice, 0.5-16.0. default 0.7 |
| `r` | `voices` | int[,int] | String comma separated list of voices to send message to, or load patch into |
| `S` | `reset` | uint | resets given oscillator. set to > OSCS to reset all oscillators, gain and EQ |
Expand Down
34 changes: 14 additions & 20 deletions amy.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def to_str(x):
def message(osc=0, wave=None, patch=None, note=None, vel=None, amp=None, freq=None, duty=None, feedback=None, time=None, reset=None, phase=None, pan=None,
client=None, retries=None, volume=None, pitch_bend=None, filter_freq = None, resonance = None, bp0=None, bp1=None, eg0_type=None, eg1_type=None,
debug=None, chained_osc=None, mod_source=None, clone_osc=None, eq=None, filter_type= None,
algorithm=None, ratio = None, latency_ms = None, algo_source=None, chorus_level=None, chorus_delay=None, chorus_freq=None, chorus_depth=None,
algorithm=None, ratio = None, latency_ms = None, algo_source=None, chorus=None,
reverb=None, load_patch=None, store_patch=None, voices=None, external_channel=None):

m = ""
Expand Down Expand Up @@ -184,10 +184,7 @@ def message(osc=0, wave=None, patch=None, note=None, vel=None, amp=None, freq=No
if(debug is not None): m = m + "D" + str(debug)
if(eq is not None): m = m + "x%s" % eq
if(filter_type is not None): m = m + "G" + str(filter_type)
if(chorus_level is not None): m = m + "k" + str(chorus_level)
if(chorus_delay is not None): m = m + "m" + str(chorus_delay)
if(chorus_depth is not None): m = m + 'q' + trunc(chorus_depth)
if(chorus_freq is not None): m =m + 'M' + trunc(chorus_freq)
if(chorus is not None): m = m + 'k%s' % chorus
if(reverb is not None): m = m + "h%s" % reverb
if(load_patch is not None): m = m + 'K' + str(load_patch)
if(voices is not None): m = m + 'r' + str(voices)
Expand Down Expand Up @@ -444,22 +441,25 @@ def c_major(octave=2,wave=SINE, **kwargs):
Chorus control
"""
def chorus(level=-1, max_delay=-1, freq=-1, amp=-1):
args = {}
if (freq >= 0):
args['chorus_freq'] = freq
if (amp >= 0):
args['chorus_depth'] = amp
chorus_level = ''
chorus_delay = ''
chorus_freq = ''
chorus_depth = ''
if (level >= 0):
args['chorus_level'] = level
chorus_level = str(level)
if (max_delay >= 0):
args['chorus_delay'] = max_delay
send(**args)
chorus_delay = str(max_delay)
if (freq >= 0):
chorus_freq = str(freq)
if (amp >= 0):
chorus_depth = str(amp)
chorus_arg = "%s,%s,%s,%s" % (chorus_level, chorus_delay, chorus_freq, chorus_depth)
send(chorus=chorus_arg)

"""
Reverb control
"""
def reverb(level=-1, liveness=-1, damping=-1, xover_hz=-1):
args = {}
reverb_level = ''
reverb_liveness = ''
reverb_damping = ''
Expand All @@ -474,9 +474,3 @@ def reverb(level=-1, liveness=-1, damping=-1, xover_hz=-1):
reverb_xover = str(xover_hz)
reverb_arg = "%s,%s,%s,%s" % (reverb_level, reverb_liveness, reverb_damping, reverb_xover)
send(reverb=reverb_arg)






23 changes: 10 additions & 13 deletions juno.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,22 +407,19 @@ def update_cho(self):
eq_l = -15
eq_m = 8
eq_h = 8
chorus_args = {
cho_args = {
'eq': '%s,%s,%s' % (str(eq_l), str(eq_m), str(eq_h)),
'chorus_level': float(self.chorus > 0)
}
if self.chorus:
chorus_args['chorus_depth'] = 0.5
if self.chorus == 1:
chorus_args['chorus_freq'] = 0.5
elif self.chorus == 2:
chorus_args['chorus_freq'] = 0.83
elif self.chorus == 3:
# We choose juno 60-style I+II. Juno 6-style would be freq=8 depth=0.25
chorus_args['chorus_freq'] = 1
chorus_args['chorus_depth'] = 0.08
if self.chorus == 0:
cho_args['chorus'] = '0'
else:
# We choose juno 60-style I+II for chorus=3. Juno 6-style would be freq=8 depth=0.25
cho_args['chorus'] = '1,,%s,%s' % (
(0, 0.5, 0.83, 1)[self.chorus], # chorus_freq
(0, 0.5, 0.5, 0.08)[self.chorus], # chorus_depth
)
# *Don't* send to oscs, these ones are global.
amy.send(**chorus_args)
amy.send(**cho_args)

# Setters for each Juno UI control
def set_param(self, param, val):
Expand Down
21 changes: 14 additions & 7 deletions src/amy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1671,23 +1671,30 @@ struct event amy_parse_message(char * message) {
case 'I': e.ratio = atoff(message + start); break;
/* j, J available */
// chorus.level
case 'k': if(AMY_HAS_CHORUS)config_chorus(atoff(message + start), chorus.max_delay, chorus.lfo_freq, chorus.depth); break;
case 'k': if(AMY_HAS_CHORUS) {
float chorus_params[4] = {AMY_UNSET_VALUE(chorus.depth), AMY_UNSET_VALUE(chorus.depth),
AMY_UNSET_VALUE(chorus.depth), AMY_UNSET_VALUE(chorus.depth)};
parse_float_list_message(message + start, chorus_params, 4, AMY_UNSET_VALUE(chorus.depth));
// cpnfig_chorus doesn't understand UNSET, copy existing values.
if (AMY_IS_UNSET(chorus_params[0])) chorus_params[0] = S2F(chorus.level);
if (AMY_IS_UNSET(chorus_params[1])) chorus_params[1] = (float)chorus.max_delay;
if (AMY_IS_UNSET(chorus_params[2])) chorus_params[2] = chorus.lfo_freq;
if (AMY_IS_UNSET(chorus_params[3])) chorus_params[3] = chorus.depth;
config_chorus(chorus_params[0], (int)chorus_params[1], chorus_params[2], chorus_params[3]);
}
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;
// chorus.lfo_freq
case 'M': if(AMY_HAS_CHORUS)config_chorus(S2F(chorus.level), chorus.max_delay, atoff(message + start), chorus.depth); break;
// chorus.max_delay
case 'm': if(AMY_HAS_CHORUS)config_chorus(S2F(chorus.level), atoi(message + start), chorus.lfo_freq, chorus.depth); break;
/* m, M unused */
case 'N': e.latency_ms = atoi(message + start); break;
case 'n': e.midi_note=atoi(message + start); break;
case 'o': e.algorithm=atoi(message+start); break;
case 'O': copy_param_list_substring(e.algo_source, message+start); break;
case 'p': e.patch=atoi(message + start); break;
case 'P': e.phase=F2P(atoff(message + start)); break;
/* q unused */
case 'Q': parse_coef_message(message + start, e.pan_coefs); break;
// chorus.depth
case 'q': if(AMY_HAS_CHORUS)config_chorus(S2F(chorus.level), chorus.max_delay, chorus.lfo_freq, atoff(message+start)); break;
case 'R': e.resonance=atoff(message + start); break;
case 'r': copy_param_list_substring(e.voices, message+start); break;
case 'S': e.reset_osc = atoi(message + start); break;
Expand Down
Loading

0 comments on commit f1ab734

Please sign in to comment.