Convolução ACM
A convolução ACM executa na CPU a reverberação particionada do FMOD. Lotes de entrada compartilhada transformam um sinal seco, aplicam partições de impulso em float ou em meia precisão e fazem overlap-add do resultado úmido.
Notas do repositório até 10 de outubro de 2026
O erro que ela fechou
Subnautica: Below Zero podia começar com estalos, cair em silêncio e se recuperar minutos depois. O PCM que chegava ao dispositivo do anfitrião continha floats não finitos. Um rastreio em memória dos callbacks de DSP do FMOD mostrou áudio finito entrando na reverberação por convolução e lixo saindo dela. As funções HLE sceAcm_ConvReverb_SharedInput, o envio do lote e sceAcmBatchWait devolviam sucesso sem escrever uma saída.
O FMOD então misturava esse buffer úmido não escrito no grafo. Os NaNs se espalhavam pelos efeitos posteriores. O saneador do AudioOut os substituía por silêncio e não conseguia reparar o histórico envenenado do filtro. Um segundo erro fazia a espera no identificador inicial de lote -1 ter sucesso. O FMOD trata uma espera inicial que falhou como 'ainda não há buffer úmido anterior' e só mistura depois de um trabalho de fato concluído.
Convolução particionada
O layout do descritor corresponde ao observado no backend ACM do FMOD. As partições da resposta ao impulso são bins complexos intercalados. Um bloco de B bins pertence a uma transformada de comprimento 2B. Os buffers de entrada, de saída e de sobreposição são separados e planares. Só são aceitos os layouts de espectro float32 e float16 de deslocamento zero que foram observados.
Um lote roda na CPU. O histórico de entrada é mantido entre grãos e através da volta do anel, e a sobreposição é carregada para o grão de saída seguinte. Várias saídas podem compartilhar uma transformada de entrada e um avanço de histórico. A entrada mono pode alimentar mais de um canal. A transformada inversa é normalizada. Uma inversa independente de uma partição de impulso real colocou a energia na primeira metade do bloco preenchido com zeros, o que confirma o sinal do espectro. O impulso empacotado que o jogo usa omite o bin de Nyquist.
Esperas, limites e o reteste
Os construtores capturam arrays de ponteiros e ganhos num registro de comando limitado. O próprio PCM é lido quando o lote começa. Uma espera só tem sucesso para um lote que este contexto de fato concluiu. Destruir o contexto libera o histórico. Limites inválidos, codificações de comando desconhecidas e layouts sem suporte devolvem erros.
Operações isoladas de FFT, de IFFT e de panner do ACM não estão implementadas. Os canais têm teto de 8, as saídas de 32 e o bloco de 1024. O armazenamento do histórico é limitado. No runner da compilação assinada, 90 instantâneos de PCM, um por segundo, não continham amostras não finitas, com pico de 0.1255 e o primeiro áudio acima de 0.001 aos 11.03 segundos. O mantenedor confirmou o som. As amostras são uma sonda, não toda amostra que o jogo emitiu.
O que está implementado
- Convolução FFT particionada para os lotes de reverberação de entrada compartilhada e de IR compartilhada do FMOD.
- Espectros complexos em float32 e float16, overlap-add e histórico entre grãos.
- Esperas que só têm sucesso para um lote concluído daquele contexto. O identificador inicial -1 falha.
- PCM finito no reteste de Subnautica, com os estalos e o silêncio posterior já ausentes.
O que ainda falta
- Chamadas isoladas de FFT, de IFFT e de panner do ACM não estão implementadas.
- Deslocamentos de espectro diferentes de zero e layouts de roteamento não medidos são rejeitados.
- A convolução roda na CPU. Não há caminho de FFT na GPU.
Mecanismos relacionados
O que estas páginas descrevem