<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Davi Augusto</title>
    <description>The latest articles on DEV Community by Davi Augusto (@agstrc).</description>
    <link>https://dev.to/agstrc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2696702%2Feefc711d-1323-4b5c-a5a7-6d4cf5c6450d.png</url>
      <title>DEV Community: Davi Augusto</title>
      <link>https://dev.to/agstrc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/agstrc"/>
    <language>en</language>
    <item>
      <title>Comente o porquê, não o quê</title>
      <dc:creator>Davi Augusto</dc:creator>
      <pubDate>Sat, 18 Jan 2025 15:47:37 +0000</pubDate>
      <link>https://dev.to/agstrc/comente-o-porque-nao-o-que-51j</link>
      <guid>https://dev.to/agstrc/comente-o-porque-nao-o-que-51j</guid>
      <description>&lt;p&gt;Comentários podem ajudar você ou seus colegas a entenderem decisões cruciais por trás do &lt;em&gt;design&lt;/em&gt; do código de um projeto. Porém, para que isso aconteça, é importante saber diferenciar quando e como comentar, de forma que os seus comentários não se transformem em informação desatualizada ou simplesmente redundante.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tipos de comentários
&lt;/h2&gt;

&lt;p&gt;Ao ler diferentes comentários em diferentes projetos, é possível notar que alguns "tipos" de comentários podem ser formados, com base no que eles informam e em como são feitos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comentários redundantes
&lt;/h3&gt;

&lt;p&gt;Alguns comentários podem ser classificado como redundantes, ou triviais. Normalmente esses comentários repetem o que já é possível inferir pelos nomes das funções chamadas e nomes de variáveis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// carrega configuração&lt;/span&gt;
&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse tipo, apesar de desnecessário, tende a não causar um impacto negativo de grande magnitude. Isso, pois ele custa tanto ao autor do código, quanto ao leitor, pouco trabalho e esforço cognitivo. Dito esse, ainda se trata de comentários desnecessários e que não agregam valor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comentários explicativos
&lt;/h3&gt;

&lt;p&gt;Esse tipo de comentário tende a variar em questão de complexidade. Em certos casos, ele pode explicar ações que desenvolvedores já acostumados com a linguagem em uso entendem facilmente, mas desenvolvedores mais novatos podem ainda não entender.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Retorna o resultado da promise que resolver primeiro&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;race&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;mainServerPromise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;secondaryServerPromise&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse caso pode trazer algum valor, simplificando a leitura para alguns. Apesar disso, ele ainda polui o projeto de informação que eventualmente pode ser considerada redundante. No exemplo acima, um desenvolvedor que ainda não conheça &lt;code&gt;Promise.race&lt;/code&gt; precisa pesquisar e aprender apenas uma vez para que o comentário não agregue mais valor. Além disso, é preciso definir em quantos casos esse comentário será usado. Se a função &lt;code&gt;Promise.race&lt;/code&gt; for usada muitas vezes em um módulo, onde você coloca esse comentários e quantas vezes?&lt;/p&gt;

&lt;p&gt;Assim, esse comentário também se torna indesejado, apesar de também causar um baixo esforço cognitivo.&lt;/p&gt;

&lt;p&gt;Outra caso em que é possível ver comentários explicativos é como explicação de implementação de algoritmos, ou alguma lógica um pouco mais estruturada. Isso é mais comum em casos em que um &lt;strong&gt;CRUD&lt;/strong&gt; clássico, normalmente relacionado apenas a implementações de regras simples, e integrações, precisa implementar alguma lógica mais estruturada.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// exemplo simplista desse caso.&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;binarySearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="c"&gt;// Calcula o índice do meio&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;// Se target é maior, ignora a metade esquerda&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c"&gt;// Se target é menor, ignora a metade direita&lt;/span&gt;
            &lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// target não encontrado&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse caso tende a agregar mais valor. Além disso, ele serve pra ajudar o leitor a se situar durante a leitura. Por mais que ele seja capaz de identificar a lógica e as etapas envolvidas no algoritmo sem o auxílio de comentários, eles podem ajudar a distinguir visualmente as etapas dele com mais facilidade.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comentários como documentação de API
