Manguebyte

Framework de áudio de código aberto para Rust

Testado, não prometido.

O Manguebyte serve para construir plugins e aplicativos de áudio em Rust: DSP e instrumentos, medição, MIDI, arquivos, dispositivos, editores e hospedagem de plugins, e exportação nativa para CLAP, VST 3 e LV2 com uma linha cada. A meta é ser para o Rust o que o JUCE é para o C++, e depois ir além. Cada afirmação deste site leva ao teste que a prova.

Como começar Ler o código

Projeto jovem. A API vai mudar antes da versão 1.0. Os editores de plugin abrem em janelas Win32 e são testados no CI, mas ninguém abriu um num host à mão ainda. Veja o que falta.

Manguebyte: um caranguejo de fones de ouvido tocando guitarra

01

Quatro coisas que ele prova a cada commit

Igualar o JUCE é a linha de base. Estes são pontos em que o Manguebyte já faz algo que o JUCE 9.0.4 não faz, cada um sustentado por um teste que roda no CI. A lista completa tem quinze, com a fonte de tudo o que se diz sobre o JUCE e o limite de cada afirmação.

  1. A automação cai na amostra exata

    Cada mudança de parâmetro chega com o frame em que cai e é aplicada nesse frame: no harness de testes, pelo CLAP e pelo VST 3, em que todos os pontos de todas as filas de automação são entregues.

    No JUCE 9.0.4
    O processador lê cada parâmetro uma vez por bloco.
    Limite
    Cabe ao processador usar ou não esse tempo. Ler um valor uma vez por bloco continua válido, e custa menos.

    Prova

  2. O mesmo som em qualquer tamanho de bloco

    Uma música renderizada com seis padrões de bloco, de 64 frames constantes a um irregular 1, 511, 7, 300, 64, 2, 129, tem de sair idêntica bit a bit. Na primeira vez em que rodou, o teste falhou e revelou um bug de verdade.

    No JUCE 9.0.4
    Se o som depende ou não do buffer do host fica por conta de cada plugin.
    Limite
    Cobre o que segue os eventos. Um valor lido uma vez por bloco muda nas bordas do bloco, por definição.

    Prova

  3. Sem heap na thread de áudio, ou o CI falha

    Os testes trocam o alocador global por um que conta. Se algum processador, ou o wrapper CLAP, alocar ou liberar memória durante o processamento, o build fica vermelho. Isso já pegou um mutex que aloca no macOS.

    No JUCE 9.0.4
    A segurança em tempo real é uma regra da documentação.
    Limite
    É um teste que roda, não uma prova em tempo de compilação. Pega uso do heap nos caminhos que os testes exercitam.

    Prova

  4. CLAP e VST 3 com uma linha cada, conferidos pelos próprios validadores

    export_clap! e export_vst3! transformam o mesmo plugin nos dois formatos. O CI roda o validador do projeto CLAP e o da Steinberg num instrumento e num efeito; os 537 testes de VST 3 passam em Windows, Linux e macOS. Os dois também foram testados à mão no REAPER, e export_lv2! acrescenta um terceiro formato.

    No JUCE 9.0.4
    A exportação para CLAP não existe de forma nativa.
    Limite
    Os editores são testados no CI, ainda não foram abertos num host à mão. O LV2 é novo: o lilv o carrega no CI, nenhum host o rodou ainda. Sem AU nem AAX.

    Prova

As quinze afirmações, com provas e limites

02

Um plugin inteiro, e depois uma linha por formato

Este é o efeito de exemplo do repositório: um ducker com sidechain. Ele se descreve, divide cada bloco nos seus eventos e é exportado duas vezes. Nada do código abaixo foi escrito para esta página.

  1. Uma descrição só

    INFO e LAYOUT são constantes. A mesma descrição vira o descritor CLAP, a classe VST 3, cujo id é igual em todas as plataformas, e os arquivos Turtle do plugin LV2.

  2. Eventos no seu frame

    split_at_events renderiza até cada evento, aplica-o e segue em frente, sem copiar nem alocar. Essa chamada é tudo o que o plugin faz para ter automação com precisão de amostra.

  3. Três formatos, três linhas

    As macros são a integração inteira, e editor entrega aos wrappers o editor do plugin. O cargo mangue gera depois um pacote .clap, um .vst3 e um .lv2 no layout de cada sistema.

