diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index c14cd4b..adcd680 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -71,13 +71,24 @@ jobs: if: inputs.build-packages run: cargo xtask bundle cagire-plugins --release --target x86_64-pc-windows-msvc - - name: Install cargo-wix + - name: Install NSIS if: inputs.build-packages - run: cargo install cargo-wix + run: choco install nsis - - name: Build MSI installer + - name: Build NSIS installer if: inputs.build-packages - run: cargo wix --no-build --nocapture --package cagire -C -arch -C x64 + shell: pwsh + run: | + $version = (Select-String -Path Cargo.toml -Pattern '^version\s*=\s*"(.+)"' | Select-Object -First 1).Matches.Groups[1].Value + $root = (Get-Location).Path + $target = "x86_64-pc-windows-msvc" + & "C:\Program Files (x86)\NSIS\makensis.exe" ` + "-DVERSION=$version" ` + "-DCLI_EXE=$root\target\$target\release\cagire.exe" ` + "-DDESKTOP_EXE=$root\target\$target\release\cagire-desktop.exe" ` + "-DICON=$root\assets\Cagire.ico" ` + "-DOUTDIR=$root\target" ` + nsis/cagire.nsi - name: Upload CLI artifact if: inputs.build-packages @@ -93,12 +104,12 @@ jobs: name: cagire-windows-x86_64-desktop path: target/x86_64-pc-windows-msvc/release/cagire-desktop.exe - - name: Upload MSI artifact + - name: Upload installer artifact if: inputs.build-packages uses: actions/upload-artifact@v4 with: - name: cagire-windows-x86_64-msi - path: target/wix/*.msi + name: cagire-windows-x86_64-installer + path: target/cagire-*-setup.exe - name: Prepare plugin artifacts if: inputs.build-packages diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d9bbd9b..68ce2c2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -78,8 +78,8 @@ jobs: elif [[ "$name" == *-vst3 ]]; then base="${name%-vst3}" cd "$dir" && zip -r "../../release/${base}-vst3.zip" cagire-plugins.vst3 && cd ../.. - elif [[ "$name" == *-msi ]]; then - cp "$dir"/*.msi release/ + elif [[ "$name" == *-installer ]]; then + cp "$dir"/*-setup.exe release/ elif [[ "$name" == *-appimage ]]; then cp "$dir"/*.AppImage release/ elif [[ "$name" == *-desktop ]]; then diff --git a/Cargo.toml b/Cargo.toml index 67719ec..c629b68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ image = { version = "0.25", default-features = false, features = ["png"], option [target.'cfg(target_os = "linux")'.dependencies] cpal = { version = "0.17", optional = true, features = ["jack"] } -[target.'cfg(windows)'.build-dependencies] +[build-dependencies] winres = "0.1" [profile.release] diff --git a/build.rs b/build.rs index 631a969..72e0b5e 100644 --- a/build.rs +++ b/build.rs @@ -13,13 +13,22 @@ fn main() { println!("cargo:rustc-link-lib=oleaut32"); } - #[cfg(windows)] - { + if target_os == "windows" { + let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let icon = format!("{manifest_dir}/assets/Cagire.ico"); + eprintln!("winres: manifest_dir = {manifest_dir}"); + eprintln!("winres: icon path = {icon}"); + eprintln!("winres: icon exists = {}", std::path::Path::new(&icon).exists()); + eprintln!("winres: OUT_DIR = {}", std::env::var("OUT_DIR").unwrap_or_default()); + eprintln!("winres: TARGET = {}", std::env::var("TARGET").unwrap_or_default()); let mut res = winres::WindowsResource::new(); - res.set_icon("assets/Cagire.ico") + res.set_icon(&icon) .set("ProductName", "Cagire") .set("FileDescription", "Forth-based music sequencer") .set("LegalCopyright", "Copyright (c) 2025 Raphaël Forment"); - res.compile().expect("Failed to compile Windows resources"); + if let Err(e) = res.compile() { + eprintln!("winres: compile error: {e:?}"); + panic!("Failed to compile Windows resources: {e}"); + } } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 1196756..dd84b98 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -543,6 +543,8 @@ pub struct Bank { pub patterns: Vec, #[serde(default)] pub name: Option, + #[serde(default)] + pub prelude: String, } impl Bank { @@ -560,6 +562,7 @@ impl Default for Bank { Self { patterns: (0..MAX_PATTERNS).map(|_| Pattern::default()).collect(), name: None, + prelude: String::new(), } } } diff --git a/demos/02.cagire b/demos/02.cagire index 4c88c11..dc27c5b 100644 --- a/demos/02.cagire +++ b/demos/02.cagire @@ -1,8375 +1 @@ -{ - "version": 1, - "banks": [ - { - "patterns": [ - { - "steps": [ - { - "i": 0, - "script": "0 8 12 rand ..\nc3 c4 g3 g2 4 pcycle key!\n0 1 2 choose 2\n6 12 rand pentatonic\n( inv ) rarely\n( inv ) sometimes arp note\ngrain sound 2 8 rand decay \n2 vib 0.125 2 / vibmod\n0.01 1.0 exprand pan\n2 release\n0.8 verb 1.0 verbdiff\n0.2 chorus\n1 morph\n0.0 1.0 rand \n0.0 1.0 rand timbre\n0.5 gain\n0.8 sustain\n2 8 rand release\n." - }, - { - "i": 4, - "script": "0 12 20 rand ..\nc3 c4 g3 g2 4 pcycle key!\n0 1 2 choose 2\n6 12 rand pentatonic\n( inv ) rarely\n( inv ) sometimes arp note\ngrain sound 2 8 rand decay \n2 vib 0.125 2 / vibmod\n0.01 1.0 exprand pan\n10 16 rand release\n0.8 verb 1.0 verbdiff\n0.2 chorus\n1 morph\n0.0 1.0 rand 0.0 1.0 rand timbre\n0.5 gain\n( . ) 2 every" - } - ], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - }, - { - "patterns": [ - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - }, - { - "steps": [], - "length": 16, - "speed": [ - 1, - 1 - ] - } - ], - "name": null - } - ], - "sample_paths": [], - "tempo": 120.0, - "playing_patterns": [ - [ - 0, - 0 - ] - ], - "prelude": ";; BIG VOICES" -} \ No newline at end of file +{"version":1,"banks":[],"tempo":120.0,"playing_patterns":[[0,0]],"prelude":""} diff --git a/docs/forth/prelude.md b/docs/forth/prelude.md index be03e6c..c27e4e2 100644 --- a/docs/forth/prelude.md +++ b/docs/forth/prelude.md @@ -1,18 +1,57 @@ -# The Prelude +# Preludes -You can define words in any step and they become available to all other steps. But as a project grows, definitions get scattered across steps and become hard to find and maintain. The **prelude** is a dedicated place for this. It is a project-wide Forth script that runs once before the first step plays. Definitions, variables, settings — everything in one place. Press `d` to open the prelude editor. Press `Esc` to save and close. Press `D` (Shift+d) to re-evaluate it without opening the editor. +Cagire has two levels of prelude: a **project prelude** shared by all banks, and **bank preludes** that travel with each bank. + +## Bank Prelude + +Each bank can carry its own prelude script. Press `p` to open the current bank's prelude editor. Press `Esc` to save, evaluate, and close. + +Bank preludes make banks self-contained. When you share a bank, its prelude travels with it — recipients get all the definitions they need without merging anything into their own project. + +```forth +: bass pulse sound 0.8 gain 400 lpf 1 lpd 8 lpe 0.6 width . ; +: pad sine sound 0.5 gain 2 spread 1.5 attack 0.4 verb . ; +``` + +Every step in that bank can now use `bass` and `pad`. Share the bank and the recipient gets these definitions automatically. + +## Project Prelude + +The project prelude is a global script shared across all banks. Press `P` (Shift+p) to open it. Use it for truly project-wide definitions, variables, and settings that every bank should see. + +```forth +c2 !root +0 !mode +42 seed +``` + +## Evaluation Order + +When preludes are evaluated (on playback start, project load, or pressing `d`): + +1. **Project prelude** runs first +2. **Bank 0 prelude** runs next (if non-empty) +3. **Bank 1 prelude**, then **Bank 2**, ... up to **Bank 31** + +Only non-empty bank preludes are evaluated. Last-evaluated wins for name collisions — a bank prelude can override a project-level definition. + +## Keybindings + +| Key | Action | +|-----|--------| +| `p` | Open current bank's prelude editor | +| `P` | Open project prelude editor | +| `d` | Re-evaluate all preludes (project + all banks) | ## Naming Your Sounds -The most common use of the prelude is to define words for your instruments. Without a prelude, every step that plays a bass has to spell out the full sound design or to create a new word before using it: +The most common use of a bank prelude is to define words for your instruments. Without a prelude, every step that plays a bass has to spell out the full sound design: ```forth -pulse sound 0.8 gain 400 lpf 1 lpd 8 lpe 0.6 width . +pulse sound c2 note 0.8 gain 400 lpf 1 lpd 8 lpe 0.6 width . ``` -Repeat this across eight steps without making a new word and you have eight copies of the same thing. Change the filter? Change it eight times. - -In the prelude, define it once: +In the bank prelude, define it once: ```forth : bass pulse sound 0.8 gain 400 lpf 1 lpd 8 lpe 0.6 width . ; @@ -20,22 +59,8 @@ In the prelude, define it once: Now every step just writes `c2 note bass`. Change the sound in one place, every step follows. -A step that used to read: - -```forth -pulse sound c2 note 0.8 gain 400 lpf 1 lpd 8 lpe 0.6 width . -``` - -Becomes: - -```forth -c2 note bass -``` - ## Building a Vocabulary -The prelude is where you build the vocabulary for your music. Not just instruments but any combination of code / words you want to reuse: - ```forth ;; instruments : bass pulse sound 0.8 gain 400 lpf 1 lpd 8 lpe 0.6 width . ; @@ -49,11 +74,11 @@ The prelude is where you build the vocabulary for your music. Not just instrumen : loud 0.9 gain ; ``` -By using the prelude and predefined words, steps become expressive and short. The prelude carries the design decisions; steps carry the composition. +Steps become expressive and short. The prelude carries the design decisions; steps carry the composition. ## Setting Initial State -The prelude also runs plain Forth, not just definitions. You can use it to set variables and seed the random generator: +The project prelude is the right place for global state: ```forth c2 !root @@ -61,18 +86,18 @@ c2 !root 42 seed ``` -Every step can then read `@root` and `@mode`. And `42 seed` makes randomness reproducible — same seed, same sequence every time you hit play. +Every step can then read `@root` and `@mode`. And `42 seed` makes randomness reproducible. -## When It Runs +## When Preludes Run -The prelude evaluates at three moments: +Preludes evaluate at three moments: 1. When you press **Space** to start playback 2. When you **load** a project -3. When you press **D** manually +3. When you press **d** manually -It runs once at these moments, not on every step. This makes it the right place for definitions and initial values. If you edit the prelude while playing, press `D` to push changes into the running session. New definitions take effect immediately; the next time a step runs, it sees the updated words. +They run once at these moments, not on every step. If you edit a prelude while playing, press `d` to push changes into the running session. ## What Not to Put Here -The prelude has no access to sequencer state. Words like `step`, `beat`, `iter`, and `phase` are meaningless here because no step is playing yet. Use the prelude for definitions and setup, not for logic that depends on timing. The prelude also should not emit sounds. It runs silently — any `.` calls here would fire before the sequencer clock is running and produce nothing useful. +Preludes have no access to sequencer state. Words like `step`, `beat`, `iter`, and `phase` are meaningless here because no step is playing yet. Use preludes for definitions and setup, not for logic that depends on timing. Preludes also should not emit sounds — any `.` calls here would fire before the sequencer clock is running. diff --git a/docs/getting-started/grid.md b/docs/getting-started/grid.md index 997322d..8e82517 100644 --- a/docs/getting-started/grid.md +++ b/docs/getting-started/grid.md @@ -32,12 +32,13 @@ To create mirrors: copy a step with `Ctrl+C`, then paste with `Ctrl+B` instead o - `Ctrl+D` — Duplicate selection - `Ctrl+H` — Harden mirrors (convert to independent copies) -## Prelude +## Preludes -The prelude is a Forth script that runs before every step, useful for defining shared variables and setup code. +Each bank has its own prelude — a Forth script for definitions and setup that travels with the bank when shared. There is also a project-wide prelude for global configuration. -- `p` — Open the prelude editor -- `d` — Evaluate the prelude +- `p` — Open current bank's prelude editor +- `P` — Open project prelude editor +- `d` — Evaluate all preludes (project + all banks) ## Pattern Controls diff --git a/nsis/cagire.nsi b/nsis/cagire.nsi new file mode 100644 index 0000000..65db774 --- /dev/null +++ b/nsis/cagire.nsi @@ -0,0 +1,98 @@ +; Cagire NSIS Installer Script +; Receives defines from command line: +; -DVERSION=x.y.z +; -DCLI_EXE=/path/to/cagire.exe +; -DDESKTOP_EXE=/path/to/cagire-desktop.exe +; -DICON=/path/to/Cagire.ico +; -DOUTDIR=/path/to/releases + +!include "MUI2.nsh" +!include "WordFunc.nsh" + +Name "Cagire ${VERSION}" +OutFile "${OUTDIR}\cagire-${VERSION}-windows-x86_64-setup.exe" +InstallDir "$PROGRAMFILES64\Cagire" +InstallDirRegKey HKLM "Software\Cagire" "InstallDir" +RequestExecutionLevel admin +Unicode True + +!define MUI_ICON "${ICON}" +!define MUI_UNICON "${ICON}" +!define MUI_ABORTWARNING + +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_BITMAP "header.bmp" +!define MUI_WELCOMEFINISHPAGE_BITMAP "sidebar.bmp" +!define MUI_UNWELCOMEFINISHPAGE_BITMAP "sidebar.bmp" + +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH + +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +!insertmacro MUI_LANGUAGE "English" + +Section "Cagire (required)" SecCore + SectionIn RO + SetOutPath "$INSTDIR" + File "/oname=cagire.exe" "${CLI_EXE}" + File "/oname=cagire-desktop.exe" "${DESKTOP_EXE}" + + WriteUninstaller "$INSTDIR\uninstall.exe" + WriteRegStr HKLM "Software\Cagire" "InstallDir" "$INSTDIR" + + ; Add/Remove Programs entry + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cagire" "DisplayName" "Cagire" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cagire" "DisplayVersion" "${VERSION}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cagire" "Publisher" "Raphael Forment" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cagire" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cagire" "DisplayIcon" '"$INSTDIR\cagire-desktop.exe"' + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cagire" "URLInfoAbout" "https://github.com/Bubobubobubobubo/cagire" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cagire" "HelpLink" "https://cagire.raphaelforment.fr" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cagire" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cagire" "NoRepair" 1 +SectionEnd + +Section "Add to PATH" SecPath + ReadRegStr $0 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path" + StrCpy $0 "$0;$INSTDIR" + WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path" "$0" + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 +SectionEnd + +Section "Start Menu Shortcut" SecStartMenu + CreateDirectory "$SMPROGRAMS\Cagire" + CreateShortCut "$SMPROGRAMS\Cagire\Cagire.lnk" "$INSTDIR\cagire-desktop.exe" "" "$INSTDIR\cagire-desktop.exe" 0 + CreateShortCut "$SMPROGRAMS\Cagire\Uninstall.lnk" "$INSTDIR\uninstall.exe" +SectionEnd + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${SecCore} "Installs Cagire CLI and Desktop binaries." + !insertmacro MUI_DESCRIPTION_TEXT ${SecPath} "Add the install location to the PATH system environment variable." + !insertmacro MUI_DESCRIPTION_TEXT ${SecStartMenu} "Add a Cagire shortcut to the Start Menu." +!insertmacro MUI_FUNCTION_DESCRIPTION_END + +Section "Uninstall" + Delete "$INSTDIR\cagire.exe" + Delete "$INSTDIR\cagire-desktop.exe" + Delete "$INSTDIR\uninstall.exe" + RMDir "$INSTDIR" + + Delete "$SMPROGRAMS\Cagire\Cagire.lnk" + Delete "$SMPROGRAMS\Cagire\Uninstall.lnk" + RMDir "$SMPROGRAMS\Cagire" + + ; Remove from PATH + ReadRegStr $0 HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path" + ; Remove ";$INSTDIR" from the path string + ${WordReplace} $0 ";$INSTDIR" "" "+" $0 + WriteRegExpandStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path" "$0" + SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 + + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Cagire" + DeleteRegKey HKLM "Software\Cagire" +SectionEnd diff --git a/nsis/header.bmp b/nsis/header.bmp new file mode 100644 index 0000000..62985a8 Binary files /dev/null and b/nsis/header.bmp differ diff --git a/nsis/sidebar.bmp b/nsis/sidebar.bmp new file mode 100644 index 0000000..0e3b735 Binary files /dev/null and b/nsis/sidebar.bmp differ diff --git a/scripts/build-all.sh b/scripts/build-all.sh index b4ab9e4..55e8d0e 100755 --- a/scripts/build-all.sh +++ b/scripts/build-all.sh @@ -327,11 +327,20 @@ copy_artifacts() { fi fi - # MSI installer for Windows targets - if [[ "$os" == "windows" ]] && command -v cargo-wix &>/dev/null; then - echo " Building MSI installer..." - cargo wix --no-build --nocapture --package cagire -C -arch -C x64 - cp target/wix/*.msi "$OUT/" 2>/dev/null && echo " MSI -> $OUT/" || true + # NSIS installer for Windows targets + if [[ "$os" == "windows" ]] && command -v makensis &>/dev/null; then + echo " Building NSIS installer..." + local version + version=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') + local abs_root + abs_root=$(pwd) + makensis -DVERSION="$version" \ + -DCLI_EXE="$abs_root/$rd/cagire.exe" \ + -DDESKTOP_EXE="$abs_root/$rd/cagire-desktop.exe" \ + -DICON="$abs_root/assets/Cagire.ico" \ + -DOUTDIR="$abs_root/$OUT" \ + nsis/cagire.nsi + echo " Installer -> $OUT/cagire-${version}-windows-x86_64-setup.exe" fi # AppImage for Linux targets diff --git a/src/app/dispatch.rs b/src/app/dispatch.rs index ecc7f28..cc81fa1 100644 --- a/src/app/dispatch.rs +++ b/src/app/dispatch.rs @@ -469,6 +469,13 @@ impl App { AppCommand::SavePrelude => self.save_prelude(), AppCommand::EvaluatePrelude => self.evaluate_prelude(link), AppCommand::ClosePreludeEditor => self.close_prelude_editor(), + AppCommand::OpenBankPreludeEditor => self.open_bank_prelude_editor(), + AppCommand::SaveBankPrelude => self.save_bank_prelude(), + AppCommand::EvaluateBankPrelude => { + let bank = self.editor_ctx.bank; + self.evaluate_bank_prelude(bank, link); + } + AppCommand::CloseBankPreludeEditor => self.close_bank_prelude_editor(), // Periodic script AppCommand::OpenScriptModal(field) => self.open_script_modal(field), diff --git a/src/app/scripting.rs b/src/app/scripting.rs index f8f59d1..9652557 100644 --- a/src/app/scripting.rs +++ b/src/app/scripting.rs @@ -109,24 +109,101 @@ impl App { self.load_step_to_editor(); } - /// Evaluate the project prelude to seed variables and definitions. - pub fn evaluate_prelude(&mut self, link: &LinkState) { - let prelude = &self.project_state.project.prelude; + /// Switch the editor to the current bank's prelude script. + pub fn open_bank_prelude_editor(&mut self) { + let bank = self.editor_ctx.bank; + let prelude = &self.project_state.project.banks[bank].prelude; + let lines: Vec = if prelude.is_empty() { + vec![String::new()] + } else { + prelude.lines().map(String::from).collect() + }; + self.editor_ctx.editor.set_content(lines); + self.editor_ctx.editor.set_candidates(Arc::clone(&COMPLETION_CANDIDATES)); + self.editor_ctx + .editor + .set_completion_enabled(self.ui.show_completion); + let tree = SampleTree::from_paths(&self.audio.config.sample_paths); + self.editor_ctx.editor.set_sample_folders(tree.all_folder_names()); + self.editor_ctx.target = EditorTarget::BankPrelude; + self.ui.modal = Modal::Editor; + } + + pub fn save_bank_prelude(&mut self) { + let bank = self.editor_ctx.bank; + let text = self.editor_ctx.editor.content(); + self.project_state.project.banks[bank].prelude = text; + } + + pub fn close_bank_prelude_editor(&mut self) { + self.editor_ctx.target = EditorTarget::Step; + self.load_step_to_editor(); + } + + /// Evaluate a single bank's prelude. + pub fn evaluate_bank_prelude(&mut self, bank: usize, link: &LinkState) { + let prelude = &self.project_state.project.banks[bank].prelude; if prelude.trim().is_empty() { return; } let ctx = self.create_step_context(0, link); match self.script_engine.evaluate(prelude, &ctx) { - Ok(_) => { - self.ui.flash("Prelude evaluated", 150, FlashKind::Info); - } + Ok(_) => {} Err(e) => { - self.ui - .flash(&format!("Prelude error: {e}"), 300, FlashKind::Error); + let fallback = format!("Bank {}", bank + 1); + let bank_name = self.project_state.project.banks[bank] + .name + .as_deref() + .unwrap_or(&fallback); + self.ui.flash( + &format!("{bank_name} prelude error: {e}"), + 300, + FlashKind::Error, + ); } } } + /// Evaluate the project prelude and all bank preludes. + pub fn evaluate_prelude(&mut self, link: &LinkState) { + let project_prelude = &self.project_state.project.prelude; + if !project_prelude.trim().is_empty() { + let ctx = self.create_step_context(0, link); + match self.script_engine.evaluate(project_prelude, &ctx) { + Ok(_) => {} + Err(e) => { + self.ui + .flash(&format!("Project prelude error: {e}"), 300, FlashKind::Error); + return; + } + } + } + for bank_idx in 0..self.project_state.project.banks.len() { + let prelude = &self.project_state.project.banks[bank_idx].prelude; + if prelude.trim().is_empty() { + continue; + } + let ctx = self.create_step_context(0, link); + match self.script_engine.evaluate(prelude, &ctx) { + Ok(_) => {} + Err(e) => { + let bank_name = self.project_state.project.banks[bank_idx] + .name + .as_deref() + .map(String::from) + .unwrap_or_else(|| format!("Bank {}", bank_idx + 1)); + self.ui.flash( + &format!("{bank_name} prelude error: {e}"), + 300, + FlashKind::Error, + ); + return; + } + } + } + self.ui.flash("Preludes evaluated", 150, FlashKind::Info); + } + /// Evaluate a script and immediately send its audio commands. /// Returns collected `print` output, if any. pub fn execute_script_oneshot( diff --git a/src/app/undo.rs b/src/app/undo.rs index 9b5fa3a..9c1d1a8 100644 --- a/src/app/undo.rs +++ b/src/app/undo.rs @@ -42,6 +42,11 @@ impl App { let data = self.project_state.project.pattern_at(*bank, *pattern).clone(); Some(UndoScope::Pattern { bank: *bank, pattern: *pattern, data }) } + AppCommand::SaveBankPrelude => { + let bank = self.editor_ctx.bank; + let data = self.project_state.project.banks[bank].clone(); + Some(UndoScope::Bank { bank, data }) + } AppCommand::ResetBank { bank } | AppCommand::PasteBank { bank } | AppCommand::ImportBank { bank } => { diff --git a/src/commands.rs b/src/commands.rs index 37bcef3..e35fa92 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -301,6 +301,10 @@ pub enum AppCommand { SavePrelude, EvaluatePrelude, ClosePreludeEditor, + OpenBankPreludeEditor, + SaveBankPrelude, + EvaluateBankPrelude, + CloseBankPreludeEditor, // Onboarding DismissOnboarding, diff --git a/src/input/main_page.rs b/src/input/main_page.rs index c02089a..37f153e 100644 --- a/src/input/main_page.rs +++ b/src/input/main_page.rs @@ -82,7 +82,8 @@ pub(super) fn handle_main_page(ctx: &mut InputContext, key: KeyEvent, ctrl: bool KeyCode::Char(']') => ctx.dispatch(AppCommand::SpeedIncrease), KeyCode::Char('L') => ctx.dispatch(AppCommand::OpenPatternModal(PatternField::Length)), KeyCode::Char('S') => ctx.dispatch(AppCommand::OpenPatternModal(PatternField::Speed)), - KeyCode::Char('p') => ctx.dispatch(AppCommand::OpenPreludeEditor), + KeyCode::Char('p') => ctx.dispatch(AppCommand::OpenBankPreludeEditor), + KeyCode::Char('P') => ctx.dispatch(AppCommand::OpenPreludeEditor), KeyCode::Delete | KeyCode::Backspace => { let (bank, pattern) = (ctx.app.editor_ctx.bank, ctx.app.editor_ctx.pattern); if let Some(range) = ctx.app.editor_ctx.selection_range() { diff --git a/src/input/modal.rs b/src/input/modal.rs index 9454444..2ed41c7 100644 --- a/src/input/modal.rs +++ b/src/input/modal.rs @@ -351,6 +351,11 @@ pub(super) fn handle_modal_input(ctx: &mut InputContext, key: KeyEvent) -> Input ctx.dispatch(AppCommand::EvaluatePrelude); ctx.dispatch(AppCommand::ClosePreludeEditor); } + EditorTarget::BankPrelude => { + ctx.dispatch(AppCommand::SaveBankPrelude); + ctx.dispatch(AppCommand::EvaluateBankPrelude); + ctx.dispatch(AppCommand::CloseBankPreludeEditor); + } } ctx.dispatch(AppCommand::CloseModal); } @@ -364,6 +369,10 @@ pub(super) fn handle_modal_input(ctx: &mut InputContext, key: KeyEvent) -> Input ctx.dispatch(AppCommand::SavePrelude); ctx.dispatch(AppCommand::EvaluatePrelude); } + EditorTarget::BankPrelude => { + ctx.dispatch(AppCommand::SaveBankPrelude); + ctx.dispatch(AppCommand::EvaluateBankPrelude); + } }, KeyCode::Char('b') if ctrl => { editor.activate_sample_finder(); diff --git a/src/input/mouse.rs b/src/input/mouse.rs index b2e73a1..1e4965b 100644 --- a/src/input/mouse.rs +++ b/src/input/mouse.rs @@ -941,6 +941,11 @@ fn handle_modal_click(ctx: &mut InputContext, col: u16, row: u16, term: Rect) { ctx.dispatch(AppCommand::EvaluatePrelude); ctx.dispatch(AppCommand::ClosePreludeEditor); } + EditorTarget::BankPrelude => { + ctx.dispatch(AppCommand::SaveBankPrelude); + ctx.dispatch(AppCommand::EvaluateBankPrelude); + ctx.dispatch(AppCommand::CloseBankPreludeEditor); + } } ctx.dispatch(AppCommand::CloseModal); } diff --git a/src/model/docs.rs b/src/model/docs.rs index 8b671dd..89403ad 100644 --- a/src/model/docs.rs +++ b/src/model/docs.rs @@ -70,7 +70,7 @@ pub const DOCS: &[DocEntry] = &[ ), Topic("Brackets", include_str!("../../docs/forth/brackets.md")), Topic("Cycling", include_str!("../../docs/forth/cycling.md")), - Topic("The Prelude", include_str!("../../docs/forth/prelude.md")), + Topic("Preludes", include_str!("../../docs/forth/prelude.md")), Topic( "Cagire vs Classic", include_str!("../../docs/forth/oddities.md"), diff --git a/src/model/palette.rs b/src/model/palette.rs index 0d3e6b8..839c2bc 100644 --- a/src/model/palette.rs +++ b/src/model/palette.rs @@ -766,17 +766,26 @@ pub static COMMANDS: LazyLock> = LazyLock::new(|| { // === Prelude === CommandEntry { - name: "Edit Prelude", - description: "Open prelude editor", + name: "Edit Bank Prelude", + description: "Open current bank's prelude editor", category: "Prelude", keybinding: "p", pages: &[Main], normal_mode: false, + action: Some(PaletteAction::Resolve(|_| Some(AppCommand::OpenBankPreludeEditor))), + }, + CommandEntry { + name: "Edit Project Prelude", + description: "Open project-wide prelude editor", + category: "Prelude", + keybinding: "P", + pages: &[Main], + normal_mode: false, action: Some(PaletteAction::Resolve(|_| Some(AppCommand::OpenPreludeEditor))), }, CommandEntry { - name: "Evaluate Prelude", - description: "Re-evaluate prelude script", + name: "Evaluate All Preludes", + description: "Re-evaluate project and all bank preludes", category: "Prelude", keybinding: "d", pages: &[Main], diff --git a/src/state/editor.rs b/src/state/editor.rs index 4d27663..1e8844e 100644 --- a/src/state/editor.rs +++ b/src/state/editor.rs @@ -8,6 +8,7 @@ pub enum EditorTarget { #[default] Step, Prelude, + BankPrelude, } #[derive(Clone, Copy, PartialEq, Eq)] diff --git a/src/views/main_view.rs b/src/views/main_view.rs index 1bc14ef..a027c15 100644 --- a/src/views/main_view.rs +++ b/src/views/main_view.rs @@ -76,7 +76,11 @@ fn render_top_layout( } if has_preview { let user_words: HashSet = app.dict.lock().keys().cloned().collect(); - let has_prelude = !app.project_state.project.prelude.trim().is_empty(); + let has_prelude = !app.project_state.project.prelude.trim().is_empty() + || !app.project_state.project.banks[app.editor_ctx.bank] + .prelude + .trim() + .is_empty(); if has_prelude { let [script_area, prelude_area] = Layout::horizontal([Constraint::Fill(1), Constraint::Fill(1)]).areas(areas[idx]); @@ -195,7 +199,11 @@ fn render_viz_area( VizPanel::Lissajous => render_lissajous(frame, app, *panel_area), VizPanel::Preview => { let user_words = user_words_once.as_ref().expect("user_words initialized"); - let has_prelude = !app.project_state.project.prelude.trim().is_empty(); + let has_prelude = !app.project_state.project.prelude.trim().is_empty() + || !app.project_state.project.banks[app.editor_ctx.bank] + .prelude + .trim() + .is_empty(); if has_prelude { let [script_area, prelude_area] = if is_vertical_layout { Layout::vertical([Constraint::Fill(1), Constraint::Fill(1)]) @@ -655,11 +663,20 @@ pub(crate) fn render_prelude_preview( area: Rect, ) { let theme = theme::get(); - let prelude = &app.project_state.project.prelude; + let bank_prelude = &app.project_state.project.banks[app.editor_ctx.bank].prelude; + let (prelude, title) = if !bank_prelude.trim().is_empty() { + let bank_name = app.project_state.project.banks[app.editor_ctx.bank] + .name + .as_deref() + .unwrap_or("Bank"); + (bank_prelude.as_str(), format!(" {bank_name} Prelude ")) + } else { + (app.project_state.project.prelude.as_str(), " Prelude ".to_string()) + }; let block = Block::default() .borders(Borders::ALL) - .title(" Prelude ") + .title(title) .border_style(Style::new().fg(theme.ui.border)); let inner = block.inner(area); frame.render_widget(block, area); diff --git a/src/views/render.rs b/src/views/render.rs index 9924a66..46f4594 100644 --- a/src/views/render.rs +++ b/src/views/render.rs @@ -911,7 +911,13 @@ fn render_modal_editor( }; let title = match app.editor_ctx.target { - EditorTarget::Prelude => "Prelude".to_string(), + EditorTarget::Prelude => "Project Prelude".to_string(), + EditorTarget::BankPrelude => { + let bank = &app.project_state.project.banks[app.editor_ctx.bank]; + let fallback = format!("Bank {}", app.editor_ctx.bank + 1); + let bank_name = bank.name.as_deref().unwrap_or(&fallback); + format!("{bank_name} Prelude") + } EditorTarget::Step => { let step_num = app.editor_ctx.step + 1; let step = app.current_edit_pattern().step(app.editor_ctx.step); diff --git a/src/views/script_view.rs b/src/views/script_view.rs index 01813a7..8bc0169 100644 --- a/src/views/script_view.rs +++ b/src/views/script_view.rs @@ -114,7 +114,11 @@ fn render_sidebar(frame: &mut Frame, app: &App, area: Rect) { if app.audio.config.show_lissajous { constraints.push(Constraint::Fill(1)); } - let has_prelude = !app.project_state.project.prelude.trim().is_empty(); + let has_prelude = !app.project_state.project.prelude.trim().is_empty() + || !app.project_state.project.banks[app.editor_ctx.bank] + .prelude + .trim() + .is_empty(); if has_prelude { constraints.push(Constraint::Fill(1)); }