Compartilhar via


Este artigo foi traduzido por máquina.

Fator DirectX

Explorando filtros no XAudio2

Charles Petzold

Baixar o código de exemplo

Charles PetzoldNo Panteão dos notáveis formas de onda, a curva de seno simples reina supremo. Só de olhar para ele, você pode ver sua natureza suavemente ondulada por excelência — abrandar como atinge seus picos quase parando que ele cristas e progressivamente pegando velocidade, atingindo a velocidade máxima que cruza o eixo horizontal para iniciar outro desaceleração.

Esta impressão visual é confirmada por uma análise mais profunda da matemática. A velocidade instantânea da curva senoidal em qualquer ponto é uma reta tangente à curva. As velocidades do gráfico e você obterá outro senoide, deslocamento a partir do original por um quarto ciclo. Fazê-lo novamente usando essa segunda curva, e que é uma senoide mostrando aceleração, deslocamento a partir do original por meio de um ciclo, como mostrado na Figura 1.

senoide, sua velocidade (em violeta) e aceleração (Aqua)
Figura 1 senoide, sua velocidade (em violeta) e aceleração (Aqua)

Em termos de cálculo, a curva senoidal é o negativo de sua própria derivada de segunda ordem. De física básica, sabemos que a força é proporcional à aceleração, o que significa que, em qualquer processo físico, onde a força é inversamente proporcional ao deslocamento, o movimento é descrito por senoidais. Molas têm esta característica: Quanto mais você esticar--los, o maior a força na direção oposta. Mas muitas outras substâncias encontradas na natureza têm uma intrínseca springiness também, incluindo a compressão e rarefação do ar.

Considere-se a arrancar de uma seqüência de tensa, a batida de uma pele de animal esticada, a vibração do ar dentro de um tubo. Estes processos envolvem objetos elástico que vibram com o movimento característico de uma curva do seno. Mais comumente, esta curva de seno é complementada por adicionais senoidais cujas freqüências são múltiplos integrais da freqüência fundamental. As curvas de seno nesta Assembléia são conhecidas como harmônicos.

Por si só, uma única curva de seno é audivelmente muito chata. Mas colocar alguns deles juntos em um relacionamento harmônico e o som fica muito mais interessante. Na vida real, muitas vezes, as freqüências desses harmônicos não são exatos integrais de uma freqüência base em são mais corretamente chamadas de sobretons. É esta combinação de sobretons — incluindo como eles mudam ao longo do tempo — que define o som característico de um instrumento musical, ou timbre.

Um pequeno fragmento de uma determinada forma de onda pode ser graficamente em função do tempo, como mostrado na parte superior em Figura 2. Se esta forma de onda se repete a cada 4 ms, tem uma frequência de 250 Hz, que é perto dó médio no piano.

de forma de onda no domínio do tempo (topo) e domínio da freqüência (inferior)
Figura 2 de forma de onda no domínio do tempo (topo) e domínio da freqüência (inferior)

Com alguma análise de Fourier, podemos separar esta forma de onda em seus componentes senoidais e representá-la de uma maneira um pouco diferente, conforme mostrado na parte inferior em Figura 2. Este gráfico mostra as amplitudes relativas destas curvas de seno constitutivos organizados pela freqüência. No jargão de processamento de sinal, Figura 2 mostra a equivalência entre a representação de domínio do tempo, de uma forma de onda no topo e a representação do domínio de freqüência na parte inferior.

Na vida real, uma representação de um som no seu domínio de freqüência seria abranger o todo espectro de áudio de 20Hz a 20.000 Hz e constantemente mudar ao longo do tempo.

Filtro básico

A representação do domínio da frequência permite-nos pensar de som como uma coleção de ondas senoidais de diferentes frequências, e que muitas vezes é útil para compreender o processamento de áudio.

Um tipo muito comum de processamento de áudio envolve amplificando ou atenuando a certos intervalos de frequências do espectro de áudio, assim alterando a composição harmônica do som. Esta é uma ferramenta conhecida como um filtro. Em processamento de sinais analógicos, os filtros são circuitos; em processamento digital de sinais, eles são algoritmos.

