Ao programar som em aplicativos e jogos, muitas vezes tive que reescrever toda a base de código dos módulos de som, uma vez que muitos deles tinham uma arquitetura muito complicada ou, ao contrário, não podiam fazer nada além de simples reproduzir sons.
A analogia com a renderização de imagens em jogos funciona bem com mecanismos de som: se você tiver um pipeline muito simples com um grande número de abstrações, dificilmente poderá programar adequadamente algo mais complicado do que um cubo com engrenagens. Por outro lado, se todo o seu código consiste em chamadas diretas de OpenGL ou D3D, você não pode dimensionar seu código espaguete sem dor.
Quão relevante é a comparação com a renderização gráfica?
Na renderização de som, ocorrem os mesmos processos que na renderização de gráficos: atualização de recursos da lógica do jogo, processamento de dados em uma forma digerível, pós-processamento, saída do resultado final. Tudo isso pode levar muito tempo, então, para fins de ilustração, estou usando minha biblioteca de áudio para testar o desempenho de renderização.
SSD , Opus , , DSP (, ), . , : Inte Core i9 9900 4.5GHz, 32GB RAM, SSD 480GB SATA. 48000 44100.
FRESPONZE_BEGIN_TEST
if (!pFirstListener) return false;
// -
if (RingBuffer.GetLeftBuffers()) return false;
RingBuffer.SetBuffersCount(RING_BUFFERS_COUNT);
RingBuffer.Resize(Frames * Channels);
OutputBuffer.Resize(Frames * Channels);
tempBuffer.Resize(Channels, Frames);
mixBuffer.Resize(Channels, Frames);
for (size_t i = 0; i < RING_BUFFERS_COUNT; i++) {
tempBuffer.Clear();
mixBuffer.Clear();
pListNode = pFirstListener;
while (pListNode) {
/* */
EmittersNode* pEmittersNode = nullptr;
if (!pListNode->pListener) break;
pListNode->pListener->GetFirstEmitter(&pEmittersNode);
while (pEmittersNode) {
tempBuffer.Clear();
pEmittersNode->pEmitter->Process(tempBuffer.GetBuffers(), Frames);
//
for (size_t o = 0; o < Channels; o++) {
MixerAddToBuffer(mixBuffer.GetBufferData((fr_i32)o), tempBuffer.GetBufferData((fr_i32)o), Frames);
}
pEmittersNode = pEmittersNode->pNext;
}
pListNode = pListNode->pNext;
}
/* */
PlanarToLinear(mixBuffer.GetBuffers(), OutputBuffer.Data(), Frames * Channels, Channels);
RingBuffer.PushBuffer(OutputBuffer.Data(), Frames * Channels);
RingBuffer.NextBuffer();
}
FRESPONZE_END_TEST("Audio render")
[00:00:59:703]: 'Audio render' operation passed: 551 microseconds
[00:00:59:797]: 'Audio render' operation passed: 512 microseconds
[00:00:59:906]: 'Audio render' operation passed: 541 microseconds
[00:01:00:000]: 'Audio render' operation passed: 583 microseconds
, , , . , , .
?
, . — -, C++, SoLoud OpenAL. , — . API , OpenAL Wwise.
. — ref_sound
ISoundManager
, , , , , . — , UAudioComponent
Unreal Engine.
void Class::Function()
{
//
snd.play_at_pos(0, Position(), false);
//
if (IsHappened())
{
// ...
}
// ...
}
, — CSoundRender_Target
, API OpenAL DirectSound, CSoundRender_Cache
— Vorbis . target
— , source
+ emitter
.
, Core (FMOD Wwise), API (PortAudio).
,
, — (hardware) (mixer). , , API. .
:
void GameScheduler::Update() {
// ...
// , .
// ,
// .
SoundManager::StopSound(id);
// ...
}
// ...
void AudioHardware::Update() {
// ...
// :
// ,
// .
AudioMixer::Render(input, frames);
memcpy(output, input, frames * channels * frame_size);
// ...
}
- . AudioHardware
PortAudio, Windows Audio Session API. SoundManager
— FMOD Wwise. AudioHardware
, .
: routing
emitters-source
. DAW, , . , side-chain
, . , - .
— emitters-source
. 2 — (source), , , (emitter) — , , -. emitters-source
( Wwise FMOD virtual emitters
), .