Skip to content

Commit

Permalink
Merge pull request #112 from bwhitman/patchstrings
Browse files Browse the repository at this point in the history
Allow patches to be added at runtime
  • Loading branch information
bwhitman authored Mar 29, 2024
2 parents 06e7ef8 + fe0becd commit 904eab5
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 8 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ Here's the full list:
| s | pitch_bend | float | Sets the global pitch bend, by default modifying all note frequencies by (fractional) octaves up or down |
| T | bp0_target | uint mask | Which parameter bp0 controls. 1=amp, 2=duty, 4=freq, 8=filter freq, 16=resonance, 32=feedback (can be added together). Can add 64 for linear ramp, otherwise exponential. **Deprecated** for setting targets, subsumbed by ControlCoefficients. |
| t | timestamp | uint | ms of expected playback since some fixed start point on your host. you should always give this if you can. |
| u | store_patch | number,string | store up to 32 patches in RAM with ID number (1024-1055) and AMY message after a comma. Must be sent alone |
| v | osc | uint 0 to OSCS-1 | which oscillator to control |
| V | volume | float 0-10 | volume knob for entire synth, default 1.0 |
| w | wave | uint 0-11 | waveform: [0=SINE, PULSE, SAW_DOWN, SAW_UP, TRIANGLE, NOISE, KS, PCM, ALGO, PARTIAL, PARTIALS, OFF]. default: 0/SINE |
Expand Down
8 changes: 5 additions & 3 deletions amy.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,13 @@ def trunc3(number):

# Construct an AMY message
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, bp0_target=None, bp1_target=None, mod_target=None,
debug=None, chained_osc=None, mod_source=None, clone_osc=None, eq_l = None, eq_m = None, eq_h = 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, reverb_level=None, reverb_liveness=None, reverb_damping=None, reverb_xover=None, load_patch=None, voices=None):
client=None, retries=None, volume=None, pitch_bend=None, filter_freq = None, resonance = None, bp0=None, bp1=None, bp0_target=None, bp1_target=None,
mod_target=None, debug=None, chained_osc=None, mod_source=None, clone_osc=None, eq_l = None, eq_m = None, eq_h = 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,
reverb_level=None, reverb_liveness=None, reverb_damping=None, reverb_xover=None, load_patch=None, store_patch=None, voices=None):