Os tipos mais comuns de filtro são chamados de passa-baixa, passa alta e passa-banda; os termos referem-se para as freqüências dos filtros que deixam passar. O filtro low-pass enfatiza freqüências mais baixas, atenuando as freqüências mais altas. Da mesma forma, o filtro passa-alta atenua freqüências mais baixas. Ambos os filtros passa-baixo e passa-alto são definidos por uma frequência de corte especial que indica onde começa a atenuação. O filtro passa-banda não tem uma freqüência de corte, mas uma freqüência central serve a uma finalidade similar. As frequências fora de um intervalo ao redor dessa freqüência central são atenuadas.

A maioria dos filtros não podem simplesmente bloquear todas as ondas senoidais acima ou abaixo de uma determinada frequência. Em vez disso, uma onda senoidal com uma determinada frequência é atenuada com base na sua distância em relação a freqüência de corte ou centro com um efeito de ro. Encosta deste ro é regida por uma propriedade do filtro conhecido como Q, o que significa qualidade. Um filtro com uma maior Q tem um roll-off mais acentuada.

O fator Q é mais fácil de interpretar com um filtro passa-banda. Figura 3 mostra o efeito de um filtro passa-faixa aplicado a uma gama de frequências. A freqüência central está marcada no f0, e duas outras freqüências são marcadas f1 e f2, onde o filtro passa-faixa atenua a amplitude para 70,7% da amplitude f0.

de largura de banda em um filtro passa-banda
Figura 3 de largura de banda em um filtro passa-banda

Por 70,7 por cento? O poder de uma forma de onda é calculado como o quadrado da amplitude da onda, e f1 e f2 indicam as freqüências onde a forma de onda tem sido atenuada a metade de seu poder original. Porque o poder é a amplitude ao quadrado, a amplitude desses pontos é a raiz quadrada de 1/2, ou 0,707.

Q é calculado dividindo-se a freqüência central da diferença entre as duas freqüências de meia potência:

Q = f0 / (f2-f1)

No entanto, a diferença entre f2 e f0 não é o mesmo que a diferença entre f0 e f1. Em vez disso, as proporções são as mesmas: F2 / f0 é igual f0 / f1. Se f2 é duplo f1, que é uma oitava, e é bastante fácil de calcular o que Q é igual a raiz quadrada de 2, ou aproximadamente 1.414.

A relação entre f1 e f2 é conhecida como largura de banda do filtro e muitas vezes é especificada em oitavas. Para uma largura de banda B em oitavas, você pode calcular o Q da seguinte forma:

Quando diminui a largura de banda, Q aumenta e o roll-off é mais acentuada.

Eu disse que a f1 e f2 são as frequências que o filtro atenua a metade da energia de f0. Metade da energia é também conhecida como-3 decibéis. O decibel é uma escala logarítmica, aproximadamente, aproximando a percepção humana de sonoridade. A diferença em decibéis entre dois níveis de potência P1 e P0 é:

DB = 10·log10(P1/P0)

Se metade P0 P1, o logaritmo de base 10 de 0.5 é-0.301 e 10 vezes que é de aproximadamente -3. Ao lidar com amplitudes, decibéis são calculados como:

DB = 20·log10(A1/A0)

O logaritmo de base 10 de 0,707 é-0.15 e 20 vezes que também é -3.

Cada duplicação da amplitude corresponde a um aumento de 6 decibéis, razão pela qual a taxa de amostragem de 16 bits de CDs de áudio, por vezes, é dito que têm uma gama dinâmica de 96 decibéis.

Aplicação de um filtro

Se você estiver usando XAudio2 em um programa do Windows 8 para gerar sons ou modificar os sons de arquivos de música existentes, a aplicação de um filtro para aqueles sons é tão simples como inicializar três campos de uma estrutura de XAUDIO2_FILTER_PARAMETERS e chamar um método chamado SetFilterParameters.

Como você viu no recentes parcelas desta coluna, um programa cria uma ou mais instâncias de IXAudio2SourceVoice para definir as formas de onda próprios e uma única instância do IXAudio2­MasteringVoice combinar eficazmente todas as vozes de fonte em um único fluxo de áudio. Neste artigo você também verá como criar instâncias de IXAudio2SubmixVoice para controlar o processamento e mistura de sons em seu caminho para a voz de masterização. Vozes de fonte e vozes submix suportam o método de SetFilterParameters, mas somente se as vozes são criadas com o sinalizador XAUDIO2_VOICE_USEFILTER.

