Il presente articolo è stato tradotto automaticamente.

DirectX Factor

Analisi dei filtri in XAudio2

Charles Petzold

Scarica il codice di esempio

Charles PetzoldNel pantheon di notevole di forme d'onda, la sinusoide semplice regna sovrana.Solo guardandolo, si può vedere la sua natura dolcemente ondulato quintessenza — rallentando raggiungere le sue vette, fermandosi quasi come esso creste e poi progressivamente raccogliendo velocità, raggiungere la velocità massima come esso attraversa l'asse orizzontale per iniziare un altro rallentamento.

Questa impressione visiva è confermata da una più profonda analisi matematica.La velocità istantanea della sinusoide in qualsiasi punto è una linea tangente alla curva.Grafico quelle velocità, e si ottiene un'altra sinusoide, compensato dall'originale da un quarto ciclo.Farlo nuovamente utilizzando questa seconda curva, e che è una curva sinusoidale mostrando accelerazione, compensato dall'originale da mezzo un ciclo, come mostrato Figura 1.

A Sine Curve, Its Velocity (in Violet) and Acceleration (in Aqua)
Figura 1 sinusoide, sua velocità (in viola) e accelerazione (in azzurro)

In termini di calcolo, la curva sinusoidale è il negativo della sua derivata seconda.Dalla base fisica, sappiamo che la forza è proporzionale all'accelerazione, che significa che in qualsiasi processo fisico dove la forza è inversamente proporzionale allo spostamento, movimento è descritto da curve seno.Molle hanno questa caratteristica: Più si tratto li, la maggiore forza nella direzione opposta.Ma molte altre sostanze presenti in natura hanno un'intrinseca springiness pure, compreso la compressione e rarefazione dell'aria.

Si consideri la spiumatura di una stringa tesa, le intercettazioni di una pelle di animale allungata, la vibrazione dell'aria all'interno di un tubo.Questi processi coinvolgono elastico oggetti che vibrano con il caratteristico movimento di una curva sinusoidale.Più comunemente, questa curva sinusoidale è integrata da ulteriori curve seno le cui frequenze sono integrali multipli della frequenza fondamentale.Le curve del seno in questo assemblaggio sono conosciute come armoniche.

Di per sé, una singola curva sinusoidale è udibile abbastanza noiosa.Ma mettere un paio di loro insieme in un rapporto armonico e il suono si ottiene molto più interessante.Nella vita reale, molto spesso le frequenze di queste armoniche non sono integrali esatti di una frequenza di base e sono più correttamente chiamate ipertoni.È questa combinazione di tratti — tra cui come cambiano nel tempo — che definisce uno strumento musicale suono caratteristico, o timbro.

Un piccolo frammento di una particolare forma d'onda può essere rappresentati graficamente in funzione del tempo, come mostrato in alto Figura 2.Se questa forma d'onda si ripete ogni 4 ms, ha una frequenza di 250 Hz, che è vicino al do centrale del pianoforte.

A Waveform in Time Domain (Top) and Frequency Domain (Bottom)
Figura 2 forme d'onda nel dominio del tempo (in alto) e dominio di frequenza (inferiore)

Con alcune analisi di Fourier, possiamo separare questa forma d'onda nelle sue curve seno costituente e rappresentarlo in un modo un po' diverso, come illustrato nella parte inferiore in Figura 2.Questo grafico mostra le ampiezze relative di questi costituenti curve seno organizzate dalla frequenza.Nel gergo di elaborazione del segnale, Figura 2 dimostra l'equivalenza della rappresentazione tempo-dominio di una forma d'onda sulla parte superiore e la rappresentazione di frequenza-dominio sul fondo.

Nella vita reale, una rappresentazione di un suono nel suo dominio di frequenza sarebbe comprendere l'intero spettro audio da 20 Hz a 20.000 Hz e cambiare continuamente nel corso del tempo.

Nozioni di base del filtro