&lt;/h3&gt;

&lt;p&gt;Um tipo que tm se popularizado mais, com linguagens com Go e Rust, mas também presentes em linguagens mais antigas como JavaScript (JSDoc) e Java (JavaDoc), é o comentário de documentação de API.&lt;/p&gt;

&lt;p&gt;Cada linguagem adota seu padrão de como esses comentários devem ser estruturados, mas eles possuem imenso valor, especialmente quando acoplados com algum gerador automático de documentação. O &lt;a href="https://pkg.go.dev/" rel="noopener noreferrer"&gt;pkg.go.dev&lt;/a&gt; (Go) e &lt;a href="https://crates.io/" rel="noopener noreferrer"&gt;crates.io&lt;/a&gt; (Rust) são exemplos fantásticos disso. Desenvolvedores de bibliotecas podem contar apenas com as funcionalidades da linguagem para ter uma documentação profunda de suas bibliotecas, sem &lt;em&gt;precisar&lt;/em&gt; de desenvolver sites externos (mas alguns ainda optam por isso para terem mais controles sobre o fluxo de sua documentação).&lt;/p&gt;

&lt;p&gt;Uma vantagem ainda maior dessa forma de documentação é ela estar próxima ao código. Atualizar a implementação e a documentação torna-se possível com um único commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Exemplo da biblioteca padrão em Go&lt;/span&gt;

&lt;span class="c"&gt;// Sort sorts a slice of any ordered type in ascending order.&lt;/span&gt;
&lt;span class="c"&gt;// When sorting floating-point numbers, NaNs are ordered before other values.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Sort&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;S&lt;/span&gt; &lt;span class="err"&gt;~&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ordered&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="n"&gt;S&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pdqsortOrdered&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse tipo de comentário está sujeito à redundância, especialmente com funções mais triviais em pacotes. Apesar disso, ainda é extremamente útil pois a ausência de ressalvas no comentário tranquiliza o leitor, enquanto a ausência de comentários pode não informar muito. Além disso, mesmo com informações redundantes, a presença deles padroniza a documentação da API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comentários de design
&lt;/h3&gt;

&lt;p&gt;Dentre os mencionados, esse é o único tipo de comentário que é capaz de adicionar informação que não está contida no próprio código. Essa classe de comentários informa ao leitor a razão de uma implementação, podendo conter contextos técnicos e de negócio, deixando o leitor a par do porquê certa ação foi tomada. Esse tipo de contexto normalmente não se encontra no resto do código e, além de justificar a implementação, ajuda o leitor a entender melhor sobre as diretrizes e objetivos do código.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Código do pacote http, na biblioteca padrão do Go&lt;/span&gt;
&lt;span class="c"&gt;// fonte: https://cs.opensource.google/go/go/+/refs/tags/go1.23.5:src/net/http/client.go;l=717&lt;/span&gt;


&lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Special case for Go 1 compatibility: return both the response&lt;/span&gt;
    &lt;span class="c"&gt;// and an error if the CheckRedirect function failed.&lt;/span&gt;
    &lt;span class="c"&gt;// See https://golang.org/issue/3795&lt;/span&gt;
    &lt;span class="c"&gt;// The resp.Body has already been closed.&lt;/span&gt;
    &lt;span class="n"&gt;ue&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;uerr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ue&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;//...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O exemplo acima descreve que erro é retornado junto à a resposta caso a função &lt;code&gt;CheckRedirect&lt;/code&gt; falhe por motivos de compatibilidade, evitando quebrar códigos antigos que dependam desse comportamento. Além disso, inclui um link de uma issue relacionada, onde o leitor pode buscar ainda mais contexto por trás da escolha.&lt;/p&gt;

&lt;p&gt;Esse tipo de comentário agrega muito valor, deixando o leitor a par das escolhas e até possíveis débitos técnicos. Ele adiciona um contexto valioso que, originalmente, apenas o autor tinha. Dessa forma, leitores futuros conseguem traçar uma forma de histórico, podendo entender problemas aos quais ele deve se atentar ao modificar o código, e tendo um norte para suas edições.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comentários desatualizados
&lt;/h2&gt;

