Skip to content

ceejbot/endless-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

endless-rs

A Rust SDK for the Polyend Endless effects pedal. Write custom audio effects in Rust instead of C++. The crate provides a safe Patch trait and a macro that generates all the ABI glue needed to produce .endl patch binaries.

The official C/C++ SDK is here on GitHub for comparison.

Requirements

  • a stable Rust, edition 2024
  • the required ARM target: thumbv7em-none-eabihf
  • the GNU ARM toolchain: arm-none-eabi-objcopy (for producing .endl binaries)
rustup target add thumbv7em-none-eabihf
brew install arm-none-eabi-gcc    # macOS; use your package manager elsewhere
just setup                        # if you use just

See examples/bitcrush/ for a port of the official SDK's bitcrush effect. If you can build and flash this successfully, you're in business.

The Patch trait

Write effects by defining a struct to hold any data you need for state, then implementing the Patch trait for your struct type. All functions defined by Patch have default no-op implementations. Override only what you need.

Method Called
init(&mut self) once when patch loads
set_working_buffer(&mut self, &'static mut [f32]) firmware provides external RAM buffer (once)
process_audio(&mut self, &mut [f32], &mut [f32]) -> f32 for every audio frame (48 kHz, stereo)
param_metadata(&self, ParamId) -> ParameterMetadata when firmware queries knob ranges
set_param(&mut self, ParamId, f32) when user turns a knob (audio thread)
handle_action(&mut self, ActionId) when the foot switch is pressed or held
state_led_color(&self) -> Color the firmware polls LED color

Constraints

Endless effects are #![no_std] code that runs on an ARM Cortex-M7. Keep in mind the limitations.

  • No heap allocation. Use static storage or the working buffer.
  • Single-precision float only. Use f32 everywhere; f64 operations are software-emulated and slow.
  • Keep process_audio fast. If it takes too long, audio frames drop.
  • Output samples in (-1.0, 1.0) to avoid digital clipping.
  • For math functions like powf or sinf, add the libm crate.

Write your own Endless effect

Here's another full example of making a new binary crate that depends on endless-rs:

# Cargo.toml
[package]
name = "hello-world"
version = "0.1.0"
edition = "2024"

[[bin]]
name = "hello-world"
path = "src/main.rs"

[dependencies]
endless-rs = { path = "../endless-rs" }

[profile.release]
opt-level = "s"
lto = true
strip = "symbols"

Add a .cargo/config.toml to target the Endless hardware:

[build]
target = "thumbv7em-none-eabihf"

[target.thumbv7em-none-eabihf]
rustflags = [
  "-C", "link-arg=-Tpatch_imx.ld",
  "-C", "link-arg=--defsym=PATCH_LOAD_ADDR=0x80000000",
  "-C", "link-arg=--defsym=end=__patch_bss_end",
]

Add a build.rs so the linker can find the script:

fn main() {
    let ld_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("path/to/endless-rs/link");
    println!("cargo:rustc-link-search={}", ld_dir.display());
    println!("cargo:rerun-if-changed={}/patch_imx.ld", ld_dir.display());
}

And now we write the hello world of DSP effects: gain!

#![no_std]
#![no_main]

use endless_rs::{endless_patch, ActionId, Color, ParamId, ParameterMetadata, Patch};

struct HelloWorld {
    gain: f32,
}

impl Patch for HelloWorld {
    fn process_audio(&mut self, left: &mut [f32], right: &mut [f32]) -> f32 {
        for (l, r) in left.iter_mut().zip(right.iter_mut()) {
            *l *= self.gain;
            *r *= self.gain;
        }
        0.0
    }

    fn set_param(&mut self, param: ParamId, value: f32) {
        if param == ParamId::Left {
            self.gain = value;
        }
    }
}

endless_patch!(HelloWorld, HelloWorld { gain: 0.5 });

Build the Rust binary, then run the ARM tool that turns it into a flashable artifact:

cargo build --release
arm-none-eabi-objcopy -O binary \
    target/thumbv7em-none-eabihf/release/hello-world \
    hello-world.endl

Connect the Endless pedal to your computer over USB C, and copy the .endl file to the mounted Endless drive. If all goes well, the pedal's LED turns yellow, then flashes green, then becomes a steady blue, indicating that the effect is active. If the pedal LED goes red, the flash failed.

License

MIT

About

A Rust SDK for the Polyend Endless programmable effects pedal

Topics

Resources

License

Stars

Watchers

Forks

Contributors