/***************************************************************************** * process_all_notes_off() * * Process all notes off for a single channel. The events for each channel * should have been queued once for each engine already. *****************************************************************************/ void process_all_notes_off(MIDI_EVENT *event, unsigned int part_num) { PART *part = get_part(part_num); VOICE *voice; unsigned int voice_num; int j; PHASEX_DEBUG(DEBUG_CLASS_MIDI_EVENT, "*** All notes off! Part %d: event_type=0x%02x\n", (part_num + 1), event->type); /* let engine shut off notes gracefully */ for (voice_num = 0; voice_num < (unsigned int) setting_polyphony; voice_num++) { voice = get_voice(part_num, voice_num); voice->keypressed = -1; } /* re-initialize this part's keylists */ part->head = NULL; part->cur = NULL; part->prev = NULL; part->midi_key = -1; part->prev_key = -1; /* re-initialize this part's keylist nodes */ for (j = 0; j < 128; j++) { part->keylist[j].midi_key = (short) j; part->keylist[j].next = NULL; } }
/***************************************************************************** * process_program_change() * * TODO: handle bank and program select controller messages. *****************************************************************************/ void process_program_change(MIDI_EVENT *event, unsigned int part_num) { PHASEX_DEBUG(DEBUG_CLASS_MIDI_EVENT, "-- Program Change: Part %d: %d %d\n", (part_num + 1), event->program, event->value); midi_select_program(part_num, event->program); }
/***************************************************************************** * process_all_sound_off() * * Process all sound off for a single channel by first turning off all notes * and then zeroing out all audio buffers. This event should have already * been queued once for each engine already. *****************************************************************************/ void process_all_sound_off(MIDI_EVENT *event, unsigned int part_num) { PHASEX_DEBUG(DEBUG_CLASS_MIDI_EVENT, "*** All notes off! Part %d: event_type=0x%02x\n", (part_num + 1), event->type); process_all_notes_off(event, part_num); init_engine_buffers(); }
/***************************************************************************** * process_controller() * * TODO: Before the if-else (or even switch-case) logic gets too much * bigger, it would be better to use a lookup table with processing function * pointers. *****************************************************************************/ void process_controller(MIDI_EVENT *event, unsigned int part_num) { PARAM *param; unsigned char j; unsigned char cc; /* last controller touched for midimap updates */ int id; /* parameter id, used for finding callbacks */ /* get controller number */ cc = event->controller & 0x7F; PHASEX_DEBUG(DEBUG_CLASS_MIDI_TIMING, DEBUG_COLOR_GREEN "ccccc %d:%d=%d ccccc " DEBUG_COLOR_DEFAULT, (part_num + 1), cc, event->value); /* 0x78-0x7F (120-127) are channel mode messages. */ /* for now, just shut notes off */ /* TODO: polyphony modes needs to map to these */ if (cc >= 0x78) { if (cc == 0x78) { process_all_sound_off(event, part_num); } else { process_all_notes_off(event, part_num); } } else if (cc == MIDI_CONTROLLER_HOLD_PEDAL) { process_hold_pedal(event, part_num); } else { /* now walk through the params in the matrix */ for (j = 0; j < 16; j++) { /* bail out if we're at the end of this controller chain */ if ((id = ccmatrix[cc][j]) < 0) { break; } param = get_param(part_num, (unsigned int) id); /* set value for parameter */ param_midi_update(param, event->value & 0x7F); /* update gui's copy and mark as updated */ gui_param_midi_update(param, event->value & 0x7F); } } /* check for active cc edit */ if (!cc_edit_ignore_midi && cc_edit_active) { cc_edit_cc_num = cc; } }
/***************************************************************************** * process_pitchbend() *****************************************************************************/ void process_pitchbend(MIDI_EVENT *event, unsigned int part_num) { PART *part = get_part(part_num); unsigned int bend_value; bend_value = ((unsigned int)(event->lsb & 0x7F) | ((unsigned int)(event->msb & 0x7F) << 7)); PHASEX_DEBUG(DEBUG_CLASS_MIDI_EVENT, "Received pitchbend event: 2-byte value = %d (lsb=%d msb=%d)\n", bend_value, event->lsb, event->msb); part->pitch_bend_target = ((sample_t)(bend_value) - 8192) * 0.0001220703125; /* 1/8192 */ }
/***************************************************************************** * midi_select_program() *****************************************************************************/ void midi_select_program(unsigned int part_num, unsigned int prog_num) { SESSION *session = get_current_session(); PATCH *patch; if (!setting_ignore_midi_program_change) { patch = set_active_patch(visible_sess_num, part_num, prog_num); init_patch_state(patch); PHASEX_DEBUG(DEBUG_CLASS_MIDI_EVENT, "\n*** MIDI Program Change: part=%d prog=%d (%d)\n\n", (part_num + 1), prog_num, (prog_num + 1)); visible_prog_num[part_num] = prog_num; session_bank[visible_sess_num].prog_num[part_num] = prog_num; if ((part_num == visible_part_num)) { pending_visible_patch = patch; } session->modified = 1; } }
/***************************************************************************** * process_aftertouch() *****************************************************************************/ void process_aftertouch(MIDI_EVENT *event, unsigned int part_num) { VOICE *voice; PATCH_STATE *state = get_active_state(part_num); int voice_num; PHASEX_DEBUG(DEBUG_CLASS_MIDI_EVENT, "Event Key Presssure: part=%d note=%d velocity=%d\n", (part_num + 1), event->note, event->velocity); for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { voice = get_voice(part_num, voice_num); if (event->note == voice->midi_key) { voice->velocity = event->aftertouch; voice->velocity_target_linear = (sample_t) event->aftertouch * 0.01; voice->velocity_target_log = velocity_gain_table[state->amp_velocity_cc][event->aftertouch]; } } }
/***************************************************************************** * process_polypressure() *****************************************************************************/ void process_polypressure(MIDI_EVENT *event, unsigned int part_num) { PART *part = get_part(part_num); PATCH_STATE *state = get_active_state(part_num); VOICE *voice; int voice_num; PHASEX_DEBUG(DEBUG_CLASS_MIDI_EVENT, "Event Channel Pressure: part=%d value=%d\n", (part_num + 1), event->polypressure); part->velocity_target = (sample_t) event->polypressure * 0.01; for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { voice = get_voice(part_num, voice_num); if (voice->active) { voice->velocity = event->polypressure; voice->velocity_target_linear = (sample_t) event->polypressure * 0.01; voice->velocity_target_log = velocity_gain_table[state->amp_velocity_cc][event->polypressure]; } } }
/***************************************************************************** * process_note_on() * * Process a note on event for a single part. Add the note to the current * part's keylist and select a voice, stealing one if necessary. All note on * events are processed with process_keytrigger() unless the velocity is set * to zero, and process_note_off() is used instead. *****************************************************************************/ void process_note_on(MIDI_EVENT *event, unsigned int part_num) { VOICE *voice; VOICE *old_voice = NULL; VOICE *loop_voice; PART *part = get_part(part_num); PATCH_STATE *state = get_active_state(part_num); int voice_num; int oldest_age; int free_voice; int steal_voice; int same_key; int osc; int voice_count = 0; /* if this is velocity 0 style note off, fall through */ if (event->velocity > 0) { /* keep track of previous to last key pressed! */ part->prev_key = part->midi_key; part->midi_key = event->note; part->last_key = event->note; /* allocate voice for the different keymodes */ switch (state->keymode) { case KEYMODE_MONO_SMOOTH: old_voice = get_voice(part_num, vnum[part_num]); old_voice->keypressed = -1; /* allocate new voice for staccato in mid-release only. */ if (old_voice->allocated && ((old_voice->cur_amp_interval == ENV_INTERVAL_RELEASE) || (old_voice->cur_amp_interval == ENV_INTERVAL_FADE))) { old_voice->keypressed = -1; old_voice->cur_amp_interval = ENV_INTERVAL_RELEASE; old_voice->cur_amp_sample = -1; PHASEX_DEBUG(DEBUG_CLASS_MIDI_NOTE, "voice %2d: ^^^^^^^ Mono-Smooth: " "Fading out voice. (Note On) ^^^^^^^\n", (old_voice->id + 1)); vnum[part_num] = (vnum[part_num] + 1) % setting_polyphony; } break; case KEYMODE_MONO_UNISON_4: voice_count = 4; vnum[part_num] = 0; break; case KEYMODE_MONO_UNISON_6: voice_count = 6; vnum[part_num] = 0; break; case KEYMODE_MONO_UNISON_8: voice_count = 8; vnum[part_num] = 0; break; case KEYMODE_MONO_MULTIKEY: vnum[part_num] = 0; break; case KEYMODE_MONO_RETRIGGER: for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { loop_voice = get_voice(part_num, voice_num); if (loop_voice->allocated) { loop_voice->keypressed = -1; loop_voice->cur_amp_interval = ENV_INTERVAL_RELEASE; loop_voice->cur_amp_sample = -1; old_voice = loop_voice; PHASEX_DEBUG(DEBUG_CLASS_MIDI_NOTE, "voice %2d: ^^^^^^^ Mono-Retrigger: " "Fading out voice. (Note On) ^^^^^^^\n", (loop_voice->id + 1)); } } vnum[part_num] = (vnum[part_num] + setting_polyphony - 1) % setting_polyphony; break; case KEYMODE_POLY: vnum[part_num] = 0; /* voice allocation with note stealing */ oldest_age = 0; free_voice = -1; steal_voice = setting_polyphony - 1; same_key = -1; /* look through all the voices */ for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { loop_voice = get_voice(part_num, voice_num); /* priority 1: a free voice */ if (loop_voice->allocated == 0) { free_voice = voice_num; vnum[part_num] = free_voice; voice_num = setting_polyphony; break; } else { if (loop_voice->midi_key == part->midi_key) { PHASEX_DEBUG(DEBUG_CLASS_MIDI_TIMING, DEBUG_COLOR_RED "+++++++++++ " DEBUG_COLOR_DEFAULT); } /* priority 2: find the absolute oldest in play */ if ((same_key == -1) && (loop_voice->age > oldest_age)) { oldest_age = loop_voice->age; steal_voice = voice_num; } } } /* priorities 1 and 2 */ if (free_voice >= 0) { vnum[part_num] = free_voice; } else { vnum[part_num] = steal_voice; PHASEX_DEBUG(DEBUG_CLASS_MIDI_NOTE, "*** Part %d: stealing voice %d!\n", (part_num + 1), vnum[part_num]); PHASEX_DEBUG(DEBUG_CLASS_MIDI_TIMING, DEBUG_COLOR_YELLOW "+++++++++++ " DEBUG_COLOR_DEFAULT); } break; } /* keep pointer to current voice around */ voice = get_voice(part_num, vnum[part_num]); /* assign midi note */ voice->midi_key = part->midi_key; voice->keypressed = part->midi_key; /* keep velocity for this note event */ voice->velocity = event->velocity; voice->velocity_target_linear = voice->velocity_coef_linear = part->velocity_target = part->velocity_coef = ((sample_t) event->velocity) * 0.01; voice->velocity_target_log = voice->velocity_coef_log = velocity_gain_table[state->amp_velocity_cc][event->velocity]; for (voice_num = 1; voice_num < voice_count; voice_num++) { if ( vnum[part_num] == voice_num ) continue; loop_voice = get_voice(part_num, voice_num); /* assign midi note */ loop_voice->midi_key = part->midi_key; loop_voice->keypressed = part->midi_key; /* keep velocity for this note event */ loop_voice->velocity = event->velocity; loop_voice->velocity_target_linear = loop_voice->velocity_coef_linear = part->velocity_target = part->velocity_coef = ((sample_t) event->velocity) * 0.01; loop_voice->velocity_target_log = loop_voice->velocity_coef_log = velocity_gain_table[state->amp_velocity_cc][event->velocity]; } if (event->velocity > 0) { part->velocity = event->velocity; PHASEX_DEBUG(DEBUG_CLASS_MIDI_NOTE, "voice %2d: Event Note On: part=%d " "voice=%2d note=%3d velocity=%3d keylist=:", (vnum[part_num] + 1), (part_num + 1), (vnum[part_num] + 1), event->note, event->velocity); PHASEX_DEBUG(DEBUG_CLASS_MIDI_TIMING, DEBUG_COLOR_RED "!!!!!!! %d !!!!!!! " DEBUG_COLOR_DEFAULT, event->note); } /* staccato, or no previous notes in play */ if ((part->prev_key == -1) || (part->head == NULL)) { old_voice = NULL; /* put this key at the start of the list */ part->head = & (part->keylist[part->midi_key]); part->head->next = NULL; } /* legato, or previous notes still in play */ else { /* Link this key to the end of the list, unlinking from the middle if necessary. */ part->cur = part->head; part->prev = NULL; while (part->cur != NULL) { PHASEX_DEBUG(DEBUG_CLASS_MIDI_NOTE, "%d:", part->cur->midi_key); if (part->cur == &part->keylist[part->midi_key]) { if (part->prev != NULL) { part->prev->next = part->cur->next; } } part->prev = part->cur; part->cur = part->cur->next; } //PHASEX_DEBUG (DEBUG_CLASS_MIDI_NOTE, "\n"); part->cur = & (part->keylist[part->midi_key]); /* if there is no end of the list, link it to the head */ if (part->prev == NULL) { part->head = part->cur; PHASEX_WARN("*** process_note_on(): [part%d] found " "previous key in play with no keylist!\n", (part_num + 1)); } else { part->prev->next = part->cur; } part->cur->next = NULL; } if (event->velocity > 0) { PHASEX_DEBUG(DEBUG_CLASS_MIDI_NOTE, "\n"); } /* process parameters dependent on keytrigger events */ process_keytrigger(event, old_voice, voice, part_num); for (voice_num = 0; voice_num < voice_count; voice_num++) { if ( vnum[part_num] == voice_num ) continue; loop_voice = get_voice(part_num, voice_num); process_keytrigger(event, old_voice, loop_voice, part_num); } } /* velocity 0 style note off */ else { process_note_off(event, part_num); } }
/***************************************************************************** * process_keytrigger() * * Process voice/patch/param updates for keytrigger events. This function * contains most of the logic for activating voices, and should be broken up * into its logical components. *****************************************************************************/ void process_keytrigger(MIDI_EVENT *UNUSED(event), VOICE *old_voice, VOICE *voice, unsigned int part_num) { VOICE *loop_voice; PART *part = get_part(part_num); PATCH_STATE *state = get_active_state(part_num); int osc; int lfo; int voice_num; int staccato = 1; int env_trigger = 0; sample_t tmp; PHASEX_DEBUG(DEBUG_CLASS_MIDI_NOTE, "voice %2d: process_keytrigger(): old_voice=%2d voice=%2d\n", (voice->id + 1), (old_voice == NULL ? 0 : (old_voice->id + 1)), (voice->id + 1)); /* check for notes currently in play */ switch (state->keymode) { case KEYMODE_MONO_RETRIGGER: env_trigger = 1; /* intentional fall-through */ case KEYMODE_MONO_UNISON_4: case KEYMODE_MONO_UNISON_6: case KEYMODE_MONO_UNISON_8: case KEYMODE_MONO_MULTIKEY: case KEYMODE_MONO_SMOOTH: if (voice->cur_amp_interval >= ENV_INTERVAL_RELEASE) { staccato = 1; env_trigger = 1; } else { staccato = 0; } break; case KEYMODE_POLY: staccato = 1; for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { loop_voice = get_voice(part_num, voice_num); if (loop_voice->cur_amp_interval < ENV_INTERVAL_RELEASE) { staccato = 0; break; } } env_trigger = 1; break; } /* staccato, mono retrig, and poly get new envelopes and initphases */ if (env_trigger) { /* start new amp and filter envelopes */ voice->cur_amp_interval = ENV_INTERVAL_ATTACK; voice->cur_filter_interval = ENV_INTERVAL_ATTACK; voice->cur_amp_sample = voice->amp_env_dur[ENV_INTERVAL_ATTACK] = env_interval_dur[ENV_INTERVAL_ATTACK][state->amp_attack]; voice->cur_filter_sample = voice->filter_env_dur[ENV_INTERVAL_ATTACK] = env_interval_dur[ENV_INTERVAL_ATTACK][state->filter_attack]; /* TODO: test with hold pedal. */ /* without hold pedal: */ /* voice->amp_env_delta[ENV_INTERVAL_ATTACK] = */ /* (1.0 - voice->amp_env_raw) / */ /* (sample_t)voice->amp_env_dur[ENV_INTERVAL_ATTACK]; */ /* voice->filter_env_delta[ENV_INTERVAL_ATTACK] = */ /* (1.0 - voice->filter_env_raw) / */ /* (sample_t)voice->filter_env_dur[ENV_INTERVAL_ATTACK]; */ /* with hold pedal: everything until next comment. */ voice->amp_env_raw = 0.0; voice->filter_env_raw = 0.0; if (state->amp_attack || state->amp_decay) { voice->amp_env_delta[ENV_INTERVAL_ATTACK] = (1.0 - voice->amp_env_raw) / (sample_t) voice->amp_env_dur[ENV_INTERVAL_ATTACK]; } else { voice->amp_env_delta[ENV_INTERVAL_ATTACK] = (state->amp_sustain - voice->amp_env_raw) / (sample_t) voice->amp_env_dur[ENV_INTERVAL_ATTACK]; } if (state->filter_attack || state->filter_decay) { voice->filter_env_delta[ENV_INTERVAL_ATTACK] = (1.0 - voice->filter_env_raw) / (sample_t) voice->filter_env_dur[ENV_INTERVAL_ATTACK]; } else { voice->filter_env_delta[ENV_INTERVAL_ATTACK] = (state->filter_sustain - voice->filter_env_raw) / (sample_t) voice->filter_env_dur[ENV_INTERVAL_ATTACK]; } /* end of hold pedal code changes */ /* everything except mono multikey gets new init phases. */ if (state->keymode != KEYMODE_MONO_MULTIKEY) { /* init phase for keytrig oscs */ for (osc = 0; osc < NUM_OSCS; osc++) { /* init phase (unshifted by lfo) at note start */ if ((state->osc_freq_base[osc] == FREQ_BASE_TEMPO_KEYTRIG) || (state->osc_freq_base[osc] == FREQ_BASE_MIDI_KEY)) { voice->index[osc] = part->osc_init_index[osc]; } } } /* init phase for keytrig lfos if needed */ /* TODO: determine if including env retrigger is appropriate here */ //if ((staccato) || (env_trigger)) { if (staccato) { for (lfo = 0; lfo < NUM_LFOS; lfo++) { switch (state->lfo_freq_base[lfo]) { case FREQ_BASE_MIDI_KEY: case FREQ_BASE_TEMPO_KEYTRIG: part->lfo_index[lfo] = part->lfo_init_index[lfo]; /* intentional fallthrough */ case FREQ_BASE_TEMPO: part->lfo_adjust[lfo] = part->lfo_freq[lfo] * wave_period; break; } } } } /* Both high key and low key are set to the last key and adjusted later if necessary */ part->high_key = part->low_key = part->last_key; /* set highest and lowest keys in play for things that need keyfollow */ part->cur = part->head; while (part->cur != NULL) { if (part->cur->midi_key < part->low_key) { part->low_key = part->cur->midi_key; } if (part->cur->midi_key > part->high_key) { part->high_key = part->cur->midi_key; } part->cur = part->cur->next; } /* volume keyfollow is set by last key for poly */ if (state->keymode == KEYMODE_POLY) { voice->vol_key = part->last_key; } /* volume keyfollow is set by high key for mono */ else { voice->vol_key = part->high_key; } /* set filter keyfollow key based on keyfollow mode */ switch (state->filter_keyfollow) { case KEYFOLLOW_LAST: tmp = (sample_t)(part->last_key + state->transpose - 64); for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { loop_voice = get_voice(part_num, voice_num); loop_voice->filter_key_adj = tmp; } break; case KEYFOLLOW_HIGH: tmp = (sample_t)(part->high_key + state->transpose - 64); for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { loop_voice = get_voice(part_num, voice_num); loop_voice->filter_key_adj = tmp; } break; case KEYFOLLOW_LOW: tmp = (sample_t)(part->low_key + state->transpose - 64); for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { loop_voice = get_voice(part_num, voice_num); loop_voice->filter_key_adj = tmp; } break; case KEYFOLLOW_MIDI: voice->filter_key_adj = (sample_t)(part->last_key + state->transpose - 64); break; case KEYFOLLOW_NONE: voice->filter_key_adj = 0.0; break; } /* start at beginning of list of keys in play */ part->cur = part->head; /* Keytrigger volume only applicable to midi key based oscs portamento applicable to midi key based oscs _and_ lfos. */ /* handle per-osc portamento for the different keymodes */ for (osc = 0; osc < NUM_OSCS; osc++) { /* portamento for midi key based main osc */ if (state->osc_freq_base[osc] == FREQ_BASE_MIDI_KEY) { /* decide which key in play to assign to this oscillator */ switch (state->keymode) { case KEYMODE_MONO_MULTIKEY: /* use notes in order in oscillators */ if (part->cur != NULL) { voice->osc_key[osc] = part->cur->midi_key; if (part->cur->next != NULL) { part->cur = part->cur->next; } else { part->cur = part->head; } } else { voice->osc_key[osc] = part->last_key; } break; case KEYMODE_MONO_UNISON_4: case KEYMODE_MONO_UNISON_6: case KEYMODE_MONO_UNISON_8: case KEYMODE_MONO_SMOOTH: case KEYMODE_MONO_RETRIGGER: /* default mono -- use key just pressed */ voice->osc_key[osc] = part->last_key; break; case KEYMODE_POLY: /* use midi key assigned to voice */ voice->osc_key[osc] = voice->midi_key; break; } /* Set oscillator frequencies based on midi note, global transpose value, and optional portamento. state->osc_transpose[osc] is taken into account every sample in the engine. */ if ((state->portamento > 0)) { /* Portamento always starts from previous key hit, no matter which voice. Mono multikey always uses same voice. */ if (state->keymode != KEYMODE_MONO_MULTIKEY) { if (part->prev_key == -1) { voice->osc_freq[osc] = freq_table[state->patch_tune_cc] [256 + part->last_key + state->transpose + state->osc_transpose_cc[osc] - 64]; } else { voice->osc_freq[osc] = freq_table[state->patch_tune_cc] [256 + part->prev_key + state->transpose + state->osc_transpose_cc[osc] - 64]; } } /* Portamento slide calculation works the same for all keymodes. Start portamento now that frequency adjustment is known. */ if ((old_voice == NULL) || (old_voice == voice)) { voice->osc_portamento[osc] = 4.0 * (freq_table[state->patch_tune_cc] [256 + voice->osc_key[osc] + state->transpose + state->osc_transpose_cc[osc] - 64] - voice->osc_freq[osc]) / (sample_t)(voice->portamento_samples - 1); part->portamento_sample = part->portamento_samples; voice->portamento_sample = voice->portamento_samples; } else { voice->osc_portamento[osc] = 4.0 * (freq_table[state->patch_tune_cc] [256 + voice->osc_key[osc] + state->transpose + state->osc_transpose_cc[osc] - 64] - old_voice->osc_freq[osc]) / (sample_t)(voice->portamento_samples - 1); part->portamento_sample = part->portamento_samples; voice->portamento_sample = voice->portamento_samples; /* Mono modes set portamento on voice just finishing to match new voice. */ if ((state->keymode == KEYMODE_MONO_SMOOTH) || (state->keymode == KEYMODE_MONO_RETRIGGER) || (state->keymode == KEYMODE_MONO_UNISON_4) || (state->keymode == KEYMODE_MONO_UNISON_6) || (state->keymode == KEYMODE_MONO_UNISON_8)) { old_voice->osc_portamento[osc] = voice->osc_portamento[osc]; old_voice->portamento_sample = voice->portamento_sample; old_voice->portamento_samples = voice->portamento_samples; } } } /* If portamento is not needed, set the oscillator frequency directly. */ else { voice->osc_freq[osc] = freq_table[state->patch_tune_cc] [256 + voice->osc_key[osc] + state->transpose + state->osc_transpose_cc[osc] - 64]; voice->osc_portamento[osc] = 0.0; voice->portamento_sample = 0; voice->portamento_samples = 0; } } } /* portamento for midi key based lfo */ for (lfo = 0; lfo < NUM_LFOS; lfo++) { if (state->lfo_freq_base[lfo] == FREQ_BASE_MIDI_KEY) { /* decide which key in play to assign to this lfo */ switch (state->keymode) { case KEYMODE_MONO_MULTIKEY: /* use notes in order in lfos */ if (part->cur != NULL) { part->lfo_key[lfo] = part->cur->midi_key; if (part->cur->next != NULL) { part->cur = part->cur->next; } else { part->cur = part->head; } } else { part->lfo_key[lfo] = part->last_key; } break; case KEYMODE_MONO_SMOOTH: case KEYMODE_MONO_RETRIGGER: case KEYMODE_MONO_UNISON_4: case KEYMODE_MONO_UNISON_6: case KEYMODE_MONO_UNISON_8: /* default mono -- use key just pressed */ part->lfo_key[lfo] = part->last_key; break; case KEYMODE_POLY: /* use midi key assigned to allocated voice */ part->lfo_key[lfo] = voice->midi_key; break; } /* Set lfo portamento frequencies based on midi note and transpose value. */ if ((state->portamento > 0) && (part->portamento_samples > 0)) { part->lfo_portamento[lfo] = (freq_table[state->patch_tune_cc] [256 + part->lfo_key[lfo]] - part->lfo_freq[lfo]) / (sample_t) part->portamento_samples; } /* If portamento is not needed, set the lfo frequency directly. */ else { part->lfo_portamento[lfo] = 0.0; part->lfo_freq[lfo] = freq_table[state->patch_tune_cc][256 + part->lfo_key[lfo]]; } } } /* allocate voice (engine actually activates allocated voices) */ voice->allocated = 1; }
/***************************************************************************** * process_note_off() * * Process a single note-off event (either a note-off message, or a note-on * message with velocity set to zero). Select a voices to be turned on/off, * remove key from list, and call process_keytrigger() for any portamento, * envelope, osc and lfo init-phase, and filter-follow triggering actions for * note-off events that cause new notes to be triggered (mono modes). *****************************************************************************/ void process_note_off(MIDI_EVENT *event, unsigned int part_num) { VOICE *voice; VOICE *old_voice = NULL; VOICE *loop_voice; PART *part = get_part(part_num); PATCH_STATE *state = get_active_state(part_num); int keytrigger = 0; int free_voice = -1; int voice_num; int unlink; int voice_count = 0; switch (state->keymode) { case KEYMODE_POLY: /* find voice mapped to note being shut off */ vnum[part_num] = -1; for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { loop_voice = get_voice(part_num, voice_num); if (loop_voice->midi_key == event->note) { loop_voice->keypressed = -1; vnum[part_num] = voice_num; } } if (vnum[part_num] == -1) { vnum[part_num] = 0; keytrigger = 0; } break; case KEYMODE_MONO_SMOOTH: /* find voice mapped to note being shut off, if any */ keytrigger = 0; for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { loop_voice = get_voice(part_num, voice_num); if (loop_voice->keypressed == event->note) { loop_voice->keypressed = -1; if (event->note == part->midi_key) { old_voice = loop_voice; } } } break; case KEYMODE_MONO_RETRIGGER: /* find voice mapped to note being shut off, if any */ keytrigger = 0; for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { loop_voice = get_voice(part_num, ((voice_num + vnum[part_num] + 1) % setting_polyphony)); if (loop_voice->allocated == 0) { free_voice = loop_voice->id; } else if (loop_voice->midi_key == event->note) { loop_voice->keypressed = -1; vnum[part_num] = (voice_num + 1) % setting_polyphony; if (event->note == part->midi_key) { old_voice = loop_voice; } } } if ((old_voice != NULL) && (free_voice > -1)) { vnum[part_num] = free_voice; } break; case KEYMODE_MONO_UNISON_4: voice_count = 4; vnum[part_num] = 0; if (part->midi_key == event->note) { keytrigger = 1; old_voice = get_voice(part_num, vnum[part_num]); } break; case KEYMODE_MONO_UNISON_6: voice_count = 6; vnum[part_num] = 0; if (part->midi_key == event->note) { keytrigger = 1; old_voice = get_voice(part_num, vnum[part_num]); } break; case KEYMODE_MONO_UNISON_8: voice_count = 8; vnum[part_num] = 0; if (part->midi_key == event->note) { keytrigger = 1; old_voice = get_voice(part_num, vnum[part_num]); } break; case KEYMODE_MONO_MULTIKEY: /* mono multikey needs keytrigger activities on note off for resetting oscillator frequencies. */ vnum[part_num] = 0; if (part->midi_key == event->note) { keytrigger = 1; old_voice = get_voice(part_num, vnum[part_num]); } break; } part->prev_key = part->midi_key; part->midi_key = event->note; PHASEX_DEBUG(DEBUG_CLASS_MIDI_NOTE, "voice %2d: Event Note Off: part=%d " "voice=%2d note=%3d velocity=%3d keylist=:", (vnum[part_num] + 1), (part_num + 1), (vnum[part_num] + 1), event->note, event->velocity); /* remove this key from the list and then find the last key */ part->prev = NULL; part->cur = part->head; unlink = 0; while (part->cur != NULL) { /* if note is found, unlink it from the list */ if (part->cur->midi_key == event->note) { PHASEX_DEBUG(DEBUG_CLASS_MIDI_NOTE, "-%d-:", part->cur->midi_key); unlink = 1; if (part->prev != NULL) { part->prev->next = part->cur->next; part->cur->next = NULL; part->cur = part->prev->next; } else { part->head = part->cur->next; part->cur->next = NULL; part->cur = part->head; } } /* otherwise, on to the next key in the list */ else { PHASEX_DEBUG(DEBUG_CLASS_MIDI_NOTE, "%d:", part->cur->midi_key); part->prev = part->cur; part->cur = part->cur->next; } } PHASEX_DEBUG(DEBUG_CLASS_MIDI_NOTE, "\n"); PHASEX_DEBUG(DEBUG_CLASS_MIDI_TIMING, DEBUG_COLOR_BLUE "------- %d ------- " DEBUG_COLOR_DEFAULT, event->note); if (!unlink) { /* Received note-off w/o corresponding note-on. */ PHASEX_DEBUG(DEBUG_CLASS_MIDI_TIMING, DEBUG_COLOR_RED "----------- " DEBUG_COLOR_DEFAULT); } /* keep pointer to current voice around */ voice = get_voice(part_num, vnum[part_num]); /* ignore the note in the note off message if found in list */ if ((part->prev != NULL) && (part->prev->midi_key == event->note)) { part->cur = part->head; while (part->cur != NULL) { if (part->cur->midi_key != event->note) { part->prev = part->cur; } part->cur = part->cur->next; } } /* check for keys left on the list */ if (part->prev != NULL) { /* set last and current keys in play respective of notes still held */ part->last_key = part->prev->midi_key; part->midi_key = part->prev->midi_key; part->prev->next = NULL; /* Retrigger and smooth modes need voice allocation with keys still on list multikey always need osc remapping until all keys are done. */ switch (state->keymode) { case KEYMODE_MONO_RETRIGGER: if (old_voice == NULL) { keytrigger--; break; } old_voice->cur_amp_interval = ENV_INTERVAL_RELEASE; old_voice->cur_amp_sample = -1; /* intentional fall-through */ case KEYMODE_MONO_SMOOTH: voice->midi_key = part->midi_key; voice->keypressed = part->midi_key; /* use previous velocity for this generated note event */ voice->velocity = part->velocity; voice->velocity_target_linear = voice->velocity_coef_linear = part->velocity_target = part->velocity_coef = ((sample_t) part->velocity) * 0.01; voice->velocity_target_log = voice->velocity_coef_log = velocity_gain_table[state->amp_velocity_cc][part->velocity]; /* intentional fall-through */ case KEYMODE_MONO_UNISON_4: case KEYMODE_MONO_UNISON_6: case KEYMODE_MONO_UNISON_8: case KEYMODE_MONO_MULTIKEY: keytrigger++; break; } } /* re-init list if no keys */ else { voice->midi_key = -1; voice->keypressed = -1; for (voice_num = 0; voice_num < voice_count; voice_num++) { if ( vnum[part_num] == voice_num ) continue; loop_voice = get_voice(part_num, voice_num); loop_voice->midi_key = -1; loop_voice->keypressed = -1; } part->head = NULL; if (!part->hold_pedal) { part->midi_key = -1; } } if (keytrigger > 0) { process_keytrigger(event, old_voice, voice, part_num); for (voice_num = 0; voice_num < voice_count; voice_num++) { if ( vnum[part_num] == voice_num ) continue; loop_voice = get_voice(part_num, voice_num); process_keytrigger(event, old_voice, loop_voice, part_num); } } }
/***************************************************************************** * process_midi_event() * * Perform complete processing for an event (straight from the queue). * All supported event types must be handled by this function. *****************************************************************************/ MIDI_EVENT * process_midi_event(MIDI_EVENT *event, unsigned int part_num) { MIDI_EVENT *next; switch (event->type) { case MIDI_EVENT_NOTE_ON: process_note_on(event, part_num); break; case MIDI_EVENT_NOTE_OFF: process_note_off(event, part_num); break; case MIDI_EVENT_AFTERTOUCH: process_aftertouch(event, part_num); break; case MIDI_EVENT_STOP: case MIDI_EVENT_SYSTEM_RESET: process_all_notes_off(event, part_num); break; case MIDI_EVENT_PROGRAM_CHANGE: process_program_change(event, part_num); break; case MIDI_EVENT_POLYPRESSURE: process_polypressure(event, part_num); break; case MIDI_EVENT_CONTROLLER: process_controller(event, part_num); break; case MIDI_EVENT_PITCHBEND: process_pitchbend(event, part_num); break; #ifdef MIDI_CLOCK_SYNC case MIDI_EVENT_CLOCK: break; #endif /* MIDI_CLOCK_SYNC */ /* The following are internal message types */ case MIDI_EVENT_BPM_CHANGE: process_bpm_change(event, part_num); break; case MIDI_EVENT_PHASE_SYNC: process_phase_sync(event, part_num); break; default: PHASEX_DEBUG(DEBUG_CLASS_MIDI_EVENT, "+++ process_midi_event(): part %d: " "received unhandled MIDI message type 0x%02x\n", (part_num + 1), event->type); break; } /* keep track of pointers to bulk lists */ next = event->next; /* Clear event. */ event->type = 0; event->channel = 0; event->byte2 = 0; event->byte3 = 0; event->next = NULL; set_midi_event_state(event, EVENT_STATE_FREE); return next; }
/***************************************************************************** * process_bpm_change() * * Process a BPM change pseudo MIDI message. Currently used when syncing * JACK Transport, this adjust BPM, independent of controller assignment. * * TODO: Rework _all_ phasex BPM code, since this is almost identical to * update_bpm() in bpm.c. *****************************************************************************/ void process_bpm_change(MIDI_EVENT *event, unsigned int part_num) { PART *part = get_part(part_num); PATCH_STATE *state = get_active_state(part_num); PARAM *param = get_param(part_num, PARAM_BPM); DELAY *delay = get_delay(part_num); CHORUS *chorus = get_chorus(part_num); VOICE *voice; int int_val = (int)(event->float_value); int cc_val = int_val - 64; int voice_num; int lfo; int osc; PHASEX_DEBUG(DEBUG_CLASS_MIDI_TIMING, "+++ Processing BPM change. New BPM = %lf +++\n", event->float_value); param->value.cc_val = cc_val; param->value.int_val = int_val; /* For now, this is handled much like the normal param callback for BPM, execpt that here we use floating point values instead of integer. */ global.bpm = event->float_value; global.bps = event->float_value / 60.0; if (param->value.cc_val != cc_val) { param->value.cc_prev = param->value.cc_val; param->value.cc_val = cc_val; param->value.int_val = int_val; param->updated = 1; } /* initialize all variables based on bpm */ state->bpm = event->float_value; state->bpm_cc = (short)(param->value.cc_val & 0x7F); /* re-initialize delay size */ delay->size = state->delay_time * f_sample_rate / global.bps; delay->length = (int)(delay->size); /* re-initialize chorus lfos */ chorus->lfo_freq = global.bps * state->chorus_lfo_rate; chorus->lfo_adjust = chorus->lfo_freq * wave_period; chorus->phase_freq = global.bps * state->chorus_phase_rate; chorus->phase_adjust = chorus->phase_freq * wave_period; /* per-lfo setup */ for (lfo = 0; lfo < NUM_LFOS; lfo++) { /* re-calculate frequency and corresponding index adjustment */ if (state->lfo_freq_base[lfo] >= FREQ_BASE_TEMPO) { part->lfo_freq[lfo] = global.bps * state->lfo_rate[lfo]; part->lfo_adjust[lfo] = part->lfo_freq[lfo] * wave_period; } } /* per-oscillator setup */ for (osc = 0; osc < NUM_OSCS; osc++) { /* re-calculate tempo based osc freq */ if (state->osc_freq_base[osc] >= FREQ_BASE_TEMPO) { for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { voice = get_voice(part_num, voice_num); voice->osc_freq[osc] = global.bps * state->osc_rate[osc]; } } } }
int load_waveform_sample(int wavenum, int num_cycles, double octaves) { float *in_buf; float *out_buf; char filename[PATH_MAX]; struct stat statbuf; SRC_DATA src_data; FILE *rawfile; unsigned int sample; unsigned int input_len; float pos_max; float neg_max; float offset; float scalar; size_t sample_t_size = sizeof(sample_t); if ((in_buf = malloc(2 * WAVEFORM_SIZE * sizeof(float))) == NULL) { phasex_shutdown("Out of Memory!\n"); } if ((out_buf = malloc(WAVEFORM_SIZE * sizeof(float))) == NULL) { phasex_shutdown("Out of Memory!\n"); } /* get file size */ snprintf(filename, PATH_MAX, "%s/%s.raw", SAMPLE_DIR, wave_names[wavenum]); if (stat(filename, &statbuf) != 0) { PHASEX_ERROR("Unable to load raw sample file '%s'\n", filename); } input_len = (unsigned int)((unsigned int) statbuf.st_size / sizeof(float)); PHASEX_DEBUG(DEBUG_CLASS_INIT, "Loading raw waveform '%s': %d samples\n", filename, input_len); /* open raw sample file */ if ((rawfile = fopen(filename, "rb")) != 0) { /* read sample data */ if (fread(in_buf, sizeof(float), input_len, rawfile) == input_len) { fclose(rawfile); /* normalize sample to [-1,1] */ pos_max = neg_max = 0; for (sample = 0; sample < input_len; sample++) { if (in_buf[sample] > pos_max) { pos_max = in_buf[sample]; } if (in_buf[sample] < neg_max) { neg_max = in_buf[sample]; } } offset = (pos_max + neg_max) / -2.0; scalar = 1.0 / (float)((pos_max - neg_max) / 2.0); for (sample = 0; sample < input_len; sample++) { in_buf[sample] = (in_buf[sample] + offset) * scalar; } /* resample to fit WAVEFORM_SIZE */ src_data.input_frames = (long int) input_len; src_data.output_frames = WAVEFORM_SIZE; src_data.src_ratio = F_WAVEFORM_SIZE / (double) input_len; src_data.data_in = in_buf; src_data.data_out = (sample_t_size == 4) ? (float *) & (wave_table[wavenum][0]) : & (out_buf[0]); src_simple(&src_data, SRC_SINC_BEST_QUALITY, 1); if (sample_t_size != 4) { for (sample = 0; sample < input_len; sample++) { wave_table[wavenum][sample] = (sample_t)(out_buf[sample]); } } /* filter resampled data */ filter_wave_table_24dB(wavenum, num_cycles, octaves); /* all done */ return 0; } fclose(rawfile); } /* copy sine data on failure */ PHASEX_ERROR("Error reading '%s'!\n", filename); for (sample = 0; sample < WAVEFORM_SIZE; sample++) { wave_table[wavenum][sample] = wave_table[WAVE_SINE][sample]; } return -1; }