/***************************************************************************** * 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; } }
/*********** * returns JB_OK if a frame is available and *data points to the packet * returns JB_NOFRAME if it's no time to play voice and or no frame available * returns JB_INTERP if interpolating is required * returns JB_EMPTY if no voice frame is in the jitterbuffer (only during silence) * * if the next frame is a silence frame we will go in silence-mode * each new instance of the jitterbuffer will start in silence mode * in silence mode we will set the jitterbuffer to the size we want * when we are not in silence mode get_voicecase will handle the rest. */ static int get_voice(jitterbuffer *jb, void **data, long now, long interpl) { jb_frame *frame; long diff; int result; diff = jb->target - jb->current; //if the next frame is a silence frame, go in silence mode... if((get_next_frametype(jb, now - jb->current) == JB_TYPE_SILENCE) ) { jb_dbg("gs"); frame = get_frame(jb, now - jb->current); *data = frame->data; frame->data = NULL; jb->info.silence =1; jb->silence_begin_ts = frame->ts; frame_free(frame); result = JB_OK; } else { if(jb->info.silence) { // we are in silence /* * During silence we can set the jitterbuffer size to the size * we want... */ if (diff) { jb->current = jb->target; } frame = get_frame(jb, now - jb->current); if (frame) { if (jb->silence_begin_ts && frame->ts < jb->silence_begin_ts) { jb_dbg("gL"); /* voice frame is late, next!*/ jb->info.frames_late++; frame_free(frame); result = get_voice(jb, data, now, interpl); } else { jb_dbg("gP"); /* voice frame */ jb->info.silence = 0; jb->silence_begin_ts = 0; jb->next_voice_time = frame->ts + frame->ms; jb->info.last_voice_ms = frame->ms; *data = frame->data; frame->data = NULL; frame_free(frame); result = JB_OK; } } else { //no frame jb_dbg("gS"); result = JB_EMPTY; } } else { //voice case result = get_voicecase(jb,data,now,interpl,diff); } } return result; }
void SOUNDCLIP::resume() { if (state_ != SoundClipPaused) { return; } int voice = get_voice(); if (voice >= 0) { voice_start(voice); state_ = SoundClipPlaying; } }
void SOUNDCLIP::pause() { if (state_ != SoundClipPlaying) { return; } int voice = get_voice(); if (voice >= 0) { voice_stop(voice); state_ = SoundClipPaused; } }
void SOUNDCLIP::set_panning(int newPanning) { if (!is_playing()) { return; } int voice = get_voice(); if (voice >= 0) { voice_set_pan(voice, newPanning); panning = newPanning; } }
/*********** * control frames have a higher priority then voice frames * returns JB_OK if a frame is available and *data points to the packet * returns JB_NOFRAME if it's no time to play voice and no control available * returns JB_INTERP if interpolating is required * returns JB_EMPTY if no voice frame is in the jitterbuffer (only during silence) */ int jb_get(jitterbuffer *jb, void **data, long now, long interpl) { int result; jb_dbg("A"); if (jb == NULL) { jb_err("no jitterbuffer in jb_get()\n"); return JB_NOJB; } result = get_control(jb, data); if (result != JB_OK ) { //no control message available maybe there is voice... result = get_voice(jb, data, now, interpl); } return result; }
/***************************************************************************** * 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]; } } }
/*********** * The voicecase has four 'options' * - difference is way off, reset * - diff > 0, we may need to grow * - diff < 0, we may need to shrink * - everything else */ static int get_voicecase(jitterbuffer *jb, void **data, long now, long interpl, long diff) { jb_frame *frame; int result; // * - difference is way off, reset if (diff > jb->settings.max_diff || -diff > jb->settings.max_diff) { jb_err("wakko diff in get_voicecase\n"); reset(jb); //reset hist because the timestamps are wakko. result = JB_NOFRAME; //- diff > 0, we may need to grow } else if ((diff > 0) && (now > (jb->last_adjustment + jb->settings.wait_grow) || (now + jb->current + interpl) < get_next_framets(jb) ) ) { //grow /* first try to grow */ if (diff<interpl/2) { jb_dbg("ag"); jb->current +=diff; } else { jb_dbg("aG"); /* grow by interp frame len */ jb->current += interpl; } jb->last_adjustment = now; result = get_voice(jb, data, now, interpl); //- diff < 0, we may need to shrink } else if ( (diff < 0) && (now > (jb->last_adjustment + jb->settings.wait_shrink)) && ((-diff) > jb->settings.extra_delay) ) { /* now try to shrink * if there is a frame shrink by frame length * otherwise shrink by interpl */ jb->last_adjustment = now; frame = get_frame(jb, now - jb->current); if(frame) { jb_dbg("as"); /* shrink by frame size we're throwing out */ jb->info.frames_dropped++; jb->current -= frame->ms; frame_free(frame); } else { jb_dbg("aS"); /* shrink by interpl */ jb->current -= interpl; } result = get_voice(jb, data, now, interpl); } else { /* if it is not the time to play a result = JB_NOFRAME * else We try to play a frame if a frame is available * and not late it is played otherwise * if available it is dropped and the next is tried * last option is interpolating */ if (now - jb->current < jb->next_voice_time) { jb_dbg("aN"); result = JB_NOFRAME; } else { frame = get_frame(jb, now - jb->current); if (frame) { //there is a frame /* voice frame is late */ if(frame->ts < jb->next_voice_time) { //late jb_dbg("aL"); jb->info.frames_late++; frame_free(frame); result = get_voice(jb, data, now, interpl); } else { jb_dbg("aP"); /* normal case; return the frame, increment stuff */ *data = frame->data; frame->data = NULL; jb->next_voice_time = frame->ts + frame->ms; jb->cnt_successive_interp = 0; frame_free(frame); result = JB_OK; } } else { // no frame, thus interpolate jb->cnt_successive_interp++; /* assume silence instead of continuing to interpolate */ if (jb->settings.max_successive_interp && jb->cnt_successive_interp >= jb->settings.max_successive_interp) { jb->info.silence = 1; jb->silence_begin_ts = jb->next_voice_time; } jb_dbg("aI"); jb->next_voice_time += interpl; result = JB_INTERP; } } } return result; }
static void stop_note(struct SoundPlugin *plugin, int64_t time, float note_num, int64_t note_id){ Data *data = (Data*)plugin->data; Voice *voice = get_voice(data); if(voice->delta_pos_at_end == -1) voice->delta_pos_at_end = time; }
static void set_note_volume(struct SoundPlugin *plugin, int64_t time, float note_num, int64_t note_id, float volume){ Data *data = (Data*)plugin->data; Voice *voice = get_voice(data); voice->velocity = volume; }
/***************************************************************************** * 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_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]; } } } }
/***************************************************************************** * process_phase_sync() * * Process a phase sync internal MIDI message. Currently used for syncing * JACK Transport, this function performs phase correction for a given voice. *****************************************************************************/ void process_phase_sync(MIDI_EVENT *event, unsigned int part_num) { PART *part = get_part(part_num); PATCH_STATE *state = get_active_state(part_num); DELAY *delay = get_delay(part_num); CHORUS *chorus = get_chorus(part_num); VOICE *voice; int voice_num; int osc; int lfo; int phase_correction = event->value; sample_t f_phase_correction = (sample_t) phase_correction; sample_t tmp_1; delay->write_index += phase_correction; while (delay->write_index < 0.0) { delay->write_index += delay->bufsize; } while (delay->write_index >= delay->bufsize) { delay->write_index -= delay->bufsize; } chorus->lfo_index_a += f_phase_correction * chorus->lfo_adjust; while (chorus->lfo_index_a < 0.0) { chorus->lfo_index_a += F_WAVEFORM_SIZE; } while (chorus->lfo_index_a >= F_WAVEFORM_SIZE) { chorus->lfo_index_a -= F_WAVEFORM_SIZE; } chorus->lfo_index_b = chorus->lfo_index_a + (F_WAVEFORM_SIZE * 0.25); while (chorus->lfo_index_b < 0.0) { chorus->lfo_index_b += F_WAVEFORM_SIZE; } while (chorus->lfo_index_b >= F_WAVEFORM_SIZE) { chorus->lfo_index_b -= F_WAVEFORM_SIZE; } chorus->lfo_index_c = chorus->lfo_index_a + (F_WAVEFORM_SIZE * 0.5); while (chorus->lfo_index_c < 0.0) { chorus->lfo_index_c += F_WAVEFORM_SIZE; } while (chorus->lfo_index_c >= F_WAVEFORM_SIZE) { chorus->lfo_index_c -= F_WAVEFORM_SIZE; } chorus->lfo_index_d = chorus->lfo_index_a + (F_WAVEFORM_SIZE * 0.75); while (chorus->lfo_index_d < 0.0) { chorus->lfo_index_d += F_WAVEFORM_SIZE; } while (chorus->lfo_index_d >= F_WAVEFORM_SIZE) { chorus->lfo_index_d -= F_WAVEFORM_SIZE; } for (voice_num = 0; voice_num < setting_polyphony; voice_num++) { voice = get_voice(part_num, voice_num); for (osc = 0; osc < NUM_OSCS; osc++) { if (state->osc_freq_base[osc] >= FREQ_BASE_TEMPO) { switch (state->freq_mod_type[osc]) { case MOD_TYPE_LFO: tmp_1 = part->lfo_out[state->freq_lfo[osc]]; break; case MOD_TYPE_OSC: tmp_1 = (voice->osc_out1[part->osc_freq_mod[osc]] + voice->osc_out2[part->osc_freq_mod[osc]]) * 0.5; break; case MOD_TYPE_VELOCITY: tmp_1 = voice->velocity_coef_linear; break; default: tmp_1 = 0.0; break; } voice->index[osc] += f_phase_correction * halfsteps_to_freq_mult((tmp_1 * state->freq_lfo_amount[osc]) + part->osc_pitch_bend[osc] + state->osc_transpose[osc] + state->voice_osc_tune[voice->id] ) * voice->osc_freq[osc] * wave_period; while (voice->index[osc] < 0.0) { voice->index[osc] += F_WAVEFORM_SIZE; } while (voice->index[osc] >= F_WAVEFORM_SIZE) { voice->index[osc] -= F_WAVEFORM_SIZE; } } } } for (lfo = 0; lfo < NUM_LFOS; lfo++) { if (state->lfo_freq_base[lfo] >= FREQ_BASE_TEMPO) { part->lfo_index[lfo] += f_phase_correction * part->lfo_freq[lfo] * halfsteps_to_freq_mult(state->lfo_transpose[lfo] + part->lfo_pitch_bend[lfo]) * wave_period; while (part->lfo_index[lfo] < 0.0) { part->lfo_index[lfo] += F_WAVEFORM_SIZE; } while (part->lfo_index[lfo] >= F_WAVEFORM_SIZE) { part->lfo_index[lfo] -= F_WAVEFORM_SIZE; } } } }