Projetos

Live Coding Musical com RP Pico e Sonic Pi

Eletrogate 24 de abril de 2026

Introdução

O live coding musical consiste na criação e manipulação de processos sonoros por meio de código executado em tempo real. Nessa prática, estruturas musicais — como padrões rítmicos, sequências melódicas e parâmetros de síntese — são definidas programaticamente e podem ser modificadas durante a execução do sistema. Essa abordagem tem sido explorada por comunidades, internacionais e brasileiras, de artistas e pesquisadores, muitas vezes reunindo músicos e programadores em torno de performances e experimentações baseadas em código, como ocorre nos eventos da Algorave e nas iniciativas da Algorave Brasil.

Neste artigo, propõe-se a construção de um controlador MIDI programável baseado no microcontrolador RP2040, capaz de interagir com o ambiente Sonic Pi para manipular estruturas musicais em tempo real. O projeto busca explorar a integração entre programação musical e interfaces físicas.

1.1 Sonic Pi

O Sonic Pi é um ambiente de programação voltado à criação sonora e musical por meio de código. Desenvolvido originalmente para o ecossistema Raspberry Pi, o software foi projetado com forte orientação educacional, buscando tornar conceitos de programação e síntese sonora acessíveis a iniciantes.

A linguagem utilizada no Sonic Pi é baseada em Ruby e apresenta uma sintaxe simplificada, com comandos específicos para geração de eventos musicais, manipulação de amostras sonoras e controle temporal. O ambiente inclui uma documentação extensa e integrada ao próprio software, facilitando o processo de aprendizagem e experimentação. Essas características tornam o Sonic Pi particularmente adequado para práticas de live coding e para a construção de sistemas interativos baseados em programação musical.

A instalação do Sonic Pi pode ser realizada a partir do site oficial do projeto, com versões disponíveis para diferentes sistemas operacionais. Após o download e instalação do software, o ambiente já se encontra pronto para execução de código musical e interação com dispositivos externos, como controladores MIDI.

https://sonic-pi.net/

1.2 Raspberry Pi Pico (RP2040) como Controlador MIDI

O Raspberry Pi Pico é uma placa de desenvolvimento baseada no microcontrolador RP2040, projetado pela Raspberry Pi Foundation. A placa oferece um conjunto de recursos que a torna adequada para aplicações de interface musical digital, incluindo múltiplos pinos de entrada e saída, conversores analógico-digitais e suporte nativo à comunicação USB. Essas características permitem que o dispositivo seja utilizado como controlador MIDI, enviando mensagens diretamente para um computador sem a necessidade de interfaces adicionais.

O RP2040 pode ser programado por meio de diferentes ambientes de desenvolvimento. Neste projeto optou-se pela utilização do CircuitPython, devido à sua simplicidade de sintaxe, facilidade de prototipagem e integração com bibliotecas voltadas à comunicação MIDI. Além disso, o processo de instalação e atualização do firmware ocorre de maneira simples, permitindo que o código seja modificado diretamente no dispositivo como se fosse um armazenamento USB.

https://circuitpython.org/

Após baixar o arquivo .uf2 correspondente à sua placa, conecte o Raspberry Pi Pico ao computador enquanto mantém pressionado o botão BOOTSEL. Arraste este arquivo para dentro da unidade de armazenamento reconhecida pelo computador. Após a transferência, a placa reiniciará automaticamente e uma nova unidade chamada CIRCUITPY aparecerá no sistema, indicando que o CircuitPython foi instalado com sucesso.


Desenvolvimento

O desenvolvimento do sistema proposto baseia-se na integração entre três elementos principais: o controlador físico baseado no microcontrolador RP2040, o protocolo de comunicação MIDI e o ambiente de programação musical Sonic Pi. A arquitetura geral do sistema permite que ações realizadas na interface física — como o pressionamento de botões ou a movimentação de potenciômetros — sejam convertidas em mensagens MIDI enviadas ao computador, onde são interpretadas pelo Sonic Pi para manipulação de estruturas musicais em tempo real.

