Como criar um aplicativo Android usando Kotlin e C++, capaz de responder a comandos MIDI e reproduzir sons sintetizados a partir de arquivos de SoundFont.

Introdução

O Musical Instrument Digital Interface (MIDI) é um protocolo amplamente utilizado no âmbito musical para comunicação entre instrumentos eletrônicos, computadores e outros dispositivos compatíveis. Por meio de mensagens digitais, o MIDI permite o envio de comandos como notas musicais, alterações de intensidade (velocity) e controle de parâmetros diversos, proporcionando uma integração robusta entre equipamentos musicais. No contexto do MIDI, destaca-se o papel dos controladores e dos sintetizadores. Um controlador MIDI é um dispositivo que gera comandos MIDI, mas não possui capacidade de produção sonora própria. Por outro lado, os sintetizadores são responsáveis por interpretar essas mensagens MIDI e gerar os sons correspondentes, seja por hardware dedicado ou via software.

A criação de sintetizadores via software revolucionou o meio musical ao permitir maior acessibilidade e flexibilidade. Entre as soluções populares para síntese via software, o FluidSynth se destaca como uma biblioteca open-source capaz de interpretar arquivos de soundfont, que são coleções de amostras de áudio organizadas de forma a reproduzir sons de instrumentos musicais. 

Implementar um aplicativo para Android que integre essas tecnologias de áudio pode ser bastante prático para os usuários finais, pois smartphones e tablets são facilmente acessíveis, porém exige aos desenvolvedores o entendimento de sua arquitetura. Os aplicativos Android são estruturados principalmente na Java Virtual Machine (JVM), permitindo o uso de linguagens como Java e Kotlin. Enquanto Java é uma linguagem madura e amplamente utilizada, Kotlin tem ganhado destaque por sua sintaxe concisa, segurança contra nulidade (null safety) e integração completa ao ecossistema Android. Porém, para aplicações que demandam alto desempenho, como é o caso do processamento de áudio em tempo real, é necessário recorrer a componentes nativos implementados em C ou C++. Por meio da Java Native Interface (JNI), é possível integrar essas linguagens ao ambiente Android, combinando a eficiência do código nativo com a flexibilidade do desenvolvimento na JVM.

Este artigo apresenta, além da conceituação necessária para o entendimento das tecnologias envolvidas, um guia prático para a implementação de um aplicativo sintetizador MIDI para Android. O projeto proposto integra Kotlin, C e C++ para criar uma solução funcional. O aplicativo é capaz de receber comandos MIDI de um controlador conectado via cabo USB-OTG, e de reproduzir sons baseados em soundfonts utilizando a biblioteca FluidSynth. Por meio desta implementação, serão abordados conceitos teóricos, práticas de desenvolvimento e desafios técnicos inerentes a este tipo de aplicação.

O projeto do aplicativo exemplo está no repositório do GitHub, disponível através da seguinte URL:

MIDI (Musical Instrument Digital Interface) 

O MIDI (Musical Instrument Digital Interface) é um protocolo padrão de comunicação musical digital criado em 1983 para interligar instrumentos eletrônicos, computadores e outros dispositivos. Diferentemente de arquivos de áudio, o MIDI não transporta som em si, mas sim instruções digitais que definem como o som deve ser reproduzido. O protocolo MIDI é essencial no meio musical, pois permite que dispositivos diversos interajam de maneira padronizada. Por exemplo, um tecladista pode usar um controlador MIDI para acionar sons armazenados em um sintetizador, sem que ambos precisem ser do mesmo fabricante.

O funcionamento do MIDI baseia-se em mensagens digitais enviadas de um dispositivo transmissor (controlador) para um receptor (sintetizador). Essas mensagens podem conter informações como:

O controlador MIDI é um dispositivo que envia comandos MIDI, mas não produz som diretamente. Exemplos incluem teclados controladores, pads e superfícies de controle. O sintetizador MIDI é um dispositivo que interpreta mensagens MIDI e gera os sons correspondentes. Podem ser instrumentos físicos (via hardware) ou programas de computador (via software).