La rappresentazione di frequenza-dominio ci permette di pensare del suono come un insieme di onde sinusoidali di varie frequenze, e che spesso è utile nella comprensione di elaborazione audio.

Un tipo molto comune di elaborazione audio coinvolge amplificando o attenuando determinate gamme di frequenze dello spettro audio, così alterando la composizione armonica del suono.Questo è uno strumento noto come un filtro.Nell'elaborazione dei segnali analogici, i filtri sono circuiti; nell'elaborazione dei segnali digitali, che sono gli algoritmi.

I più comuni tipi di filtro sono chiamati passa-basso, passa-alto e passa-banda; i termini si riferiscono alle frequenze dei filtri ha lasciati attraverso.Il filtro passa-basso enfatizza le frequenze più basse di attenuare le frequenze più alte.Allo stesso modo, il filtro passa-alto attenua le frequenze più basse.Entrambi i filtri passa-basso e passa-alto sono definiti da una particolare frequenza di taglio che indica dove inizia l'attenuazione.Il filtro passa-banda non ha una frequenza di taglio, ma una frequenza centrale serve uno scopo simile.Le frequenze di fuori di un intervallo intorno a quella frequenza centrale vengono attenuate.

La maggior parte dei filtri non possono semplicemente bloccare tutte le onde sinusoidali di sopra o di sotto di una determinata frequenza.Invece, una sinusoide con una particolare frequenza viene attenuata basato sulla relativa distanza dalla frequenza di cut-off o al centro con un effetto di roll-off.La pendenza di questo roll-off è governata da una proprietà del filtro conosciuto come Q, che è sinonimo di qualità.Un filtro con un più alto Q ha un roll-off più ripida.

Il fattore Q è più facile da interpretare con un filtro passa-banda.Figura 3 Mostra l'effetto di un filtro passa-banda applicato ad una gamma di frequenze.La frequenza centrale è contrassegnata a f0, e due altre frequenze sono contrassegnate f1 e f2 dove il filtro passa-banda attenua l'ampiezza al 70,7% dell'ampiezza f0.

Bandwidth in a Band-Pass Filter
Figura 3 larghezza di banda di un filtro passa-banda

Perché 70,7 per cento?La potenza di una forma d'onda viene calcolata come il quadrato dell'ampiezza della forma d'onda, e f1 e f2 indicare le frequenze dove la forma d'onda è stata attenuata per mezzo suo potere originale.Perché il potere è ampiezza al quadrato, l'ampiezza in quei punti è la radice quadrata di 1/2, o 0.707.

Q è calcolata dividendo la frequenza centrale per la differenza tra le due frequenze di metà potenza:

Q = f0 / (f2 – f1)

Tuttavia, la differenza tra f2 e f0 non è lo stesso come la differenza tra f0 e f1.Invece, i rapporti sono gli stessi: F2 / f0 è uguale a f0 / f1.Se f2 è f1 doppia, che è un'ottava, ed è abbastanza facile da calcolare che Q è uguale alla radice quadrata di 2, o circa 1,414.

Il rapporto tra f2 e f1 è noto come larghezza di banda del filtro e spesso viene specificato in ottave.Per una larghezza di banda B in ottave, è possibile calcolare Q in questo modo:

Come si riduce la larghezza di banda, Q aumenta e il roll-off è più ripida.

Ho detto che f2 e f1 sono le frequenze che il filtro attenua a metà della potenza di f0.La metà della potenza è anche conosciuto come-3 decibel.Il decibel è una scala logaritmica approssimazione circa la percezione umana della sonorità.La differenza in decibel tra due livelli di potenza P1 e P0 è:

DB = 10·log10(P1/P0)

Se P1 è mezza P0, il logaritmo in base 10 di 0,5 è-0.301 e 10 volte che è di circa -3.Quando si tratta di ampiezze, decibel sono calcolati come:

DB = 20·log10(A1/A0)

Il logaritmo in base 10 di 0.707 è-0.15 e 20 volte che è anche -3.

