Visão geral
Entrada: o usuário informa o tamanho / formato / materiais de acabamento da sala, as posições dos alto-falantes e da posição de audição — ou a geometria da sala é medida com a trena em AR (sem necessidade de LiDAR) ou capturada por um escaneamento com LiDAR (RoomPlan).
- Localização de pontos de reflexão inicial
- RT60 (medido + simulado) mais EDT / C50 / C80 / D50 (derivados da RIR simulada)
- Lista de frequências dos modos da sala
- Plano de disposição dos painéis (posição, quantidade, SKU do produto)
- Visualização 3D + mapa de calor da energia das reflexões
- Exportação de PDF em nível de manual de instalação
- Medição no dispositivo por varredura ESS / palma (microfone integrado ou microfone de medição USB)
- Calibração de microfone de medição USB e referência SPL, espectro em tempo real (RTA)
- Sobreposições de resposta em frequência e médias multiposição, interoperabilidade com REW, PEQ / alinhamento de subwoofer, assistente de IA
Há suporte para salas retangulares, em L, em U e com qualquer polígono simples; as reflexões de primeira ordem e os cálculos de área / contenção usam o polígono real; as reflexões de 2ª/3ª ordem e os modos da sala são aproximados pela caixa delimitadora (ver §9).
Modelo acústico
2.1 Sabine RT60
A clássica fórmula de Sabine:
- Volume da sala (m³); para polígonos, área Shoelace × altura, menos obstáculos como colunas / vigas.
- Área da superfície × coeficiente de absorção por face. Os coeficientes usam 6 bandas de oitava (125 / 250 / 500 / 1k / 2k / 4k Hz).
- = 24·ln(10)/c, com c = 343 m/s (20 °C).
O app traz 4 predefinições de acabamento (cômodo vazio de superfícies duras / acabamento duro típico / ambiente com estofados / ambiente já tratado), cada uma com uma curva de absorção por banda; a estimativa de RT60 em valor único do topo e o orçamento de painéis usam um coeficiente médio de absorção das bandas médias-altas, enquanto a curva Sabine de 6 bandas alimenta a exibição por banda. O RT60 alvo também é uma curva de 6 bandas (não um valor único), em conformidade com normas como EBU Tech 3276 / THX, que permitem uma cauda de reverberação mais longa nas baixas frequências.
2.2 Método das Fontes de Imagem (ISM)
As reflexões de primeira ordem usam espelhamento: espelha-se a fonte em relação a uma parede; a interseção do segmento “fonte espelhada → ouvinte” com a parede é o ponto de reflexão.
- Sala retangular: espelhar em todos os 6 planos (4 paredes + piso + teto) — forma fechada.
- Não retangular (
PolygonReflectionCalculator): espelha cada segmento de parede deRoomPolygon.walls()e valida o parâmetro s ∈ [0, 1] dentro do segmento (reflexões de piso / teto exigem ainda que o XY da reflexão esteja dentro do polígono); caminhos de primeira ordem também passam por uma verificação de oclusão 2D — qualquer trecho fonte→ponto→ouvinte que cruze outro segmento de parede é descartado.
Reflexões de múltiplas ordens (ordens 1–3) são implementadas pelo MultiOrderReflectionCalculator seguindo a abordagem recursiva do pyroomacoustics:
- Enumera todas as sequências de paredes [s₁, …, sₙ], com paredes adjacentes distintas
- Iteração da fonte imagem: S₀ = source, Sₖ = mirror(Sₖ₋₁, sₖ)
- Traçado reverso a partir do ouvinte: a reta Sₙ → ouvinte cruza sₙ em Pₙ; a reta Sₙ₋₁ → Pₙ cruza sₙ₋₁ em Pₙ₋₁; recursão até o início
- Cada interseção deve estar dentro do intervalo válido da parede e com t ∈ (0, 1) no segmento
2.3 Energia do caminho
Energia de banda larga em função do número de reflexões e da atenuação pela distância:
- α é limitado a [0, 0.99] para que α = 1 não produza log(0) ou pow(negativo, N) = NaN, o que contaminaria todo o caminho
- A atenuação pela distância segue o espalhamento esférico 1/r²
- A conversão para dB usa 10·log₁₀ (domínio da energia)
A energia por banda é calculada separadamente para as 6 bandas de oitava (cada uma usando o coeficiente da respectiva banda de finishProfile.absorptionByBand).
2.4 Estimativa de RT60 (medida)
ImpulseResponseEstimator:
- Filtragem casada: a varredura gravada passa por filtragem inversa (ESS / Farina) e é comprimida em um único impulso; ganho de processamento ≈ 10·log₁₀(T·B) — cerca de +41 dB de SNR efetiva na predefinição de telefone de 3 s, 180 Hz–4 kHz
- rmsEnvelope: envoltória RMS de janela deslizante
- Integração reversa de Schroeder: E_back(t) = ∫ₜ^∞ s²(τ) dτ, normalizada com 10·log₁₀ para gerar a curva EDC
- Regressão linear da inclinação: prefira T30 [−5, −35 dB] conforme o SNR, com fallback para T20 [−5, −25] ou T10 [−5, −15]
- RT₆₀ = −60 / inclinação
RT60 por banda: sobre as amostras após o filtro casado, passa-banda nos centros de oitava ISO escolhidos conforme a faixa real da varredura (Q = √2) e Schroeder em cada banda.
2.5 Simulação de RIR
SimulatedRIRGenerator transforma os caminhos de reflexão multi-ordem em eventos de impulso discretos gravados em um buffer PCM de 8 kHz × 0,5 s:
for path in reflectionPaths:
t = path.totalDistance / speedOfSound
amp = 10^(path.relativeEnergyDB / 20)
impulse[round(t * sampleRate)] += amp
Some o impulso do som direto e depois execute a integração reversa de Schroeder para extrair EDT / RT60 / C50 / C80 / D50.
2.6 Modos da sala
Frequências modais de uma sala retangular:
Classificados por quantos dos índices nₓ, n_y, n_z são não nulos: 1 = axial, 2 = tangencial, 3 = oblíquo.
A enumeração cobre n ≤ 4 por eixo até 300 Hz. Salas não retangulares estimam os modos a partir da caixa delimitadora (modos exatos exigem FEM); isso é indicado explicitamente na interface.
Sistema de geometria
3.1 Formatos suportados
- Retângulo — width × length × height
- Formato em L — remove um recorte nw × nl em um canto escolhido
- Formato U — remove um recorte de nw × nl no meio de uma parede escolhida
- Personalizado — qualquer polígono simples (≥ 3, ≤ 24 vértices)
Todos os formatos não retangulares viram uma lista de vértices em CCW via RoomPolygon.from(shape:, bboxWidth:, bboxLength:). As reflexões de primeira ordem, os cálculos de área / perímetro e de contenção usam esses vértices diretamente; um polígono personalizado inválido (< 3 / > 24 vértices, com autointerseção, área quase nula) degrada silenciosamente para o retângulo delimitador.
3.2 Utilitários de geometria de polígonos
- Área — fórmula de Shoelace
- Perímetro — soma dos comprimentos das arestas
- contains(x, y) — ray-casting com fallback de epsilon na fronteira (evita falsos negativos nos cantos)
- clamped(point) — retângulos usam o caminho rápido [0, w] × [0, l]; não retângulos usam o verdadeiro [minₓ, maxₓ] × [min_y, max_y] do
polygon.boundingBox, testam polygon.contains e fazem busca binária em direção ao centro do bbox se estiver fora - walls() — divide o polígono em segmentos de parede classificados pela normal interna (agrupados automaticamente em leftWall / rightWall / frontWall / backWall), permitindo reutilizar o recomendador de painéis da versão retangular
- PolygonValidator.isSimple — valida um polígono simples sem autointerseção
3.3 Escaneamento com RoomPlan
Usa-se a RoomCaptureSession (API do iOS 16+; no app, o escaneamento exige iOS 17), e o CapturedRoomMapper mapeia o escaneamento para um ScannedRoom (wallSegments + openings + furniture). Mapeamento de coordenadas:
Os escaneamentos são classificados pela contagem de paredes: exatamente 4 segmentos de parede são tratados diretamente como retângulo; qualquer outro caso passa pelo RectangularRoomSimplifier.polygonShapeFromScan, encadeando os wallSegments em vértices de polígono. Os endpoints do RoomPlan têm erro típico de 3–8 cm; um loop de 12 segmentos pode acumular 5 cm, então o epsilon de fechamento é 12 cm. Se a cadeia não fechar, ele recorre ao bounding box e pede para o usuário escanear de novo.
3.4 Trena AR
Em dispositivos sem LiDAR, o ARTapeMeasureView (uma ARView do RealityKit com world tracking) assume: mire a cruz de mira na parede e toque duas vezes para registrar os pontos inicial / final, passando por largura → comprimento → altura, depois confirme para gerar uma sala retangular — com orientação sobre permissão de câmera e dicas para falha de rastreamento.
3.5 Editor de vértices livres
FreeVertexEditorView permite ao usuário arrastar vértices em um Canvas do SwiftUI:
- Pressione e segure para excluir (quando o número de vértices > 3, de modo que restem ≥ 3)
- Cada quadro do arrasto é validado com
isSimple; uma nova posição com autointerseção simplesmente não é aplicada (o vértice fica onde está). Tocar em um espaço vazio insere um vértice na aresta mais próxima; há também um editor numérico por vértice e um botão de redefinir para o retângulo delimitador - Snap na grade de 0,05 m
- Uma margem de max(0,5, bbox × 0,2) m fora da bbox permite que o usuário arraste vértices para além da bbox atual (a view pai aumenta automaticamente room.width / length)
Zonas de audição e múltiplos ouvintes
AcousticConfiguration.listeners aceita 1..N posições de audição (com 0 ouvintes, uma posição primária padrão é sintetizada com um aviso; com 0 alto-falantes, os templates de caso de uso da §5.2 assumem o controle). A análise executa o método das fontes-imagem uma vez por ouvinte e mescla os pontos de reflexão em um único array (os pontos do ouvinte primário primeiro). O recomendador de painéis acústicos pondera cada parede pelo número de pontos de reflexão (cap = max(6, speakerCount × listenerCount)); vários assentos compartilhando a mesma parede naturalmente a empurram em direção ao teto do orçamento.
- No 3D, os nós de secondary listener usam opacity 0,45 + scale 0,75
- Na vista superior (RoomPlanView), os pontos de reflexão primários são r=5 em cor cheia, os secundários r=3 com opacidade 0,45
- A lista de reflexões (RecommendationView) mostra apenas os pontos primários; as posições de audição secundárias recebem uma nota de rodapé “+ N reflexões já contabilizadas no plano”
Reflexões de múltiplas ordens / RIR / mapa de calor ainda são calculados uma única vez para o ouvinte principal (por ouvinte, O(speakers × surfaces³) é pesado demais, e o RIR é uma métrica de ponto único).
Motor de recomendação
PanelPlacementRecommender gera [PanelRecommendation], cada item com: posição (parede esquerda / parede direita / teto / parede frontal / parede traseira / canto), quantidade, prioridade (essencial / forte / recomendado / opcional), effectivenessMultiplier, targetReflectionPointId.
5.1 Alocação proporcional (maiores restos)
O antigo add() guloso deixava paredes sem nada: a parede esquerda consumia o orçamento → a parede direita ficava com 0; com orçamentos apertados, o teto / a frente / o fundo também ficavam sem. A nova versão usa o método do maior resto (largest-remainder):
- Calcular o desejado bruto de cada parede (contagem de pontos de reflexão + profundidade da sala + caso de uso)
- floor(raw × budget / total) como alocação inicial
- paredes com raw > 0 recebem pelo menos 1 painel; somente quando o orçamento é menor que o número de paredes com demanda é que as superfícies são zeradas, começando pelas de menor prioridade
- se a soma > orçamento, corta a partir do menor resto fracionário
- passagem de simetria esquerda/direita: se raw[0] == raw[1], força alloc[0] == alloc[1]
- redistribuir qualquer sobra pela maior fração
5.2 Modelos por caso de uso
Sem alto-falantes posicionados, aplicam-se templates específicos por caso de uso:
- Sala de reunião: teto 35% + parede do fundo 20% + cada parede lateral 12%
- Podcast: diretamente à frente do microfone 25% + cada lado 18% + teto 20%
- Estúdio: amortecimento simétrico nas quatro paredes + até 4 armadilhas de graves de canto dentro do orçamento (o verdadeiro desvio de orçamento só acontece no caminho de modos de baixa frequência densos da §5.3)
- Audição geral: base simétrica + um aviso para adicionar alto-falantes
5.3 Os itens físicos indispensáveis contornam o orçamento
As armadilhas de graves de canto são forçadas a 4 quando os modos de 80–150 Hz são densos, ignorando o limite totalPanel. Motivo: os cantos têm a maior pressão sonora e as armadilhas de graves são ~1,5× mais eficientes ali do que nas paredes — uma necessidade física, não um item de orçamento.
5.4 Ponderação próxima ao ouvinte
Um pós-processamento marca as recomendações de parede a até 1,5 m de um ouvinte como .nearListener, define effectivenessMultiplier ×1,15 e indica no título / motivo que se trata de uma “zona de absorção sensível a curta distância”.
Medição
6.1 Varredura ESS
Varredura logarítmica ESS (método de Farina): quatro predefinições + personalizada, durações de 3 / 6 / 12 s:
- Teste rápido no telefone: 180 Hz–4 kHz na saída integrada, 75 Hz–4 kHz na saída externa (única predefinição que permite o alto-falante integrado)
- Graves / subwoofer: 15–300 Hz (requer saída externa)
- Faixa completa: 20 Hz–20 kHz (requer saída externa)
- Personalizada: limites livres dentro de 10 Hz–20 kHz cobrindo pelo menos uma oitava (requer saída externa)
- Filtro inverso (
makeInverseSweep) = reversão temporal + um envelope de amplitude de +6 dB/oitava (crescente com a frequência, para compensar o espectro rosa do ESS) - Gravação via AVAudioRecorder em Linear PCM sem perdas (16 bits mono .caf, faixa dinâmica de ~96 dB — codecs perceptuais não têm lugar na medição de impulso; a taxa de amostragem segue a sessão de áudio — 44,1 kHz no microfone integrado, os 48 kHz nativos em microfones de medição USB)
- Leitura de volta → filtro casado → analyzeImpulse
O sinal de reprodução, a varredura inversa e a análise compartilham uma única configuração, com a frequência máxima limitada pela taxa de amostragem real; as predefinições de graves / faixa completa / personalizada se recusam a iniciar sem saída externa. As bandas de oitava do RT60 são escolhidas conforme a faixa real da varredura; a faixa de uma predefinição é a configuração do estímulo — a faixa confiável ainda depende do alto-falante, do microfone e da qualidade da medição.
makeInverseSweep(sampleRate:) é parametrizado) — a filtragem casada está sempre ativa, tanto a 44,1 kHz no microfone integrado quanto a 48 kHz em microfones de medição USB.6.2 Impulso de palma
Sem reprodução — o usuário bate uma palma forte dentro de uma janela de 1 s; são gravados 4,5 s no total (~3,5 s de cauda). O sinal vai direto para analyzeImpulse (não precisa de filtragem casada; uma palma já é um impulso estreito). Isso cobre a banda de oitava de 125 Hz, que a predefinição de telefone (a partir de 180 Hz) não alcança.
6.3 Medido vs simulado
MeasuredVsSimulatedCard obtém o RT60 medido mais recente do projeto e mostra o desvio em relação ao RT60 simulado da geometria atual:
6.4 Ruído ambiente & calibração
AmbientNoiseEstimator grava 3 s de silêncio (aparando 100 ms em cada extremidade) para o ruído de fundo da sala: média de banda larga / pico em dBFS, mais uma classificação de silêncio em 5 níveis (o dBFS é apenas relativo até uma calibração de SPL, depois da qual passa a ser exibido dB SPL). A calibração de latência de campo próximo reutiliza a mesma varredura ESS: segure o telefone a até 30 cm do alto-falante, meça o atraso do início da gravação até o pico do impulso (incluindo um pre-roll proposital de 250 ms), idealmente em 2–3 execuções — uma dispersão das últimas 3 leituras abaixo de 5 ms indica confiança alta; abaixo de 20 ms, média. A latência fica vinculada à rota atual de entrada + saída e se autoinvalida quando qualquer uma das duas muda; as medições de campo distante mostram o atraso de propagação já com ela subtraída.
6.5 Referência de tempo e qualidade da medição
- Referência de tempo acústica: um chirp precede a varredura como âncora temporal, redetectado a cada medição; em caso de falha na detecção, há fallback com indicação explícita da confiança de fase — sem alegar alinhamento absoluto entre medições.
- Desvio de relógio: estima a diferença de relógio entre os caminhos de reprodução e gravação e a corrige quando as condições permitem.
- Referência de loopback elétrico: interfaces USB com múltiplas entradas podem usar um segundo canal como referência de loopback; quando as condições não são atendidas, há fallback para a referência acústica. A rota do assistente de reprodução para Mac não usa o loopback elétrico local.
- Campos de qualidade: cada registro guarda a SNR, o piso de ruído, o status de clipping, o deslocamento temporal do IR, a entrada real e a taxa de amostragem disponíveis; o que não puder ser determinado fica vazio, nunca é inventado.
- Tratamento de clipping: uma varredura com clipping detectado é invalidada com aviso para refazer a medição; registros de palma são marcados e sinalizados.
- Comparação simulação / medição: a medição mais recente da sala é comparada lado a lado com o RT60 da simulação geométrica para avaliar o ajuste do modelo (limites em 6.3).
6.6 Microfones de medição USB e roteamento de saída
O seletor de entrada da página de medição lista o microfone integrado e entradas USB class-compliant (ex.: miniDSP UMIK-1 / UMIK-2); um listener de routeChangeNotification retorna ao microfone integrado quando o externo desaparece. As taxas de gravação e da varredura seguem a sessão (UMIK-1 = 48 kHz). Arquivos .txt de calibração de frequência no estilo REW / UMIK são importados diretamente (interpretando Sens Factor e o número de série, interpolação no domínio de frequência logarítmica; o arquivo de 90° é recomendado para medição de salas). A calibração é vinculada ao UID da porta de entrada — ao trocar para uma entrada incompatível, a calibração anterior não é aplicada.
Calibração única de referência SPL: encaixe um calibrador de nível sonoro (padrão 94 dB @ 1 kHz) sobre o microfone, o app amostra 3 s de nível para derivar o deslocamento dBFS → SPL; dB SPL só é exibido quando a entrada corresponde e existe uma calibração SPL — caso contrário, as leituras permanecem em dBFS. Instantâneo de calibração no momento da medição: novos registros de varredura / palma salvam a curva de calibração, o nome, o número de série, o Sens Factor, o offset de SPL e o UID de entrada vigentes naquele momento — substituir, limpar ou revincular a calibração depois não altera as leituras desses registros; o detalhe da medição, a prévia da resposta em frequência após a janela de IR, a exportação de texto da resposta e o PDF usam o mesmo instantâneo salvo. A calibração em si é persistida por UID da porta de entrada em ~/Library/Application Support/mic_calibration.json.
A reprodução passa pelo seletor de rota do sistema (um wrapper de AVRoutePickerView, colocado nos cards de varredura / calibração de campo próximo); a sessão libera AirPlay / Bluetooth A2DP / HFP para que os tons de teste toquem por alto-falantes externos, e volta para o alto-falante do aparelho quando nenhum está conectado. A entrada de gravação fica sempre explicitamente fixada na escolha do usuário (microfone USB ou integrado), para que o microfone de um dispositivo Bluetooth não roube a entrada no meio da reprodução e contamine a medição. Assistente de reprodução para Mac: descobre o AcouveroMacSpeaker na LAN via Bonjour, transfere primeiro o WAV da varredura para o Mac e depois envia o comando de reprodução, enquanto o iPhone grava — para rotas de saída de Mac que não conseguem hospedar diretamente o áudio da sessão de medição (essa rota não usa o loopback elétrico local).
6.7 Interoperabilidade com REW e arquivos
- Importação / exportação de texto de resposta em frequência: compatível com delimitadores comuns, linhas de comentário, notação científica e formatos decimais, validando o eixo de frequências na importação; arquivos sem coluna de fase não geram dados de fase.
- Exportação ciente da calibração: exporta dB SPL quando existe um instantâneo válido de calibração SPL, dBFS caso contrário — o cabeçalho rotula a unidade honestamente.
- Importação de áudio de IR: há suporte para respostas ao impulso WAV / AIFF; arquivos multicanal exigem escolha explícita do canal.
- Análise de varredura offline: exporte o arquivo de estímulo para reprodução externa e, após importar a gravação, é preciso informar as frequências inicial / final e a duração antes da deconvolução — os parâmetros da varredura nunca são adivinhados.
- Exportação de áudio para compartilhamento: IRs ou gravações brutas disponíveis podem ser compartilhados como WAV.
- Os resultados importados podem ser arquivados e fluir para os gráficos, sobreposições e análises aplicáveis; a admissão em um grupo de medição depende do tipo de dado e do escopo de confiança. Arquivos de projeto REW
.mdatnão são lidos no momento.
Gráficos e análise de medição
A página de detalhes da medição mostra gráficos conforme os dados que cada registro realmente possui — curvas ausentes nunca são fabricadas; os gráficos suportam modo claro / escuro, Dynamic Type e resumos de acessibilidade.
- RT60: gráfico de barras por banda de oitava com linha-alvo e cores de status.
- Decaimento de Schroeder: curva EDC com o intervalo de ajuste T20.
- Resposta em frequência: eixo logarítmico, suavização por oitava e leitura por cursor; exibida em dB SPL enquanto o instantâneo de calibração for válido.
- Resposta ao impulso: forma de onda com marcadores de zero temporal e de pico; amplitude relativa ao pico.
- Fase: vistas wrapped / unwrapped, inversão de polaridade, ajuste fino do zero temporal e indicação de confiança da referência.
- Atraso de grupo: atraso de grupo na banda válida, com análise opcional de fase mínima / fase em excesso.
- Janelamento de IR: referência de pico ou t=0, larguras esquerda / direita independentes, retangular / Hann / Tukey / Blackman; respostas original e janelada em prévia lado a lado — janelamento e ajuste de fase afetam apenas a prévia, nunca a medição armazenada.
- Espectrograma: decaimento tempo-frequência do IR, com intervalo de tempo e faixa dinâmica ajustáveis.
- Cascata e RT60 Decay: visualização do decaimento de graves, comprimento de janela / tempo / faixa dinâmica ajustáveis; ajustes de qualidade insuficiente não são exibidos.
- Espectro em tempo real (RTA): exibição de 1/1–1/24 de oitava, ponderação A / C / Z, resposta instantânea / Fast / Slow, retenção e limpeza de picos, nível global, nível de pico e frequência dominante.
- O RTA usa a calibração da entrada correspondente e é rotulado dBFS até existir uma calibração SPL; é mutuamente exclusivo com as medições formais (entrar libera o monitor de nível da página de medição, fechar o restaura), e callbacks antigos nunca sobrescrevem o estado novo após parar ou trocar de entrada.
Histórico, médias multiposição e sobreposições
- Arquivo V2: as medições guardam metadados, a gravação bruta / sidecar de IR disponível e os dados de análise; registros antigos continuam compatíveis em modo somente leitura, sem fabricar os dados brutos ausentes.
- Gestão do histórico: ver e apagar medições por sala, abrir detalhes ou escolher registros para análises posteriores.
- Comparação antes / depois: escolha dois registros para comparar RT60 global e por banda, respostas sobrepostas e diferenças nas bandas válidas.
- Sobreposição de 2–6 respostas: suavização de 1/3–1/48 de oitava, exibir / ocultar por curva, predefinições de faixa completa / graves; níveis sem alinhar, zerados na banda média ou alinhados à primeira curva, com deslocamentos na legenda; dados de origem intactos, e registros que não podem ser sobrepostos explicam o motivo.
- Grupos de medição: os registros carregam rótulo de posição, pesos e etapa antes / depois; crie um grupo a partir do histórico, entre em um, ou meça uma nova posição diretamente em um grupo.
- Médias multiposição: média aritmética em dB, média de potência RMS e — com referência de tempo confiável e espectros complexos — média complexa vetorial; mostra a curva média, as curvas membro e a faixa espacial min / max por frequência.
- Comparação de grupos: compara as respostas médias, as faixas espaciais e os RT60 médios de dois grupos, concluindo sobre a consistência espacial.
PEQ e alinhamento de subwoofer
9.1 Ajuste de alvo PEQ
- Curva-alvo parametrizada: nível-alvo, reforço de graves, inclinação de agudos e banda de ajuste.
- Restrições de filtros: quantidade de filtros, reforço / corte máximo e faixa de Q.
- Previsão e justificativa: medição, alvo, respostas dos filtros e resultado previsto em um mesmo gráfico, com métricas de melhoria e justificativa filtro a filtro.
- Exclusão de quedas profundas: quedas profundas causadas por cancelamento acústico são excluídas automaticamente, evitando grandes reforços sobre um nulo.
- Exportação: texto Generic, Equalizer APO, coeficientes miniDSP e YAML do CamillaDSP — apenas cálculo e exportação de ajustes; nada é gravado diretamente nos dispositivos.
Ainda não há editor manual de filtros, biblioteca de alvos nem arquivamento de resultados de EQ.
9.2 Alinhamento de duas vias e multi-subwoofer
- Alinhamento de duas vias: escolha uma medição da caixa de referência e uma do subwoofer ajustável, preveja a soma complexa e resolva atraso, ganho e polaridade; a curva somada é mostrada antes e depois do alinhamento.
- Multi-sub, multiassento: 1–4 subwoofers com medições independentes escolhidas por assento × fonte; otimize para todos os assentos, limite pelo pior assento ou otimize explicitamente apenas o assento principal.
- Resultados e compartilhamento: ajustes por sub, melhoria por assento, métricas médias e de pior caso, com ajustes compartilháveis como texto.
- Requisitos de dados: registros sem configuração de varredura, referência de tempo confiável ou IR listam o motivo da indisponibilidade em vez de serem excluídos silenciosamente.
Os resultados multiassento são atualmente uma tabela numérica sem sobreposição de curvas somadas por assento; o alinhamento é uma previsão que deve ser verificada medindo novamente após aplicar os ajustes; misturar fontes em taxas de amostragem diferentes ainda precisa de validação.
Assistente acústico de IA
A barra de ferramentas do histórico de medições oferece uma entrada de chat que usa a própria chave de API da Anthropic do usuário (guardada no Keychain do aparelho); o assistente envia à Anthropic a conversa e os dados de ferramentas necessários para cada requisição, e os custos de API ficam por conta da conta do usuário.
- Interface de conversa: respostas em streaming, status de execução de ferramentas, parar e limpar, estimativa de uso / custo.
- Leitura de dados de medição: listar registros, resumos e respostas parciais, e comparar medições.
- Ferramentas de cálculo: invoca exatamente os mesmos algoritmos de recomendação de PEQ e alinhamento de subwoofer da interface manual.
- Avaliação de sala e layout: lista salas, lê posições de caixas / ouvinte e modos, e prevê — em uma cópia do projeto — o efeito de mover equipamentos ou mudar o perfil de absorção; projetos salvos nunca são reescritos.
- Medição somente após confirmação: a IA propõe a finalidade e as salas candidatas e só inicia a varredura após a confirmação do usuário; recusar ou parar cancela a solicitação.
- Configurações de medição compartilhadas: a IA e a medição manual reutilizam o mesmo espaço de trabalho, herdando entrada, assistente de reprodução para Mac, faixa / duração da varredura, atraso e loopback; a página de chat mostra nível / progresso ao vivo com cancelamento e, ao terminar, restaura o contexto original de sala e grupo de medição.
- Restauração de sessão: uma sessão atual é salva e restaurável, marcando na restauração a execução de ferramenta interrompida; limpar a sessão apaga também o histórico e o uso.
Visualização e saída
11.1 Visualização 3D
Room3DController renderiza com SceneKit: paredes / piso / teto / nós de alto-falantes (cabinet fino + accent tick + channel pill) / ouvinte (FE glyph + drop stem) / obstáculos (SCNBox translúcido) / painéis recomendados (mockups de painéis por parede) / segmentos de caminho de reflexão de múltiplas ordens / sobreposição de mapa de calor de energia de reflexão.
11.2 Mapa de calor de energia de reflexão
O ReflectionEnergyMapGenerator acumula a energia de cada caminho de múltiplas ordens na grade uCells × vCells da parede atingida (padrão 12×6). Um mapa de energia por parede, sobreposto em 3D como uma camada de cor de energia (azul → ciano → verde → amarelo → vermelho).
EnergyHeatmapRenderer transforma a grade em uma UIImage: células em estilo de blocos de 16 px cada (sem interpolação entre as células, mantendo as bordas da grade legíveis; a suavização na parede vem da filtragem bilinear de textura do SceneKit) + um degradê personalizado de 5 paradas (azul → ciano → verde → amarelo → vermelho) + alfa mapeado linearmente de 0,20 a 0,85 conforme a posição da energia em uma escala fixa de −30…0 dB (−∞ totalmente transparente).
11.3 Prévia da convolução RIR
RIRPreviewPlayer usa AVAudioEngine + AVAudioPlayerNode para convolver um sinal seco sintetizado proceduralmente (corda dedilhada / sequência de palmas / fala semelhante a vogais — sem amostras embutidas) com a RIR simulada e reproduzi-lo, para que os usuários ouçam a diferença entre “sem tratamento” e “com tratamento”.
A convolução no domínio do tempo RIRConvolver.convolve(dry:rir:) é O(N × M) direta (N = amostras do sinal dry, M = amostras do RIR), normalizada pelo pico em 0,9 para evitar clipping.
11.4 Manual de instalação em PDF
ReportExporter renderiza um PDF A4 (595 × 842 pt) via UIGraphicsPDFRenderer:
- Visão geral da sala + captura 3D
- Gráfico de barras de RT60 em 6 bandas: estimativa de Sabine vs. alvo, com valores medidos sobrepostos quando houver dados
- Tabela de pontos de reflexão + tabela de modos + resumo de riscos
- Tabela de painéis recomendados (a BOM da lista de compras é exibida no app, não no PDF)
- Quando há dados de medição, são adicionados gráficos de RT60 / EDC / resposta em frequência / espectrograma / cascata / fase e um resumo de média multiposição; a unidade da resposta usa o instantâneo de calibração salvo no momento da medição (rotulado honestamente como dB SPL ou dBFS)
- Fonte Geist + fallback PingFangSC (cascadeList do CoreText, para o CJK não renderizar como □□□)
11.5 Persistência de projetos
ProjectStore (um actor) grava ~/Library/Application Support/Acouvero/projects.json: [RoomProject], cada um contendo o Room completo + speakers + listeners + recommendations + panelCountOverrides. No primeiro lançamento, os dados migram automaticamente do diretório legado FindEcho/ (FindEcho é o codinome interno anterior à mudança de nome, mantido deliberadamente nos símbolos do código).
MeasurementStore (um actor) grava ~/Library/Application Support/Acouvero/measurements.json: [MeasurementResult], vinculado a um projeto pelo roomId.
Ambos usam recuperação de decodificação em nível de elemento (um wrapper Throwable<T>) — um registro corrompido não derruba a lista inteira.
MicCalibrationStore grava ~/Library/Application Support/mic_calibration.json (raiz do diretório, sem subdiretório): a curva de calibração do REW + offset de SPL, vinculada ao UID da porta de entrada — trocar de entrada exige uma nova importação / calibração.
Notas de engenharia
12.1 SwiftUI + @Observable
A ViewModel (FindEchoViewModel) usa a macro @Observable do Swift 5.9 — os campos de estado são observados diretamente, sem Combine. Os resultados da AcousticAnalysis são armazenados em cache por meio de uma comparação Equatable de AcousticConfiguration:
var analysis: AcousticAnalysis {
let cfg = configuration
if let cached = cachedAnalysis, cachedConfiguration == cfg {
return cached
}
let result = analysisService.analyze(cfg)
cachedConfiguration = cfg
cachedAnalysis = result
return result
}
12.2 Isolamento de actors
ProjectStore e MeasurementStore são actors, serializando a lógica de IO / assíncrona de forma natural; RoomPlanService é uma classe @MainActor. Os chamadores da UI usam async / await, então a thread principal nunca é bloqueada por IO de disco.
12.3 Ciclo de vida do AVAudio
AudioSessionManager centraliza a categoria da sessão (.playback para a prévia do RIR, .playAndRecord para a medição). A proteção contra interrupções cobre os dois caminhos: no lado da medição, um InterruptionMonitor dentro de AudioMeasurementService observa AVAudioSession.interruptionNotification — uma ligação / Siri durante uma varredura, palma ou medição de ruído ambiente descarta o resultado com um erro; no lado da prévia, RIRPreviewPlayer também observa, parando automaticamente o nó do player e limpando isPlaying. Uma mudança de rota de áudio no meio da medição também invalida a execução.
12.4 Cancelamento de Task
A reprodução da convolução é uma tarefa em segundo plano; toques rápidos no botão disparam pendingPlayTask?.cancel() mais uma dupla verificação de Task.isCancelled para evitar escritas fora de ordem na MainActor.
12.5 Cobertura de testes
AllShapesAndUseCasesTests usa o framework Swift Testing para rodar a matriz completa — cada formato × cada caso de uso (um único ouvinte; os alto-falantes são um par estéreo ou um único central por caso de uso) — verificando:
analysis.reflectionPaths.count >= 1analysis.warningsinclui a nota de aproximação para salas não retangulares- o total de painéis recomendados é não negativo (totalPanelCount ≥ 0; sem asserção de correspondência com o orçamento)
- polígonos personalizados que se autointersectam fazem fallback com segurança (sem crash, degradando para um retângulo)
- vértices < 3 e outros fallbacks de casos de borda
12.6 Plataforma e localização
Destino de implantação iOS 17.0, iPhone + iPad; zero dependências de terceiros (sem pacotes SPM / CocoaPods) — apenas frameworks do sistema (SwiftUI / SceneKit / ARKit / RoomPlan / AVFoundation / Accelerate). A localização usa um catálogo .xcstrings: chinês como idioma de origem, mais English / Français / Deutsch / Español / Português (Brasil) — 6 idiomas no total, alternáveis no app ou seguindo o sistema.
Limitações conhecidas
- Multi-ordem em salas não retangulares usa a caixa delimitadora — o risco de flutter / slapback de 2ª/3ª ordem perto de cantos côncavos pode sair impreciso. Os pontos de primeira ordem em si usam o polígono real.
- Os modos da sala em salas não retangulares são estimados a partir do bounding box — modos exatos exigem FEM.
- Medição com o microfone integrado do iPhone — não calibrado; resposta de frequência / sensibilidade não plana. A confiança é sempre marcada como
.low, usada como referência cruzada com a estimativa geométrica. Com um microfone de medição USB (miniDSP UMIK-1 / UMIK-2) e o arquivo de calibração do REW carregado, os níveis por banda são corrigidos pela curva de calibração; uma calibração de referência única de 94 dB SPL então exibe os níveis ambiente e de medição como dB SPL absolutos (somente dBFS até lá). - Hipótese de fonte única do ISM — ignora a difração da caixa, a diretividade da corneta e outras características de alto-falantes reais.
- Sem modelagem de armadilhas de graves — problemas abaixo de 80 Hz ficam fora do modelo de Sabine e precisam de tratamento dedicado para baixas frequências.
- Precisão da varredura AR — os endpoints do RoomPlan erram tipicamente 3-8 cm; um loop de 12 segmentos pode acumular mais de 5 cm. A espessura das paredes não é modelada.
- Limites do escopo de análise — a média complexa vetorial entre taxas de amostragem ainda não é suportada; os resultados de alinhamento multiassento são atualmente uma tabela numérica, sem sobreposição de curvas somadas por assento; as sobreposições de resposta ainda não entram no relatório PDF.
- Ferramentas profissionais ainda não implementadas — sem registro de SPL de longo prazo, inteligibilidade STI ou medição de distorção (THD); impedância / Thiele-Small e API REST também estão ausentes.
- Aceitação em dispositivo pendente — o caminho real de API do assistente de IA (streaming com chave real / medição confirmada / arquivamento de gravações) e a cadeia completa de microfones UMIK em hardware real (incluindo o UMIK-2 a 96 kHz) ainda precisam de aceitação; passar no simulador não comprova o loop de áudio no dispositivo.
Referências
- Sabine, W. C. (1922). Collected Papers on Acoustics. Harvard University Press.
- Allen, J. B., & Berkley, D. A. (1979). Image method for efficiently simulating small-room acoustics. JASA, 65(4), 943–950.
- Schroeder, M. R. (1965). New method of measuring reverberation time. JASA, 37(3), 409–412.
- Farina, A. (2000). Simultaneous measurement of impulse response and distortion with a swept-sine technique. Audio Engineering Society Convention 108.
- Scheibler, R., Bezzam, E., & Dokmanić, I. (2018). Pyroomacoustics: A Python package for audio room simulation and array processing algorithms. ICASSP 2018.
- EBU Tech 3276. Listening conditions for the assessment of sound programme material.
- ITU-R BS.1116. Methods for the subjective assessment of small impairments in audio systems.
- BB93 — Acoustic design of schools (UK).
- THX Pro Cinema Certification requirements.