O protocolo MIDI (Musical Instrument Digital Interface) é amplamente utilizado na comunicação entre instrumentos musicais eletrônicos, controladores e softwares de produção musical. Em vez de transmitir áudio, o MIDI envia mensagens que descrevem eventos musicais, como o início ou término de uma nota, mudanças de parâmetros e comandos de controle.

No contexto deste projeto, essas mensagens são utilizadas como elementos de controle para estruturas musicais programadas no Sonic Pi. Botões físicos da interface podem disparar mensagens Note On/Note Off para ativar eventos sonoros ou alternar estados do sistema, enquanto potenciômetros enviam mensagens Control Change, permitindo modificar parâmetros musicais em tempo real, como intensidade, filtros ou velocidade de execução.

Dessa forma, o controlador desenvolvido atua como uma camada de interação física entre o performer e o ambiente de programação musical, possibilitando manipulações expressivas durante a execução do código.

2.1 Implementação no RP2040 (CircuitPython)

A implementação do controlador MIDI foi realizada utilizando o microcontrolador RP2040, programado por meio do ambiente CircuitPython. Esse ambiente oferece uma interface simplificada para programação de dispositivos embarcados, além de bibliotecas específicas para comunicação MIDI via USB e leitura de entradas digitais e analógicas.

O funcionamento do controlador baseia-se em um loop contínuo de leitura das entradas físicas, no qual o estado dos botões e a posição do potenciômetro são monitorados constantemente. Sempre que ocorre uma alteração nesses estados, o microcontrolador envia a mensagem MIDI correspondente ao computador.

Inicialmente, o programa importa as bibliotecas necessárias para o funcionamento do sistema. Entre elas estão módulos responsáveis pela comunicação MIDI, manipulação de pinos digitais e leitura de entradas analógicas.

import board
import digitalio
import time
import usb_midi
import adafruit_midi
import analogio

from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff
from adafruit_midi.control_change import ControlChange

Após a importação das bibliotecas, o código inicializa a interface MIDI responsável pelo envio das mensagens ao computador e define os números de nota associados aos botões do controlador. Essas notas serão utilizadas para gerar mensagens Note On e Note Off, enquanto o potenciômetro será utilizado para enviar mensagens do tipo Control Change.

midi = adafruit_midi.MIDI(
    midi_out=usb_midi.ports[1],
    out_channel=0
)

NOTE1 = 60
NOTE2 = 62
NOTE3 = 64

CC_NUMBER = 1

Durante a execução do programa, o estado de cada botão é comparado ao estado registrado na iteração anterior do loop. Dessa forma, o sistema identifica apenas mudanças de estado, evitando o envio contínuo de mensagens MIDI enquanto o botão permanece pressionado. Quando ocorre o pressionamento, é enviada uma mensagem Note On; quando o botão é liberado, é enviada a mensagem Note Off correspondente.

state1 = button1.value

if state1 != last_state1:

    if not state1:
        midi.send(NoteOn(NOTE1, 120))

    else:
        midi.send(NoteOff(NOTE1, 0))

last_state1 = state1

Além das entradas digitais, o controlador utiliza um potenciômetro conectado a uma entrada analógica. O valor retornado pelo conversor analógico-digital do RP2040 varia entre 0 e 65535. Como as mensagens MIDI do tipo Control Change utilizam valores entre 0 e 127, é necessário realizar uma conversão para essa faixa.

pot_value = pot.value        # 0 – 65535
cc_value = pot_value >> 9    # 0 – 127

midi.send(ControlChange(CC_NUMBER, cc_value))

Esse valor pode então ser utilizado para controlar parâmetros contínuos dentro do ambiente Sonic Pi, como intensidade de efeitos, filtros ou velocidade de execução de estruturas musicais.

Todas essas operações são executadas dentro de um loop principal, responsável por realizar a leitura das entradas e enviar as mensagens MIDI correspondentes. Essa estrutura permite que o controlador responda continuamente às interações do usuário, funcionando como uma interface física para manipulação de processos musicais programados no Sonic Pi.

2.2 Arquitetura de execução no Sonic Pi

Uma vez enviadas pelo controlador físico, as mensagens MIDI podem ser capturadas e interpretadas pelo ambiente Sonic Pi para manipulação de estruturas musicais em tempo real.

