Beim Programmieren von Sound in Anwendungen und Spielen musste ich häufig die gesamte Codebasis von Soundmodulen neu schreiben, da viele von ihnen entweder eine zu komplizierte Architektur hatten oder im Gegenteil nichts anderes als einfaches Spielen von Sounds konnten.
Die Analogie zum Rendern von Bildern in Spielen funktioniert gut mit Sound-Engines: Wenn Sie eine zu einfache Pipeline mit einer großen Anzahl von Abstraktionen haben, können Sie etwas Komplizierteres als einen Würfel mit Zahnrädern kaum angemessen programmieren. Wenn Ihr gesamter Code aus direkten OpenGL- oder D3D-Aufrufen besteht, können Sie Ihren Spaghetti-Code nicht ohne Schmerzen skalieren.
Wie relevant ist der Vergleich mit grafischem Rendering?
Beim Sound-Rendering finden dieselben Prozesse statt wie beim Grafik-Rendering: Aktualisieren von Ressourcen aus der Spielelogik, Verarbeiten von Daten in eine verdauliche Form, Nachbearbeiten, Ausgeben des Endergebnisses. All dies kann ziemlich lange dauern. Zur Veranschaulichung verwende ich meine Audiobibliothek, um die Renderleistung zu testen.
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
), .