reachability_treet::dfs_position::dfs_position(const reachability_treet &rt) { std::list<std::shared_ptr<execution_statet>>::const_iterator it; // Iterate through each position in the DFS tree recording data into this // object. for (it = rt.execution_states.begin(); it != rt.execution_states.end();it++){ reachability_treet::dfs_position::dfs_state state; auto ex = *it; state.location_number = ex->get_active_state().source.pc->location_number; state.num_threads = ex->threads_state.size(); state.explored = ex->DFS_traversed; // The thread taken in this DFS path isn't decided at this execution state, // instead it's whatever thread is active in the /next/ state. So, take the // currently active thread no and assign it to the previous dfs state // we recorded. if (states.size() > 0) states.back().cur_thread = ex->get_active_state_number(); states.push_back(state); } // The final execution state in a DFS is a dummy, there are no paths from it, // so assign a dummy cur_thread value. states.back().cur_thread = 0; checksum = 0; // Use this in the future. ileaves = 0; // Can use this depending on a future refactor. }
/** Determine the current active state. * Note that there might be multiple active states if there are sub-fsms. It will * only consider the active state of the bottom-fsm. * @return the current active state */ std::string SkillGuiGraphDrawingArea::get_active_state(graph_t *graph) { if (! graph) { return ""; } // Loop through the nodes in the graph/subgraph and find the active node for (node_t *n = agfstnode(graph); n; n = agnxtnode(graph, n)) { const char *actattr = agget(n, (char *)"active"); if (actattr && (strcmp(actattr, "true") == 0) ) { node_t *mn = agmetanode(graph); graph_t *mg = mn->graph; // Check to see if the node has an edge going into a subgraph for (edge_t *me = agfstout(mg, mn); me; me = agnxtout(mg, me)) { graph_t *subgraph = agusergraph(me->head); for (edge_t *e = agfstout(graph, n); e; e = agnxtout(graph, e)) { for (node_t *subnode = agfstnode(subgraph); subnode; subnode = agnxtnode(subgraph, subnode)) { if (agnameof(subnode) == agnameof(e->head)) { // The node goes into a subgraph, recursively find and return the active subnode name return get_active_state(subgraph); } } } } // The node has no subgraph, return the name of the active node return agnameof(n); } } return ""; }
/***************************************************************************** * 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]; } } }
/** Update __translation_x and __translation_y * for state following animation. */ void SkillGuiGraphDrawingArea::update_translations() { timeval now; gettimeofday(&now, NULL); double now_time = (float)now.tv_sec + now.tv_usec/1000000.; if (__follow_active_state && __scale_override && !__mouse_motion) { double px, py; std::string active_state = get_active_state(__graph); if (! get_state_position(active_state, px, py)) { px = py = 0.5; } Gtk::Allocation alloc = get_allocation(); __translation_x_setpoint = alloc.get_width()/2 - px*__bbw*__scale; __translation_y_setpoint = alloc.get_height()/2 - py*__bbh*__scale + __bbh * __scale; float d, dx, dy, dt; dx = __translation_x_setpoint - __translation_x; dy = __translation_y_setpoint - __translation_y; d = sqrt(pow(dx,2) + pow(dy,2)); dt = std::min(1.0, std::max(0.1, now_time - __last_update_time)); double v = 0.0; //printf("d=%f speed=%f\n", d, __speed); if (d < 0.05) { // At goal, don't move v = 0; //printf("Reached, v=%f, d=%f, dx=%f, dy=%f, txs=%f tys=%f, tx=%f ty=%f\n", // v, d, dx, dy, __translation_x_setpoint, __translation_y_setpoint, // __translation_x, __translation_y); __translation_x = __translation_x_setpoint; __translation_y = __translation_y_setpoint; } else if (d < __speed_ramp_distance) { // Approaching goal, slow down v = __speed - __speed_max * dt; //printf("Approaching, v=%f\n", v); } else if (__speed == 0.0) { // just starting v = __speed_max / 10.; dt = 0.1; //printf("Starting, v=%f\n", v); } else if (__speed < __speed_max) { // Starting toward goal, speed up v = __speed + __speed_max * dt; //printf("Speeding up, v=%f\n", v); } else { // Moving towards goal, keep going v = __speed; //printf("Moving, v=%f\n", v); } // Limit v to __speed_max v = std::min(v, __speed_max); // Set new translations //printf("d=%f v=%f dx=%f dy=%f dt=%f dto=%f\n", d, v, dx, dy, dt, // now_time - __last_update_time); if ( d <= v * dt ) { __translation_x = __translation_x_setpoint; __translation_y = __translation_y_setpoint; __speed = 0; } else { __translation_x = (dx / d) * v * dt + __translation_x; __translation_y = (dy / d) * v * dt + __translation_y; __speed = v; } if (false) { __translation_x = __translation_x_setpoint; __translation_y = __translation_y_setpoint; __speed = 0; queue_draw(); } __last_update_time = now_time; if (__speed > 0) { queue_draw(); } } }
/***************************************************************************** * 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; } } } }