Uma chamada para SetFilterParameters requer um ponteiro para uma estrutura XAUDIO2_FILTER_PARAMETERS, que tem três campos:

Tipo: definido como um dos membros da enumeração XAUDIO2_FILTER_TYPE, que inclui membros de passa-baixa, passa-alta e filtros passa-banda, bem como um filtro entalhe (ou rejeição de banda), e um pólo filtros passa-baixo e passa-alta, que (como você verá em breve), são os filtros mais simples.

Frequency: definido como 2·sin(π·f0/fs), onde f0 é a frequência de corte e fs é a freqüência de amostragem, onde f0 não seja superior a 1/6 fs, o que significa que o valor definido para o campo é não maior que 1.

OneOverQ: 1 dividido pelo fator Q desejado, maior que zero e não superior a 1,5. Assim, Q não pode ser menor que 2/3, que corresponde a uma largura de banda de 2 oitavas.

Eu ainda não tenha mostrado você gráficos, semelhantes ao Figura 3, que ilustram como os filtros passa-baixo e passa-alta atenuam as frequências. Às vezes tais gráficos simplesmente mostram um efeito ro e assim podem ser perigosamente enganosos se o filtro real bastante não funciona assim. Tal é o caso com filtros XAudio2. Para o passa-baixa, passa-alta, passa-banda e filtros do entalhe, XAudio2 implementa um tipo de filtro digital, conhecido como um biquad, que envolve um algoritmo bastante simples, mas não cria um efeito de ro simples para filtros passa-baixo e passa-alta. (Se você estiver interessado no algoritmo, siga os links no artigo da Wikipédia "Filtro de biquad Digital" em bit.ly/Yoeeq1.)

Biquad filtros tendem a repercutir na freqüência central de um filtro passa-banda e perto das frequências de corte dos filtros passa-baixo e passa-alta. Isso significa que o filtro não atenua algumas frequências só amplifica outros. Para usar esses filtros inteligente, você deve estar ciente deste efeito. Felizmente, esta amplificação é bastante fácil de prever. O filtro passa-banda, a amplitude de uma onda senoidal na freqüência de centro é aumentada por um fator igual a Q. Para os filtros passa-baixo e passa-alta, a ampliação máxima perto da frequência de corte é igual a Q para valores mais elevados de Q, mas um pouco maior que Q para valores mais baixos.

Figura 4 mostra os efeitos de todos os tipos de filtro XAudio2 definidos para uma freqüência de 261,6 Hz (médio C) e um Q de 1.414. O eixo horizontal é logarítmico com um intervalo de 3 oitavas acima e abaixo da média c. O eixo vertical mostra a amplitude resultante para senoidais a essas frequências. A linha preta horizontal em uma amplitude de 1 é para sem filtro. Todas as outras linhas são identificadas com cores diferentes.

o efeito de filtros para um Q de 1.414
Figura 4 o efeito de filtros para um Q de 1.414

Por exemplo, o filtro passa-baixa não permite através de freqüências abaixo da freqüência de corte só amplifica-los, e esta amplificação aumenta à medida que você se aproximar para a frequência de corte. O filtro passa-alta tem o efeito oposto.

Figura 5 é semelhante ao Figura 4 , mas para um Q de 4.318, que está associado com uma largura de banda de 1/3 oitava. Observe que o eixo vertical é diferente para acomodar a maior amplificação.

o efeito de filtros para um Q de 1.414
Figura 5 o efeito de filtros para um Q de 1.414

Se você quiser usar um filtro passa-baixa ou passa-alta simples que não vai ampliar em tudo, ficar com os filtros de um pólo. Estas são filtros muito simples, regidos simplesmente por uma frequência de corte e eles não usam a configuração de Q. Eles funcionam muito como o simples graves e de agudos no estéreo do carro. Mas se você quiser usar os filtros mais sofisticados, o programa deve compensar qualquer amplificação pelo filtro.