Ogni raddoppio di ampiezza corrisponde ad un aumento di 6 decibel, motivo per cui la frequenza di campionamento a 16 bit del CD audio è a volte detto di avere una gamma dinamica di 96 decibel.

Applicazione di un filtro

Se si sta utilizzando XAudio2 in un programma di Windows 8 per generare suoni o modificare i suoni di file musicali esistenti, applicando un filtro di quei suoni è semplice come l'inizializzazione di tre campi di una struttura XAUDIO2_FILTER_PARAMETERS e chiamata a un metodo denominato SetFilterParameters.

Come si è visto nel recente rate di questa colonna, un programma crea una o più istanze di IXAudio2SourceVoice per definire le forme d'onda in se stessi e una singola istanza di IXAudio2­MasteringVoice a combinare efficacemente tutte le voci di origine in un unico flusso audio.Più avanti in questo articolo si vedrà come creare istanze di IXAudio2SubmixVoice per controllare l'elaborazione e la miscelazione di suoni sul loro senso alla voce mastering.Voci di fonte sia voci submix supportano il metodo SetFilterParameters, ma solo se le voci sono create con il flag XAUDIO2_VOICE_USEFILTER.

Una chiamata a SetFilterParameters richiede un puntatore a una struttura XAUDIO2_FILTER_PARAMETERS, che dispone di tre campi:

Tipo: impostare su uno dei membri dell'enumerazione XAUDIO2_FILTER_TYPE, che include membri per passa-basso, passa-alto e filtri passa-banda, come pure un filtro notch (o band-reject), e filtri passa-basso e passa-alto di single-pole, che (come vedrà a breve) sono i più semplici filtri.

Frequenza: impostare su 2·sin(π·f0/fs) dove f0 è la frequenza di taglio e fs è la frequenza di campionamento, dove f0 non è maggiore di 1/6 fs, che significa che il valore impostato per il campo non sia superiore a 1.

OneOverQ: 1 diviso per il fattore Q desiderato, maggiore di zero e non superiore a 1,5.Così, Q non può essere inferiore a 2/3, che corrisponde ad una larghezza di banda di 2 ottave.

Ancora non ho mostrato si grafici, simili a Figura 3, che illustrano come i filtri passa-basso e passa-alto attenuano le frequenze.A volte tali grafici semplicemente mostrano un effetto roll-off e quindi possono essere pericolosamente ingannevoli se il filtro effettivo abbastanza non funziona come quello.Tale è il caso con filtri XAudio2.Per il passa-basso, passa-alto, passa-banda e filtri notch, XAudio2 implementa un tipo di filtro digitale noto come una biquad, che coinvolge un algoritmo piuttosto semplice, ma non creare un semplice effetto di roll-off per i filtri passa-basso e passa-alto.(Se siete interessati nell'algoritmo, seguire il link dell'articolo di Wikipedia su "Filtro digitale biquad" a bit.ly/Yoeeq1.)

Filtri biquad tendono a risuonare alla frequenza centrale di un filtro passa-banda e vicino le frequenze di taglio dei filtri passa-basso e passa-alto.Questo significa che il filtro non solo attenua alcune frequenze, ma amplifica gli altri.Per utilizzare questi filtri in modo intelligente, è necessario essere consapevoli di questo effetto.Fortunatamente, questa amplificazione è abbastanza facile da prevedere.Per il filtro passa-banda, l'ampiezza di un'onda sinusoidale alla frequenza di centro è aumentato di un fattore pari a Q.Per i filtri passa-basso e passa-alto, l'amplificazione massima vicino la frequenza di taglio è uguale a Q per i valori maggiori di Q, ma un po' maggiore di Q per valori inferiori.

Figura 4 Mostra gli effetti di tutti i tipi di filtro XAudio2 impostati per una frequenza di 261,6 Hz (do centrale) e un Q di 1.414.L'asse orizzontale è logaritmica con una gamma di 3 ottave sopra e sotto c. medioL'asse verticale indica l'ampiezza risulta per curve in seno a tali frequenze.La linea nera orizzontale ad un'ampiezza di 1 è per nessun filtro.Tutte le altre linee sono identificate con colori diversi.