No sistema desenvolvido, a organização do programa é baseada em buffers lógicos, que separam diferentes responsabilidades do código. Cada buffer contém um conjunto específico de dados ou rotinas de execução, permitindo estruturar o sistema em camadas independentes. Essa divisão facilita tanto a compreensão do funcionamento do programa quanto sua expansão para novas funcionalidades.

O primeiro bloco do código armazena os dados musicais utilizados pelo sistema. Nesse buffer são definidos parâmetros gerais do sequenciador, como o número de passos rítmicos e a duração de cada passo, além de bancos de padrões rítmicos e melódicos. Esses dados são armazenados utilizando a função set, que permite registrar valores em um espaço de memória compartilhado acessível por diferentes partes do programa.

## BUFFER0 - MUSICAL DATA ##

set :steps, 16
set :step_time, 0.25

set :kick_bank, [
  (ring 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0),
  (ring 1,0,0,1, 0,0,0,0, 1,0,0,1, 0,0,0,0),
  (ring 1,0,0,1, 0,0,0,0, 1,0,0,1, 0,0,0,0),
  (ring 1,0,0,0, 1,1,0,0, 1,0,0,0, 1,1,0,0)
]

Nesse contexto, cada elemento do banco representa um padrão musical armazenado como uma estrutura do tipo ring. Essa estrutura permite acesso cíclico aos elementos, característica útil para a implementação de sequenciadores baseados em passos.

A execução musical propriamente dita ocorre em um motor central de reprodução, implementado por meio de um live_loop. Esse loop é responsável por percorrer continuamente os passos do sequenciador e verificar quais eventos devem ser executados em cada momento.

live_loop :engine do
  
  use_bpm (get(:bpm) || 90)
  
  steps = get(:steps) || 16
  step_time = get(:step_time) || 0.25
  
  i = tick % steps
  
  kick_bank  = get(:kick_bank)
  snare_bank = get(:snare_bank)
  hihat_bank = get(:hihat_bank)
  
  groove_mode = get(:groove_mode) || 0
  groove = groove_mode % kick_bank.length
  
  kick  = kick_bank[groove]
  snare = snare_bank[groove]
  hihat = hihat_bank[groove]
  
  sample :bd_haus if kick[i] == 1
  sample :sn_dolf if snare[i] == 1
  sample :drum_cymbal_closed if hihat[i] == 1
  
  sleep step_time
  
end

Nesse mecanismo, a função tick atua como um contador interno que avança a cada iteração do loop, permitindo percorrer sequencialmente os passos do padrão rítmico. A cada ciclo, o sistema verifica se há eventos ativos na posição atual do padrão e dispara os sons correspondentes.

Além do motor de reprodução, o sistema possui buffers responsáveis pela interpretação das mensagens MIDI recebidas do controlador. Esses buffers atuam como módulos de controle que alteram parâmetros utilizados pelo motor de execução.

Para organizar essas diferentes formas de interação, o sistema utiliza uma variável de estado denominada controller_mode. Esse parâmetro define qual conjunto de controles MIDI estará ativo em determinado momento, permitindo que a mesma interface física seja reutilizada para diferentes funções dentro do sistema.

No modo de controle dedicado ao sequenciador, os botões do controlador alternam entre diferentes padrões armazenados nos bancos musicais, modificando parâmetros como o groove rítmico, a linha de baixo ou a melodia executada pelo motor principal.

## BUFFER2 - MIDI LOOP CONTROLLER ##

live_loop :midi_loop_controller do
  use_real_time
  
  note, vel = sync "/midi*/note_on"
  
  controller_mode = get(:controller_mode) || 0
  
  if controller_mode == 1
    
    case note
    
    when 60
      set :bass_mode, ((get(:bass_mode) || 0) + 1)
      
    when 62
      set :groove_mode, ((get(:groove_mode) || 0) + 1)
      
    when 64
      set :melody_mode, ((get(:melody_mode) || 0) + 1)
      
    end
    
  end
  
end

Em um segundo modo de operação, os mesmos botões passam a atuar como disparadores de eventos sonoros baseados em samples, que são processados pelo motor de execução juntamente com os demais elementos musicais.