Se sim, você poderia implementar seus próprios filtros, você pode fazer isso também, criando um XAudio2 áudio processamento objeto (XAPO), que é uma classe que obtém acesso a um fluxo de áudio e você pode implementar efeitos.

Observando o Volume

Para permitir que eu (e você) a experimentar com filtros, criei um projeto de Windows 8 chamado AudioFilterDemo que está incluído no código para download deste artigo. Figura 6 mostra-lo funcionando.

o programa de AudioFilterDemo
Figura 6 o programa de AudioFilterDemo

Os três osciladores para cima são todos independentemente controláveis, com uma faixa de freqüência que engloba 3 oitavas em ambos os lados do médio c. O slider é uma escala logarítmica, mas ajustável para 10 divisões entre as notas, que é um incremento conhecido como 10 centavos.

O filtro tem um slider de frequência, bem como um controle deslizante para Q. Todos os seletores de frequência têm as dicas de ferramentas que identificam a nota e sua freqüência. Figura 7 mostra o método que define o filtro sobre as três vozes de origem de forma de onda, sempre que houver uma mudança nos controles.

Figura 7 AudioFilterDemo configuração XAudio2 parâmetros de filtro

 

void MainPage::SetFilterParameters()
{
  if (pSawtoothOscillator != nullptr)
  {
    XAUDIO2_FILTER_PARAMETERS filterParameters;
    if (currentFilterType != -1)
    {
      double cutoffFrequency =
        440 * pow(2, (filterFrequencySlider->Value - 69) / 12);
      filterParameters.Type = XAUDIO2_FILTER_TYPE(currentFilterType);
      filterParameters.Frequency =
        float(2 * sin(3.14 * cutoffFrequency / 44100));
      filterParameters.OneOverQ = float(1 / filterQSlider->Value);
    }
    else
    {
      // Documentation:
      // "acoustically equivalent to the filter being fully bypassed"
      filterParameters.Type = LowPassFilter;
      filterParameters.Frequency = 1.0f;   
      filterParameters.OneOverQ = 1.0f;
    }
    pSawtoothOscillator->GetVoice()->SetFilterParameters(
      &filterParameters, XAUDIO2_COMMIT_ALL);
    pSquareWaveOscillator->GetVoice()->SetFilterParameters(
      &filterParameters, XAUDIO2_COMMIT_ALL);
    pSineWaveOscillator->GetVoice()->SetFilterParameters(
      &filterParameters, XAUDIO2_COMMIT_ALL);
  }
}

O painel inferior é um medidor de volume dimensionado em decibéis. Isso permite que você veja o volume resultante de uma particular forma de onda e filtro configurações. O programa faz sem ajustes de volume que não por meio de configurações de usuário. Se este medidor vai no vermelho, significa que o programa está gerando um som que é muito alto, e essa forma de onda é ser cortada antes de ir para o sistema de som no seu computador.

O medidor de volume baseia-se em uma classe de efeitos predefinidos. Figura 8 mostra o código que eu usei para criar uma instância desse efeito. O programa, em seguida, define um timer e chama o GetEffectParameters para obter os níveis de pico do som saída desde a última vez que foi chamado de GetEffectParameters.

Figura 8 criando um Volume efeito do medidor

 

// Create volume meter effect
IUnknown * pVolumeMeterAPO;
XAudio2CreateVolumeMeter(&pVolumeMeterAPO);
// Reference the effect with two structures
XAUDIO2_EFFECT_DESCRIPTOR effectDescriptor;
effectDescriptor.pEffect = pVolumeMeterAPO;
effectDescriptor.InitialState = true;
effectDescriptor.OutputChannels = 2;
XAUDIO2_EFFECT_CHAIN effectChain;
effectChain.EffectCount = 1;
effectChain.pEffectDescriptors = &effectDescriptor;
// Set the effect on the mastering voice
pMasteringVoice->SetEffectChain(&effectChain);
// Release the local reference to the effect
pVolumeMeterAPO->Release();

