This page collects the current API for AMY.
Please see our interactive AMY tutorial for more tips on using AMY
Parsing, creating and adding events to AMY:
// returns default empty amy_event
amy_event amy_default_event();
// clears an existing AMY event
amy_clear_event(amy_event *e);
// given an event play / schedule the event directly
amy_add_event(amy_event *e);
// given a wire message string play / schedule the event directly
amy_add_message(char *message);Get and set external audio buffers:
// get AUDIO_IN0 and AUDIO_IN1
amy_get_input_buffer(output_sample_type * samples);
// set AUDIO_EXT0 and AUDIO_EXT1
amy_set_external_input_buffer(output_sample_type * samples);If not running live, render a new block of AMY audio into a int16_t buffer:
output_sample_type * amy_simple_fill_buffer();Get the AMY time:
// on all platforms, sysclock is based on total samples played, using audio out (i2s or etc) as system clock
uint32_t amy_sysclock();Start and stop AMY:
// Emscripten web start
amy_start_web();
amy_start_web_no_synths();
// Start AMY with a config. If c.audio is set, will attempt to start live audio
amy_start(amy_config_t c);
// Stop AMY including any live audio output
amy_stop();Default MIDI handlers:
void amy_enable_juno_filter_midi_handler(); // assigns the Juno-6 MIDI CC handlerAMY provides a high-level JavaScript API (amy_send) that mirrors the Python amy.send() interface. It is auto-generated from the same source of truth (amy/__init__.py and amy/constants.py) so parameter names are always identical to Python. The connector and API are bundled into amy.js, so you only need two includes:
<script src="enable-threads.js"></script>
<script src="amy.js"></script>Build and send an AMY wire message from a JS object. Parameter names and types are the same as Python amy.send().
// Play a sine wave at 440 Hz
amy_send({osc: 0, wave: AMY.SINE, freq: 440, vel: 1})
// Load a Juno-6 patch
amy_send({patch: 0, synth: 1, num_voices: 6})
amy_send({synth: 1, vel: 1, note: 60})
// FM synthesis with CtrlCoef dict
amy_send({osc: 0, amp: {const: 1, vel: 0, eg0: 2}, bp0: '0,0,5000,1,0,0'})Like amy_send but returns the wire string without sending it. Useful for debugging or building messages to send later.
amy_message({osc: 0, wave: AMY.SINE, freq: 440}) // => "v0w0f440Z"All constants from amy/constants.py are available on the global AMY object:
AMY.SINE // 0
AMY.SAW_DOWN // 2
AMY.PULSE // 1
AMY.FILTER_LPF // 1
AMY.ALGO // 8
// ... all constants from amy/constants.pyThe JS API is generated during make web and bundled into docs/amy.js by make deploy-web. To regenerate after changing amy/__init__.py or amy/constants.py:
make deploy-webAMY JavaScript REPL — write and run amy_send() commands in your browser.
Use like:
amy_config_t amy_config = amy_default_config()
amy_config.max_sequencer_tags = 128;
amy_start(amy_config);| Field | Values | Default | Notes |
|---|---|---|---|
features.chorus |
0=off, 1=on |
On | If chorus is enabled (uses RAM) |
features.reverb |
0=off, 1=on |
On | If reverb is enabled (uses RAM) |
features.echo |
0=off, 1=on |
On | If echo is enabled (uses RAM) |
features.partials |
0=off, 1=on |
On | If partials are enabled |
features.custom |
0=off, 1=on |
On | If custom C oscillators are enabled |
features.audio_in |
0=off, 1=on |
Off | If audio_in gets processed via the audio interface. Must be 1 for AUDIO_IS_MINIAUDIO |
features.default_synths |
0=off, 1=on |
Off | If AMY boots with Juno-6 on synth 1 and GM drums on synth 10 |
features.startup_bleep |
0=off, 1=on |
Off | If AMY plays a startup sound on boot |
platform.multicore |
0=off, 1=on |
On | Attempts to use 2nd core if available |
platform.multithread |
0=off, 1=on |
On | Attempts to multithreading if available (ESP/RTOS) |
midi |
AMY_MIDI_IS_NONE, AMY_MIDI_IS_UART, AMY_MIDI_IS_USB_GADGET, AMY_MIDI_IS_WEBMIDI |
AMY_MIDI_IS_NONE |
Which MIDI interface(s) are active |
audio |
AMY_AUDIO_IS_NONE, AMY_AUDIO_IS_I2S, AMY_AUDIO_IS_USB_GADGET, AMY_AUDIO_IS_MINIAUDIO |
I2S or miniaudio | Which audio interface(s) are active |
write_samples_fn |
fn ptr | NULL |
If provided, amy_update will call this with each new block of samples |
max_oscs |
Int | 180 | How many oscillators to support |
max_sequencer_tags |
Int | 256 | How many sequencer items to handle |
max_voices |
Int | 64 | How many voices |
max_synths |
Int | 64 | How many synths |
max_memory_patches |
Int | 32 | How many in memory patches to supprot |
i2s_lrc, i2s_dout, i2s_din, i2s_bclk, i2s_mclk |
Int | -1 | Pin numbers for the I2S interface |
midi_out, midi_in |
Int | -1 | Pin number for the MIDI UART pins |
midi_uart |
0,1,[2] | -1 | UART device index for MCU. Default 1 (UART1) on Pi Pico and ESP. Teensy is always 8 |
capture_device_id, playback_device_id |
Int | -1 | Which miniaudio device to use, -1 is auto |
Hooks are configured on amy_config_t before calling amy_start:
amy_config_t amy_config = amy_default_config();
amy_config.amy_external_midi_input_hook = my_midi_hook;
amy_config.amy_external_render_hook = my_render_hook;
amy_start(amy_config);Hook fields in amy_config_t:
| Hook | Signature | Used by | Description |
|---|---|---|---|
amy_external_render_hook |
uint8_t (uint16_t osc, SAMPLE *buf, uint16_t len) |
— | Custom oscillator renderer for redirecting the output waveforms on an osc-by-osc level. Return 1 if handled, in which case that osc does not contribute to the normal output. |
amy_external_coef_hook |
float (uint16_t channel) |
— | Provide external coefficient values (e.g. CV input). |
amy_external_block_done_hook |
void (void) |
— | Called after each audio block is rendered. |
amy_external_midi_input_hook |
void (uint8_t *bytes, uint16_t len, uint8_t is_sysex) |
— | Called when MIDI bytes are received. |
amy_external_sequencer_hook |
void (uint32_t tick_count) |
— | Called on each sequencer tick. |
amy_external_fopen_hook |
uint32_t (char *filename, const char *mode) |
zT, zD, zF |
Open a file on host disk. Returns opaque handle. |
amy_external_fwrite_hook |
uint32_t (uint32_t fptr, uint8_t *bytes, uint32_t len) |
zT |
Write bytes to a file opened via fopen hook. |
amy_external_fread_hook |
uint32_t (uint32_t fptr, uint8_t *bytes, uint32_t len) |
zD |
Read bytes from a file opened via fopen hook. |
amy_external_fseek_hook |
void (uint32_t fptr, uint32_t pos) |
zD |
Seek to position in a file opened via fopen hook. |
amy_external_fclose_hook |
void (uint32_t fptr) |
zT, zD, zF |
Close a file opened via fopen hook. |
amy_external_file_transfer_done_hook |
void (const char *filename) |
zT |
Called after a zT file transfer completes. On AMYboard, restarts sketch.py. |
amy_external_update_file_hook |
void (const char *filename) |
zA |
Called by zA to update a file with current AMY state. On AMYboard, splices live knob state into sketch.py. |
amy_external_exec_hook |
void (const char *code) |
zP |
Called by zP to execute a string on the host. On AMYboard, runs the string as Python via exec(). |
amy_external_reboot_hook |
void (uint8_t mode) |
zB |
Called by zB to reboot the host. mode selects which post-reboot state: 0 = bootloader (skip sketch on next boot), 1 = normal reboot (run sketch), 2 = ROM download / flash mode. Handled in pure C before mp_sched_schedule. On AMYboard, sets an RTC flag with the requested mode and calls esp_restart(). |
All hook fields default to NULL in amy_default_config().
AMY parameters can be set via three interfaces:
- C: Set fields on
amy_eventstructs - Python:
amy.send(param=value, ...) - JavaScript:
amy_send({param: value, ...})
Python and JavaScript use identical parameter names (shown in the Python / JS column below). A few parameters are not yet available via C amy_event (marked TODO).
Please see AMY synthesizer details for more explanation on the synthesizer parameters.
Please see our interactive AMY tutorial for more tips on using AMY
A note on list parameters: When an argument is a list of parameters, you can in general set any subset of those parameters by omitting the values you don't want to change - either by leaving them in their initial AMY_UNSET value in C, or by having missing values in Python lists. For instance, you can set up an envelope that moves immediately to 1, then decase to a sutain level of 0.5 over 200ms, then has a 300ms decay to zero on note-off, with bp0='0,1,200,0.5,300,0'. Subsequently, you could change just the sustain level (the 4th value in the list) to 0.2 with bp0=,,,0.2`. However, there's at present no way to say ".. and the list should now only be 4 items long. This only affects breakpoint sets which are variable length, but the net result is that once you have a certain number of breakpoints in a list, you cannot shorten it except by resetting the whole osc and building it all up again.
| Wire code | C amy_event |
Python / JS | Type-range | Notes |
|---|---|---|---|---|
i |
synth |
synth |
0-31 | Define a set of voices for voice management. |
ic |
TODO | midi_cc |
C,L,N,X,O,CMD | MIDI Control Code command for this synth (1-16). C=MIDI CC (0-127), L=log mapping (0/1), N=min val, X=max val, O=offset, CMD=wire command to execute, where %i is replaced by the channel number and %v is replaced by the value after min/max/offset/log mapping. Providing C with no further args deletes that CC. C=255 deletes all CC mappings for the specified synth. See #524 |
if |
synth_flags |
synth_flags |
uint | Flags for synth creation: 1 = Use MIDI drum note->preset translation; 2 = Drop note-off events. |
id |
synth_delay_ms |
synth_delay |
uint | Delay (in ms) applied to synth note-ons. Gives time for decay of 'stolen' notes. |
it |
to_synth |
to_synth |
0-31 | New synth number, when changing the number (MIDI channel for n=1..16) of an entire synth. |
iv |
num_voices |
num_voices |
int | The number of voices to allocate when defining a synth, alternative to directly specifying voice numbers with voices=. Only valid with synth=X, patch[_number]=Y. |
in |
oscs_per_voice |
oscs_per_voice |
>0 | Reserve this many oscs for each voice. Needed when initializing a synth (or voice) withouth an initial patch. Setting oscs_per_voice on an existing synth resets all oscs to their default state. |
im |
grab_midi_notes |
grab_midi_notes |
0/1 | Use amy.send(synth=CHANNEL, grab_midi_notes=0) to prevent the default direct forwarding of MIDI note-on/offs to synth CHANNEL. |
ip |
pedal |
pedal |
int | Non-zero means pedal is down (i.e., sustain). Must be used with synth. |
K |
patch_number |
patch |
uint 0-X | Apply a saved or user patch to a specified synth or voice. |
r |
voices[] |
voices |
int[,int] | Comma separated list of voices to send message to, or load patch into. |
u |
TODO | patch_string |
string | Provide AMY message to define up to 32 patches in RAM with ID numbers (1024-1055) provided via patch_number, or directly configure a synth. |
| Wire code | C amy_event |
Python / JS | Type-range | Notes |
|---|---|---|---|---|
v |
osc |
osc |
uint 0 to OSCS-1 | Which oscillator to control |
w |
wave |
wave |
uint 0-21 | Waveform: [0=SINE, PULSE, SAW_DOWN, SAW_UP, TRIANGLE, NOISE, KS, PCM, ALGO, PARTIAL, BYO_PARTIALS, INTERP_PARTIALS, AUDIO_IN0, AUDIO_IN1, AUDIO_EXT0, AUDIO_EXT1, AMY_MIDI, PCM_LEFT, PCM_RIGHT, WAVETABLE, CUSTOM, OFF]. default: 0/SINE |
S |
reset_osc |
reset |
uint | Resets given oscillator. set to RESET_ALL_OSCS to reset all oscillators, gain and EQ. RESET_TIMEBASE resets the clock (immediately, ignoring time). RESET_AMY restarts AMY. RESET_SEQUENCER clears the sequencer. |
A |
eg0_times[], eg0_values[] |
bp0 |
string (wire) / arrays (amy_event) |
Envelope Generator 0 breakpoints as time(ms),value pairs. Wire/Python format remains comma-separated, e.g. 100,0.5,50,0.25,200,0. In C amy_event, use typed arrays (eg0_times[i], eg0_values[i]). The last pair is release (triggers on note off). |
B |
eg1_times[], eg1_values[] |
bp1 |
string (wire) / arrays (amy_event) |
Envelope Generator 1 breakpoints. Wire/Python format remains comma-separated; in C amy_event, use typed arrays (eg1_times[i], eg1_values[i]). |
b |
feedback |
feedback |
float 0-1 | Use for the ALGO synthesis type in FM or for karplus-strong, or to indicate PCM looping (0 off, >0, on) |
c |
chained_osc |
chained_osc |
uint 0 to OSCS-1 | Chained oscillator. Note/velocity events to this oscillator will propagate to chained oscillators. VCF is run only for first osc in chain, but applies to all oscs in chain. |
G |
filter_type |
filter_type |
0-4 | Filter type: 0 = none (default.) 1 = lowpass, 2 = bandpass, 3 = highpass, 4 = double-order lowpass. |
I |
ratio |
ratio |
float | For ALGO types, ratio of modulator frequency to base note frequency |
L |
mod_source |
mod_source |
0 to OSCS-1 | Which oscillator is used as an modulation/LFO source for this oscillator. Source oscillator will be silent. |
m |
portamento |
portamento |
uint | Time constant (in ms) for pitch changes when note is changed without intervening note-off. default 0 (immediate), 100 is good. |
n |
midi_note |
note |
float, but typ. uint 0-127 | Midi note, sets frequency. Fractional Midi notes are allowed. |
o |
algorithm |
algorithm |
uint 1-32 | DX7 FM algorithm to use for ALGO type |
O |
algo_source[] |
algo_source |
string | Which oscillators to use for the FM algorithm. list of six (starting with op 6), use empty for not used, e.g 0,1,2 or 0,1,2,,, |
p |
preset |
preset |
int | Which predefined PCM or wavetable preset patch to use, or number of partials if < 0. For wave=WAVETABLE, use the wavetable presets appended to PCM. (Juno/DX7 patches are different - see patch_number). |
p |
preset |
num_partials |
int | Alias for preset. Must be used with wave=BYO_PARTIALS. Cannot be combined with preset in the same message. |
P |
phase |
phase |
float 0-1 | Where in the oscillator's cycle to begin the waveform (also works on the PCM buffer). default 0 |
R |
resonance |
resonance |
float | Q factor of variable filter, 0.5-16.0. default 0.7 |
T |
eg_type[0] |
eg0_type |
uint 0-3 | Type for Envelope Generator 0 - 0: Normal (RC-like) / 1: Linear / 2: DX7-style / 3: True exponential. |
X |
eg_type[1] |
eg1_type |
uint 0-3 | Type for Envelope Generator 1 - 0: Normal (RC-like) / 1: Linear / 2: DX7-style / 3: True exponential. |
l |
velocity |
vel |
float | Note on velocity. Use to start an envelope or set amplitude |
These per-oscillator parameters use CtrlCoefs notation
| Wire code | C amy_event |
Python / JS | Type-range | Notes |
|---|---|---|---|---|
Q |
pan_coefs[] |
pan |
float[,float...] | Panning index ControlCoefficients (for stereo output), 0.0=left, 1.0=right. default 0.5. |
a |
amp_coefs[] |
amp |
float[,float...] | Control the amplitude of a note; a set of ControlCoefficients. Default is 0,0,1,1 (i.e. the amplitude comes from the note velocity multiplied by by Envelope Generator 0.) |
d |
duty_coefs[] |
duty |
float[,float...] | Duty cycle for pulse wave, ControlCoefficients, defaults to 0.5 |
f |
freq_coefs[] |
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_coefs[] |
filter_freq |
float[,float...] | Center/break frequency for variable filter, set of ControlCoefficients |
| Wire code | C amy_event |
Python / JS | Type-range | Notes |
|---|---|---|---|---|
z |
TODO | load_sample |
uint x 6 | Signal to start loading sample. preset number, length(frames), samplerate, channels, midinote, loopstart, loopend. All subsequent messages are base64 encoded WAVE-style frames of audio until length is reached. Set preset and length=0 to unload a sample from RAM. |
zF |
TODO | disk_sample |
uint,string,uint | Set a PCM preset to play live from a WAV filename on AMY host disk. Params: preset number, filename, midinote. See hooks for reading files on host disk. Only one file sample can be played at once per preset number. Use multiple presets if you want polyphony from a single sample. |
zS |
TODO | start_sample |
uint x 6 | Start sampling to a stereo PCM preset from bus. Params: preset number, bus, max length in frames, midinote, loopstart, loopend. bus = 1 is AMY mixed output. bus = 2 is AUDIO_IN0 + 1. Will sample until max length is reached, stop_sample is issued, or a new start_sample is issued. |
zO |
TODO | stop_sample |
uint | Stop sampling. Does nothing if no sampling active. param ignored. |
wave=WAVETABLE is available when AMY is built with -DAMY_WAVETABLE.
- Wavetable samples are baked into
pcm_tinyand exposed as contiguous PCM presets. - The preset range is dynamic at runtime:
- start:
pcm_wavetable_base - count:
pcm_wavetable_samples - valid presets:
pcm_wavetable_base ... pcm_wavetable_base + pcm_wavetable_samples - 1
- start:
- Each wavetable preset is expected to be one 64-cycle table (normally
16384samples total,256samples per cycle). dutycrossfades across the 64 cycles inside the selected wavetable preset.
| Wire code | C amy_event |
Python / JS | Type-range | Notes |
|---|---|---|---|---|
H |
sequence[3] |
sequence |
int,int,int | Tick offset, period, tag for sequencing |
h |
reverb_level, reverb_liveness, reverb_damping, reverb_xover_hz |
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. |
j |
tempo |
tempo |
float | The tempo (BPM, quarter notes) of the sequencer. Defaults to 108.0. |
k |
chorus_level, chorus_max_delay, chorus_lfo_freq, chorus_depth |
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). |
M |
echo_level, echo_delay_ms, echo_max_delay_ms, echo_feedback, echo_filter_coef |
echo |
float[,int,int,float,float] | Echo parameters -- level, delay_ms, max_delay_ms, feedback, filter_coef (-1 is HPF, 0 is flat, +1 is LPF). |
N |
latency_ms |
latency_ms |
uint | Sets latency in ms. default 0 (see LATENCY) |
s |
pitch_bend |
pitch_bend |
float | Sets the global pitch bend, by default modifying all note frequencies by (fractional) octaves up or down |
t |
time |
time |
uint | Request playback time relative to some fixed start point on your host, in ms. Allows precise future scheduling. |
V |
volume |
volume |
float 0-10 | Volume knob for entire synth, default 1.0 |
x |
eq_l, eq_m, eq_h |
eq |
float,float,float | Equalization in dB low (~800Hz) / med (~2500Hz) / high (~7500Gz) -15 to 15. 0 is off. default 0. |
g |
client |
client |
uint | Client number for Alles distributed synthesis. |
W |
external_channel |
external_channel |
uint | External channel routing (used by Tulip for CV output). |
D |
TODO | debug |
uint, 2-4 | 2 shows queue sample, 3 shows oscillator data, 4 shows modified oscillator. Will interrupt audio! |
zT |
TODO | transfer_file |
string,uint | Transfer a file to the host. Params: destination filename, file size. See hooks for writing files on host disk. |
zA |
TODO | update_file |
string (optional) | Update a file on disk with current AMY state via update_file_hook. Default path: /user/current/sketch.py. On AMYboard, splices _auto_generated_knobs section with live state. The filename is "rest of message": a trailing Z end-of-message marker is stripped, so interior capital-Z characters (e.g. /user/ZFILE.py) are preserved. Filenames whose last character is Z are not addressable. |
zD |
TODO | dump_sysex |
string (optional) | Dump data over MIDI sysex (base64-encoded, wrapped with SPSS manufacturer ID 00 03 45). With no params (zDZ): dumps all active instrument state. With a filename (zD/user/current/sketch.pyZ): reads file and sends it. The filename is "rest of message": a trailing Z end-of-message marker is stripped, so interior capital-Z characters (e.g. /user/ZFILE.py) are preserved. Filenames whose last character is Z are not addressable. |
zP |
TODO | exec |
string | Execute code on the host via amy_external_exec_hook. On AMYboard, runs the string as Python (e.g. zPimport amyboard; amyboard.restart_sketch()). Max 255 chars. The code string is "rest of message": a trailing Z end-of-message marker is stripped, so interior capital-Z characters in the code are preserved. Code whose last character is Z is not expressible. |
zI |
TODO | ping |
(none) | Ping the host. Replies with a short sysex frame F0 00 03 45 'O' 'K' F7 so the caller can confirm the board is alive and sysex is flowing. Handled entirely in pure C (no scheduler needed). |
zB |
TODO | reboot |
uint (optional) | Reboot the host via amy_external_reboot_hook. Optional mode argument selects the post-reboot state: zBZ / zB0Z = bootloader mode (skip sketch on next boot), zB1Z = normal reboot (run sketch), zB2Z = ROM download / flash mode. Handled in pure C (no scheduler needed). On AMYboard, sets an RTC flag with the mode and calls esp_restart(). |