Figura 1 – Controlador e Sintetizador MIDI
Fonte: https://nektartech.com/article/blog/all-you-need-to-know-about-midi/ 

As mensagens MIDI são pacotes de dados que contêm informações sobre os eventos musicais. Elas se dividem em dois tipos principais:

Código NuméricoTipo de MensagemDescrição
0x90Note OnIndica o início de uma nota (código da nota e velocity).
0x80Note OffIndica o fim de uma nota.
0xB0Control Change (CC)Altera parâmetros como volume, pan, ou efeitos.
0xC0Program ChangeTroca o timbre ou instrumento ativo.
0xE0Pitch BendAltera a altura da nota de forma contínua.
Tabela 1 – Principais mensagens MIDI de canal
Fonte: https://midi.org/summary-of-midi-1-0-messages 

Código NuméricoTipo de MensagemDescrição
0xF0System Exclusive (Sysex)Transmissão de dados específicos do fabricante.
0xF8Timing ClockSincroniza dispositivos MIDI.
0xFA/0xFC/0xFBStart/Stop/ContinueControla início, pausa e retomada de sequências.
Tabela 2 – Principais mensagens MIDI do sistema
Fonte: https://midi.org/summary-of-midi-1-0-messages

A maioria dos dispositivos encontrados atualmente são compatíveis com o protocolo MIDI 1.0, como descrito acima. No entanto, a MIDI Manufacturers Association (MMA) introduziu oficialmente em 2020, uma nova versão do protocolo original, chamada de MIDI 2.0. Ela mantém a compatibilidade com o MIDI 1.0, mas traz diversas melhorias e novos recursos que visam expandir as possibilidades criativas e técnicas dos músicos e desenvolvedores. Entre as novidades mais esperadas estão:

O MIDI 2.0 representa um passo significativo na evolução da tecnologia musical, fornecendo aos músicos e produtores ferramentas mais poderosas para criar e controlar música digitalmente. Embora sua adoção ainda esteja atualmente em andamento, já existem alguns dispositivos e softwares compatíveis, prometendo revolucionar o fluxo de trabalho musical.

Conexão MIDI

Atualmente existem dois tipos de conexão possível com equipamentos compatíveis com o protocolo MIDI. A primeira, mais clássica, se faz por meio de conectores DIN-5, conhecida como interface MIDI. Esse tipo de conexão fornece uma comunicação serial assíncrona com isolação elétrica (via opto-acoplamento) entre os equipamentos.

Figura 2 – Conectores da interface MIDI (DIN-5)
Fonte: https://nektartech.com/article/blog/all-you-need-to-know-about-midi/ 

Os equipamentos mais modernos, no entanto, utilizam conectores Universal Serial Bus (USB) tipo B (USB-B), e implementam o protocolo da classe USB-MIDI. Esse tipo de conexão permite uma melhor compatibilidade com computadores. Para interligar o equipamento ao computador, basta usar um cabo USB-A para USB-B convencional.

Para interligar equipamentos mais clássicos, que possuem conectores MIDI padrão DIN-5 a computadores ou outros dispositivos compatíveis com USB, existem no mercado cabos adaptadores conhecidos como “cabo MIDI”, de diferentes formatos, qualidades e preços. Esses adaptadores possuem em uma ponta um conector USB-A, compatível com computadores, e na outra ponta, os conectores DIN-5 no mesmo padrão das portas MIDI dos instrumentos musicais.

Se o objetivo for interligar um equipamento MIDI com conector USB a um smartphone ou tablet que possui um conector USB-C (como é o caso dos dispositivos com Android), deve ser utilizado um cabo ou adaptador chamado de USB-OTG (On-The-Go).

Um adaptador OTG (também chamado de cabo OTG ou conector OTG) permite conectar um dispositivo USB de tamanho normal, ou um cabo USB-A ao smartphone ou tablet, por meio da porta USB-C. Ele permite a transferência de dados, bem como a conectividade com diversos periféricos, transformando o smartphone ou tablet em um host, ou seja, fazendo com que ele se comporte como se fosse um computador.

Sintetizadores de Software e Soundfonts