The Effect of Filters for a Q of 1.414
Figura 4 l'effetto dei filtri per un Q di 1.414

Ad esempio, il filtro passa-basso non solo lascia passare le frequenze di sotto della frequenza di taglio, ma li amplifica, e questa amplificazione aumenta quanto più ci si avvicina alla frequenza di taglio.Il filtro passa-alto ha l'effetto opposto.

Figura 5 è simile a Figura 4 , ma per un Q di 4.318, che è associato con una larghezza di banda di 1/3 di ottava.Si noti che l'asse verticale è diverso per accogliere l'amplificazione maggiore.

The Effect of Filters for a Q of 4.318
Figura 4 l'effetto dei filtri per un Q di 1.414

Se si desidera utilizzare un semplice filtro passa-basso o passa-alto che non amplifica affatto, bastone ai filtri unipolari.Questi sono filtri molto semplice governati semplicemente da una frequenza di taglio e non utilizzare l'impostazione di Q.Funzionano molto come il semplice bass e treble controlli su un'autoradio.Ma se si desidera utilizzare i filtri più sofisticati, il vostro programma deve compensare qualsiasi amplificazione dal filtro.

Se è piuttosto sarebbe implementare filtri personalizzati, è possibile fare che pure creando un XAudio2 Audio Processing Object (XAPO), che è una classe che ottiene l'accesso a un flusso audio e in grado di implementare gli effetti.

Guardando il Volume

Per consentire a me (e voi) di sperimentare con i filtri, ho creato un progetto di Windows 8 denominato AudioFilterDemo che è incluso nel codice scaricabile per questo articolo.Figura 6 illustrato in esecuzione.

The AudioFilterDemo Program
Figura 6 il programma AudioFilterDemo

I tre oscillatori verso la parte superiore sono tutti indipendentemente controllabili, con una gamma di frequenze che comprende 3 ottave su entrambi i lati del medio c.Il dispositivo di scorrimento è una scala logaritmica, ma regolabile a 10 divisioni tra le note, che è un incremento conosciuto come 10 centesimi.

Il filtro ha un dispositivo di scorrimento frequenza così come un dispositivo di scorrimento per Q.Tutti i cursori di frequenza hanno descrizioni comandi che identificano la nota e la sua frequenza.Figura 7 illustra il metodo che imposta il filtro sulle tre voci di origine delle forme d'onda, ogni volta che c'è un cambiamento nei controlli.

Figura 7 AudioFilterDemo impostazione parametri di filtro XAudio2

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);
  }
}

Il pannello inferiore è un misuratore di volume in scala in decibel. Questo consente di vedere il volume risulta per una particolare forma d'onda e filtro impostazioni. Il programma non rende regolazioni di volume diverso attraverso le impostazioni dell'utente. Se questo contatore va in rosso, significa che il programma sta generando un suono che è troppo forte, e che forma d'onda sono essere ritagliata prima di entrare nel sistema audio sul vostro computer.

Il misuratore del volume è basato su una classe di effetti predefiniti. Figura 8 viene illustrato il codice che ho utilizzato per creare un'istanza di tale effetto. Il programma quindi imposta un timer e chiama GetEffectParameters per ottenere i livelli di picco del suono uscita dall'ultima volta che è stato chiamato GetEffectParameters.

Figura 8 la creazione di un Volume metro effetto

// 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();

Un esercizio interessante in questo programma è quello di giocare un onda quadra o un'onda a dente di sega attraverso un filtro passa-banda con una Q di almeno 4 o giù di lì. Come si modifica la frequenza del filtro, è possibile ascoltare i singoli tratti della forma d'onda. Un'onda quadra ha solo armoniche dispari, ma un'onda a dente di sega ha armoniche sia pari e dispari.

L'equalizzatore grafico