Um exercício interessante neste programa é jogar uma onda quadrada ou uma onda dente de serra através de um filtro passa-faixa com um Q de pelo menos 4 ou mais. Como alterar a freqüência do filtro, você pode ouvir os tons individuais da forma de onda. Uma onda quadrada tem apenas harmônicas ímpares, mas uma onda dente de Serra tem harmônicos ímpares e mesmo.

O equalizador gráfico

Tempo foi, toda configuração de áudio bem equipada casa incluiu um equalizador gráfico contendo uma fileira de potenciômetros de corrediça vertical, controlando um banco de filtros passa-faixa. Um equalizador gráfico, cada filtro band-pass cobre dois terços ou um terço de oitava do espectro de áudio total. Entre os técnicos de som profissionais, equalizadores gráficos são usados para ajustar a resposta acústica de uma sala de reforço ou várias freqüências de corte. Usuários domésticos muitas vezes organizar os controles deslizantes em um padrão de "smile" como imitou no Figura 9, impulsionando a ambas as extremidades de altas e baixas e deixando a faixa do meio mais suave para não excessivamente interferir na conversa.

o programa de GraphicEqualizer
Figura 9 o programa de GraphicEqualizer**

O programa de GraphicEqualizer permite que você carregar um arquivo MP3 ou WMA de sua biblioteca de música do Windows 8 e jogá-lo através de um equalizador gráfico de 1/3-oitava. O programa contém 26 barras verticais, cada um deles está associada um filtro passa-banda. Como você pode ver, cada controle deslizante é rotulado com a freqüência central para essa banda. Em teoria, cada freqüência central deve ser a raiz de cubo de 2 (ou cerca de 1,26) maior do que o filtro anterior, mas lotes de arredondamento é empregada para manter os números sensata. Baseado em uma fotografia de um equalizador gráfico de 1/3, achei na Wikipedia, eu rotulado os 26 sliders, começando com 20 Hz, 25, 31,5, 40 e até através de 6,3 KHz, parando aquém do limite de 7.350 Hz para uma taxa de amostragem de 44.100 Hz.

Equalizadores gráficos mais têm um grupo separado de potenciómetros para canais esquerdo e direito, mas eu decidi abrir mão dessa comodidade.

Você já viu como um único filtro pode ser aplicado a uma instância específica de IXAudio2SourceVoice, mas o programa de GraphicEqualizer precisa para aplicar filtros de 26 para uma voz de fonte. Isso é feito criando 26 instâncias de IXAudio2SubmixVoice correspondente a esses filtros (além de um par mais) e criando o que é chamado no XAudio2 um "gráfico de processamento de áudio", como mostrado na Figura 10. Cada caixa é uma instância de uma das três interfaces que derivam de IXAudio2Voice, e a caixa identifica o nome da variável usado no programa GraphicEqualizer.

o gráfico de processamento de áudio usado no programa GraphicEqualizer
Figura 10 o gráfico de processamento de áudio usado no programa GraphicEqualizer

Uma instância de IXAudio2SubmixVoice não é possível gerar som por conta própria. Que privilégio é reservado para vozes de fonte. Mas ele pode obter entrado de uma voz de fonte (ou outro voz de submix); aplicar um volume, filtro ou efeito; e passar o resultado para uma ou mais vozes submix ou a voz de masterização.

Na parte superior do Figura 10 é a voz de fonte que gera a música de um arquivo de música. No fundo é a voz de masterização que envia o resultado para o hardware de som do computador. São entre todas as vozes submix.

É um modelo de envio: Sempre que você cria uma voz de fonte ou submix voz, você pode indicar o destino voz (ou vozes) você quer receber a saída dessa voz. Mais tarde, você pode alterar o destino dos que saída com uma chamada para SetOutputVoices. Se você especificar NULL em ambos os casos, a saída vai para a voz de masterização.

Você indicar onde você deseja a saída de voz para ir com um ponteiro para uma estrutura XAUDIO2_VOICE_SENDS, que contém dois campos:

  • SendCount do tipo inteiro sem sinal
  • pSends, que é um ponteiro para zero ou mais estruturas de XAUDIO2_VOICE_DESCRIPTOR