Os sintetizadores de software são ferramentas fundamentais na produção musical moderna. Diferentemente dos sintetizadores baseados em hardware, que são dispositivos físicos dedicados à geração de som, os sintetizadores de software são programas que simulam ou criam sons de maneira digital. Eles oferecem uma vasta gama de possibilidades criativas, desde emulações de instrumentos tradicionais até a geração de sons completamente inovadores. Dentre suas principais vantagens, destacam-se:

Esses sintetizadores geralmente utilizam soundfonts, que são arquivos contendo amostras de áudio e informações sobre como essas amostras devem ser reproduzidas para criar sons realistas ou abstratos. Os formatos mais comuns incluem SF2 e SF3, cada um com suas características específicas.

Existem várias opções populares de sintetizadores de software que podem ser utilizadas conforme as necessidades específicas de um projeto musical ou de desenvolvimento. Dentre os sintetizadores de software mais conhecidos:

Cada um desses sintetizadores possui suas próprias vantagens e áreas de especialização, tornando-os adequados para diferentes tipos de usuários e aplicações.

FluidSynth

O FluidSynth é uma biblioteca de software amplamente utilizada para síntese de áudio em tempo real. Desenvolvido em linguagem C, é um projeto open-source licenciado sob a LGPL (GNU Lesser General Public License), versão 2.1. Esse licenciamento permite que a biblioteca seja embarcada em aplicações comerciais e não-comerciais. No entanto, caso alterações sejam feitas na própria biblioteca FluidSynth dentro de uma aplicação, o código-fonte deve ser disponibilizado de forma aberta, de acordo com os termos da LGPL.

O FluidSynth foi projetado para reproduzir sons a partir de soundfonts nos formatos SF2 e SF3, oferecendo alta qualidade de áudio e baixa latência. Pode ser usado como uma aplicação independente ou integrado em outros programas como uma biblioteca. Sua flexibilidade e desempenho o tornam uma escolha popular entre desenvolvedores de áudio e músicos. Suas características principais incluem:

A seguir é demonstrado um exemplo simples em linguagem C de como usar a API da biblioteca FluidSynth para reproduzir uma nota musical:

#include <fluidsynth.h>

int main() {
    // FluidSynth initialization
    fluid_settings_t* settings = new_fluid_settings();
    fluid_synth_t* synth = new_fluid_synth(settings);
    // Load the soundfont
    if (fluid_synth_sfload(synth, "soundfont.sf2", 1) == FLUID_FAILED) {
        printf("Error loading soundfont.\n");
        return 1;
    }
    // Audio driver initialization
    fluid_audio_driver_t* adriver = new_fluid_audio_driver(settings, synth);
    // Plays a note (channel: 0, note: C4, velocity: 100)
    fluid_synth_noteon(synth, 0, 60, 100);
    // Wait for 1 sec
    sleep(1);
    // Stops the note
    fluid_synth_noteoff(synth, 0, 60);
    // Free resources
    delete_fluid_audio_driver(adriver);
    delete_fluid_synth(synth);
    delete_fluid_settings(settings);
    // Exit
    return 0;
}

Esse exemplo ilustra os seguintes passos essenciais:

  1. Criação de configurações e inicialização do sintetizador.
  2. Carregamento de um arquivo de soundfont.
  3. Execução de uma nota musical e encerramento da nota.
  4. Liberação dos recursos.

Com sua versatilidade e amplo suporte, o FluidSynth é uma excelente biblioteca para desenvolvedores da área musical. Sua possibilidade de integração em aplicações como sintetizadores MIDI permite criar experiências musicais ricas e responsivas em múltiplas plataformas.

Soundfonts

Os arquivos soundfont desempenham um papel crucial na síntese de áudio digital, servindo como uma coleção de amostras de som e informações sobre como essas amostras devem ser reproduzidas. Introduzidos originalmente pela Creative Labs na década de 1990, os soundfonts foram concebidos para uso em placas de som, como a Sound Blaster AWE32, permitindo que músicos e desenvolvedores incorporassem sons realistas em suas composições e aplicações.

Os soundfonts evoluíram ao longo do tempo, resultando em diferentes formatos, cada um com suas vantagens e desvantagens, dentre as quais podem-se destacar:

SF2 (SoundFont 2.0):

SF3 (SoundFont 3.0):

SFZ:

Um arquivo soundfont pode conter diversos instrumentos e bancos de sons (banks). Os bancos permitem organizar diferentes timbres e instrumentos dentro de um único arquivo, facilitando a seleção de sons para sintetizadores ou aplicações musicais.

Cada instrumento em um soundfont é criado a partir de uma ou mais amostras de som (áudio gravado) combinadas com informações de configuração, como:

Os soundfonts podem ser criados ou editados utilizando ferramentas específicas, como:

Esses programas permitem que usuários gravem suas próprias amostras ou ajustem os parâmetros dos instrumentos, criando arquivos personalizados para uso em sintetizadores ou aplicações musicais.

Além disso, há alguns repositórios públicos disponíveis na internet que disponibilizam arquivos de soundfont para download, alguns com licenciamento que permite o livre uso em aplicações comerciais ou não-comerciais. Um exemplo desse tipo de repositório é o Polyphone SoundFonts.

Sistema Operacional Android e seus Aplicativos

O sistema operacional Android, desenvolvido pelo Google, é amplamente utilizado em dispositivos móveis como smartphones e tablets. Sua arquitetura é projetada para oferecer flexibilidade, desempenho e segurança, combinando diferentes camadas que interagem de forma eficiente. Essas camadas incluem o kernel, o espaço de execução nativa e a máquina virtual Java (JVM). A arquitetura em camadas do Android foi projetada para reforçar a segurança do sistema. Cada aplicativo roda em um ambiente isolado, conhecido como sandbox, o que impede que aplicativos maliciosos interfiram em outros processos ou acessem dados sem permissão. Essas camadas trabalham juntas para proteger tanto o dispositivo quanto os dados do usuário.

Figura 7 – Camadas do Sistema Android
Fonte: https://developer.android.com/guide/platform 

Kernel

Atua como uma barreira entre o hardware e o software, garantindo que apenas códigos autorizados interajam com os recursos do sistema. O Android utiliza o kernel do Linux como base de seu sistema operacional. Essa escolha fornece recursos essenciais, como:

A menos que seja um fornecedor de componentes de hardware para smartphones, raramente se desenvolve software na camada de kernel de um sistema operacional Android.

Android Runtime

No Android, os aplicativos geralmente são escritos em Java e/ou Kotlin, linguagens que são compiladas para bytecode e executadas em uma máquina virtual (chamada de Java Virtual Machine – JVM). Inicialmente, o Android utilizava a Dalvik VM, mas, a partir do Android 5.0, a ART (Android Runtime) tornou-se a máquina virtual padrão. A ideia de rodar aplicativos dentro de uma máquina virtual adiciona uma camada de abstração, dificultando ataques diretos ao código do aplicativo.

Em relação a Dalvik, a ART oferece melhorias significativas, como:

Quanto às linguagens Java e/ou Kotlin, ambas podem ser utilizadas para escrever aplicativos Android. As duas são linguagens de programação de paradigma orientado a objetos (POO). As principais diferenças entre elas são:

Linguagem Java:

Linguagem Kotlin:

Java continua sendo uma escolha para projetos legados e para desenvolvedores que já possuem experiência com a linguagem. No entanto, Kotlin supera Java em produtividade, segurança e recursos modernos, sendo a escolha recomendada para novos desenvolvimentos Android.

Espaço de Execução Nativa

Embora a maior parte dos aplicativos Android seja desenvolvida em linguagens que rodam na JVM (como Java e/ou Kotlin), o Android também oferece suporte a execução nativa. Códigos em linguagens como C e/ou C++ podem ser executados diretamente, utilizando bibliotecas e recursos nativos do sistema. Essa camada é utilizada principalmente para:

No entanto, para integrar código nativo com aplicativos escritos em Java e/ou Kotlin, é necessário utilizar a JNI (Java Native Interface). A JNI é uma ponte que permite que os códigos nativos sejam chamados a partir da JVM, e vice-versa, garantindo a comunicação entre essas camadas de execução.

