eventi Time-Stamped

Il quadro generale della tempistica del sintetizzatore è che invece di inviare una nota esattamente quando deve essere riprodotta, ogni nota viene contrassegnata e inserita in un buffer. Questo buffer viene quindi elaborato e riprodotto entro due millisecondi dall'ora specificata dal timestamp. Anche se la risoluzione temporale è in centinaia di nanosecondi, si parlerà in termini di millisecondi, che sono unità temporali più utili per questa discussione.

Poiché la latenza è nota al sistema tramite l'orologio di latenza, gli eventi con timestamp possono essere in attesa in un buffer per la riproduzione in un momento appropriato, invece di eliminare semplicemente gli eventi in una coda e sperando che la latenza sia bassa.

Un orologio master implementa un'interfaccia COM IReferenceClock (descritta nella documentazione di Microsoft Windows SDK). Tutti i dispositivi nel sistema usano questo tempo di riferimento.

L'implementazione del sink wave di Microsoft crea un thread che si riattiva ogni 20 millisecondi. Il processo del thread consiste nel creare un altro buffer e consegnarlo a DirectSound. Per creare tale buffer, chiama nel sintetizzatore e chiede di eseguire il rendering di una quantità specificata di dati musicali. La quantità richiesta è determinata dall'ora effettiva in cui il thread si riattiva, che è improbabile che sia esattamente di 20 millisecondi.

Ciò che viene effettivamente passato al sintetizzatore è semplicemente un puntatore alla posizione in memoria in cui iniziare a scrivere dati nel buffer PCM e un parametro di lunghezza che specifica la quantità di dati da scrivere. Il synth può quindi scrivere dati PCM in questo buffer e compilarlo fino alla quantità specificata. Ovvero, viene eseguito il rendering dall'indirizzo iniziale fino a raggiungere la lunghezza specificata. Tale blocco di memoria può essere un DirectSoundBuffer (che è il caso predefinito), ma potrebbe anche essere un grafico DirectShow o un altro obiettivo definito dal sink d'onda.

Il buffer PCM è concettualmente ciclico , ovvero è costantemente looping. Il sintetizzatore esegue il rendering dei numeri a 16 bit che descrivono il suono in sezioni successive del buffer. Le dimensioni della sezione sono leggermente diverse ogni volta che il thread si risveglia, perché il sink non può riattivarsi esattamente ogni 20 millisecondi. Quindi, ogni volta che il thread si riattiva, viene riprodotto per determinare la distanza di avanzamento attraverso il buffer prima di tornare a dormire.

Dal punto di vista dell'applicazione, il driver della porta synth stesso ha una funzione IDirectMusicSynth::GetLatencyClock che ottiene l'orologio dal sink dell'onda. Quindi ci sono due orologi:

  • L'orologio master che tutti, incluso il sink d'onda, ascoltano.

  • Clock di latenza implementato dal sink d'onda, visualizzato dall'applicazione come porta DirectMusic che fornisce il clock di latenza.

In altre parole, l'applicazione chiede l'orologio di latenza, ma vede l'orologio come proveniente dall'astrazione della porta DirectMusic anziché dal sink d'onda.

L'ora restituita da questo orologio di latenza è la prima volta in cui è possibile eseguire il rendering del buffer, perché il rendering del synth è già stato eseguito fino a quel punto nel buffer. Se il synth avesse eseguito il rendering di un buffer più piccolo nell'ultima scrittura, anche la latenza sarebbe più piccola.

Pertanto, il sink wave chiama IDirectMusicSynth::Render sul synth, presentando il buffer e richiedendo che venga compilato con i dati di cui è stato eseguito il rendering. Come illustrato nella figura seguente, il synth accetta tutti gli eventi con timestamp risultanti dalle chiamate di funzione IDirectMusicSynth::P layBuffer .

Diagramma che illustra il processo di accodamento dei messaggi con timestamp in un sintetizzatore.

Ogni buffer di input contiene messaggi con timestamp. Ognuno di questi messaggi viene inserito in una coda di cui eseguire il rendering in un buffer al momento specificato dal timestamp.

Uno degli aspetti importanti di questo modello è che non esiste un ordine specifico diverso dal timestamp. Questi eventi vengono trasmessi in modo che possano essere aggiunti alla coda in qualsiasi momento prima del rendering. Tutto è basato su eventi per quanto riguarda il tempo. Ad esempio, se l'ora di riferimento è attualmente a 400 unità di tempo, tutto il timestamp da eseguire al momento 400 avviene ora. Gli eventi con timestamp per l'esecuzione di 10 unità da ora verranno eseguiti alla volta 410.