m = ""
if(store_patch is not None): return "u" + str(store_patch)
if(time is not None): m = m + "t" + str(time)
if(osc is not None): m = m + "v" + str(osc)
if(wave is not None): m = m + "w" + str(wave)
Expand Down
3 changes: 2 additions & 1 deletion src/amy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1568,9 +1568,10 @@ struct event amy_parse_message(char * message) {
case 'S': e.reset_osc = atoi(message + start); break;
case 's': e.pitch_bend = atoff(message + start); break;
case 'T': e.bp0_target = atoi(message + start); break;
case 'W': e.bp1_target = atoi(message + start); break;
case 'u': patches_store_patch(message+start); return amy_default_event();
case 'v': e.osc=((atoi(message + start)) % AMY_OSCS); break; // allow osc wraparound
case 'V': e.volume = atoff(message + start); break;
case 'W': e.bp1_target = atoi(message + start); break;
case 'w': e.wave=atoi(message + start); break;
case 'x': e.eq_l = atoff(message+start); break;
case 'y': e.eq_m = atoff(message+start); break;
Expand Down
1 change: 1 addition & 0 deletions src/amy.h
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ extern void partials_note_off(uint16_t osc);
extern void patches_load_patch(struct event e);
extern void patches_event_has_voices(struct event e);
extern void patches_reset();
extern void patches_store_patch(char * message);

extern SAMPLE render_partials(SAMPLE *buf, uint16_t osc);
extern SAMPLE render_custom(SAMPLE *buf, uint16_t osc) ;
Expand Down
46 changes: 42 additions & 4 deletions src/patches.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ if you get a osc and a voice, you add the osc to the base_osc lookup and send th
*/

#define MAX_VOICES 16
#define MEMORY_PATCHES 32
char * memory_patch[MEMORY_PATCHES];
uint16_t memory_patch_oscs[MEMORY_PATCHES];
uint8_t osc_to_voice[AMY_OSCS];
uint16_t voice_to_base_osc[MAX_VOICES];

Expand All @@ -33,8 +36,36 @@ void patches_reset() {
for(uint16_t i=0;i<AMY_OSCS;i++) {
AMY_UNSET(osc_to_voice[i]);
}
for(uint8_t i=0;i<MEMORY_PATCHES;i++) {
if(memory_patch[i] != NULL) free(memory_patch[i]);
memory_patch_oscs[i] = 0;
}
}

void patches_store_patch(char * message) {
// patch#,amy patch string
// put it in ram
uint16_t patch_number = atoi(message);
char * patch = message + 5; // always 4 digit patch + ,
// Now find out how many oscs this message uses

uint16_t max_osc = 0;
char sub_message[255];
uint16_t start = 0;
for(uint16_t i=0;i<strlen(patch);i++) {
if(message[i] == 'Z') {
strncpy(sub_message, message + start, i - start + 1);
sub_message[i-start+1]= 0;
struct event patch_event = amy_parse_message(sub_message);
if(patch_event.osc > max_osc) max_osc = patch_event.osc;
start = i+1;
}
}
if(memory_patch[patch_number-1024] != NULL) free(memory_patch[patch_number-1024]);
memory_patch[patch_number-1024] = malloc(strlen(patch));
memory_patch_oscs[patch_number-1024] = max_osc + 1;
strcpy(memory_patch[patch_number-1024], patch);
}

// This is called when i get an event with voices in it, BUT NOT with a load_patch
// So i know that the patch / voice alloc already exists and the patch has already been set!
Expand All @@ -61,8 +92,15 @@ void patches_load_patch(struct event e) {

int16_t voices[MAX_VOICES];
uint8_t num_voices = parse_int_list_message(e.voices, voices, MAX_VOICES);

char*message = (char*)patch_commands[e.load_patch];
char *message;
uint16_t patch_osc = 0;
if(e.load_patch > 1023) {
message = memory_patch[e.load_patch-1024];
patch_osc = memory_patch_oscs[e.load_patch-1024];
} else {
message = (char*)patch_commands[e.load_patch];
patch_osc = patch_oscs[e.load_patch];
}
for(uint8_t v=0;v<num_voices;v++) {
// Find the first osc with patch_oscs[e.load_patch] free oscs
// First, is this an old voice we're re-doing?
Expand Down Expand Up @@ -90,14 +128,14 @@ void patches_load_patch(struct event e) {
if(AMY_IS_UNSET(osc_to_voice[osc])) {
// Are there num_voices patch_oscs free oscs after this one?
good = 1;
for(uint16_t j=0;j<patch_oscs[e.load_patch];j++) {
for(uint16_t j=0;j<patch_osc;j++) {
good = good & (AMY_IS_UNSET(osc_to_voice[osc+j]));
}
if(good) {
//fprintf(stderr, "found %d consecutive oscs starting at %d for voice %d\n", patch_oscs[e.load_patch], osc, voices[v]);
//fprintf(stderr, "setting base osc for voice %d to %d\n", voices[v], osc);
voice_to_base_osc[voices[v]] = osc;
for(uint16_t j=0;j<patch_oscs[e.load_patch];j++) {
for(uint16_t j=0;j<patch_osc;j++) {
//fprintf(stderr, "setting osc %d for voice %d to amy osc %d\n", j, voices[v], osc+j);
osc_to_voice[osc+j] = voices[v];
reset_osc(osc+j);
Expand Down

0 comments on commit 904eab5

Please sign in to comment.