Quanto às linguagens C e/ou C++, ambas podem ser utilizadas para escrever código nativo no Android. As principais diferenças entre elas são:

Linguagem C:

Linguagem C++:

Para ter código nativo (seja em C e/ou C++) no aplicativo, é necessário utilizar o Android NDK para acessar as diversas bibliotecas nativas da plataforma diretamente a partir do código.

Android Native MIDI API

Os aplicativos Android MIDI costumam usar a midi API para se comunicar com o serviço Android MIDI. Esses aplicativos dependem principalmente da classe MidiManager para descobrir, abrir e fechar um ou mais objetos MidiDevice, além de transmitir dados de cada dispositivo e para eles pelas portas de entrada e saída MIDI.

Figura 8 – Android MIDI
Fonte: https://developer.android.com/ndk/guides/audio/midi 

Essa abordagem, no entanto, supõe que o aplicativo inteiro roda na JVM (escrito em Java e/ou Kotlin), incluindo a implementação do processador de mensagens MIDI e o sintetizador, caso esta seja a finalidade da aplicação (síntese MIDI).

No caso de um aplicativo que roda código nativo, por exemplo, o sintetizador FluidSynth escrito em C embarcado na aplicação, e que se comunica com o código Java/Kotlin via JNI, essa abordagem insere demasiada latência. O seguinte fluxo demonstra claramente esse efeito:

  1. Os comandos MIDI disparados pelo controlador chegam como mensagens via USB-MIDI ao Android;
  2. As mensagens MIDI são interceptadas pela JVM e repassadas para o aplicativo;
  3. O aplicativo usa a midi API para receber e tratar as mensagens, e faz um processamento para analisar cada uma delas;
  4. O aplicativo invoca via JNI funções da biblioteca FluidSynth que roda como código nativo;
  5. A biblioteca FluidSynth responde às chamadas e produz a síntese de áudio solicitada;
  6. A biblioteca retorna via JNI ao aplicativo que está rodando na JVM para continuar a execução do programa.

Para minimizar a latência e aumentar a performance das aplicações de áudio, foi criada a Native MIDI API do Android. Ela está disponível no Android NDK r20b e versões mais recentes. Ela permite que os desenvolvedores de aplicativos enviem e recebam dados MIDI com código C/C++ nativo.

Ao usar a Native MIDI API, deve-se transmitir o endereço de um MidiDevice para a camada de código nativo com uma chamada JNI. A partir daí, a API cria uma referência a um AMidiDevice, que tem a maior parte da funcionalidade de um MidiDevice. Seu código nativo usa funções da Native MIDI API que se comunicam diretamente com um AMidiDevice. O AMidiDevice se conecta diretamente ao serviço MIDI.

Figura 9 – Android Native MIDI API
Fonte: https://developer.android.com/ndk/guides/audio/midi 

Usando chamadas Native MIDI API, é possível integrar a lógica de áudio/controle C/C++ do aplicativo de forma mais próxima à transmissão MIDI. Há menos necessidade de chamadas JNI ou callbacks para a parte em Java/Kotlin do aplicativo. Por exemplo, um sintetizador de software implementado em código C (como o FluidSynth) poderia receber eventos do teclado controlador (MIDI) diretamente de AMidiDevice, em vez de esperar uma chamada de JNI para enviar os eventos da parte em Java/Kotlin. Ou, supondo que o aplicativo seja um controlador MIDI, um processo de composição algorítmica poderia ser escrito em C/C++, e enviar comandos MIDI diretamente a um AMidiDevice sem fazer callback para a parte em Java/Kotlin para transmitir os eventos ao sintetizador de hardware.

Embora a Native MIDI API melhore a conexão direta a dispositivos MIDI, os aplicativos ainda precisam usar o MidiManager para descobrir e abrir objetos MidiDevice. O AMidiDevice pode assumir a partir de então.

Às vezes, pode ser necessário passar informações da camada de interface de usuário (UI) para o código nativo. Por exemplo, quando eventos MIDI são enviados em resposta a botões na tela. Para isso, é necessário criar chamadas JNI personalizadas para a lógica nativa. Se for preciso enviar dados de volta para atualizar a UI, poderá ser feito um callback da camada nativa.

