Fix: try to fix the non working sync
This commit is contained in:
20
Cargo.lock
generated
20
Cargo.lock
generated
@@ -873,7 +873,7 @@ dependencies = [
|
||||
"cpal 0.17.1",
|
||||
"crossbeam-channel",
|
||||
"crossterm",
|
||||
"doux 0.0.14",
|
||||
"doux",
|
||||
"eframe",
|
||||
"egui",
|
||||
"egui_ratatui",
|
||||
@@ -925,7 +925,7 @@ dependencies = [
|
||||
"cagire-ratatui",
|
||||
"crossbeam-channel",
|
||||
"crossterm",
|
||||
"doux 0.0.13",
|
||||
"doux",
|
||||
"egui_ratatui",
|
||||
"nih_plug",
|
||||
"nih_plug_egui",
|
||||
@@ -1822,22 +1822,6 @@ dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doux"
|
||||
version = "0.0.13"
|
||||
source = "git+https://github.com/sova-org/doux?tag=v0.0.13#b8150d907e4cc2764e82fdaa424df41ceef9b0d2"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"clap",
|
||||
"cpal 0.17.1",
|
||||
"crossbeam-channel",
|
||||
"ringbuf",
|
||||
"rosc",
|
||||
"rustyline",
|
||||
"soundfont",
|
||||
"symphonia",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doux"
|
||||
version = "0.0.14"
|
||||
|
||||
@@ -14,7 +14,7 @@ cagire = { path = "../..", default-features = false, features = ["block-renderer
|
||||
cagire-forth = { path = "../../crates/forth" }
|
||||
cagire-project = { path = "../../crates/project" }
|
||||
cagire-ratatui = { path = "../../crates/ratatui" }
|
||||
doux = { git = "https://github.com/sova-org/doux", tag = "v0.0.13", features = ["native", "soundfont"] }
|
||||
doux = { git = "https://github.com/sova-org/doux", tag = "v0.0.14", features = ["native", "soundfont"] }
|
||||
nih_plug = { git = "https://github.com/robbert-vdh/nih-plug", features = ["standalone"] }
|
||||
nih_plug_egui = { git = "https://github.com/robbert-vdh/nih-plug" }
|
||||
egui_ratatui = "2.1"
|
||||
|
||||
@@ -185,6 +185,7 @@ impl Plugin for CagirePlugin {
|
||||
self.sample_rate,
|
||||
self.output_channels,
|
||||
64,
|
||||
buffer_config.max_buffer_size as usize,
|
||||
);
|
||||
self.bridge
|
||||
.sample_registry
|
||||
|
||||
@@ -51,7 +51,7 @@ while [[ $# -gt 0 ]]; do
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --platforms <list> Comma-separated: macos-arm64,macos-x86_64,linux-x86_64,linux-aarch64,windows-x86_64"
|
||||
echo " --targets <list> Comma-separated: cli,desktop,plugins"
|
||||
echo " --targets <list> Comma-separated: cli,desktop,plugins,installer"
|
||||
echo " --all Build all platforms and targets"
|
||||
echo " --yes Skip confirmation prompt"
|
||||
echo ""
|
||||
@@ -105,22 +105,30 @@ prompt_platforms() {
|
||||
}
|
||||
|
||||
prompt_targets() {
|
||||
local show_installer=false
|
||||
for p in "${selected_platforms[@]}"; do
|
||||
[[ "$p" == *windows* ]] && show_installer=true
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Select targets (0=all, comma-separated):"
|
||||
echo " 0) All"
|
||||
echo " 1) cagire"
|
||||
echo " 2) cagire-desktop"
|
||||
echo " 3) cagire-plugins (CLAP/VST3)"
|
||||
$show_installer && echo " 4) installer (NSIS, implies cli+desktop+plugins)"
|
||||
read -rp "> " choice
|
||||
|
||||
build_cagire=false
|
||||
build_desktop=false
|
||||
build_plugins=false
|
||||
build_installer=false
|
||||
|
||||
if [[ "$choice" == "0" || -z "$choice" ]]; then
|
||||
build_cagire=true
|
||||
build_desktop=true
|
||||
build_plugins=true
|
||||
$show_installer && build_installer=true
|
||||
else
|
||||
IFS=',' read -ra targets <<< "$choice"
|
||||
for t in "${targets[@]}"; do
|
||||
@@ -129,10 +137,24 @@ prompt_targets() {
|
||||
1) build_cagire=true ;;
|
||||
2) build_desktop=true ;;
|
||||
3) build_plugins=true ;;
|
||||
4)
|
||||
if $show_installer; then
|
||||
build_installer=true
|
||||
else
|
||||
echo "Invalid target: $t"; exit 1
|
||||
fi
|
||||
;;
|
||||
*) echo "Invalid target: $t"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
# Installer requires cli+desktop+plugins
|
||||
if $build_installer; then
|
||||
build_cagire=true
|
||||
build_desktop=true
|
||||
build_plugins=true
|
||||
fi
|
||||
}
|
||||
|
||||
confirm_summary() {
|
||||
@@ -148,6 +170,7 @@ confirm_summary() {
|
||||
$build_cagire && echo " - cagire"
|
||||
$build_desktop && echo " - cagire-desktop"
|
||||
$build_plugins && echo " - cagire-plugins (CLAP/VST3)"
|
||||
$build_installer && echo " - installer (NSIS)"
|
||||
echo ""
|
||||
read -rp "Proceed? [Y/n] " yn
|
||||
case "${yn,,}" in
|
||||
@@ -328,7 +351,7 @@ copy_artifacts() {
|
||||
fi
|
||||
|
||||
# NSIS installer for Windows targets
|
||||
if [[ "$os" == "windows" ]] && command -v makensis &>/dev/null; then
|
||||
if $build_installer && [[ "$os" == "windows" ]] && command -v makensis &>/dev/null; then
|
||||
echo " Building NSIS installer..."
|
||||
local version
|
||||
version=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
|
||||
@@ -384,6 +407,7 @@ if $cli_all; then
|
||||
build_cagire=true
|
||||
build_desktop=true
|
||||
build_plugins=true
|
||||
build_installer=true
|
||||
elif [[ -n "$cli_platforms" || -n "$cli_targets" ]]; then
|
||||
# Resolve platforms from CLI
|
||||
if [[ -n "$cli_platforms" ]]; then
|
||||
@@ -405,6 +429,7 @@ elif [[ -n "$cli_platforms" || -n "$cli_targets" ]]; then
|
||||
build_cagire=false
|
||||
build_desktop=false
|
||||
build_plugins=false
|
||||
build_installer=false
|
||||
if [[ -n "$cli_targets" ]]; then
|
||||
IFS=',' read -ra tgts <<< "$cli_targets"
|
||||
for t in "${tgts[@]}"; do
|
||||
@@ -413,13 +438,22 @@ elif [[ -n "$cli_platforms" || -n "$cli_targets" ]]; then
|
||||
cli) build_cagire=true ;;
|
||||
desktop) build_desktop=true ;;
|
||||
plugins) build_plugins=true ;;
|
||||
*) echo "Unknown target: $t (expected: cli, desktop, plugins)"; exit 1 ;;
|
||||
installer) build_installer=true ;;
|
||||
*) echo "Unknown target: $t (expected: cli, desktop, plugins, installer)"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
build_cagire=true
|
||||
build_desktop=true
|
||||
build_plugins=true
|
||||
build_installer=true
|
||||
fi
|
||||
|
||||
# Installer requires cli+desktop+plugins
|
||||
if $build_installer; then
|
||||
build_cagire=true
|
||||
build_desktop=true
|
||||
build_plugins=true
|
||||
fi
|
||||
else
|
||||
prompt_platforms
|
||||
|
||||
@@ -299,6 +299,7 @@ struct ActivePattern {
|
||||
step_index: usize,
|
||||
iter: usize,
|
||||
last_step_beat: f64,
|
||||
activation_beat: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -490,6 +491,30 @@ fn check_quantization_boundary(
|
||||
}
|
||||
}
|
||||
|
||||
fn quantization_boundary_beat(
|
||||
quantization: LaunchQuantization,
|
||||
prev_beat: f64,
|
||||
quantum: f64,
|
||||
) -> Option<f64> {
|
||||
match quantization {
|
||||
LaunchQuantization::Immediate => None,
|
||||
LaunchQuantization::Beat => Some(prev_beat.floor() + 1.0),
|
||||
LaunchQuantization::Bar => Some(((prev_beat / quantum).floor() + 1.0) * quantum),
|
||||
LaunchQuantization::Bars2 => {
|
||||
let q = quantum * 2.0;
|
||||
Some(((prev_beat / q).floor() + 1.0) * q)
|
||||
}
|
||||
LaunchQuantization::Bars4 => {
|
||||
let q = quantum * 4.0;
|
||||
Some(((prev_beat / q).floor() + 1.0) * q)
|
||||
}
|
||||
LaunchQuantization::Bars8 => {
|
||||
let q = quantum * 8.0;
|
||||
Some(((prev_beat / q).floor() + 1.0) * q)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type StepKey = (usize, usize, usize);
|
||||
|
||||
struct RunsCounter {
|
||||
@@ -892,6 +917,8 @@ impl SequencerState {
|
||||
}
|
||||
}
|
||||
};
|
||||
let boundary =
|
||||
quantization_boundary_beat(pending.quantization, prev_beat, quantum);
|
||||
self.runs_counter
|
||||
.clear_pattern(pending.id.bank, pending.id.pattern);
|
||||
self.audio_state.active_patterns.insert(
|
||||
@@ -902,6 +929,7 @@ impl SequencerState {
|
||||
step_index: start_step,
|
||||
iter: 0,
|
||||
last_step_beat: beat,
|
||||
activation_beat: boundary,
|
||||
},
|
||||
);
|
||||
self.buf_activated.push(pending.id);
|
||||
@@ -982,8 +1010,13 @@ impl SequencerState {
|
||||
.unwrap_or_else(|| pattern.speed.multiplier());
|
||||
|
||||
let step_beats = substeps_in_window(frontier, lookahead_end, speed_mult);
|
||||
let activation = active.activation_beat.take();
|
||||
let skip = activation.map_or(0, |ab| {
|
||||
step_beats.iter().take_while(|&&b| b < ab).count()
|
||||
});
|
||||
|
||||
for step_beat in step_beats {
|
||||
for step_beat in &step_beats[skip..] {
|
||||
let step_beat = *step_beat;
|
||||
result.any_step_fired = true;
|
||||
active.last_step_beat = step_beat;
|
||||
let step_idx = active.step_index % pattern.length;
|
||||
@@ -2391,6 +2424,118 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quantization_boundary_beat() {
|
||||
let quantum = 4.0;
|
||||
|
||||
// Immediate → None
|
||||
assert_eq!(
|
||||
quantization_boundary_beat(LaunchQuantization::Immediate, 1.5, quantum),
|
||||
None
|
||||
);
|
||||
|
||||
// Beat → next integer beat
|
||||
assert_eq!(
|
||||
quantization_boundary_beat(LaunchQuantization::Beat, 1.5, quantum),
|
||||
Some(2.0)
|
||||
);
|
||||
assert_eq!(
|
||||
quantization_boundary_beat(LaunchQuantization::Beat, 3.0, quantum),
|
||||
Some(4.0)
|
||||
);
|
||||
|
||||
// Bar → next multiple of quantum
|
||||
assert_eq!(
|
||||
quantization_boundary_beat(LaunchQuantization::Bar, 3.9, quantum),
|
||||
Some(4.0)
|
||||
);
|
||||
assert_eq!(
|
||||
quantization_boundary_beat(LaunchQuantization::Bar, 0.0, quantum),
|
||||
Some(4.0)
|
||||
);
|
||||
|
||||
// Bars2 → next multiple of quantum*2
|
||||
assert_eq!(
|
||||
quantization_boundary_beat(LaunchQuantization::Bars2, 3.9, quantum),
|
||||
Some(8.0)
|
||||
);
|
||||
|
||||
// Bars4 → next multiple of quantum*4
|
||||
assert_eq!(
|
||||
quantization_boundary_beat(LaunchQuantization::Bars4, 3.9, quantum),
|
||||
Some(16.0)
|
||||
);
|
||||
|
||||
// Bars8 → next multiple of quantum*8
|
||||
assert_eq!(
|
||||
quantization_boundary_beat(LaunchQuantization::Bars8, 3.9, quantum),
|
||||
Some(32.0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_activation_beat_prevents_early_substeps() {
|
||||
let mut state = make_state();
|
||||
|
||||
state.tick(tick_with(
|
||||
vec![SeqCommand::PatternUpdate {
|
||||
bank: 0,
|
||||
pattern: 0,
|
||||
data: simple_pattern(16),
|
||||
}],
|
||||
0.0,
|
||||
));
|
||||
|
||||
// Queue Bar-quantized start (boundary at beat 4.0, quantum=4)
|
||||
state.tick(tick_with(
|
||||
vec![SeqCommand::PatternStart {
|
||||
bank: 0,
|
||||
pattern: 0,
|
||||
quantization: LaunchQuantization::Bar,
|
||||
sync_mode: SyncMode::Reset,
|
||||
}],
|
||||
3.5,
|
||||
));
|
||||
assert!(!state.audio_state.active_patterns.contains_key(&pid(0, 0)));
|
||||
|
||||
// Simulate a wide lookahead window that crosses the bar boundary:
|
||||
// frontier=3.875, lookahead_end=4.125 — spans both sides of beat 4.0
|
||||
// Without activation_beat filtering, substeps before 4.0 would fire.
|
||||
state.tick(TickInput {
|
||||
commands: Vec::new(),
|
||||
playing: true,
|
||||
beat: 4.125,
|
||||
lookahead_end: 4.125,
|
||||
tempo: 120.0,
|
||||
quantum: 4.0,
|
||||
fill: false,
|
||||
nudge_secs: 0.0,
|
||||
current_time_us: 0,
|
||||
audio_sample_pos: 0,
|
||||
sr: 48000.0,
|
||||
mouse_x: 0.5,
|
||||
mouse_y: 0.5,
|
||||
mouse_down: 0.0,
|
||||
});
|
||||
|
||||
// Pattern should be active
|
||||
assert!(state.audio_state.active_patterns.contains_key(&pid(0, 0)));
|
||||
|
||||
// The activation_beat should have been consumed (set to None)
|
||||
let ap = state.audio_state.active_patterns.get(&pid(0, 0)).unwrap();
|
||||
assert!(ap.activation_beat.is_none(), "activation_beat should be consumed after first execute");
|
||||
|
||||
// Step index should reflect only substeps at/after beat 4.0, not before
|
||||
// At 1x speed: substeps at 0.25-beat intervals. From frontier 3.875 to 4.125:
|
||||
// substeps_in_window yields beats at 4.0 (= 16/4). Pre-4.0 substeps should be skipped.
|
||||
assert_eq!(ap.step_index, 1, "Only substep at beat 4.0 should fire, not pre-boundary ones");
|
||||
|
||||
// Second tick: activation_beat already consumed, all substeps should fire normally
|
||||
let _output2 = state.tick(tick_at(4.375, true));
|
||||
let ap2 = state.audio_state.active_patterns.get(&pid(0, 0)).unwrap();
|
||||
assert_eq!(ap2.step_index, 2, "Second tick should advance normally");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_no_false_boundary_after_pause_within_same_bar() {
|
||||
let mut state = make_state();
|
||||
|
||||
Reference in New Issue
Block a user