&lt;p&gt;Tendo isso em vista, vale observar que todos esses tipos de comentários são passíveis de se tornarem desatualizados. Para qualquer tipo, isso pode gerar um trecho de código contra-produtivo, que desinforma o leitor. Dito isso, é algo que acontece mais facilmente com comentários que explicam o código, ao invés de comentários que explicam o porquê (comentários de design). Isso se dá pela própria natureza dos comentários. Aqueles que estão 100% atrelados ao código e apenas reforçam ideias que o próprio código já expressa estão fadados à necessidade de sempre mudarem com o código. Já os comentários de design, mesmo quando desatualizados (a implementação que eles justificavam foi alterada), ainda proveem contexto histórico, por mais que desnecessário.&lt;/p&gt;

&lt;p&gt;Dessa forma, observa-se necessário dar atenção aos comentários como parte do processo de commits, e PRs. Sempre que algum trecho de código é alterado, é importante que seja dada atenção aos blocos de comentários relacionados a esse trecho. Isso, claro, é mais fácil quando os comentários são imediatos ao código a que se referem. Dê uma atenção aos comentários durante o seu próximo processo de code review :).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Como regra geral, é sempre interessante comentar o porquê  do seu código, dando ao leitor contexto que você tinha durante a autoria do código. Evitar redundância em comentários deixa o seu código limpo e menos confuso, além de manter um fluxo mais legível.&lt;/p&gt;

&lt;p&gt;Comentários, assim como programação em si, não seguem regras fixas. Há casos diferentes e sempre contexto a ser considerado, mas algumas diretrizes podem ser adotas para guiar a inserção de comentários.&lt;/p&gt;

&lt;p&gt;E não caia no papo de que código bom não precisa de comentários.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Desvendando Subprocessos: Criando um Bot de Música com Go</title>
      <dc:creator>Davi Augusto</dc:creator>
      <pubDate>Mon, 13 Jan 2025 17:19:37 +0000</pubDate>
      <link>https://dev.to/agstrc/desvendando-subprocessos-criando-um-bot-de-musica-com-go-f1g</link>
      <guid>https://dev.to/agstrc/desvendando-subprocessos-criando-um-bot-de-musica-com-go-f1g</guid>
      <description>&lt;p&gt;Ao tentar criar um bot de música para o Discord em Go, aprendi um pouco mais sobre alguns conceitos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Subprocessos&lt;/li&gt;
&lt;li&gt;Áudio Opus&lt;/li&gt;
&lt;li&gt;Formato Ogg&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vou ensinar o que aprendi nesse post. Ressalto que esse post é para fins educacionais e há formas mais simples de atingir o objetivo do bot de música. O intuito aqui é ensinar um pouco do que aprendi durante minha primeira tentativa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visão geral
&lt;/h2&gt;

&lt;p&gt;Em alto nível, o bot vai funcionar com uma pipeline. Usaremos o &lt;a href="https://github.com/yt-dlp/yt-dlp" rel="noopener noreferrer"&gt;yt-dlp&lt;/a&gt; para extrair o áudio do YouTube (o programa aceita várias fontes, mas aqui iremos focar no YouTube) e o &lt;a href="https://www.ffmpeg.org/" rel="noopener noreferrer"&gt;ffmpeg&lt;/a&gt; para garantir um encoding padrão e consistente do áudio. Por fim, vamos precisar extrair frames &lt;a href="https://opus-codec.org/" rel="noopener noreferrer"&gt;Opus&lt;/a&gt; do áudio providenciado pelo &lt;em&gt;ffmpeg&lt;/em&gt; para enviar ao Discord, mas isso será feito diretamente no código Go. Dado isso, certifique-se que ambos o &lt;em&gt;yt-dlp&lt;/em&gt; e o &lt;em&gt;ffmpeg&lt;/em&gt; estejam instalados para o funcionamento correto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Baixando o áudio
&lt;/h2&gt;