Maiores informações sobre a Android Native MIDI API podem ser encontradas na documentação oficial do Google Android Developers, disponível em: Android Native MIDI API Guide.

Latência de Som no Android

Latência é o tempo necessário para um sinal percorrer um sistema. Alguns tipos comuns de latência relacionados a aplicativos de áudio incluem:

É difícil medir a latência na entrada e na saída de áudio de forma isolada, já que é preciso saber exatamente quando a primeira amostra foi enviada para o caminho de áudio (embora isso possa ser feito usando um circuito de testes e um osciloscópio). Se a latência de ida e volta do áudio é conhecida, é possível usar a seguinte regra geral: a latência na entrada (e na saída) de áudio é metade da latência de ida e volta em caminhos sem processamento de sinal.

A latência de ida e volta do áudio varia significativamente de acordo com o modelo do dispositivo e a versão de build do Android. É possível medir a latência de ida e volta do áudio criando um aplicativo que gere um sinal de áudio, acompanhando esse sinal e medindo o tempo entre o envio e o recebimento dele. A latência mais baixa é alcançada por caminhos de áudio com processamento mínimo de sinal.

Desta forma, é muito difícil especificar ou estabelecer com exatidão a latência para um aplicativo Android específico. No entanto, há algumas práticas que podem ajudar a obter a menor latência de áudio possível:

Maiores informações sobre como reduzir a latência de áudio em aplicativos para Android podem ser encontradas na documentação oficial do Google Android Developers, disponível em: Android Audio Latency Guide.

Latência de Som no FluidSynth

A biblioteca FluidSynth também possui configurações que podem influenciar na latência de áudio do sintetizador, inclusive quando compilado para o Android:

Projeto Prático: Um sintetizador MIDI como aplicativo para Android

Este artigo traz como exemplo para os conceitos apresentados um projeto prático: um aplicativo para Android de um sintetizador MIDI. O código-fonte completo deste projeto está disponível no repositório do GitHub, através da seguinte URL:

https://github.com/robsonsmartins/android-midi-synth

Licenciamento

O código-fonte deste projeto está licenciado sob a MIT License, que é uma licença para software livre e aberto: permite que o código seja utilizado para fins comerciais ou não-comerciais. Um cuidado especial porém está no licenciamento de alguns componentes de terceiros usados no projeto:

De qualquer maneira, ao reutilizar este código para qualquer propósito, convém mencionar os autores originais e creditá-los devidamente.

Caso o código-fonte da biblioteca FluidSynth seja alterado, também deverá ser disponibilizado o código-fonte do projeto, conforme estabelece a LGPL 2.1.

Passos para Compilar o Projeto

Para compilar e executar o projeto é necessário ter um ambiente configurado com o git, o Android Studio (incluindo o Java SDK, Android SDK e Android NDK) e o emulador Android (ou um smartphone ou tablet com modo desenvolvedor ativado). Isso poderá ser feito em uma máquina com sistema operacional Microsoft Windows® (instale o Git for Windows), GNU/Linux ou Apple macOS®.

  1. Clone o repositório do projeto, usando o comando git clone.
  2. Abra o projeto, apontando o diretório para o Android Studio.
  3. Aguarde o sincronismo do projeto feito pelo Gradle.
  4. Use o comando Build /Rebuild Project para compilar; ou selecione o “Running Device” e aperte o botão “Run app” para executar diretamente o aplicativo no emulador ou no smartphone selecionado.

Para testar o projeto, é necessário:

  1. Usar um smartphone ou tablet com o modo desenvolvedor ativado para transferir o aplicativo via Android Studio (pode ser usado o botão “Run app”).
  2. Conectar a saída de fone-de-ouvido do aparelho a uma caixa de som, ou alto-falante externo caso queira amplificar o som emitido.
  3. Conectar um cabo ou adaptador OTG a porta USB-C do aparelho, e em seguida conectar um cabo USB A-B ao teclado controlador MIDI (ou se ele tiver somente saídas MIDI com conectores DIN-5, usar um adaptador MIDI-USB conectado ao cabo OTG).
  4. Verificar se na tela do aplicativo aparece a mensagem de conexão do instrumento (device), e pressionar algumas teclas (notas musicais) e pedal de sustain. Os sons devem ser emitidos pelo aplicativo sintetizador, e na tela, devem ser exibidas quais teclas foram pressionadas (note on) e com qual intensidade (velocity), e quais foram soltas (note off).
