bool Cgiter_get_local_bp_dist(const Cgiter* cgiter, Tstamp* dist) { rassert(cgiter != NULL); rassert(dist != NULL); rassert(Tstamp_cmp(dist, TSTAMP_AUTO) >= 0); if (Cgiter_has_finished(cgiter)) return false; const Pattern* pattern = find_pattern(cgiter); if (pattern == NULL) return false; // Check pattern end const Tstamp* pat_length = Pattern_get_length(pattern); const Tstamp* dist_to_end = Tstamp_sub(TSTAMP_AUTO, pat_length, &cgiter->pos.pat_pos); if (Tstamp_cmp(dist_to_end, TSTAMP_AUTO) <= 0) { // We cannot move forwards in playback time Tstamp_set(dist, 0, 0); return true; } // Check next trigger row Column* column = Pattern_get_column(pattern, cgiter->col_index); rassert(column != NULL); Column_iter* citer = Column_iter_init(COLUMN_ITER_AUTO); Column_iter_change_col(citer, column); const Tstamp* epsilon = Tstamp_set(TSTAMP_AUTO, 0, 1); Tstamp* next_pos_min = Tstamp_add(TSTAMP_AUTO, &cgiter->pos.pat_pos, epsilon); const Trigger_list* row = Column_iter_get_row(citer, next_pos_min); if (row != NULL) { rassert(row->next != NULL); rassert(row->next->trigger != NULL); if (Tstamp_cmp(&row->next->trigger->pos, pat_length) <= 0) { // Trigger row found inside this pattern const Tstamp* dist_to_row = Tstamp_sub( TSTAMP_AUTO, &row->next->trigger->pos, &cgiter->pos.pat_pos); Tstamp_mina(dist, dist_to_row); return true; } } // No trigger row found Tstamp_mina(dist, dist_to_end); return true; }
bool Event_channel_set_arpeggio_speed_process( Channel* ch, Device_states* dstates, const Value* value) { assert(ch != NULL); assert(dstates != NULL); (void)dstates; assert(value != NULL); assert(value->type == VALUE_TYPE_FLOAT); ch->arpeggio_speed = value->value.float_type; const double unit_len = Tstamp_toframes( Tstamp_set(TSTAMP_AUTO, 1, 0), *ch->tempo, *ch->freq); for (int i = 0; i < KQT_GENERATORS_MAX; ++i) { Event_check_voice(ch, i); Voice_state* vs = ch->fg[i]->state; vs->arpeggio_length = unit_len / value->value.float_type; } return true; }
bool Streader_read_tstamp(Streader* sr, Tstamp* dest) { rassert(sr != NULL); if (Streader_is_error_set(sr)) return false; int64_t beats = 0; int64_t rem = 0; if (!(Streader_match_char(sr, '[') && Streader_read_int(sr, &beats) && Streader_match_char(sr, ',') && Streader_read_int(sr, &rem) && Streader_match_char(sr, ']'))) { Streader_set_error(sr, "Expected a valid timestamp"); return false; } if (rem < 0 || rem >= KQT_TSTAMP_BEAT) { Streader_set_error( sr, "Timestamp remainder %" PRId64 " out of range [0..%ld)", rem, KQT_TSTAMP_BEAT); return false; } if (dest != NULL) Tstamp_set(dest, beats, (int32_t)rem); return true; }
Jump_context* new_Jump_context(void) { Jump_context* jc = memory_alloc_item(Jump_context); if (jc == NULL) return NULL; jc->piref.pat = -1; jc->piref.inst = -1; Tstamp_set(&jc->row, 0, 0); jc->ch_num = 0; jc->order = 0; jc->counter = 0; jc->target_piref.pat = -1; jc->target_piref.inst = -1; Tstamp_set(&jc->target_row, 0, 0); return jc; }
bool Event_control_play_pattern_process( General_state* global_state, Channel* channel, const Value* value) { rassert(global_state != NULL); rassert(channel != NULL); rassert(value != NULL); rassert(value->type == VALUE_TYPE_PAT_INST_REF); Master_params* master_params = (Master_params*)global_state; master_params->playback_state = PLAYBACK_PATTERN; master_params->pattern_playback_flag = true; master_params->cur_pos.track = -1; master_params->cur_pos.system = -1; Tstamp_set(&master_params->cur_pos.pat_pos, 0, 0); master_params->cur_pos.piref = value->value.Pat_inst_ref_type; // Reset parameters to make sure we start at the new location immediately master_params->parent.pause = false; Tstamp_set(&master_params->delay_left, 0, 0); return true; }
END_TEST START_TEST(Read_valid_tstamp) { static const struct { const char* data; const Tstamp expected; } tstamps[] = { { "[0, 0]", { 0, 0 } }, { "[2,5]", { 2, 5 } }, { " [2,5]", { 2, 5 } }, { "[ 2,5]", { 2, 5 } }, { "[2 ,5]", { 2, 5 } }, { "[2, 5]", { 2, 5 } }, { "[2,5 ]", { 2, 5 } }, { "[-1, 3]", { -1, 3 } }, { "[0, 882161279]", { 0, KQT_TSTAMP_BEAT - 1 } }, }; for (size_t i = 0; i < arr_size(tstamps); ++i) { char data[128] = ""; sprintf(data, "%s x", tstamps[i].data); Streader* sr = init_with_cstr(data); Tstamp* result = Tstamp_set(TSTAMP_AUTO, 99, 99); fail_if(!Streader_read_tstamp(sr, result), "Could not read timestamp " PRIts " from `%s`: %s", PRIVALts(tstamps[i].expected), data, Streader_get_error_desc(sr)); fail_if(Tstamp_cmp(result, &tstamps[i].expected) != 0, "Streader stored " PRIts " instead of " PRIts " when reading `%s`", PRIVALts(*result), PRIVALts(tstamps[i].expected), data); fail_if(!Streader_match_char(sr, 'x'), "Streader did not consume timestamp from `%s` correctly", data); } }
void Cgiter_move(Cgiter* cgiter, const Tstamp* dist) { rassert(cgiter != NULL); rassert(dist != NULL); rassert(Tstamp_cmp(dist, TSTAMP_AUTO) >= 0); if (cgiter->pos.piref.pat < 0) return; // Find current pattern const Pattern* pattern = Module_get_pattern(cgiter->module, &cgiter->pos.piref); if (pattern == NULL) { cgiter->has_finished = true; return; } // Check if we are at the end of a pattern const Tstamp* pat_length = Pattern_get_length(pattern); if (Tstamp_cmp(&cgiter->pos.pat_pos, pat_length) >= 0) { // dist must be 0 or the pattern length changed if (cgiter->is_pattern_playback_state) { Tstamp_set(&cgiter->pos.pat_pos, 0, 0); // Play zero-length pattern only once to avoid infinite loop if (Tstamp_cmp(pat_length, TSTAMP_AUTO) == 0) cgiter->has_finished = true; } else { Cgiter_go_to_next_system(cgiter); } cgiter->row_returned = false; return; } // Move forwards Tstamp_adda(&cgiter->pos.pat_pos, dist); if (Tstamp_cmp(dist, TSTAMP_AUTO) > 0) cgiter->row_returned = false; return; }
static void Cgiter_go_to_next_system(Cgiter* cgiter) { rassert(cgiter != NULL); rassert(!cgiter->is_pattern_playback_state); Tstamp_set(&cgiter->pos.pat_pos, 0, 0); ++cgiter->pos.system; cgiter->pos.piref.pat = -1; const Pat_inst_ref* piref = find_pat_inst_ref(cgiter->module, cgiter->pos.track, cgiter->pos.system); if (piref != NULL) cgiter->pos.piref = *piref; else cgiter->has_finished = true; return; }
bool Event_channel_arpeggio_on_process( Channel* ch, Device_states* dstates, const Value* value) { assert(ch != NULL); assert(dstates != NULL); (void)dstates; (void)value; for (int i = 0; i < KQT_GENERATORS_MAX; ++i) { Event_check_voice(ch, i); Voice* voice = ch->fg[i]; Voice_state* vs = voice->state; //pitch_t orig_pitch = -1; if (vs->arpeggio || voice->gen->ins_params->pitch_locks[i].enabled) continue; #if 0 if (voice->gen->ins_params->scale != NULL && *voice->gen->ins_params->scale != NULL && **voice->gen->ins_params->scale != NULL) { orig_pitch = Scale_get_pitch_from_cents( **voice->gen->ins_params->scale, vs->orig_cents); } else { orig_pitch = exp2(vs->orig_cents / 1200) * 440; } if (orig_pitch <= 0) { vs->arpeggio = false; continue; } #endif if (isnan(ch->arpeggio_ref)) ch->arpeggio_ref = vs->orig_cents; if (isnan(ch->arpeggio_tones[0])) ch->arpeggio_tones[0] = ch->arpeggio_ref; vs->arpeggio_ref = ch->arpeggio_ref; memcpy(vs->arpeggio_tones, ch->arpeggio_tones, KQT_ARPEGGIO_NOTES_MAX * sizeof(double)); #if 0 int last_nonzero = -1; for (int k = 0; k < KQT_ARPEGGIO_NOTES_MAX; ++k) { if (data[k + 1].field.double_type != 0) { last_nonzero = k; } pitch_t new_pitch = -1; if (voice->gen->ins_params->scale != NULL && *voice->gen->ins_params->scale != NULL && **voice->gen->ins_params->scale != NULL) { Scale* scale = **voice->gen->ins_params->scale; new_pitch = Scale_get_pitch_from_cents(scale, vs->orig_cents + data[k + 1].field.double_type); } else { new_pitch = vs->orig_cents + data[k + 1].field.double_type; } if (new_pitch <= 0) { last_nonzero = -1; break; } else { vs->arpeggio_factors[k] = new_pitch / orig_pitch; } } if (last_nonzero == -1) { vs->arpeggio = false; continue; } else if (last_nonzero < KQT_ARPEGGIO_NOTES_MAX - 1) { vs->arpeggio_factors[last_nonzero + 1] = -1; } #endif const double unit_len = Tstamp_toframes( Tstamp_set(TSTAMP_AUTO, 1, 0), *ch->tempo, *ch->freq); vs->arpeggio_length = unit_len / ch->arpeggio_speed; vs->arpeggio_frames = 0; vs->arpeggio_note = 0; vs->arpeggio = true; } return true; }