Isenção de responsabilidade: não considero algoritmos e APIs para trabalhar com reconhecimento de som e fala. Este artigo é sobre problemas de áudio e como resolvê-los com o Go.

phono
é uma estrutura de aplicação para trabalhar com som. Sua principal função é criar um transportador a partir de várias tecnologias que processam o som para você da maneira que você precisa.
O que o transportador tem a ver com isso, além de diferentes tecnologias, e por que outra estrutura? Agora vamos descobrir.
De onde vem o som?
Em 2018, o som se tornou a maneira padrão como os humanos interagem com a tecnologia. A maioria dos gigantes de TI criou seu próprio assistente de voz ou está fazendo isso agora. O controle de voz já está na maioria dos sistemas operacionais e as mensagens de voz são um recurso típico de qualquer mensageiro. No mundo, cerca de mil startups estão trabalhando no processamento de linguagem natural e cerca de duzentas no reconhecimento de fala.
Com música, uma história semelhante. É reproduzido a partir de qualquer dispositivo e a gravação de som está disponível para todos que possuem um computador. O software musical é desenvolvido por centenas de empresas e milhares de entusiastas em todo o mundo.
Se você tivesse que trabalhar com som, as seguintes condições pareceriam familiares:
- O áudio deve ser obtido de um arquivo, dispositivo, rede etc.
- O áudio deve ser processado : adicione efeitos, transcodifique, analise etc.
- O áudio deve ser transferido para um arquivo, dispositivo, rede etc.
- Os dados são transmitidos em pequenos buffers.
Acontece um pipeline regular - há um fluxo de dados que passa por vários estágios de processamento.
Soluções
Para maior clareza, vamos dar uma tarefa da vida real. Por exemplo, você precisa converter uma voz em texto:
- Gravamos áudio do dispositivo
- Remover ruídos
- Igualar
- Passe o sinal para a API de reconhecimento de fala
Como qualquer outra tarefa, esta possui várias soluções.
Testa
Apenas hardcore ciclistas programadores. Gravamos som diretamente através do driver da placa de som, escrevemos redução inteligente de ruído e equalizador de várias bandas. Isso é muito interessante, mas você pode esquecer sua tarefa original por vários meses.
Longo e muito difícil.
Normal
Uma alternativa é usar APIs existentes. Você pode gravar áudio usando ASIO, CoreAudio, PortAudio, ALSA e outros. Existem também vários tipos de plugins para processamento: AAX, VST2, VST3, AU.
Uma ampla escolha não significa que você pode usar tudo de uma vez. Normalmente, as seguintes restrições se aplicam:
- Sistema operacional Nem todas as APIs estão disponíveis em todos os sistemas operacionais. Por exemplo, AU é tecnologia nativa do OS X e só está disponível lá.
- Linguagem de programação A maioria das bibliotecas de áudio é escrita em C ou C ++. Em 1996, Steinberg lançou a primeira versão do VST SDK, ainda o padrão de plug-in mais popular. Após 20 anos, não é mais necessário escrever em C / C ++: para o VST, existem wrappers em Java, Python, C #, Rust e quem sabe o que mais. Embora o idioma continue sendo uma limitação, agora o som é processado em JavaScript.
- Funcional. Se a tarefa for simples e direta, não será necessário escrever um novo aplicativo. O mesmo FFmpeg pode fazer muito.
Nesta situação, a complexidade depende da sua escolha. Na pior das hipóteses, você precisa lidar com várias bibliotecas. E se você não tiver sorte, com abstrações complexas e interfaces completamente diferentes.
Qual é o resultado?
Você precisa escolher entre muito complexo e complexo :
- ou lide com várias APIs de baixo nível para escrever suas bicicletas
- ou lide com várias APIs e tente fazer amizade com elas
Independentemente do método selecionado, a tarefa sempre se resume ao transportador. As tecnologias utilizadas podem variar, mas a essência é a mesma. O problema é que, novamente, em vez de resolver um problema real, você deve escrever a bicicleta correia transportadora.
Mas há uma saída.
phono

phono
criado para solucionar problemas comuns - para " receber, processar e transmitir " sons. Para fazer isso, ele usa o pipeline como a abstração mais natural. Há um artigo no blog oficial do Go que descreve o padrão de pipeline. A idéia principal do pipeline é que existem vários estágios do processamento de dados que funcionam independentemente um do outro e trocam dados por canais. O que você precisa
Porque ir
Primeiro, a maioria dos programas e bibliotecas de áudio são escritos em C, e o Go é frequentemente chamado de sucessor. Além disso, existem cgo e muitos fichários para bibliotecas de áudio existentes. Você pode pegar e usar.
Em segundo lugar, na minha opinião pessoal, Go é uma boa linguagem. Não irei fundo, mas observarei seu multithreading . Canais e gorutinas simplificam bastante a implementação do transportador.
Abstração
O coração do phono
é o tipo de pipe.Pipe
. É ele quem implementa o pipeline. Como na amostra do blog , existem três tipos de estágios:
pipe.Pump
(bomba inglesa) - recebendo som, apenas canais de saídapipe.Processor
(processador inglês) - processamento de som, canais de entrada e saídapipe.Sink
(coletor inglês) - transmissão de som, apenas canais de entrada
Dentro do pipe.Pipe
dados são passados em buffers. Regras pelas quais construir um pipeline:

