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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ src/amy.egg-info/
.python-version
amy.egg-info/
build/
venv/


# Prerequisites
Expand Down
2 changes: 1 addition & 1 deletion amy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def str_of_int(arg):
('to_synth', 'itI'), ('grab_midi_notes', 'imI'), ('synth_delay', 'idI'),
('preset', 'pI'), ('num_partials', 'pI'), # note aliasing
('start_sample', 'zSL'), ('stop_sample', 'zOI'),
('midi_cc', 'icL'),
('midi_cc', 'icL'), ('midi_note_cmd', 'ioL'),
('patch_string', 'uS'), # patch_string MUST be last because we can't identify when it ends except by end-of-message.
]
_KW_PRIORITY = {k: i for i, (k, _) in enumerate(_KW_MAP_LIST)} # Maps each key to its index within _KW_MAP_LIST.
Expand Down
2 changes: 1 addition & 1 deletion amy/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
EVENT_SCHEDULED=1
EVENT_TRANSFER_DATA=2
EVENT_SEQUENCE=3
NOTE_SOURCE_MIDI=2
NOTE_SOURCE_MIDI=1
ENVELOPE_NORMAL=0
ENVELOPE_LINEAR=1
ENVELOPE_DX7=2
Expand Down
7 changes: 4 additions & 3 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,13 +213,14 @@ A note on list parameters: When an argument is a list of parameters, you can in
| ------ | -------- | ---------- | ---------- | ------------------------------------- |
| `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](https://github.com/shorepine/amy/issues/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`. |
| `if` | `synth_flags` | `synth_flags` | uint | Flags for synth creation: 1 = Use MIDI drum note->preset translation; 2 = Drop note-off events. |
| `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. |
| `io` | **TODO** | `midi_note_cmd` | M,L,N,X,O,CMD | MIDI Note on/off command for this synth. M=MIDI note number, or -1 for all notes. Other args map the velocity, as for `ic`. `%n` is substituted with the note number. |
| `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`. |
| `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`. |
| `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`. |
Expand Down
2 changes: 1 addition & 1 deletion src/amy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,7 @@ void show_debug(uint8_t type) {
patches_debug();
}
if (type > 5) {
cc_mapping_debug();
midi_mapping_debug();
}
if (type > 6) {
for (int synth = 0; synth < 32 /* MAX_INSTRUMENTS */; ++synth) {
Expand Down
21 changes: 15 additions & 6 deletions src/amy.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ enum coefs{
#define EVENT_SEQUENCE 3

// note_source values
#define NOTE_SOURCE_MIDI 2
#define NOTE_SOURCE_MIDI 1

// Envelope generator types (for synth[osc].env_type[eg]).
#define ENVELOPE_NORMAL 0
Expand Down Expand Up @@ -901,13 +901,22 @@ extern int parse_int_list_message16(char *message, int16_t *vals, int max_num_va
extern void reset_osc_by_pointer(struct synthinfo *psynth, struct mod_synthinfo *pmsynth);
extern void reset_osc(uint16_t i );

extern int midi_store_control_code(int channel, int code, int is_log, float min_val, float max_val, float offset_val, char *message);
extern int midi_clear_control_code(int channel, int code);
extern bool midi_fetch_control_code_command(int channel, int code, char *s, size_t len);
extern void cc_mapping_debug();
// Values for midi_mapping.type
#define MIDI_MAP_TYPE_ANY (-1)
#define MIDI_MAP_TYPE_CC (0)
#define MIDI_MAP_TYPE_NOTE (1)

// Value for code (or note) that matches anything
#define MIDI_MAP_CODE_ANY (-1)

extern int midi_store_mapping(int channel, int type, int code, int is_log, float min_val, float max_val, float offset_val, char *message);
extern int midi_clear_mapping(int channel, int type, int code);
extern bool midi_fetch_mapping_command(int channel, int type, int code, char *s, size_t len);
extern void midi_mapping_debug();
extern void midi_mappings_init();
extern void midi_mappings_deinit();
extern void midi_clear_channel_mappings(int channel);
extern void midi_clear_channel_mappings(int channel, int type);
extern void midi_msg_handler(uint8_t * bytes, uint16_t len, uint8_t is_sysex, uint32_t time);

extern float render_am_lut(float * buf, float step, float skip, float incoming_amp, float ending_amp, const float* lut, int16_t lut_size, float *mod, float bandwidth);
extern void ks_init();
Expand Down
37 changes: 8 additions & 29 deletions src/amy_midi.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ static void debug_print_midi_hex(const uint8_t *data, uint32_t len, uint8_t syse
// Send a MIDI note on OUT
void amy_send_midi_note_on(uint16_t osc) {
// don't forward on a note coming in through MIDI IN
//fprintf(stderr, "amy_send_midi_note_on: osc %d source %d note %.1f vel %.3f\n",
// osc, synth[osc]->note_source, synth[osc]->midi_note, synth[osc]->velocity);
if(synth[osc]->note_source != NOTE_SOURCE_MIDI) {
uint8_t bytes[3];
bytes[0] = 0x90;
Expand All @@ -65,37 +67,14 @@ void amy_send_midi_note_off(uint16_t osc) {
// don't forward on a note coming in through MIDI IN
if(synth[osc]->note_source != NOTE_SOURCE_MIDI) {
uint8_t bytes[3];
bytes[0] = 0x80;
// Send note-off as a note-on with vel 0.
bytes[0] = 0x90;
bytes[1] = (uint8_t)roundf(synth[osc]->midi_note);
bytes[2] = (uint8_t)roundf(synth[osc]->velocity*127.0f);
bytes[2] = 0;
midi_out(bytes, 3);
}
}

// Given a MIDI note on IN, create a AMY message on that instrument and play it
void amy_received_note_on(uint8_t channel, uint8_t note, uint8_t vel, uint32_t time) {
if (!instrument_grab_midi_notes(channel)) return;
amy_event e = amy_default_event();
e.time = time;
e.synth = channel;
e.note_source = NOTE_SOURCE_MIDI;
e.midi_note = note;
e.velocity = ((float)vel/127.0f);
amy_add_event(&e);
}

// Given a MIDI note off IN, create a AMY message on that instrument and play it
void amy_received_note_off(uint8_t channel, uint8_t note, uint8_t vel, uint32_t time) {
if (!instrument_grab_midi_notes(channel)) return;
amy_event e = amy_default_event();
e.time = time;
e.synth = channel;
e.note_source = NOTE_SOURCE_MIDI;
e.midi_note = note;
e.velocity = 0;
amy_add_event(&e);
}

void amy_received_control_change(uint8_t channel, uint8_t control, uint8_t value, uint32_t time) {
if (control == 0) {
// Bank select coarse.
Expand Down Expand Up @@ -167,16 +146,16 @@ void amy_event_midi_message_received(uint8_t * data, uint32_t len, uint8_t sysex
uint8_t status = status_byte & 0xF0;
uint8_t channel = status_byte & 0x0F;
// Do the AMY instrument things here
if(status == 0x80) amy_received_note_off(channel+1, data[1], data[2], time);
else if(status == 0x90) amy_received_note_on(channel+1, data[1], data[2], time);
else if(status == 0xB0 && data[1] == 0x40) amy_received_pedal(channel+1, data[2], time);
/* if(status == 0x90) amy_received_note_on(channel+1, data[1], data[2], time);
else */ if(status == 0xB0 && data[1] == 0x40) amy_received_pedal(channel+1, data[2], time);
else if(status == 0xB0 && data[1] == 0x7B) amy_received_all_notes_off(channel+1, time);
else if(status == 0XB0) amy_received_control_change(channel+1, data[1], data[2], time);
else if(status == 0xC0) amy_received_program_change(channel+1, data[1], time);
else if(status == 0xE0) amy_received_pitch_bend(channel+1, data[1], data[2], time);
else if(status_byte == 0xFA) sequencer_midi_start();
else if(status_byte == 0xFC) sequencer_midi_stop();
}
midi_msg_handler(data, len, sysex, time);

// Also send the external hooks if set
if(amy_global.config.amy_external_midi_input_hook != NULL) {
Expand Down
7 changes: 0 additions & 7 deletions src/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,16 +259,13 @@ void amy_add_event(amy_event *e) {

// defined in midi_mappings.c
extern void juno_filter_midi_handler(uint8_t * bytes, uint16_t len, uint8_t is_sysex);
extern void midi_cc_handler(uint8_t * bytes, uint16_t len, uint8_t is_sysex);

#ifdef __EMSCRIPTEN__
void amy_start_web() {
// a shim for web AMY, as it's annoying to build structs in js
amy_config_t amy_config = amy_default_config();
amy_config.midi = AMY_MIDI_IS_WEBMIDI;
amy_config.features.default_synths = 1;
amy_config.features.startup_bleep = 1;
amy_config.amy_external_midi_input_hook = midi_cc_handler;
amy_start(amy_config);
}

Expand All @@ -277,7 +274,6 @@ void amy_start_web_no_synths() {
amy_config_t amy_config = amy_default_config();
amy_config.midi = AMY_MIDI_IS_WEBMIDI;
amy_config.features.default_synths = 0;
amy_config.amy_external_midi_input_hook = midi_cc_handler;
amy_start(amy_config);
}
#endif
Expand Down Expand Up @@ -387,9 +383,6 @@ void amy_start(amy_config_t c) {
if (amy_global.config.audio == AMY_AUDIO_IS_MINIAUDIO)
miniaudio_start();
#endif
if (amy_global.config.amy_external_midi_input_hook == NULL) {
amy_global.config.amy_external_midi_input_hook = midi_cc_handler;
}
}

void amy_stop() {
Expand Down
Loading
Loading