Manguebyte

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.

  1. 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:

    Shell
    sudo apt install libasound2-dev
  2. Adicione a dependência

    Dependa direto do repositório:

    TOML
    [dependencies]
    manguebyte = { git = "https://github.com/alessonqueirozdev-hub/manguebyte" }
    De README.md no commit c5835bf, como está no arquivo.
  3. 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.

    Rust
    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.
  4. Toque ao vivo

    O mesmo tipo de cadeia, no dispositivo de saída padrão. A closure roda na thread de áudio.

    Rust
    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.
  5. Rode os exemplos

    Clone o repositório para rodar os exemplos e gerar os plugins de exemplo:

    Shell
    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 -- -12
  6. Gere 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 .clap e um .vst3 no layout de cada sistema (com --format lv2, um pacote .lv2):

    Shell
    cargo run -p cargo-mangue -- bundle -p mangue-pluck --release

    cargo mangue new meu-synth --kind instrument começa um plugin seu a partir de um modelo; install copia um plugin para as pastas de plugins do usuário, e validate roda 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.

  7. 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.

    Rust
    /// 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.
  8. 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.

    Rust
    #[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.
  9. 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.

    Shell
    cargo run -p mangue-pluck --features standalone --example play -- -12 hold