Tempo era, ogni configurazione audio casa ben attrezzata incluso un equalizzatore grafico contenente una riga di potenziometri slide verticale controllare una banca di filtri passa-banda. In un equalizzatore grafico, ogni filtro passa-banda copre due terzi o un terzo di ottava dello spettro audio totale. Tra ingegneri del suono professionali, equalizzatori grafici sono utilizzati per regolare la risposta acustica di una stanza da incrementare o varie frequenze di taglio. Gli utenti domestici spesso organizzare i cursori in un modello "smile" come imitato Figura 9, incrementando l'estremità alta e bassa e lasciando la gamma media morbida in modo da non spropositatamente interferire con la conversazione.

The GraphicEqualizer Program
Figura 9 il programma GraphicEqualizer

Il programma GraphicEqualizer consente di caricare un file MP3 o WMA dalla tua libreria Musica di Windows 8 e riprodurla attraverso un equalizzatore grafico a 1/3-ottava. Il programma contiene 26 cursori verticali, ciascuno dei quali è associato un filtro passa-banda. Come potete vedere, ogni cursore è etichettato con la frequenza di centro banda. In teoria, ogni frequenza centrale dovrebbe essere la radice cubica di 2 (o circa 1.26) superiore al filtro precedente, ma un sacco di arrotondamento è impiegato per mantenere i numeri sensata. Basato su una fotografia di un equalizzatore grafico a 1/3 ho trovato su Wikipedia, etichettato i 26 cursori a partire da 20 Hz, 25, 31,5, 40 e fino attraverso 6,3 KHz, fermandosi a corto di limite per una frequenza di campionamento di 44.100 Hz Hz 7.350.

Equalizzatori grafici più avere un gruppo separato di potenziometri per i canali destro e sinistro, ma ho deciso di rinunciare a tale servizio.

Hai visto come un singolo filtro può essere applicato a una particolare istanza di IXAudio2SourceVoice, ma il programma GraphicEqualizer ha bisogno di applicare 26 filtri per una voce di origine. Questa operazione viene eseguita creando 26 istanze di IXAudio2SubmixVoice corrispondenti a questi filtri (più un altro paio) e la creazione di che cosa ha chiamato XAudio2 un "grafico di elaborazione audio", come mostrato Figura 10. Ogni scatola è un'istanza di una delle tre interfacce che derivano da IXAudio2Voice, e la casella identifica il nome della variabile utilizzata nel programma GraphicEqualizer.

The Audio Processing Graph Used in the GraphicEqualizer Program
Figura 10 il grafico di elaborazione Audio utilizzato nel programma di GraphicEqualizer

Un'istanza di IXAudio2SubmixVoice non può generare suono sul proprio. Che privilegio è riservato a voci di origine. Ma si può ottenere input da una fonte voce (o un altro submix); applicare un volume, filtro o effetto; e passare il risultato su una o più voci submix, o la voce di mastering.

Nella parte superiore della Figura 10 è la voce di sorgente che genera la musica da un file musicale. In fondo è la voce di mastering che invia il risultato l'hardware audio del computer. Nel mezzo ci sono tutte le voci submix.

Si tratta di un modello push: Ogni volta che si crea una voce di origine o submix, è possibile indicare la voce di destinazione (o voci) si desidera ricevere l'output di tale voce. Successivamente è possibile modificare la destinazione di output con una chiamata a SetOutputVoices. Se si specifica NULL in entrambi i casi, l'uscita va alla voce mastering.

Indicare dove si desidera che l'output di una voce di andare con un puntatore a una struttura XAUDIO2_VOICE_SENDS, che contiene due campi:

  • SendCount di tipo unsigned integer
  • pSends, che è un puntatore a zero o più strutture XAUDIO2_VOICE_DESCRIPTOR

Il SendCount indica il numero di strutture XAUDIO2_VOICE_DESCRIPTOR punta pSends. Può essere zero per indicare che la voce non va da nessuna parte. La struttura XAUDIO2_VOICE_DESCRIPTOR ha anche due campi:

  • Bandiere, che possono essere 0 o XAUDIO2_SEND_USEFILTER
  • pOutputVoice, di tipo IXAudio2Voice

