Clayton BorgesDESENVOLVEDOR
← ← Todos os Projetos

SPECtations – Visualizador de Áudio

2025
Native macOS Desktop App / Real-Time Audio Analysis

App desktop macOS para visualização de áudio do sistema em tempo real via loopback BlackHole. Pipeline FFT duplo: np.fft.rfft a 15fps para forma de onda ao vivo; scipy.signal.spectrogram com n_fft=2048 para heatmap tempo-frequência deslizante de 5 segundos. Acelerado por OpenGL via PyQtGraph. Distribuível como bundle .app via PyInstaller.

Visão Geral

SPECtations é um aplicativo desktop macOS para visualização em tempo real do áudio de saída do sistema. Captura todo o áudio sendo reproduzido no Mac (música, vídeo, qualquer app) usando o BlackHole, um driver de loopback de áudio virtual, processa com dois pipelines FFT independentes e renderiza visualizações sincronizadas de forma de onda e espectrograma. O app foi criado para uso contínuo em segundo plano durante produção musical e trabalho com mídia.

Problema

O macOS não expõe a saída de áudio do sistema como um dispositivo de entrada capturável (ao contrário do loopback WASAPI do Windows). Os visualizadores de áudio existentes exigem roteamento pelo DAW ou só visualizam entrada do microfone. O desafio de engenharia era tanto no nível do SO (workaround BlackHole) quanto no nível de processamento de sinal: um único tamanho FFT produz um tradeoff entre resolução temporal (boa para forma de onda) e resolução de frequência (boa para espectrograma).

Objetivo

Construir um visualizador de áudio nativo para macOS que: (1) capture a saída de áudio do sistema via BlackHole sem silenciar os alto-falantes; (2) execute dois pipelines FFT com resoluções diferentes simultaneamente; (3) renderize um heatmap de espectrograma deslizante e forma de onda em tempo real; (4) suporte configuração rica de presets (35 colormaps, sensibilidade, persistência); (5) distribua como .app standalone sem exigir instalação do Python.

Meu Papel
  • ◈Desenvolvedor solo — pipeline de áudio, processamento de sinal, UI Qt, empacotamento macOS
Stack
Python 3.10+PySide6 6.5PyQtGraph 0.13sounddeviceNumPySciPyBlackHolePyInstaller
Decisões Técnicas
  1. 01BlackHole 2ch como única solução de loopback macOS viável — o modelo de privacidade do macOS impede a captura direta da saída de áudio. O BlackHole age como um dispositivo de saída virtual que simultaneamente se expõe como entrada. Um Multi-Output Device no Audio MIDI Setup roteia o áudio tanto para os alto-falantes reais quanto para o BlackHole.
  2. 02Tamanhos FFT duais: 1024 para forma de onda, 2048 para espectrograma — a forma de onda precisa de baixa latência (1024 amostras ≈ 23ms). O espectrograma precisa de maior resolução de frequência (FFT de 2048 pontos dá 21,5Hz por bin). scipy.signal.spectrogram com 75% de sobreposição para a visão histórica vs. np.fft.rfft para a visão em tempo real dá a cada visualização seus parâmetros ótimos.
  3. 03Política drop-oldest na fronteira produtor/consumidor — o PortAudio chama o callback de áudio em uma thread em tempo real; o Qt roda na thread principal. A Queue(maxsize=10) os desacopla. Quando o processamento fica para trás, a fila enche. Descartar o chunk mais antigo aceita glitches visuais ocasionais sobre esgotamento de memória ou travamento da thread de áudio.
  4. 04Taxa de atualização de 15fps para legibilidade — uma taxa inicial de 30fps fazia as formas de onda se moverem rápido demais para leitura em tempo real. Reduzir para 15fps torna os movimentos legíveis. O timer de animação de partículas de 60fps roda independentemente.
  5. 05PySide6 6.5.0 fixado exatamente — o PyQtGraph tinha problemas de integração específicos de versão com Qt 6.4–6.6. O fix exato de 6.5.0 resolveu um bug de renderização no caminho de atualização do ImageItem do PyQtGraph.
  6. 06PyInstaller para distribuição .app — bundlea o interpretador Python, todas as dependências (incluindo PyTorch) e o ponto de entrada do app em um .app macOS autocontido.
O Que Aprendi
  • →O roteamento BlackHole exige uma configuração Multi-Output Device não óbvia — o usuário deve criar um dispositivo combinado no Audio MIDI Setup que envie áudio tanto para alto-falantes reais quanto para o BlackHole. Sem isso, o áudio é roteado exclusivamente para o BlackHole e os alto-falantes ficam silenciosos.
  • →Resolução FFT dupla vale a complexidade de código — versões iniciais usavam um único FFT de 1024 pontos para ambas as visões. O espectrograma parecia borrado com baixa distinção de frequência. Adicionar um segundo cálculo scipy.signal.spectrogram em 2048 pontos melhorou dramaticamente a qualidade.
  • →Python nativo ARM64 importa para o PortAudio no Apple Silicon — o Python x86_64 traduzido por Rosetta funcionava para computação, mas causava o PortAudio a identificar incorretamente dispositivos de áudio e ocasionalmente travar em máquinas M1/M2.
  • →PyQtGraph é significativamente mais rápido que matplotlib para visualização em tempo real — um protótipo matplotlib rodava a 8fps antes de perder frames. Mudar para PyQtGraph (com backend OpenGL) alcançou 15fps estável.
Status

Funcional — ferramenta desktop pessoal, distribuível como .app macOS