## BUFFER3 - MIDI SAMPLE CONTROLLER ##

live_loop :midi_sample_controller do
  use_real_time
  
  note, vel = sync "/midi*/note_on"
  
  controller_mode = get(:controller_mode) || 0
  
  if controller_mode == 2
    
    case note
    
    when 60
      set :sample_event, :impact
      
    when 62
      set :sample_event, :reverse
      
    when 64
      set :sample_event, :noise
      
    end
    
  end
  
end

A seleção entre esses modos ocorre por meio do parâmetro controller_mode, que é continuamente verificado pelos diferentes buffers de controle. Dessa forma, apenas o conjunto de rotinas correspondente ao modo ativo responde às mensagens MIDI recebidas.

Essa estratégia permite reutilizar uma mesma interface física para controlar diferentes partes do sistema, sem necessidade de modificar a estrutura principal do sequenciador.

Além disso, a arquitetura baseada em buffers torna o sistema facilmente extensível. Novos instrumentos, eventos sonoros ou estratégias de interação podem ser adicionados simplesmente criando novos buffers condicionados a outros valores de controller_mode. Assim, a mesma interface MIDI pode assumir múltiplas funções dentro da performance, ampliando as possibilidades de controle sem aumentar a complexidade do hardware.


Diagramas

Diagrama físico do controlador com RP Pico:


Diagrama lógico do Sonic Pi:


Diagrama geral do projeto:

 


Códigos Sonic Pi

BUFFER0 – Dados musicais

## BUFFER0 - MUSICAL DATA ##

set :steps, 16
set :step_time, 0.25

## GROOVE BANK ##

set :kick_bank, [
  (ring 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0), #0
  
  (ring 1,0,0,1, 0,0,0,0, 1,0,0,1, 0,0,0,0), #1
  
  (ring 1,0,0,1, 0,0,0,0, 1,0,0,1, 0,0,0,0), #2
  
  (ring 1,0,0,0, 1,1,0,0, 1,0,0,0, 1,1,0,0) #3
]

set :snare_bank, [
  (ring 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0), #0
  
  (ring 0,0,0,0, 0,0,1,0, 0,0,0,0, 0,0,1,0), #1
  
  (ring 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0), #2
  
  (ring 0,0,1,0, 0,0,1,0, 0,0,1,0, 0,0,1,0) #3
]

set :hihat_bank, [
  (ring 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0), #0
  
  (ring 1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0), #1
  
  (ring 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1), #2
  
  (ring 1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1) #3
]


set :melody_bank, [
  (ring
   nil,nil,nil,nil,nil,nil,nil,nil,
   nil,nil,nil,nil,nil,nil,nil,nil
   ), #0
  
  (ring
   :g4,nil,:bb4,nil,:d5,nil,:f5,nil,
   :d5,:d5,:bb4,:bb4,:g4,:g4,:f5,nil
   ), #1
  
  (ring
   :g4,nil,:bb4,nil,:c5,nil,:e5,nil,
   :c5,:c5,:bb4,:bb4,:g4,:g4,:e5,nil
   ), #2
  
  (ring
   :a4,nil,:c5,nil,:d5,nil,:gb5,nil,
   :d5,:d5,:c5,:c5,:a4,:a4,:d5,nil
   ) #3
  
]

set :bass_bank, [
  
  (ring
   nil,nil,nil,nil,nil,nil,nil,nil,
   nil,nil,nil,nil,nil,nil,nil,nil
   ), #0
  
  (ring
   :g3,nil,nil,:bb3,nil,nil,:f3,nil,
   :g3,nil,nil,:g4,:bb3,nil,:d4,nil
   ), #1
  
  (ring
   :g4,nil,nil,:e3,nil,nil,:bb3,nil,
   :c3,nil,nil,:c4,:e3,nil,:g3,nil
   ), #2
  
  (ring
   :d3,nil,nil,:a4,nil,nil,:a3,nil,
   :d3,nil,nil,:a4,:a3,nil,:d3,nil
   ) #3
  
]

BUFFER1 – Motor musical

## BUFFER1 - ENGINE CONTROL ##