Figura 10 – Aplicativo Sintetizador MIDI para Android
Fonte: Autor

Detalhamento do Código-Fonte

O projeto de aplicativo Android do sintetizador MIDI está organizado da seguinte forma:

Arquivo de Manifesto

No arquivo AndroidManifest.xml estão as configurações básicas do aplicativo. As cláusulas que merecem destaque nesta aplicação são as de uses-feature:

Biblioteca FluidSynth

Para embarcar a biblioteca FluidSynth no aplicativo, foi usada a versão pré-compilada para Android, disponível em FluidSynth Releases. A versão usada neste projeto foi a 2.4.0, lançada em 31 de outubro de 2024. Os passos sugeridos para fazer esse procedimento são descritos a seguir:

  1. Faça o download do arquivo zip contendo a release pré-compilada para Android;
  2. Descompacte o arquivo;
  3. Copie toda a pasta lib para o projeto, ficando no caminho <PROJECT_DIR>/app/src/main/cpp/fluidsynth/lib;
  4. Copie toda a pasta include para o projeto, ficando no caminho <PROJECT_DIR>/app/src/main/cpp/fluidsynth/include.

Todo esse procedimento, com detalhes, está descrito no documento oficial do FluidSynth: Using prebuilt libraries on Android.

Outra maneira para fazer isso é clonar o repositório do FluidSynth, e compilar o código-fonte para gerar os binários para Android, usando o NDK. Esse procedimento, com detalhes, está descrito no documento oficial do FluidSynth: Building For Android.

De qualquer maneira, para que o aplicativo utilize as bibliotecas nativas do FluidSynth, é preciso configurar corretamente o CMake, através do arquivo <PROJECT_DIR>/app/src/main/cpp/CMakeLists.txt.

Classes Nativas C++

Para manipular facilmente o código nativo, algumas classes foram criadas em C++ para esse propósito:

Interface JNI

Para promover a comunicação entre as instâncias das classes nativas em C++ e as classes que rodam na JVM (Kotlin), é necessário construir uma implementação JNI. No caso deste projeto, as declarações JNI foram adicionadas ao final dos arquivos SynthManager.cpp e MidiManager.cpp. Usando a cláusula extern “C”, as funções declaradas pelo JNI (puro C) fazem um wrapper para as instâncias e métodos das duas classes C++ correspondentes. Desta forma, o código C++ pode se compatibilizar ao modelo do JNI, que é puro C (apenas funções).

Classes Kotlin

Para se comunicar com o código nativo via JNI, foram implementadas as seguintes classes em Kotlin:

Além dessas classes, também há a implementação em Kotlin da Activity principal do aplicativo (para prover a interface de usuário):

Conclusão

Este artigo apresentou uma visão geral sobre o desenvolvimento de um aplicativo sintetizador MIDI para Android, combinando o uso de linguagens Kotlin, C, C++ e da biblioteca FluidSynth. Foram detalhados os principais conceitos e tecnologias necessários para implementar uma solução funcional, desde os fundamentos do protocolo MIDI até a integração de soundfonts para a reprodução de sons.

A arquitetura Android, com suporte à execução nativa por meio da JNI e ao processamento de áudio em tempo real, demonstrou ser uma plataforma interessante para este tipo de aplicação. A combinação de APIs MIDI, configurações otimizadas para baixa latência e a flexibilidade da biblioteca FluidSynth possibilitaram a criação prática de um projeto exemplo, que foi demonstrado tanto em seus aspectos técnicos como quanto aos desafios inerentes à abordagem utilizada.

Ao longo do texto, também foram discutidas as vantagens e limitações das ferramentas utilizadas, assim como as configurações e etapas necessárias para preparar o ambiente de desenvolvimento e compilar o aplicativo. O exemplo prático fornecido ilustra de maneira clara como conectar controladores MIDI a dispositivos Android, integrar bibliotecas nativas e utilizar soundfonts para síntese sonora.

