Como começar
Do zero a um plugin
Tudo nesta página vem do README do repositório e dos seus dois plugins de exemplo, como estão no commit c5835bf.
Antes de construir em cima dele: o Manguebyte é jovem, ainda não está no crates.io, e a API vai mudar antes da versão 1.0. Os editores de plugin existem e são testados no CI, mas nenhum foi aberto num host à mão ainda.
Antes de começar
Você precisa de um toolchain Rust estável, versão 1.85 ou mais nova.
No Linux, o backend de dispositivos precisa dos cabeçalhos de desenvolvimento do ALSA:
sudo apt install libasound2-devAdicione a dependência
Dependa direto do repositório:
[dependencies] manguebyte = { git = "https://github.com/alessonqueirozdev-hub/manguebyte" }De README.md no commit c5835bf, como está no arquivo.Faça um som
Uma tupla de processadores é ela mesma um processador. Monte uma voz de sintetizador pondo três deles numa tupla e renderize um segundo dela num arquivo WAV. Não precisa de hardware de áudio.
use manguebyte::prelude::*; fn main() -> Result<(), wav::WavError> { let spec = ProcessSpec { sample_rate: 48_000.0, max_block_size: 48_000, num_channels: 2 }; // A tuple of processors is itself a processor. let mut voice = ( Oscillator::new(Waveform::Saw, note_to_hz(45.0)), Filter::new(FilterShape::Lowpass, 1_200.0, 1.5), Gain::from_db(-12.0), ); voice.prepare(&spec); let mut second = AudioBuffer::<f32>::new(2, 48_000); voice.process(&mut second); wav::write_file("saw.wav", &second, 48_000, WavFormat::Int16) }De README.md no commit c5835bf, como está no arquivo.Toque ao vivo
O mesmo tipo de cadeia, no dispositivo de saída padrão. A closure roda na thread de áudio.
use manguebyte::prelude::*; fn main() -> Result<(), DeviceError> { let device = OutputDevice::open_default()?; let spec = ProcessSpec { sample_rate: device.sample_rate(), max_block_size: 512, num_channels: device.num_channels(), }; let mut voice = (Oscillator::new(Waveform::Sine, 440.0), Gain::<f32>::from_db(-18.0)); voice.prepare(&spec); // The closure runs on the audio thread. let _stream = device.start(512, move |buffer| voice.process(buffer))?; std::thread::sleep(std::time::Duration::from_secs(2)); Ok(()) }De README.md no commit c5835bf, como está no arquivo.Rode os exemplos
Clone o repositório para rodar os exemplos e gerar os plugins de exemplo:
git clone https://github.com/alessonqueirozdev-hub/manguebyte cd manguebyte # Render a short synth riff to a WAV file. Needs no audio hardware. cargo run --example render_wav -- riff.wav # Play the same riff on your speakers. The optional argument is a level in dB. cargo run --example play_riff -- -12Gere um plugin
plugins/pluck é um instrumento completo num arquivo só: um sintetizador dedilhado de 16 vozes, com expressão e modulação por nota, automação na amostra exata, um reverb e um editor pequeno. plugins/duck é o mesmo para um efeito: um ducker com sidechain, uma chave de bypass que não estala e um editor com medidor de redução de ganho. Cada um vira plugin CLAP, VST 3 e LV2 com uma linha por formato.
Transforme qualquer um dos dois em algo que um host consiga carregar com a ferramenta de linha de comando do framework, o
cargo mangue, que grava um.clape um.vst3no layout de cada sistema (com--format lv2, um pacote.lv2):cargo run -p cargo-mangue -- bundle -p mangue-pluck --releasecargo mangue new meu-synth --kind instrumentcomeça um plugin seu a partir de um modelo;installcopia um plugin para as pastas de plugins do usuário, evalidateroda nele os validadores do CLAP e do VST 3. Os dois exemplos passam por esses validadores a cada commit, e foram testados à mão no REAPER como CLAP e VST 3, antes de terem editores.Declare os parâmetros
Os parâmetros são campos de uma struct: tipados, lock-free, com faixas, unidades e conversão em texto. Esta é a declaração inteira do efeito de exemplo.
/// The plugin's parameters. pub struct DuckParams { /// Sidechain level, in decibels, at which the input starts to go down. pub threshold: FloatParam, /// The most the input is turned down, in decibels. pub depth: FloatParam, /// How fast the input goes down, in milliseconds. pub attack: FloatParam, /// How fast it comes back, in milliseconds. pub release: FloatParam, /// Lets the input through untouched. pub bypass: BoolParam, } param_set!(DuckParams { threshold, depth, attack, release, bypass });De plugins/duck/src/lib.rs no commit c5835bf, como está no arquivo.Teste sem host
O mesmo plugin roda dentro de um teste unitário por meio do
Harness, numa máquina sem placa de som. Este teste do instrumento de exemplo toca um lá e confere, por FFT, que o que sai é 440 Hz.#[test] fn plays_the_pitch_it_is_asked_for() { use manguebyte::dsp::fft::{Complex, RealFft}; let mut harness = Harness::<Pluck>::new(RATE, 512); harness.params().reverb.set(0.0); // No resonant peak, so the fundamental is the loudest partial. harness.params().resonance.set(0.7); harness.note(0, 40_000, 69, 1.0); // A4, 440 Hz let sound = harness.render(32_768); let mut fft = RealFft::<f32>::new(32_768); let mut spectrum = vec![Complex::ZERO; fft.spectrum_len()]; fft.forward(sound.channel(0), &mut spectrum); let loudest = (1..spectrum.len()) .max_by(|&a, &b| spectrum[a].norm_sqr().total_cmp(&spectrum[b].norm_sqr())) .unwrap(); let hertz = loudest as f64 * RATE / 32_768.0; assert!((hertz - 440.0).abs() < 2.0, "loudest partial at {hertz} Hz"); }De plugins/pluck/src/lib.rs no commit c5835bf, como está no arquivo.Toque sem host
O wrapper standalone roda o instrumento direto na placa de som, com qualquer teclado MIDI conectado. O argumento é o nível em decibéis.
cargo run -p mangue-pluck --features standalone --example play -- -12 holdO que ler depois
- ARCHITECTURE.md: como os crates se encaixam, e qual thread roda o quê.
- Os registros de decisão: por que os parâmetros não têm listeners, por que há um só fluxo de eventos, e o resto.
- Além do JUCE: o que o framework prova, e o limite de cada afirmação.
- CONTRIBUTING.md: as regras para o código que roda na thread de áudio.