Le due istanze di IXAudio2SubmixVoice che alimentano la banca di 26 voci submix e quella che consolida l'output da quelle 26 voci, non sono strettamente necessari per costruire l'equalizzatore grafico, ma semplificano la struttura del programma. Ogni volta che il programma crea una nuova voce di origine — che succede ogni volta che l'utente carica in un nuovo file di musica, ha solo bisogno di dirigere la voce di origine in uscita per l'istanza di pSourceVoiceOutput.

Il programma ha anche un pulsante casella di controllo per bypassare l'equalizzatore. Per scollegare l'equalizzatore grafico di elaborazione audio, tutto ciò che è necessario è di chiamare SetOutputVoices su pSourceVoiceOutput con un puntatore NULL indica dovrebbe andare alla voce mastering. Ripristinando l'equalizzatore coinvolge poche righe di codice per ripristinare l'uscita da pSource­VoiceOutput a pEqualizerInput.

Ci sono un paio di modi per definire i filtri che compongono un equalizzatore grafico. Un approccio è per ogni dispositivo di scorrimento modificare il Q di tale filtro, rendendo in pratica il filtro più restrittivo come si aumenta il dispositivo di scorrimento. Ma ho deciso di mantenere costante il fattore Q di ciascun filtro in una banda di 1/3-ottava, o 4.318 e usare il cursore per variare il volume di quella voce submix. Equalizzatori grafici consentono solitamente la banca di cursori tra un intervallo di ± 6 dB e un intervallo di ± 12 dB di commutazione, ma ho deciso su una gamma di dB corente per effetti più estremi.

Quando un dispositivo di scorrimento dell'equalizzatore è in posizione centrale, la corrispondente voce submix ha un volume predefinito di 1. Normalmente questo significa che un suono passa solo attraverso la voce submix inalterata. Tuttavia, applicando un filtro con una Q di 4.318 voce submix provoca l'ampiezza aumentare di un fattore di 4.318 presso il centro fre­Frequency. Per compensare per questo, il programma imposta il volume della pEqualizerOutput voce submix 1 diviso da Q.

Con tutti i dispositivi di scorrimento impostati nelle loro posizioni di centro, cliccando la casella di controllo per attivare l'equalizzatore dentro e fuori il grafico audio non provoca nessun cambiamento di volume. Il suono cambia un po ' —­senza dubbio risultante dal modo in cui i vari passa-banda filtri sovrapposizione — ma non il volume complessivo.

I cursori dell'equalizzatore sono loro proprietà minimo impostato su -24 e massimo impostato su 24, corrispondente al guadagno in decibel. Quando i cambiamenti di valore del cursore, il volume per la corrispondente voce submix è impostata nel gestore dell'evento ValueChanged, come così:

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

Tale calcolo ampiezza è un inverso del calcolo decibel mostrato in precedenza. Gli intervalli di ampiezza risultante da circa 0,06 (a-24 dB) a circa 16 (a 24 dB). Se tenete a mente che ogni cambiamento di 6 dB è un dimezzamento o raddoppio dell'ampiezza di 1 centro, queste gamme hanno senso. Ma se voi alzare tutti i cursori alle loro impostazioni di massime, l'ampiezza complessiva aumenta di un fattore pari a 16, e il risultato rischia di essere ritagliato e distorto.

In altre parole, il programma presuppone implicitamente che l'utente mantiene un approccio equilibrato alla vita e ridurrà alcuni cursori aumentando gli altri.

Charles Petzold è un collaboratore di lunga data di MSDN Magazine e autore di "Programming Windows, 6a edizione" (o ' Reilly Media, 2012), un libro sulla scrittura di applicazioni per Windows 8. Il suo sito Web è charlespetzold.com.

Grazie all'esperto tecnica seguente per la revisione di questo articolo: Richard Fricks (Microsoft)