O projeto disponibilizado na íntegra no repositório GitHub serve como um ponto de partida para desenvolvedores interessados em explorar o potencial do Android para aplicações de áudio e música digital. Com isso, espera-se que o material apresentado neste artigo forneça não apenas conhecimento técnico, mas também inspiração para novas implementações e inovações no campo musical.

Referências

FluidSynth. A SoundFont Synthesizer. 2017. Disponível em: https://www.fluidsynth.org/. Acesso em: 14 de dez. 2024.

FluidSynth. FluidSynth 2.4 Developer Documentation. 2024. Disponível em: https://www.fluidsynth.org/api/. Acesso em: 14 de dez. 2024.

FluidSynth. FluidSynth GitHub Repository. 2017. Disponível em: https://github.com/FluidSynth/fluidsynth. Acesso em: 14 de dez. 2024.

FluidSynth. Wiki: Building For Android. 2023. Disponível em: https://github.com/FluidSynth/fluidsynth/wiki/BuildingForAndroid. Acesso em: 14 de dez. 2024.

FluidSynth. Wiki: Fluid Features. 2023. Disponível em: https://github.com/FluidSynth/fluidsynth/wiki/FluidFeatures. Acesso em: 14 de dez. 2024.

FluidSynth. Wiki: Using prebuilt libraries on Android. 2023. Disponível em: https://github.com/FluidSynth/fluidsynth/wiki/Using-prebuilt-libraries-on-Android. Acesso em: 14 de dez. 2024.

Google. Android Developers. 2008. Disponível em: https://developer.android.com. Acesso em: 14 de dez. 2024.

Google. Android Developers: Audio Latency Guide. 2015. Disponível em: https://developer.android.com/ndk/guides/audio/audio-latency. Acesso em: 14 de dez. 2024.

Google. Android Developers: Native MIDI API Guide. 2019. Disponível em: https://developer.android.com/ndk/guides/audio/midi. Acesso em: 14 de dez. 2024.

Google. Android Developers: Native MIDI sample. 2019. Disponível em: https://github.com/android/ndk-samples/tree/main/native-midi. Acesso em: 14 de dez. 2024.

Google. Android Developers: Platform architecture. 2024. Disponível em: https://developer.android.com/guide/platform. Acesso em: 14 de dez. 2024.

Google. Android Open Source Project. 2008. Disponível em: https://source.android.com. Acesso em: 14 de dez. 2024.

Martins, R. Android MIDI Synth Project. 2024. Disponível em: https://github.com/robsonsmartins/android-midi-synth. Acesso em: 14 de dez. 2024.

Martins, R. MIDI – PC Adapter. 2008. Disponível em: https://robsonmartins.com/content/eletr/projects/midi/. Acesso em: 14 de dez. 2024.

Martins, R. MIDI – USB Adapter. 2012. Disponível em: https://robsonmartins.com/content/eletr/projects/midiusb/. Acesso em: 14 de dez. 2024.

Polyphone. Polyphone SoundFont Editor. 2013. Disponível em: https://www.polyphone.io/. Acesso em: 14 de dez. 2024.

Polyphone. Polyphone soundfonts. 2013. Disponível em: https://www.polyphone.io/en/soundfonts. Acesso em: 14 de dez. 2024.

Polyphone. The different soundfont formats. 2013. Disponível em: https://www.polyphone.io/en/documentation/manual/annexes/the-different-soundfont-formats. Acesso em: 14 de dez. 2024.

Ricardo, H. Creating a Fluidsynth Hello World App for Android. 2020. Disponível em: https://medium.com/swlh/creating-a-fluidsynth-hello-world-app-for-android-5e112454a8eb. Acesso em: 14 de dez. 2024.

SFZ Format. SFZ Format Overview. 2020. Disponível em: https://sfzformat.com/. Acesso em: 14 de dez. 2024.

The MIDI Association. MIDI Specifications. 2024. Disponível em: https://midi.org/specs. Acesso em: 14 de dez. 2024.

Imagem de Destaque: Gerada por IA.