Eventos de Time-Stamped
A visão geral do tempo do sintetizador é que, em vez de enviar uma nota exatamente quando precisa ser reproduzida, cada anotação é carimbada e colocada em um buffer. Esse buffer é processado e reproduzido dentro de dois milissegundos da hora especificada pelo carimbo de data/hora. (Embora a resolução de tempo esteja em centenas de nanossegundos, falaremos em termos de milissegundos, que são unidades de tempo mais convenientes para esta discussão.)
Como a latência é conhecida pelo sistema por meio do relógio de latência, os eventos com carimbo de data/hora podem estar esperando em um buffer para serem reproduzidos no horário adequado, em vez de apenas soltar eventos em uma fila e esperar que a latência seja baixa.
Um relógio master implementa uma interface COM IReferenceClock (descrita na documentação do SDK do Microsoft Windows). Todos os dispositivos no sistema usam esse tempo de referência.
A implementação do coletor de ondas da Microsoft cria um thread que acorda a cada 20 milissegundos. O trabalho do thread é criar outro buffer e entregá-lo ao DirectSound. Para criar esse buffer, ele chama o sintetizador e solicita que ele renderize uma quantidade especificada de dados de música. O valor que ele solicita é determinado pelo tempo real em que o thread é ativado, o que é improvável que seja exatamente 20 milissegundos.
O que é realmente passado para o sintetizador é simplesmente um ponteiro para o local na memória no qual começar a gravar dados no buffer pcm e um parâmetro de comprimento que especifica quantos dados gravar. Em seguida, o sintetizador pode gravar dados pcm nesse buffer e preenchê-los até o valor especificado. Ou seja, ele é renderizado do endereço inicial até atingir o comprimento especificado. Esse bloco de memória pode ser um DirectSoundBuffer (que é o caso padrão), mas também pode ser um grafo DirectShow ou algum outro destino definido pelo coletor de ondas.
O buffer pcm é conceitualmente cíclico (ou seja, ele está constantemente em loop). O sintetizador renderiza os números de 16 bits que descrevem o som em fatias sucessivas do buffer. O tamanho da fatia é ligeiramente diferente sempre que o thread acorda, porque o coletor não pode acordar exatamente a cada 20 milissegundos. Portanto, toda vez que o thread é ativado, ele é reproduzido para determinar até que ponto ele deve progredir pelo buffer antes de voltar a dormir.
Na perspectiva do aplicativo, o driver de porta de sintetizador em si tem uma função IDirectMusicSynth::GetLatencyClock que obtém o relógio do coletor de ondas. Portanto, há dois relógios:
O relógio master que todos, incluindo o coletor de ondas, ouvem.
O relógio de latência implementado pelo coletor de ondas, que é visto pelo aplicativo como uma porta DirectMusic que fornece o relógio de latência.
Em outras palavras, o aplicativo solicita o relógio de latência, mas vê o relógio como proveniente da abstração da porta DirectMusic em vez do coletor de ondas.
O tempo retornado por esse relógio de latência é a primeira vez em que o buffer pode ser renderizado, pois o sintetizador já foi renderizado até esse ponto no buffer. Se o sintetizador tivesse renderizado um buffer menor em sua última gravação, a latência também seria menor.
Portanto, o coletor de ondas chama IDirectMusicSynth::Render no sintetizador, apresentando o buffer e solicitando que ele seja preenchido com dados renderizados. Conforme mostrado na figura a seguir, o sintetizador usa todos os eventos carimbados pelo tempo que chegam como resultado de chamadas de função IDirectMusicSynth::P layBuffer .
Cada buffer de entrada contém mensagens com carimbo de data/hora. Cada uma dessas mensagens é colocada em uma fila a ser renderizada em um buffer no momento especificado pelo carimbo de data/hora.
Uma das coisas importantes sobre esse modelo é que não há nenhuma ordem específica além do carimbo de data/hora. Esses eventos são transmitidos para que possam ser adicionados à fila a qualquer momento antes da renderização. Tudo é baseado em eventos em relação ao tempo. Por exemplo, se o tempo de referência estiver atualmente em 400 unidades de tempo, tudo o que está marcado para acontecer no momento 400 está acontecendo agora. Os eventos com carimbo de data/hora para acontecer 10 unidades a partir de agora ocorrerão no momento 410.