Rust
impl Plugin for Duck {
    const INFO: PluginInfo = PluginInfo {
        id: "org.manguebyte.duck",
        name: "Mangue Duck",
        vendor: "Manguebyte",
        version: env!("CARGO_PKG_VERSION"),
        description: "A sidechain ducker, as an example of the Manguebyte framework",
        kind: PluginKind::Effect,
    };
    const LAYOUT: AudioLayout = AudioLayout {
        inputs: 2,
        outputs: 2,
        sidechain: 2,
    };
    type Params = DuckParams;
    // ... new, params and prepare are left out here
    fn process(&mut self, buffer: &mut AudioBuffer<f32>, context: &ProcessContext<'_, f32>) {
        self.cursor = 0;
        let sidechain = context.sidechain;
        split_at_events(self, buffer, context.events, Self::handle, |duck, piece| {
            duck.render(piece, sidechain)
        });
        self.publish_reduction();
    }
    // ... reset
    fn editor(&self) -> Option<Box<dyn EditorSpec>> {
        Some(Box::new(self.editor_spec()))
    }
}

manguebyte::clap::export_clap!(Duck);
manguebyte::vst3::export_vst3!(Duck);
manguebyte::lv2::export_lv2!(Duck);
Trecho de plugins/duck/src/lib.rs no commit c5835bf. As linhas que começam com // ... marcam o que ficou de fora.

E um teste dele, sem host

Harness toca áudio, um sidechain e automação no plugin dentro de um teste unitário comum. Este confere que uma mudança de profundidade cai no frame 1.000 do bloco, e não um frame antes.

Rust
#[test]
fn automation_lands_on_its_frame_with_audio_running_through() {
    let mut harness = Harness::<Duck>::new(RATE, 256);
    let input = steady(1.0, 24_000);
    let loud = steady(1.0, 24_000);
    // Let the follower settle, then ask for a different depth on one
    // particular frame in the middle of a block.
    harness.process_with_sidechain(&input, &loud);
    harness.automate(24_000 + 1_000, "depth", 24.0);
    let output = harness.process_with_sidechain(&input, &loud);

    let before = db_to_gain(-12.0) as f32;
    let after = db_to_gain(-24.0) as f32;
    assert!((output.channel(0)[999] - before).abs() < 1e-4);
    assert!((output.channel(0)[1_000] - after).abs() < 1e-4);
}
De plugins/duck/src/lib.rs no commit c5835bf, como está no arquivo.

03

O que já existe

Vinte e três crates que funcionam juntos, cada um utilizável sozinho, um deles uma ferramenta de linha de comando. O manguebyte-core não tem nenhuma dependência.

Todos os recursos, por área

04

Onde o JUCE está à frente

O JUCE tem mais de vinte anos de uso em produção, e muito mais abrangência. Hoje ele tem, e o Manguebyte não:

  • um toolkit de interface gráfica completo, com janelas de editor de plugin em todos os sistemas e formatos (os editores do Manguebyte só têm janelas no Win32, e nenhum foi aberto num host à mão ainda);
  • os formatos de plugin Audio Unit e AAX;
  • hospedagem de plugins AU e LV2, varredura de plugins e os editores dos plugins hospedados (o Manguebyte hospeda plugins CLAP e VST 3, sem os editores);
  • mais blocos de DSP, e mais formatos de arquivo;
  • iOS e Android;
  • documentação, tutoriais e uma comunidade grande.

O roadmap diz o que existe e em que ordem essa distância está sendo fechada.

05

O ecossistema Manguebyte

Mais dois projetos do Recife crescem do mesmo mangue, feitos para rodar sobre o Manguebyte.

  • Este framework

    Manguebyte

    O framework de áudio de código aberto para Rust, sob MIT ou Apache-2.0. O Aratu já toca sobre o seu motor para o navegador; o Parabolic será construído sobre ele.

  • Em desenvolvimento

    Aratu

    Uma linguagem de live coding que se lê como música: compassos, tempos e figuras em primeiro lugar, em português, espanhol e inglês. O editor roda no navegador e toca sobre o Manguebyte.

  • Planejado

    Parabolic

    Uma ferramenta para músicos gravarem os próprios instrumentos e os transformarem em plugins e samplers sem escrever código.

Mais sobre o Aratu

06

Caranguejos com cérebro

O nome vem do Recife, a cidade do Nordeste do Brasil onde este projeto nasceu. O Recife foi construído sobre manguezais.

No começo dos anos 1990, um grupo de músicos da cidade respondeu à crise com um manifesto, Caranguejos com Cérebro: ligar as raízes ricas e lamacentas da cultura da região à rede global de ideias. A imagem que escolheram foi uma antena parabólica fincada na lama do mangue.

A história do nome

Comece com uma dependência

O Manguebyte ainda não está no crates.io. Dependa dele direto do Git:

TOML
[dependencies]
manguebyte = { git = "https://github.com/alessonqueirozdev-hub/manguebyte" }
De README.md no commit c5835bf, como está no arquivo.

O guia de primeiros passos O repositório no GitHub