O SendCount indica o número de estruturas de XAUDIO2_VOICE_DESCRIPTOR apontado pelo pSends. Pode ser zero para indicar que a voz não vai em qualquer lugar. A estrutura de XAUDIO2_VOICE_DESCRIPTOR também possui dois campos:

  • Sinalizadores, que podem ser 0 ou XAUDIO2_SEND_USEFILTER
  • pOutputVoice, do tipo IXAudio2Voice

As duas instâncias de IXAudio2SubmixVoice que alimentam o banco de 26 vozes de submix e aquele que consolida a saída dessas 26 vozes, não são estritamente necessários para construir o equalizador gráfico, mas eles simplificam a estrutura do programa. Sempre que o programa cria uma nova voz de origem — que acontece sempre que o usuário carrega em um novo arquivo de música — ele só precisa direcionar a saída da voz de origem para a instância de pSourceVoiceOutput.

O programa também tem um botão de caixa de seleção ignorar o equalizador. Para desligar o equalizador no gráfico de processamento de áudio, tudo que é necessário é chamada de SetOutputVoices em pSourceVoiceOutput com um ponteiro NULL indicando deve ir para a voz de masterização. Restaurar o equalizador envolve algumas linhas de código para restaurar a saída de pSource­VoiceOutput para pEqualizerInput.

Existem algumas maneiras para definir os filtros que compõem um equalizador gráfico. Uma abordagem é para cada controle deslizante alterar o Q desse filtro — com efeito, fazendo o filtro mais restritivo como você aumentar o controle deslizante. Mas eu decidi manter o fator Q de cada filtro constante em uma largura de banda de 1/3-oitava ou 4.318 e use o controle deslizante para variar o volume dessa voz submix. Equalizadores gráficos geralmente permitem que o banco de sliders entre uma gama de dB ± 6 e uma gama de dB ±12 de comutação, mas me decidi por uma gama de dB ±24 para mais efeitos extremos.

Quando um controle deslizante de equalizador está na sua posição central, a voz de submix correspondente tem um volume padrão de 1. Normalmente, isso significaria que um som apenas passa através da voz de submix inalterada. No entanto, aplicar um filtro com um Q de 4.318 em voz submix faz com que a amplitude aumentar por um fator de 4.318 no fre centro­Paulo. Para compensar isto, o programa define o volume do pEqualizerOutput de voz submix a 1 dividido por Q.

Com todos os controles deslizantes definidos em suas posições de centro, clique a caixa de seleção para alternar o equalizador dentro e fora do gráfico de áudio faz com que nenhuma mudança no volume. O som muda um pouco —­, sem dúvida, resultante da forma como os diversos band-pass filtros sobreposição — mas não o volume global.

Os controles deslizantes do equalizador tem suas propriedades mínimo definidas como -24 e máximo definido para 24, correspondente ao ganho em decibéis. Quando as alterações de valor do controle deslizante, o volume para a voz de submix correspondente é definida no manipulador de evento ValueChanged, como assim:

 

Slider^ slider = dynamic_cast<Slider^>(sender);
int bandIndex = (int)slider->Tag;
float amplitude = float(pow(10, (args->NewValue / 20)));
pEqualizerBands[bandIndex]->SetVolume(amplitude, 0);

Que o cálculo da amplitude é um inverso do cálculo decibel mostrado anteriormente. Os intervalos de amplitude resultante de cerca de 0,06 (a-24 dB) para cerca de 16 (em 24 dB). Caso você tenha em mente que cada mudança de 6 dB é uma redução para metade, ou duplicar a amplitude de centro de 1, esses intervalos fazem sentido. Mas se você pôr em marcha todos os controles deslizantes para suas configurações máximas, a amplitude total aumenta por um fator de 16, e o resultado é susceptível de ser cortado e distorcido.

Em outras palavras, o programa assume implicitamente que o usuário mantém uma abordagem equilibrada à vida e irá reduzir alguns sliders aumentando outros.

Charles Petzold é colaborador da MSDN Magazine há muito tempo e é o autor de “Programming Windows, 6th edition” (O’Reilly Media, 2012), um livro sobre a criação de aplicativos para o Windows 8. O site dele é charlespetzold.com.

Agradecemos ao seguinte especialista técnico pela revisão deste artigo: Richard Fricks (Microsoft)