#=begin DESCOMENTE PARA CONTROLE POR INTERFACE MIDI
set :groove_mode, 0
set :melody_mode, 0
set :bass_mode, 0
set :bpm, 90
#=end

set :controller_mode, 0

set :fx_cutoff, 90

set :bpm_min, 60
set :bpm_max, 140

set :sample_event, nil
set :sample_fx, 0.5


live_loop :engine do
  
  use_bpm (get(:bpm) || 90)
  
  steps = get(:steps) || 16
  step_time = get(:step_time) || 0.25
  
  i = tick % steps
  
  ## GROOVE
  
  kick_bank  = get(:kick_bank)
  snare_bank = get(:snare_bank)
  hihat_bank = get(:hihat_bank)
  
  groove_mode = get(:groove_mode) || 0
  
  groove = groove_mode % kick_bank.length
  
  kick  = kick_bank[groove]
  snare = snare_bank[groove]
  hihat = hihat_bank[groove]
  
  sample :bd_haus if kick[i] == 1
  sample :sn_dolf if snare[i] == 1
  sample :drum_cymbal_closed if hihat[i] == 1
  
  
  ## MELODY
  
  melody_bank = get(:melody_bank) || [[nil]*16]
  melody_mode = (get(:melody_mode) || 0) % melody_bank.length
  
  melody = melody_bank[melody_mode]
  
  note = melody[i]
  play note if note
  
  
  ## BASS
  
  bass_bank = get(:bass_bank) || [[nil]*16]
  bass_mode = (get(:bass_mode) || 0) % bass_bank.length
  
  bass = bass_bank[bass_mode]
  
  note = bass[i]
  play note, release: 0.4 if note
  
  
  ## SAMPLE EVENT ENGINE
  
  event = get(:sample_event)
  fx = get(:sample_fx) || 0.5
  
  case event
  
  when :impact
    
    with_fx :reverb, mix: fx, room: 0.8 do
      with_fx :echo, mix: fx * 0.6, phase: 0.25 do
        
        rate = -0.5 - (fx * 0.5)
        
        sample :ambi_glass_rub,
          rate: rate,
          attack: 0.1,
          release: 2,
          amp: 1.2
        
      end
    end
    
  when :reverse
    
    with_fx :reverb, mix: fx * 0.5 do
      with_fx :echo, mix: fx, phase: 0.25 do
        
        sample :elec_blip2,
          rate: -1,
          release: 0.3,
          amp: 1
        
      end
      
      set :sample_event, nil if event
    end
    
  when :noise
    
    with_fx :reverb, mix: fx * 0.6, room: 0.9 do
      with_fx :lpf, cutoff: 60 + (fx * 60) do
        
        sample :ambi_soft_buzz,
          attack: 0.05,
          release: 1.5 + fx,
          amp: 1.2
        
      end
    end
    
  end
  
  sleep step_time
  
end

BUFFER2 – MIDI Controlador de LOOPS

## BUFFER2 - MIDI LOOP CONTROLLER ##

live_loop :midi_loop_controller do
  use_real_time
  
  note, vel = sync "/midi*/note_on"
  
  controller_mode = get(:controller_mode) || 0
  
  if controller_mode == 1
    
    bass_bank   = get(:bass_bank)   || []
    kick_bank   = get(:kick_bank)   || []
    melody_bank = get(:melody_bank) || []
    
    case note
    
    ## BOTAO 1 — BASS MODE
    when 60
      
      if bass_bank.length > 0
        mode = ((get(:bass_mode) || 0) + 1) % bass_bank.length
        set :bass_mode, mode
        print "Bass mode:", mode
      end
      
      ## BOTAO 2 — GROOVE MODE
    when 62
      
      if kick_bank.length > 0
        mode = ((get(:groove_mode) || 0) + 1) % kick_bank.length
        set :groove_mode, mode
        print "Groove mode:", mode
      end
      
      ## BOTAO 3 — MELODY MODE
    when 64
      
      if melody_bank.length > 0
        mode = ((get(:melody_mode) || 0) + 1) % melody_bank.length
        set :melody_mode, mode
        print "Melody mode:", mode
      end
      
    end
    
  end
  
end


