add a realtime scheduler class and priority-inheritance mutexes with timeouts and abandonment, add an HDA audio driver and a SYS_AUDIO_* syscall family, flesh out kernel32 with Win32 threads, events, mutexes, critical sections, and condition variables, add SYS_RESUME_THREAD for suspended thread creation, add KeInitializeMemoryTypes to reprogram MTRRs and install a canonical PAT for WB/WC/UC control, rework the display pipeline with a pitched present path and console output flush, bring back disk.vhd with mtools since vvfat fucking sucks, harden AHCI with serialized cache flush, USB mass storage with SCSI sense-data decoding, and xHCI with cleaner DMA teardown, fix the last stray for(;;) in HalStopProcessor to while(true), pile on smoke tests for audio, win32 sync primitives, and thread suspend, implement AHCI timeout, kill readahead since it was desperetaly trying to murder me, fix doom sound x2.

This commit is contained in:
maksw committed 2026-08-11 18:38:36 +02:00
1 parent 8b254f84f4
commit 4add6789ba
177 files changed
+22565 -3070

No files matched your search

+802
View File
@@ -0,0 +1,802 @@
// Doom audio mixes PureDOOM SFX with optional SoundFont MIDI into one native render stream.
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <intrin.h>
#include <ntlite/syscall.h>
#include "doom_audio.h"
#include "PureDOOM.h"
#define DOOM_AUDIO_SAMPLE_RATE DOOM_SAMPLERATE
#define DOOM_AUDIO_CHANNELS 2u
#define DOOM_AUDIO_BLOCK_FRAMES 512u
#define DOOM_AUDIO_WRITE_QUANTUM_FRAMES 256u
#define DOOM_AUDIO_PREFILL_FRAMES DOOM_AUDIO_BLOCK_FRAMES
#define DOOM_AUDIO_REFILL_WATERMARK_FRAMES DOOM_AUDIO_WRITE_QUANTUM_FRAMES
#define DOOM_AUDIO_MIDI_BATCH_MESSAGES 256u
#define DOOM_AUDIO_SOUNDFONT_PATH "doom.sf2"
#define DOOM_AUDIO_MAX_SOUNDFONT_BYTES (64u * 1024u * 1024u)
#define DOOM_AUDIO_WAIT_MILLISECONDS 100u
#define DOOM_AUDIO_WORKER_PRIORITY 2u
#define DOOM_AUDIO_SYNTH_LOADER_PRIORITY 0u
#define DOOM_AUDIO_MAX_VOICES 128
#define DOOM_AUDIO_MUSIC_GAIN 1.0f
// Computes a natural logarithm for the positive finite values used by TinySoundFont.
static double DoomAudioLog(double value) {
union {
double Value;
uint64_t Bits;
} bits;
const double ln2 = 0.69314718055994530942;
int exponent;
double mantissa;
double ratio;
double ratio2;
double term;
double sum;
if (value <= 0.0)
return -1.0e300;
bits.Value = value;
exponent = (int)((bits.Bits >> 52) & 0x7FFu) - 1023;
bits.Bits = (bits.Bits & UINT64_C(0x000FFFFFFFFFFFFF)) |
UINT64_C(0x3FF0000000000000);
mantissa = bits.Value;
ratio = (mantissa - 1.0) / (mantissa + 1.0);
ratio2 = ratio * ratio;
term = ratio;
sum = term;
term *= ratio2;
sum += term / 3.0;
term *= ratio2;
sum += term / 5.0;
term *= ratio2;
sum += term / 7.0;
term *= ratio2;
sum += term / 9.0;
term *= ratio2;
sum += term / 11.0;
term *= ratio2;
sum += term / 13.0;
return (double)exponent * ln2 + 2.0 * sum;
}
// Scales a normal double by an integral power of two without a runtime math library.
static double DoomAudioScalePowerOfTwo(double value, int exponent) {
union {
double Value;
uint64_t Bits;
} scale;
if (exponent > 1023)
return 1.0e300;
if (exponent < -1022)
return 0.0;
scale.Bits = (uint64_t)(exponent + 1023) << 52;
return value * scale.Value;
}
// Computes an exponential over the bounded ranges used by TinySoundFont envelopes.
static double DoomAudioExp(double value) {
const double ln2 = 0.69314718055994530942;
const double inverseLn2 = 1.4426950408889634074;
int exponent;
double reduced;
double result;
if (value >= 700.0)
return 1.0e300;
if (value <= -700.0)
return 0.0;
exponent = (int)(value * inverseLn2 + (value >= 0.0 ? 0.5 : -0.5));
reduced = value - (double)exponent * ln2;
result = 1.0 + reduced *
(1.0 + reduced *
(0.5 + reduced *
(0.16666666666666666667 + reduced *
(0.04166666666666666667 + reduced *
(0.00833333333333333333 + reduced *
(0.00138888888888888889 + reduced *
0.00019841269841269841))))));
return DoomAudioScalePowerOfTwo(result, exponent);
}
// Computes a positive real power for TinySoundFont frequency and gain conversion.
static double DoomAudioPow(double base, double exponent) {
if (base <= 0.0)
return 0.0;
return DoomAudioExp(exponent * DoomAudioLog(base));
}
// Computes the float exponential required by TinySoundFont envelopes.
static float DoomAudioExpf(float value) {
return (float)DoomAudioExp((double)value);
}
// Computes the float power required by TinySoundFont frequency and gain conversion.
static float DoomAudioPowf(float base, float exponent) {
return (float)DoomAudioPow((double)base, (double)exponent);
}
// Computes base-ten logarithms from the shared natural logarithm approximation.
static double DoomAudioLog10(double value) {
return DoomAudioLog(value) * 0.43429448190325182765;
}
// Computes square roots with a bit-derived seed and stable Newton refinement.
static double DoomAudioSqrt(double value) {
union {
double Value;
uint64_t Bits;
} estimate;
if (value <= 0.0)
return 0.0;
estimate.Value = value;
estimate.Bits = (estimate.Bits >> 1) + UINT64_C(0x1FF8000000000000);
for (int iteration = 0; iteration < 6; iteration++)
estimate.Value = 0.5 * (estimate.Value + value / estimate.Value);
return estimate.Value;
}
// Computes the float square root required by TinySoundFont panning.
static float DoomAudioSqrtf(float value) {
return (float)DoomAudioSqrt((double)value);
}
// Computes tangent accurately over TinySoundFont's normalized low-pass range.
static double DoomAudioTan(double value) {
double squared = value * value;
double sine = value *
(1.0 + squared *
(-0.16666666666666666667 + squared *
(0.00833333333333333333 + squared *
(-0.00019841269841269841 + squared *
(0.00000275573192239859 + squared *
(-0.00000002505210838544 + squared *
0.00000000016059043837))))));
double cosine = 1.0 + squared *
(-0.5 + squared *
(0.04166666666666666667 + squared *
(-0.00138888888888888889 + squared *
(0.00002480158730158730 + squared *
(-0.00000027557319223986 + squared *
(0.00000000208767569879 + squared *
-0.00000000001147074560))))));
if (cosine > -1.0e-12 && cosine < 1.0e-12)
cosine = cosine < 0.0 ? -1.0e-12 : 1.0e-12;
return sine / cosine;
}
#pragma warning(push)
#pragma warning(disable: 4244 4267 4706)
#define TSF_NO_STDIO
#define TSF_MALLOC malloc
#define TSF_REALLOC realloc
#define TSF_FREE free
#define TSF_MEMCPY memcpy
#define TSF_MEMSET memset
#define TSF_POW DoomAudioPow
#define TSF_POWF DoomAudioPowf
#define TSF_EXPF DoomAudioExpf
#define TSF_LOG DoomAudioLog
#define TSF_TAN DoomAudioTan
#define TSF_LOG10 DoomAudioLog10
#define TSF_SQRT DoomAudioSqrt
#define TSF_SQRTF DoomAudioSqrtf
#define TSF_IMPLEMENTATION
#include "third_party/tsf.h"
#pragma warning(pop)
typedef struct DOOM_AUDIO_STATE {
uint32_t CoreMutex;
uint32_t StreamHandle;
uint32_t WorkerHandle;
uint32_t WorkerId;
uint32_t SynthLoaderHandle;
uint32_t SynthLoaderId;
volatile long StopRequested;
tsf* volatile Synth;
} DOOM_AUDIO_STATE;
static DOOM_AUDIO_STATE g_doomAudio;
// Writes one complete diagnostic line to the process console.
static void DoomAudioPrint(const char* text) {
SYS_WRITE_CONSOLE_REQUEST request = { SysSvFromCstr(text) };
(void)SysWriteConsole(&request);
}
// Opens the default available native render endpoint.
static KSTATUS DoomAudioFindEndpoint(uint64_t* endpointIdOut) {
SYS_AUDIO_ENDPOINT_INFO endpoints[SYS_AUDIO_MAX_ENDPOINTS];
SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST request;
uint32_t count = 0;
const SYS_AUDIO_ENDPOINT_INFO* fallback = NULL;
KSTATUS status;
if (!endpointIdOut)
return NTLSTATUS_INVALID_PARAMETER;
*endpointIdOut = 0;
memset(endpoints, 0, sizeof(endpoints));
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Endpoints = endpoints;
request.Capacity = SYS_AUDIO_MAX_ENDPOINTS;
request.CountOut = &count;
status = SysAudioEnumerateEndpoints(&request);
if (status != NTLSTATUS_SUCCESS)
return status;
if (count > SYS_AUDIO_MAX_ENDPOINTS)
count = SYS_AUDIO_MAX_ENDPOINTS;
for (uint32_t index = 0; index < count; index++) {
const SYS_AUDIO_ENDPOINT_INFO* endpoint = &endpoints[index];
if (endpoint->State != SysAudioEndpointReady ||
(endpoint->Flags & SYS_AUDIO_ENDPOINT_FLAG_AVAILABLE) == 0)
continue;
if (!fallback)
fallback = endpoint;
if (endpoint->Flags & SYS_AUDIO_ENDPOINT_FLAG_DEFAULT) {
*endpointIdOut = endpoint->EndpointId;
return NTLSTATUS_SUCCESS;
}
}
if (!fallback)
return NTLSTATUS_NOT_FOUND;
*endpointIdOut = fallback->EndpointId;
return NTLSTATUS_SUCCESS;
}
// Returns the currently published synthesizer without taking the PureDOOM lock.
static tsf* DoomAudioGetSynth(void) {
return (tsf*)_InterlockedCompareExchangePointer(
(void* volatile*)&g_doomAudio.Synth, NULL, NULL);
}
// Maps the optional SoundFont so TinySoundFont can parse it without a separate input copy.
static KSTATUS DoomAudioMapSoundFont(void** dataOut, uint32_t* sizeOut) {
uint32_t fileHandle = 0;
uint32_t mappingHandle = 0;
uint64_t fileSize = 0;
void* base = NULL;
KSTATUS status;
if (!dataOut || !sizeOut)
return NTLSTATUS_INVALID_PARAMETER;
*dataOut = NULL;
*sizeOut = 0;
SYS_OPEN_FILE_REQUEST open = SysMakeOpenFileRequest(
SysSvFromCstr(DOOM_AUDIO_SOUNDFONT_PATH),
SYS_FILE_ACCESS_QUERY | SYS_FILE_ACCESS_READ,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&fileHandle);
status = SysOpenFile(&open);
if (status != NTLSTATUS_SUCCESS)
return status;
status = SysGetFileSize(fileHandle, &fileSize);
if (status != NTLSTATUS_SUCCESS || fileSize == 0 ||
fileSize > DOOM_AUDIO_MAX_SOUNDFONT_BYTES ||
fileSize > INT32_MAX) {
(void)SysCloseHandle(fileHandle);
return status == NTLSTATUS_SUCCESS ? NTLSTATUS_INVALID_PARAMETER : status;
}
status = SysCreateFileMapping(fileHandle, SYS_VM_READ, SYS_MAP_FLAG_NONE,
fileSize, &mappingHandle);
if (status != NTLSTATUS_SUCCESS || !mappingHandle) {
(void)SysCloseHandle(fileHandle);
return status == NTLSTATUS_SUCCESS ? NTLSTATUS_FAILURE : status;
}
status = SysMapFile(mappingHandle, 0, (size_t)fileSize,
SYS_MAP_ACCESS_READ, SYS_MAP_FLAG_NONE, &base);
(void)SysCloseHandle(mappingHandle);
(void)SysCloseHandle(fileHandle);
if (status != NTLSTATUS_SUCCESS || !base)
return status == NTLSTATUS_SUCCESS ? NTLSTATUS_FAILURE : status;
*dataOut = base;
*sizeOut = (uint32_t)fileSize;
return NTLSTATUS_SUCCESS;
}
// Configures one parsed General MIDI synthesizer for PureDOOM's native sample rate.
static KSTATUS DoomAudioConfigureSynth(tsf* synth) {
if (!synth)
return NTLSTATUS_INVALID_PARAMETER;
tsf_set_output(synth, TSF_STEREO_INTERLEAVED,
(int)DOOM_AUDIO_SAMPLE_RATE, 0.0f);
if (!tsf_set_max_voices(synth, DOOM_AUDIO_MAX_VOICES))
return NTLSTATUS_NO_MEMORY;
for (int channel = 0; channel < 16; channel++) {
if (!tsf_channel_set_presetnumber(synth, channel, 0, channel == 9))
return NTLSTATUS_FAILURE;
}
return NTLSTATUS_SUCCESS;
}
// Loads optional MIDI state at low priority without blocking Doom or SFX startup.
static void DoomAudioSynthLoader(uintptr_t argument) {
uint32_t soundFontSize = 0;
void* soundFontBase = NULL;
tsf* synth = NULL;
KSTATUS status;
(void)argument;
status = DoomAudioMapSoundFont(&soundFontBase, &soundFontSize);
if (status == NTLSTATUS_NOT_FOUND) {
DoomAudioPrint("doom: doom.sf2 unavailable; continuing with sound effects only\n");
SysExitThread(0);
}
if (status != NTLSTATUS_SUCCESS) {
DoomAudioPrint("doom: doom.sf2 could not be mapped; continuing with sound effects only\n");
SysExitThread(0);
}
if (_InterlockedCompareExchange(&g_doomAudio.StopRequested, 0, 0)) {
(void)SysFreeVm(soundFontBase);
SysExitThread(0);
}
synth = tsf_load_memory(soundFontBase, (int)soundFontSize);
(void)SysFreeVm(soundFontBase);
if (!synth) {
DoomAudioPrint("doom: doom.sf2 is invalid or synth allocation failed; continuing with sound effects only\n");
SysExitThread(0);
}
status = DoomAudioConfigureSynth(synth);
if (status != NTLSTATUS_SUCCESS ||
_InterlockedCompareExchange(&g_doomAudio.StopRequested, 0, 0)) {
if (status != NTLSTATUS_SUCCESS)
DoomAudioPrint("doom: MIDI synthesizer initialization failed; continuing with sound effects only\n");
tsf_close(synth);
SysExitThread(0);
}
(void)_InterlockedExchangePointer(
(void* volatile*)&g_doomAudio.Synth, synth);
DoomAudioPrint("doom: MIDI synthesizer ready\n");
SysExitThread(0);
}
// Applies one packed WinMM-style short MIDI message to TinySoundFont.
static void DoomAudioDispatchMidi(tsf* synth, uint32_t message) {
uint8_t status = (uint8_t)(message & 0xFFu);
uint8_t data1 = (uint8_t)((message >> 8) & 0x7Fu);
uint8_t data2 = (uint8_t)((message >> 16) & 0x7Fu);
int channel = status & 0x0F;
if (!synth)
return;
switch (status & 0xF0) {
case 0x80:
tsf_channel_note_off(synth, channel, data1);
break;
case 0x90:
if (data2)
(void)tsf_channel_note_on(synth, channel, data1,
(float)data2 / 127.0f);
else
tsf_channel_note_off(synth, channel, data1);
break;
case 0xB0:
(void)tsf_channel_midi_control(synth, channel, data1, data2);
break;
case 0xC0:
(void)tsf_channel_set_presetnumber(synth, channel, data1,
channel == 9);
break;
case 0xE0:
(void)tsf_channel_set_pitchwheel(synth, channel,
(int)data1 | ((int)data2 << 7));
break;
default:
break;
}
}
// Advances one PureDOOM MIDI tick and dispatches every event outside the core lock.
static void DoomAudioTickMidi(tsf* synth) {
uint32_t messages[DOOM_AUDIO_MIDI_BATCH_MESSAGES];
int tickComplete = 0;
if (!synth)
return;
while (!tickComplete) {
uint32_t count = 0;
DoomAudioLockCore();
while (count < DOOM_AUDIO_MIDI_BATCH_MESSAGES) {
uint32_t message = (uint32_t)doom_tick_midi();
if (!message) {
tickComplete = 1;
break;
}
messages[count++] = message;
}
DoomAudioUnlockCore();
for (uint32_t index = 0; index < count; index++)
DoomAudioDispatchMidi(synth, messages[index]);
}
}
// Copies one complete PureDOOM SFX block while holding the shared core lock.
static void DoomAudioReadSfxBlock(int16_t* frames) {
short* source;
DoomAudioLockCore();
source = doom_get_sound_buffer();
memcpy(frames, source,
DOOM_AUDIO_BLOCK_FRAMES * DOOM_AUDIO_CHANNELS * sizeof(int16_t));
DoomAudioUnlockCore();
}
// Mixes rendered floating-point music into signed 16-bit PureDOOM SFX.
static void DoomAudioMixMusic(int16_t* destination,
const float* music,
uint32_t frameOffset,
uint32_t frameCount) {
uint32_t firstSample = frameOffset * DOOM_AUDIO_CHANNELS;
uint32_t sampleCount = frameCount * DOOM_AUDIO_CHANNELS;
for (uint32_t index = 0; index < sampleCount; index++) {
float scaled = music[index] * (32767.0f * DOOM_AUDIO_MUSIC_GAIN);
int32_t synthSample;
int32_t mixed;
if (scaled > 32767.0f)
synthSample = 32767;
else if (scaled < -32768.0f)
synthSample = -32768;
else
synthSample = (int32_t)scaled;
mixed = (int32_t)destination[firstSample + index] + synthSample;
if (mixed > 32767)
mixed = 32767;
else if (mixed < -32768)
mixed = -32768;
destination[firstSample + index] = (int16_t)mixed;
}
}
// Renders MIDI at exactly 140 ticks per second into one native PureDOOM block.
static void DoomAudioRenderMusic(tsf* synth,
int16_t* frames,
uint32_t* phaseInOut) {
float music[DOOM_AUDIO_BLOCK_FRAMES * DOOM_AUDIO_CHANNELS];
uint32_t frameOffset = 0;
memset(music, 0, sizeof(music));
while (frameOffset < DOOM_AUDIO_BLOCK_FRAMES) {
uint32_t remaining;
uint32_t framesUntilTick;
while (*phaseInOut >= DOOM_AUDIO_SAMPLE_RATE) {
*phaseInOut -= DOOM_AUDIO_SAMPLE_RATE;
DoomAudioTickMidi(synth);
}
remaining = DOOM_AUDIO_BLOCK_FRAMES - frameOffset;
framesUntilTick =
(DOOM_AUDIO_SAMPLE_RATE - *phaseInOut + DOOM_MIDI_RATE - 1u) /
DOOM_MIDI_RATE;
if (framesUntilTick > remaining)
framesUntilTick = remaining;
tsf_render_float(synth,
music + frameOffset * DOOM_AUDIO_CHANNELS,
(int)framesUntilTick,
0);
DoomAudioMixMusic(frames, music + frameOffset * DOOM_AUDIO_CHANNELS,
frameOffset, framesUntilTick);
frameOffset += framesUntilTick;
*phaseInOut += framesUntilTick * DOOM_MIDI_RATE;
}
}
// Renders one complete native-rate block containing synchronized music and SFX.
static void DoomAudioRenderBlock(int16_t* frames,
tsf** activeSynthInOut,
uint32_t* midiPhaseInOut) {
tsf* synth;
DoomAudioReadSfxBlock(frames);
synth = DoomAudioGetSynth();
if (synth != *activeSynthInOut) {
*activeSynthInOut = synth;
*midiPhaseInOut = DOOM_AUDIO_SAMPLE_RATE;
}
if (synth)
DoomAudioRenderMusic(synth, frames, midiPhaseInOut);
}
// Queries one coherent native stream snapshot.
static KSTATUS DoomAudioQueryStream(SYS_AUDIO_STREAM_INFO* infoOut) {
if (!infoOut)
return NTLSTATUS_INVALID_PARAMETER;
memset(infoOut, 0, sizeof(*infoOut));
infoOut->Size = sizeof(*infoOut);
return SysAudioQueryStream(g_doomAudio.StreamHandle, infoOut);
}
// Arms a wait for writable space or a terminal stream condition.
static KSTATUS DoomAudioWaitWritable(const SYS_AUDIO_STREAM_INFO* info,
uint32_t writableFrames) {
SYS_AUDIO_ARM_WAIT_REQUEST request;
KSTATUS status;
if (!info || !writableFrames || writableFrames > info->CapacityFrames)
return NTLSTATUS_INVALID_PARAMETER;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.StreamHandle = g_doomAudio.StreamHandle;
request.Mask = SYS_AUDIO_WAIT_WRITABLE |
SYS_AUDIO_WAIT_FAILED |
SYS_AUDIO_WAIT_DISCONTINUITY;
request.WritableFrames = writableFrames;
request.DiscontinuityGeneration = info->DiscontinuityGeneration;
status = SysAudioArmWait(&request);
if (status != NTLSTATUS_SUCCESS)
return status;
return SysWaitSingle(g_doomAudio.StreamHandle,
SYS_WAIT_FLAG_NONE,
DOOM_AUDIO_WAIT_MILLISECONDS);
}
// Waits until the buffered source has fallen to the low-latency refill watermark.
static KSTATUS DoomAudioWaitForRefill(const SYS_AUDIO_STREAM_INFO* info) {
if (!info || info->CapacityFrames <= DOOM_AUDIO_REFILL_WATERMARK_FRAMES)
return NTLSTATUS_INVALID_PARAMETER;
return DoomAudioWaitWritable(
info, info->CapacityFrames - DOOM_AUDIO_REFILL_WATERMARK_FRAMES);
}
// Writes as much of one pending block as the nonblocking stream accepts.
static KSTATUS DoomAudioWritePending(const int16_t* frames,
uint32_t* frameOffsetInOut) {
SYS_AUDIO_WRITE_REQUEST request;
uint32_t written = 0;
KSTATUS status;
if (!frames || !frameOffsetInOut ||
*frameOffsetInOut >= DOOM_AUDIO_BLOCK_FRAMES)
return NTLSTATUS_INVALID_PARAMETER;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.StreamHandle = g_doomAudio.StreamHandle;
request.FrameCount = DOOM_AUDIO_BLOCK_FRAMES - *frameOffsetInOut;
if (request.FrameCount > DOOM_AUDIO_WRITE_QUANTUM_FRAMES)
request.FrameCount = DOOM_AUDIO_WRITE_QUANTUM_FRAMES;
request.Frames = frames + *frameOffsetInOut * DOOM_AUDIO_CHANNELS;
request.WrittenFramesOut = &written;
status = SysAudioWrite(&request);
if (status != NTLSTATUS_SUCCESS)
return status;
if (!written)
return NTLSTATUS_WOULD_BLOCK;
*frameOffsetInOut += written;
return NTLSTATUS_SUCCESS;
}
// Keeps a bounded low-latency stream queue at PureDOOM's native cadence.
static void DoomAudioWorker(uintptr_t argument) {
int16_t block[DOOM_AUDIO_BLOCK_FRAMES * DOOM_AUDIO_CHANNELS];
uint32_t pendingOffset = DOOM_AUDIO_BLOCK_FRAMES;
uint32_t midiPhase = 0;
tsf* activeSynth = NULL;
uint8_t started = 0;
(void)argument;
while (true) {
SYS_AUDIO_STREAM_INFO info;
uint32_t requiredFrames;
KSTATUS status;
if (_InterlockedCompareExchange(&g_doomAudio.StopRequested, 0, 0))
break;
status = DoomAudioQueryStream(&info);
if (status != NTLSTATUS_SUCCESS || info.State == SysAudioStreamFailed) {
DoomAudioPrint("doom: native audio stream failed\n");
break;
}
if (!started && info.BufferedFrames >= DOOM_AUDIO_PREFILL_FRAMES) {
status = SysAudioStart(g_doomAudio.StreamHandle);
if (status != NTLSTATUS_SUCCESS) {
DoomAudioPrint("doom: native audio start failed\n");
break;
}
started = 1;
continue;
}
if (pendingOffset == DOOM_AUDIO_BLOCK_FRAMES) {
if (started &&
info.BufferedFrames > DOOM_AUDIO_REFILL_WATERMARK_FRAMES) {
status = DoomAudioWaitForRefill(&info);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_TIMEOUT) {
DoomAudioPrint("doom: native audio wait failed\n");
break;
}
continue;
}
DoomAudioRenderBlock(block, &activeSynth, &midiPhase);
pendingOffset = 0;
}
requiredFrames = DOOM_AUDIO_BLOCK_FRAMES - pendingOffset;
if (requiredFrames > DOOM_AUDIO_WRITE_QUANTUM_FRAMES)
requiredFrames = DOOM_AUDIO_WRITE_QUANTUM_FRAMES;
if (info.WritableFrames < requiredFrames) {
status = DoomAudioWaitWritable(&info, requiredFrames);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_TIMEOUT) {
DoomAudioPrint("doom: native audio wait failed\n");
break;
}
continue;
}
status = DoomAudioWritePending(block, &pendingOffset);
if (status == NTLSTATUS_WOULD_BLOCK)
continue;
if (status != NTLSTATUS_SUCCESS) {
DoomAudioPrint("doom: native audio write failed\n");
break;
}
}
if (started)
(void)SysAudioStop(g_doomAudio.StreamHandle);
SysExitThread(0);
}
// Releases resources owned before or after the producer thread lifetime.
static void DoomAudioReleaseResources(void) {
if (g_doomAudio.StreamHandle) {
(void)SysCloseHandle(g_doomAudio.StreamHandle);
g_doomAudio.StreamHandle = 0;
}
tsf* synth = (tsf*)_InterlockedExchangePointer(
(void* volatile*)&g_doomAudio.Synth, NULL);
if (synth)
tsf_close(synth);
if (g_doomAudio.CoreMutex) {
(void)SysCloseHandle(g_doomAudio.CoreMutex);
g_doomAudio.CoreMutex = 0;
}
}
// Starts one process-owned audio stream and its exclusive producer thread.
void DoomAudioStart(void) {
uint64_t endpointId = 0;
uint32_t streamHandle = 0;
KSTATUS status;
if (DOOM_AUDIO_PREFILL_FRAMES >= SYS_AUDIO_STREAM_CAPACITY_FRAMES ||
DOOM_AUDIO_REFILL_WATERMARK_FRAMES >= DOOM_AUDIO_PREFILL_FRAMES ||
DOOM_AUDIO_WRITE_QUANTUM_FRAMES > DOOM_AUDIO_BLOCK_FRAMES) {
DoomAudioPrint("doom: invalid native audio buffering configuration\n");
return;
}
memset(&g_doomAudio, 0, sizeof(g_doomAudio));
status = SysCreateMutex(&g_doomAudio.CoreMutex);
if (status != NTLSTATUS_SUCCESS) {
DoomAudioPrint("doom: audio synchronization unavailable; continuing without sound\n");
return;
}
status = DoomAudioFindEndpoint(&endpointId);
if (status != NTLSTATUS_SUCCESS) {
DoomAudioPrint("doom: no audio endpoint; continuing without sound\n");
DoomAudioReleaseResources();
return;
}
SYS_AUDIO_OPEN_STREAM_REQUEST open;
memset(&open, 0, sizeof(open));
open.Size = sizeof(open);
open.EndpointId = endpointId;
open.Format.SampleRate = DOOM_AUDIO_SAMPLE_RATE;
open.Format.ChannelCount = DOOM_AUDIO_CHANNELS;
open.Format.SampleFormat = SysAudioSampleS16;
open.StreamHandleOut = &streamHandle;
status = SysAudioOpenStream(&open);
if (status != NTLSTATUS_SUCCESS) {
DoomAudioPrint("doom: native audio open failed; continuing without sound\n");
DoomAudioReleaseResources();
return;
}
g_doomAudio.StreamHandle = streamHandle;
SYS_CREATE_THREAD_REQUEST thread;
memset(&thread, 0, sizeof(thread));
thread.Entry = (uintptr_t)DoomAudioWorker;
thread.Priority = DOOM_AUDIO_WORKER_PRIORITY;
thread.ThreadHandleOut = &g_doomAudio.WorkerHandle;
thread.ThreadIdOut = &g_doomAudio.WorkerId;
status = SysCreateThread(&thread);
if (status != NTLSTATUS_SUCCESS) {
DoomAudioPrint("doom: audio worker creation failed; continuing without sound\n");
DoomAudioReleaseResources();
return;
}
memset(&thread, 0, sizeof(thread));
thread.Entry = (uintptr_t)DoomAudioSynthLoader;
thread.Priority = DOOM_AUDIO_SYNTH_LOADER_PRIORITY;
thread.ThreadHandleOut = &g_doomAudio.SynthLoaderHandle;
thread.ThreadIdOut = &g_doomAudio.SynthLoaderId;
status = SysCreateThread(&thread);
if (status != NTLSTATUS_SUCCESS)
DoomAudioPrint("doom: MIDI loader creation failed; continuing with sound effects only\n");
}
// Stops playback immediately without waiting for the producer thread.
void DoomAudioAbort(void) {
_InterlockedExchange(&g_doomAudio.StopRequested, 1);
if (g_doomAudio.StreamHandle)
(void)SysAudioStop(g_doomAudio.StreamHandle);
}
// Stops the producer before releasing its stream, synthesizer, and core lock.
void DoomAudioShutdown(void) {
DoomAudioAbort();
if (g_doomAudio.WorkerHandle) {
(void)SysWaitSingle(g_doomAudio.WorkerHandle,
SYS_WAIT_FLAG_NONE,
SYS_WAIT_TIMEOUT_INFINITE);
(void)SysCloseHandle(g_doomAudio.WorkerHandle);
g_doomAudio.WorkerHandle = 0;
}
if (g_doomAudio.SynthLoaderHandle) {
(void)SysWaitSingle(g_doomAudio.SynthLoaderHandle,
SYS_WAIT_FLAG_NONE,
SYS_WAIT_TIMEOUT_INFINITE);
(void)SysCloseHandle(g_doomAudio.SynthLoaderHandle);
g_doomAudio.SynthLoaderHandle = 0;
}
DoomAudioReleaseResources();
}
// Enters the lock shared by the game loop and PureDOOM audio callbacks.
void DoomAudioLockCore(void) {
if (g_doomAudio.CoreMutex)
(void)SysWaitSingle(g_doomAudio.CoreMutex,
SYS_WAIT_FLAG_NONE,
SYS_WAIT_TIMEOUT_INFINITE);
}
// Leaves the lock shared by the game loop and PureDOOM audio callbacks.
void DoomAudioUnlockCore(void) {
if (g_doomAudio.CoreMutex)
(void)SysReleaseMutex(g_doomAudio.CoreMutex);
}