- Um
pipe.Pump
pipe.Processor
colocado sequencialmente um após o outro- Um ou mais
pipe.Sink
colocada em paralelo - Todos os componentes
pipe.Pipe
devem ter o mesmo:
- Tamanho do buffer (mensagens)
- Taxa de amostragem
- Número de canais
A configuração mínima é Pump e um dissipador, o restante é opcional.
Vejamos alguns exemplos.
Simples
Tarefa: reproduz o arquivo wav.
Vamos trazê- lo para o formulário " receber, processar, transferir ":
- Obter áudio de um arquivo wav
- Transferir áudio para um dispositivo portaudio

O áudio é lido e reproduzido imediatamente.
Código package example import ( "github.com/dudk/phono" "github.com/dudk/phono/pipe" "github.com/dudk/phono/portaudio" "github.com/dudk/phono/wav" )
Primeiro, criamos os elementos do pipeline futuro: wav.Pump
e portaudio.Sink
e os passamos para o pipe.New
construtor. A função de p.Do(pipe.actionFn) error
inicia o pipeline e aguarda o término.
Mais difícil
Tarefa: divida o arquivo wav em amostras, componha uma faixa, salve o resultado e toque-o simultaneamente.
Uma faixa é uma sequência de amostras e uma amostra é um pequeno segmento de áudio. Para cortar o áudio, você deve primeiro carregá-lo na memória. Para fazer isso, use o tipo asset.Asset
do pacote phono/asset
. Dividimos a tarefa em etapas padrão:
- Obter áudio de um arquivo wav
- Transferir áudio para a memória
Agora fazemos amostras com nossas mãos, adicionamos à faixa e concluímos a tarefa:
- Obter áudio de uma faixa
- Transfira o áudio para
- arquivo wav
- dispositivo portaudio

Novamente, sem uma etapa de processamento, mas dois pipelines!
Código package example import ( "github.com/dudk/phono" "github.com/dudk/phono/asset" "github.com/dudk/phono/pipe" "github.com/dudk/phono/portaudio" "github.com/dudk/phono/track" "github.com/dudk/phono/wav" )
Comparado ao exemplo anterior, existem dois pipe.Pipe
. O primeiro transfere dados para a memória para que você possa cortar as amostras. O segundo tem dois destinatários no final: wav.Sink
e portaudio.Sink
. Com esse esquema, o som é gravado simultaneamente em um arquivo wav e reproduzido.
Mais difícil
Tarefa: leia dois arquivos wav, misture, processe o plug-in vst2 e salve em um novo arquivo wav.
Existe um simples mixer.Mixer
no mixer.Mixer
phono/mixer
. Pode transmitir sinais de várias fontes e misturar um. Para fazer isso, ele implementa simultaneamente pipe.Pump
e pipe.Sink
.
Novamente, a tarefa consiste em duas subtarefas. O primeiro é assim:
- Obtenha o arquivo wav de áudio
- Transferir áudio para o mixer
Segundo:
- Obtenha áudio do mixer.
- Processando plugin de áudio
- Transferir áudio para arquivo wav

Código package example import ( "github.com/dudk/phono" "github.com/dudk/phono/mixer" "github.com/dudk/phono/pipe" "github.com/dudk/phono/vst2" "github.com/dudk/phono/wav" vst2sdk "github.com/dudk/vst2" )
Já existem três pipe.Pipe
, todos interconectados através de um mixer. Para iniciar, use a função p.Begin(pipe.actionFn) (pipe.State, error)
. Diferentemente do p.Do(pipe.actionFn) error
, ele não bloqueia a chamada, mas simplesmente retorna um estado que pode ser esperado com o p.Wait(pipe.State) error
.
O que vem a seguir?
Quero que o phono
torne a estrutura de aplicativo mais conveniente. Se você tem algum problema com o som, não precisa entender APIs complexas e gastar tempo estudando padrões. Tudo o que é necessário é construir um transportador a partir de elementos adequados e executá-lo.
Durante meio ano, os seguintes pacotes foram filmados:
phono/wav
- lê / grava arquivos wavphono/vst2
- ligações incompletas do VST2 SDK, enquanto você só pode abrir o plug-in e chamar seus métodos, mas nem todas as estruturasphono/mixer
- mixer, adiciona sinais N, sem equilíbrio e volumephono/asset
- amostragem de bufferphono/track
- leitura seqüencial de amostras (estratificação quebrada)phono/portaudio
- reprodução de sinal durante experimentos
Além desta lista, há uma lista crescente de novas idéias e idéias, incluindo:
- Countdown
- Variável em tempo real
- Bomba / pia HTTP
- Automação de parâmetros
- Processador de reamostragem
- Balanço e volume do misturador
- Bomba em tempo real
- Bomba sincronizada para várias faixas
- Vst2 completo
Nos seguintes artigos, analisarei:
- ciclo de vida do
pipe.Pipe
- devido à estrutura complexa, seu estado é controlado pelo átomo final - como escrever seus estágios de pipeline
Este é o meu primeiro projeto de código aberto, por isso serei grato por qualquer ajuda e recomendações. De nada.
Referências