live_loop :midi_bpm_knob do
  use_real_time
  
  cc, val = sync "/midi*/control_change"
  
  controller_mode = get(:controller_mode) || 0
  
  ## MODO LOOP PLAYER → controla BPM
  
  if controller_mode == 1 && cc == 1
    
    min = get(:bpm_min)
    max = get(:bpm_max)
    
    bpm = min + (val / 127.0) * (max - min)
    
    set :bpm, bpm.round
    
    print "BPM:", bpm.round
    
    
    
  end
  
end

BUFFER3 – MIDI Controlador de SAMPLES

## BUFFER3 - MIDI SAMPLE CONTROLLER ##

live_loop :midi_sample_controller do
  use_real_time
  
  note, vel = sync "/midi*/note_on"
  
  controller_mode = get(:controller_mode) || 0
  
  if controller_mode == 2
    
    case note
    
    when 60
      set :sample_event, :impact
      
    when 62
      set :sample_event, :reverse
      
    when 64
      set :sample_event, :noise
      
    end
    
  end
  
end

live_loop :midi_fx_knob do
  use_real_time
  
  cc, val = sync "/midi*/control_change"
  
  controller_mode = get(:controller_mode) || 0
  
  if controller_mode == 2 && cc == 1
    
    fx = val / 127.0
    set :sample_fx, fx
    
    print "FX:", fx
    
  end
  
end

Código CircuitPy

import board
import digitalio
import time
import usb_midi
import adafruit_midi
import analogio

from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff
from adafruit_midi.control_change import ControlChange

# ------------------------------------------------
# CONFIGURAÇÃO DA PLACA
# ------------------------------------------------
# Escolha qual placa está sendo utilizada
#
# RP2040 ZERO  -> LED RGB (WS2812) no pino GP16
# RASPBERRY PI PICO -> LED onboard simples no pino GP25
#
# Defina uma das opções como True

USE_RP2040_ZERO = True
USE_PICO = False

# ------------------------------------------------
# LED DE FEEDBACK
# ------------------------------------------------

if USE_RP2040_ZERO:

    import neopixel

    pixel = neopixel.NeoPixel(board.GP16, 1)
    pixel.fill((0,0,0))

    def led_off():
        pixel.fill((0,0,0))

    def led_green():
        pixel.fill((0,255,0))

    def led_blue():
        pixel.fill((0,0,255))

    def led_red():
        pixel.fill((255,0,0))


elif USE_PICO:

    led = digitalio.DigitalInOut(board.GP25)
    led.direction = digitalio.Direction.OUTPUT

    def led_off():
        led.value = False

    def led_green():
        led.value = True

    def led_blue():
        led.value = True

    def led_red():
        led.value = True


# ------------------------------------------------
# MIDI
# ------------------------------------------------

midi = adafruit_midi.MIDI(
    midi_out=usb_midi.ports[1],
    out_channel=0
)

NOTE1 = 60
NOTE2 = 62
NOTE3 = 64

CC_NUMBER = 1   # modulation wheel


# ------------------------------------------------
# BOTÕES
# ------------------------------------------------

button1 = digitalio.DigitalInOut(board.GP1)
button1.direction = digitalio.Direction.INPUT
button1.pull = digitalio.Pull.UP

button2 = digitalio.DigitalInOut(board.GP2)
button2.direction = digitalio.Direction.INPUT
button2.pull = digitalio.Pull.UP

button3 = digitalio.DigitalInOut(board.GP3)
button3.direction = digitalio.Direction.INPUT
button3.pull = digitalio.Pull.UP


# ------------------------------------------------
# POTENCIÔMETRO
# ------------------------------------------------

pot = analogio.AnalogIn(board.GP26)


# ------------------------------------------------
# ESTADO
# ------------------------------------------------

last_state1 = True
last_state2 = True
last_state3 = True

last_cc = -1

print("Sistema iniciado - MIDI Controller")


# ------------------------------------------------
# LOOP PRINCIPAL
# ------------------------------------------------