&lt;p&gt;O &lt;em&gt;yt-dlp&lt;/em&gt;, por padrão, baixa vídeos por completo do YouTube. No nosso caso, queremos apenas o áudio. Isso pode ser atingido com a flag &lt;code&gt;--extract-audio&lt;/code&gt; (um alias mais curto dessa flag é &lt;code&gt;-x&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yt-dlp &lt;span class="nt"&gt;--extract-audio&lt;/span&gt; &lt;span class="s2"&gt;"https://www.youtube.com/watch?v=dQw4w9WgXcQ"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como podemos executar isso em código Go? É simples, usando o pacote &lt;a href="https://pkg.go.dev/os/exec" rel="noopener noreferrer"&gt;exec&lt;/a&gt; da biblioteca padrão, conseguimos facilmente executar o mesmo comando. O pacote é responsável por configurar e iniciar subprocessos, que executam programas externos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;dlp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"yt-dlp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"--extract-audio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"https://www.youtube.com/watch?v=dQw4w9WgXcQ"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;dlp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com esse trecho, iniciamos um subprocesso que executa o &lt;em&gt;yt-dlp&lt;/em&gt;. O uso do método &lt;a href="https://pkg.go.dev/os/exec#Cmd.Run" rel="noopener noreferrer"&gt;cmd.Run&lt;/a&gt; inicia o subprocesso, e espera sua finalização na goroutine atual.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;É possível que o resultado do &lt;em&gt;yt-dlp&lt;/em&gt; sendo chamado manualmente gere um arquivo &lt;code&gt;.opus&lt;/code&gt; válido. Isso acontece porque o &lt;em&gt;yt-dlp&lt;/em&gt; já chama o &lt;em&gt;ffmpeg&lt;/em&gt; nesse caso. Isso não será útil para nós porque ao usar &lt;em&gt;pipes&lt;/em&gt; (visto posteriormente), o áudio não é gerado nesse formato&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Codificando o áudio
&lt;/h2&gt;

&lt;p&gt;A &lt;a href="https://discord.com/developers/docs/topics/voice-connections#transport-encryption-and-sending-voice" rel="noopener noreferrer"&gt;documentação da API do Discord&lt;/a&gt; menciona que o áudio deve ser enviado no formato Opus a um &lt;em&gt;sample rate&lt;/em&gt; de 48kHz e com dois canais (stereo). A biblioteca que iremos usar mais a frente, &lt;a href="https://github.com/bwmarrin/discordgo" rel="noopener noreferrer"&gt;discordgo&lt;/a&gt;, lida com todos os outros detalhes técnicos do envio, como criptografia &lt;em&gt;end-to-end&lt;/em&gt; e gerenciamento do estado. Dito isso, ainda cabe a nós o envio apropriado dos bytes de áudio.&lt;/p&gt;

&lt;p&gt;Tendo isso em mente, procurei a ferramenta mais famosa para processamento multimídia, o &lt;em&gt;ffmpeg&lt;/em&gt;. Podemos gerar um arquivo opus da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; audio.mp3 &lt;span class="nt"&gt;-f&lt;/span&gt; opus &lt;span class="nt"&gt;-frame_duration&lt;/span&gt; 20 &lt;span class="nt"&gt;-ar&lt;/span&gt; 48000 &lt;span class="nt"&gt;-ac&lt;/span&gt; 2 audio.opus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-i audio.mp3&lt;/code&gt;: arquivo de entrada para a codificação&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-f opus&lt;/code&gt;: seleciona o formato Opus para saída&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-frame_duration&lt;/code&gt;: define a duração de frames Opus a 20 milissegundos&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-ar 48000&lt;/code&gt;: define o sample rate como 48kHz&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ac 2&lt;/code&gt;: define a saída em modo stereo&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;audio.opus&lt;/code&gt;: arquivo de saída&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;De forma análoga ao &lt;em&gt;yt-dlp&lt;/em&gt;, podemos executar o comando em Go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ffmpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"audio.mp3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"opus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-frame_duration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-ar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"48000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-ac"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"audio.opus"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ffmpeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora, podemos executar os dois comandos e obter um áudio apropriado para o Discord. Porém, ainda temos dois problemas. O primeiro é que tudo que fizemos foi executar os dois comandos a parte. Dessa forma, teriamos um trabalho manual de baixar a música, codifica-la e salva-lá em um arquivo que apenas então seria enviado ao Discord. O que buscamos aqui é criar uma &lt;em&gt;stream&lt;/em&gt; de áudio, sem precisar salvar arquivos em cada etapa.&lt;/p&gt;

&lt;p&gt;O segundo problema é que o Discord requer que enviemos &lt;em&gt;Opus frames&lt;/em&gt;, um por um. Um arquivo &lt;code&gt;.opus&lt;/code&gt; é, na verdade, um arquivo com metadados (normalmente usando o formato/container &lt;em&gt;Ogg&lt;/em&gt;), que encapsulam os frames. Os frames são a unidade mais básica de áudio do formato. Por essa razão, não podemos simplesmente enviar os bytes do arquivo final para a API do Discord.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gerando uma stream de dados
&lt;/h2&gt;

&lt;p&gt;Para gerar uma stream de dados, evitando que precisemos salvar o arquivo a cada etapa do processo, vamos usar o de &lt;a href="https://en.wikipedia.org/wiki/Pipeline_(Unix)" rel="noopener noreferrer"&gt;pipes&lt;/a&gt;, normalmente aplicado no &lt;em&gt;shell&lt;/em&gt; de sistemas &lt;em&gt;Unix-like&lt;/em&gt;. No fim dessa etapa, vamos ter uma cadeia de ações que baixa o áudio do YouTube, transmite-o diretamente para o &lt;em&gt;ffmpeg&lt;/em&gt; e então escreve seus bytes na saída padrão. O código será equivalente ao seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yt-dlp &lt;span class="nt"&gt;--extract-audio&lt;/span&gt; &lt;span class="s2"&gt;"https://www.youtube.com/watch?v=dQw4w9WgXcQ"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; - | &lt;span class="se"&gt;\&lt;/span&gt;
ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; - &lt;span class="nt"&gt;-f&lt;/span&gt; opus &lt;span class="nt"&gt;-frame_duration&lt;/span&gt; 20 &lt;span class="nt"&gt;-ar&lt;/span&gt; 48000 &lt;span class="nt"&gt;-ac&lt;/span&gt; 2 -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Atenção&lt;/strong&gt;: não execute o comando diretamente no seu terminal, porque ele vai gerar uma saída ilegível e pode congelar seu terminal.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No comando acima, o uso de &lt;code&gt;-&lt;/code&gt; indica &lt;a href="https://en.wikipedia.org/wiki/Standard_streams" rel="noopener noreferrer"&gt;entradas ou saídas padrão (stdin/stdout)&lt;/a&gt;.&lt;br&gt;
Esse processo permita que o &lt;em&gt;ffmpeg&lt;/em&gt; leia "diratemente" do &lt;em&gt;yt-dlp&lt;/em&gt; e então jogue sua saída para o &lt;em&gt;stdout&lt;/em&gt;. Vamos usar isso no código Go para obter a saída sem necessidade de chamadas adicionais de leitura de arquivo.&lt;/p&gt;

&lt;p&gt;Primeiramente, vamos criar uma &lt;em&gt;pipe&lt;/em&gt; para a saída do &lt;em&gt;yt-dlp&lt;/em&gt;. Isso pode ser feito com o método &lt;a href="https://pkg.go.dev/os/exec#Cmd.StdoutPipe" rel="noopener noreferrer"&gt;StdoutPipe&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;dlp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"yt-dlp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"--extract-audio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"https://www.youtube.com/watch?v=dQw4w9WgXcQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"-o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dlp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StdoutPipe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código estabelece que a saída padrão do &lt;em&gt;yt-dlp&lt;/em&gt; irá conter os bytes do arquivo de áudio extraído. Essa saída está pode ser acessada pela variável &lt;em&gt;pipe&lt;/em&gt;, a qual é um &lt;a href="https://pkg.go.dev/io#ReadCloser" rel="noopener noreferrer"&gt;io.ReadCloser&lt;/a&gt;. Ele implementa a interface padrão de leitura do Go. Não vamos nos preocupar em fechar essa pipe pois isso será tratado depois pelo próprio pacote, na chamada de &lt;a href="https://pkg.go.dev/os/exec#Cmd.Wait" rel="noopener noreferrer"&gt;cmd.Wait&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Como discutido, o &lt;em&gt;ffmpeg&lt;/em&gt; vai receber seu arquivo de entrada a partir da entrada padrão &lt;em&gt;stdin&lt;/em&gt;. Dessa forma, vamos associar a pipe à entrada padrão do &lt;em&gt;ffmpeg&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// ... código anterior&lt;/span&gt;
&lt;span class="n"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"ffmpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"-i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"-f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"opus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"-frame_duration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"-ar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"48000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"-ac"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ffmpeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;
&lt;span class="n"&gt;audioPipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ffmpeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StdoutPipe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nós também fazemos um pipe com a saída do &lt;em&gt;ffmpeg&lt;/em&gt;. Isso nos permite ler a saída diretamente no código. Agora, já podemos usufruir do resultado do &lt;em&gt;ffmpeg&lt;/em&gt;. Para isso, basta iniciar os dois comandos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// exemplo: usando os processos para exibir a hash sha256 do áudio&lt;/span&gt;
&lt;span class="c"&gt;//&lt;/span&gt;
&lt;span class="c"&gt;// ... código anterior&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dlp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ffmpeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;audioPipe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// ignorando tratativa de erro&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sum256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c"&gt;// exibe os bytes puros da hash&lt;/span&gt;

&lt;span class="c"&gt;// ignorando erros&lt;/span&gt;
&lt;span class="c"&gt;// esperamos os processos finalizar para garantir liberação de recursos&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dlp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ffmpeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extraindo os frames Opus
&lt;/h2&gt;

&lt;p&gt;Antes de enviar o áudio para o Discord, agora precisamos extrair os frames de áudio Opus do arquivo. Isso acontece porque estamos gerando arquivos &lt;code&gt;.opus&lt;/code&gt;, com container Ogg. Primeiro, uma breve explicação de codecs e containers:&lt;/p&gt;

&lt;p&gt;Um codec (compressor/descompressor) é responsável pela codificação ou decodificação de dados multimídia. Eles definem com a mídia deve ser representada, buscando objetivos como ocupar menos espaço, ou maior eficiência de transmissão. Um container é um formato que permite várias streams, podendo elas serem áudio, vídeo ou até legendas, em um único arquivo. O container define formatos para representar metadados, e como representar os dados "crus" do codec sendo contido.&lt;/p&gt;

&lt;p&gt;Nesse contexto, um arquivo &lt;code&gt;.opus&lt;/code&gt; normalmente é um arquivo que utiliza o container Ogg para armazenar áudio com o codec Opus. O Ogg é responsável por definir metadados que podem armazenar tags, comentários e também onde a stream Opus se encontra.&lt;/p&gt;

&lt;h3&gt;
  
  
  O formato Ogg
&lt;/h3&gt;

&lt;p&gt;O Ogg organiza dados em &lt;strong&gt;pages&lt;/strong&gt; (páginas). Cada page contém &lt;strong&gt;packets&lt;/strong&gt; (pacotes) que são unidades menores que contém os dados brutos.&lt;/p&gt;

&lt;p&gt;No caso de áudio Opus, as duas primeiras pages contém metadados, os quais identificam o arquivo como uma stream Opus, e dados de tags e comentários. A terceira page contém, os packets de áudio puro, os frames Opus que queremos. Cada packet é um frame.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extraindo os frames Opus em Go
&lt;/h2&gt;

&lt;p&gt;Tendo essa breve introdução aos formatos em mente, vamos extrair os frames em Go. Para isso, vamos usar um decoder do formato Ogg. O pacote é &lt;a href="https://pkg.go.dev/github.com/jonas747/ogg" rel="noopener noreferrer"&gt;github.com/jonas747/ogg&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A seguir, um exemplo em que exibimos o tamanho de cada frame:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// assumindo código preexistente com audioPipe saindo do ffmpeg&lt;/span&gt;
&lt;span class="n"&gt;pageDecoder&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ogg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;audioPipe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// pulamos as duas primeiras páginas&lt;/span&gt;
&lt;span class="n"&gt;pageDecoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;pageDecoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;packetDecoder&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ogg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPacketDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageDecoder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;packetDecoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tamanho do frame:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso é possível pois fizemos uma pipe da saída do &lt;em&gt;ffmpeg&lt;/em&gt;. Ela implementa &lt;strong&gt;io.Reader&lt;/strong&gt;, o qual deve ser usado para o decoder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enviando o áudio para o Discord
&lt;/h2&gt;

&lt;p&gt;Agora que já conseguimos extrair o áudio, vamos exemplificar como fazer um bot musical do Discord. Ressalto que, como o foco dessa postagem não é sobre o bot, e sim sobre as tecnologias e o aprendizado. Portanto, a lógica do bot será simples e não interativa, focando apenas em tocar a música.&lt;/p&gt;

&lt;p&gt;Com tudo que construimos, podemos gerar o código final. Ele consiste em usar o discordgo para inicializar um bot, conectar a um canal de voz e, então, tocar uma música utilizando os subprocessos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log/slog"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"os/exec"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/bwmarrin/discordgo"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/jonas747/ogg"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// atualize com seus valores&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"seuToken"&lt;/span&gt;
    &lt;span class="n"&gt;guildID&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"seuGuildID"&lt;/span&gt;
    &lt;span class="n"&gt;channelID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"seuChannelID"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mainWithError&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;mainWithError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;discordgo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bot "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to create bot session: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;slog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Opening bot session"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to open bot session: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// entrar no canal de voz&lt;/span&gt;
    &lt;span class="n"&gt;voice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChannelVoiceJoin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;guildID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;channelID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to join voice channel: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// configurando fonte de áudio&lt;/span&gt;
    &lt;span class="n"&gt;dlp&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"yt-dlp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"--extract-audio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"https://youtu.be/jDHmg8Mb9wo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"-o"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dlpPipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dlp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StdoutPipe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to get yt-dlp pipe: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;dlp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="c"&gt;// obter saída informativa do yt-dlp&lt;/span&gt;

    &lt;span class="n"&gt;ffmpeg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"ffmpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"-i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"-f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"opus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"-frame_duration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"-ar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"48000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"-ac"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ffmpegPipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ffmpeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StdoutPipe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"failed to get ffmpeg pipe: %w"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;ffmpeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dlpPipe&lt;/span&gt; &lt;span class="c"&gt;// associa saída do yt-dlp à entrada do ffmpeg&lt;/span&gt;
    &lt;span class="n"&gt;ffmpeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dlp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ffmpeg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;pageDecoder&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ogg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ffmpegPipe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pageDecoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;pageDecoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// sinaliza ao Discord que estamos enviando áudio&lt;/span&gt;
    &lt;span class="n"&gt;voice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Speaking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;packetDecoder&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ogg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPacketDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageDecoder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;packetDecoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c"&gt;// esperamos que o único erro seja io.EOF&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c"&gt;// enviando o áudio para o Discord&lt;/span&gt;
        &lt;span class="n"&gt;voice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpusSend&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;packet&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finalizando
&lt;/h2&gt;

&lt;p&gt;Assim, podemos criar um bot simples de música. Para projetos reais, eu recomendaria outras abordagens, evitando o &lt;em&gt;overhead&lt;/em&gt; e complexidade de subprocessos. Um projeto em potencial seria &lt;a href="https://github.com/kkdai/youtube" rel="noopener noreferrer"&gt;esse&lt;/a&gt;. Ainda seria necessário o uso do &lt;em&gt;ffmpeg&lt;/em&gt;, para a geração de Opus, mas a coordenação de um único subprocesso é consideravelmente mais fácil.&lt;/p&gt;

&lt;p&gt;Comente se aprendeu algo novo, se sobraram dúvidas, ou se encontrou algum erro. Eu não sou da área multimídia, e tudo que aprendi foi com esse projeto.&lt;/p&gt;

</description>
      <category>go</category>
      <category>discord</category>
      <category>programming</category>
      <category>ffmpeg</category>
    </item>
  </channel>
</rss>