while True:

    # -------------------------
    # BOTÃO 1
    # -------------------------

    state1 = button1.value

    if state1 != last_state1:

        if not state1:

            print("BOTAO1 -> NoteOn")
            led_green()
            midi.send(NoteOn(NOTE1, 120))

        else:

            print("BOTAO1 -> NoteOff")
            led_off()
            midi.send(NoteOff(NOTE1, 0))

    last_state1 = state1


    # -------------------------
    # BOTÃO 2
    # -------------------------

    state2 = button2.value

    if state2 != last_state2:

        if not state2:

            print("BOTAO2 -> NoteOn")
            led_blue()
            midi.send(NoteOn(NOTE2, 120))

        else:

            print("BOTAO2 -> NoteOff")
            led_off()
            midi.send(NoteOff(NOTE2, 0))

    last_state2 = state2


    # -------------------------
    # BOTÃO 3
    # -------------------------

    state3 = button3.value

    if state3 != last_state3:

        if not state3:

            print("BOTAO3 -> NoteOn")
            led_red()
            midi.send(NoteOn(NOTE3, 120))

        else:

            print("BOTAO3 -> NoteOff")
            led_off()
            midi.send(NoteOff(NOTE3, 0))

    last_state3 = state3


    # -------------------------
    # POTENCIÔMETRO
    # -------------------------

    pot_value = pot.value           # 0–65535
    cc_value = pot_value >> 9       # converte para 0–127

    if abs(cc_value - last_cc) > 1:

        midi.send(ControlChange(CC_NUMBER, cc_value))

        print("CC:", cc_value)

        last_cc = cc_value


    time.sleep(0.01)

# fim

Conclusão

Este artigo apresentou o desenvolvimento de um sistema de controle físico para performance musical em live coding, explorando a comunicação MIDI entre um controlador construído com o microcontrolador RP2040 e o ambiente de programação musical Sonic Pi.

A proposta teve como objetivo demonstrar, de forma acessível, como microcontroladores de baixo custo podem ser utilizados na construção de interfaces musicais personalizadas. A utilização de bibliotecas abertas do ecossistema CircuitPython possibilitou implementar rapidamente o envio de mensagens MIDI a partir de uma interface física simples, composta por botões e potenciômetros, evidenciando o potencial dessas ferramentas para aplicações musicais e educacionais.

No ambiente Sonic Pi, foi apresentada uma estratégia de organização do código baseada na separação entre dados musicais, motor de execução e rotinas de controle. Essa arquitetura modular favorece a legibilidade do código e facilita a expansão do sistema, permitindo a incorporação de novos parâmetros de controle, estruturas musicais ou algoritmos generativos.

Além de introduzir os fundamentos técnicos dessa integração, o projeto busca também incentivar a experimentação por parte do leitor. A combinação entre programação, eletrônica embarcada e criação musical abre espaço para que músicos e programadores explorem novas formas de interação performática, desenvolvendo seus próprios controladores e estratégias algorítmicas de controle musical.

Dessa forma, a integração entre interfaces físicas programáveis e ambientes de live coding revela-se não apenas uma ferramenta de performance, mas também um campo fértil para experimentação artística, ensino de programação e desenvolvimento de novas abordagens para a criação musical em tempo real.


Referências

ALGORAVE COMMUNITY.
Algorave – About.
https://algorave.com/about/

MIDI MANUFACTURERS ASSOCIATION.
MIDI 1.0 Specification.
https://midi.org/specifications

RASPBERRY PI LTD.
RP2040 Datasheet.
https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf

ADAFRUIT INDUSTRIES.
Adafruit MIDI Library for CircuitPython.

AARON, Sam et al.
Sonic Pi.
https://sonic-pi.net/

ALGORAVE BRASIL.
Algorave Brasil – Comunidade brasileira de live coding.
https://algoravebrasil.gitlab.io/

ALGORAVE BRASIL.
Algorave Brasil – Sobre a comunidade.
https://algoravebrasil.gitlab.io/sobre/

COSTA, Joenio Marques da.
Conferência de Live Coding.
https://joenio.me/conferencia-livecoding/


Sobre o Autor


Erwin de Mattos

 


Lista de Materiais

Eletrogate

24 de abril de 2026

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Os comentários estão desativados.

Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!

Eletrogate Robô

Assine nossa newsletter e
receba  10% OFF  na sua
primeira compra!