Files
nt-lite/code/user/tests/audio_stream_smoke/audio_stream_smoke.c
T

730 lines
29 KiB
C

// Native render-stream syscall smoke test.
#include <stdint.h>
#include <string.h>
#include <ntlite/syscall.h>
#define TEST_WAIT_MILLISECONDS 3000ull
#define TEST_EXTRA_FRAMES 512u
#define TEST_REFILL_FRAMES 1024u
#define TEST_TONE_FRAMES 48000u
#define TEST_AUDIBLE_FRAMES SYS_AUDIO_STREAM_CAPACITY_FRAMES
#define TEST_AUDIBLE_RAMP_FRAMES 240u
#define TEST_CLOSE_RUNDOWN_ITERATIONS 72u
#define TEST_CLOSE_RUNDOWN_SLEEP_MILLISECONDS 5ull
#define TEST_IDLE_QUIESCE_MILLISECONDS 300ull
#define TEST_TONE_PHASE_MODULUS 1200u
#define TEST_TONE_PHASE_STEP 11u
#define TEST_TONE_QUARTER_PHASE 300u
#define TEST_TONE_PEAK 8192
static int16_t gFrames[TEST_TONE_FRAMES * 2u];
static int16_t gAudibleFrames[TEST_AUDIBLE_FRAMES * 2u];
static uint32_t gOpenStream;
typedef char TEST_AUDIO_FORMAT_SIZE[(sizeof(SYS_AUDIO_FORMAT) == 8) ? 1 : -1];
typedef char TEST_AUDIO_ENDPOINT_INFO_SIZE[(sizeof(SYS_AUDIO_ENDPOINT_INFO) == 64) ? 1 : -1];
typedef char TEST_AUDIO_ENUMERATE_REQUEST_SIZE[(sizeof(SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST) == 32) ? 1 : -1];
typedef char TEST_AUDIO_OPEN_REQUEST_SIZE[(sizeof(SYS_AUDIO_OPEN_STREAM_REQUEST) == 40) ? 1 : -1];
typedef char TEST_AUDIO_WRITE_REQUEST_SIZE[(sizeof(SYS_AUDIO_WRITE_REQUEST) == 32) ? 1 : -1];
typedef char TEST_AUDIO_ARM_WAIT_REQUEST_SIZE[(sizeof(SYS_AUDIO_ARM_WAIT_REQUEST) == 56) ? 1 : -1];
typedef char TEST_AUDIO_STREAM_INFO_SIZE[(sizeof(SYS_AUDIO_STREAM_INFO) == 128) ? 1 : -1];
typedef char TEST_AUDIO_VOLUME_REQUEST_SIZE[(sizeof(SYS_AUDIO_SET_VOLUME_REQUEST) == 24) ? 1 : -1];
typedef char TEST_AUDIO_MASTER_VOLUME_REQUEST_SIZE[(sizeof(SYS_AUDIO_SET_MASTER_VOLUME_REQUEST) == 24) ? 1 : -1];
// Writes one NUL-terminated string to the console.
static void print(const char* text) {
SYS_WRITE_CONSOLE_REQUEST request = { SysSvFromCstr(text) };
(void)SysWriteConsole(&request);
}
// Writes one unsigned integer to the console.
static void print_u64(uint64_t value) {
char digits[32];
uint32_t count = 0;
if (!value) {
print("0");
return;
}
while (value && count < sizeof(digits)) {
digits[count++] = (char)('0' + value % 10u);
value /= 10u;
}
while (count) {
char character = digits[--count];
SYS_WRITE_CONSOLE_REQUEST request = { SysSvFromParts(&character, 1) };
(void)SysWriteConsole(&request);
}
}
// Stops and closes the canonical stream after success, failure, or process interruption.
static void cleanup_stream(void) {
if (!gOpenStream)
return;
(void)SysAudioStop(gOpenStream);
(void)SysCloseHandle(gOpenStream);
gOpenStream = 0;
}
// Terminates the test process.
static void exit_now(int32_t code) {
cleanup_stream();
while (true) (void)SysExitProcess(code);
}
// Fails the test with one status value.
static void fail_status(const char* operation, KSTATUS status) {
print("audio_stream_smoke: ");
print(operation);
print(" status=");
print_u64(status);
print("\n");
exit_now(1);
}
// Fails the test with one plain diagnostic.
static void fail(const char* message) {
print("audio_stream_smoke: ");
print(message);
print("\n");
exit_now(1);
}
// Returns one exact 440 Hz integer triangle-wave sample for the supplied phase.
static int16_t tone_sample(uint32_t phase) {
int32_t sample;
if (phase < TEST_TONE_QUARTER_PHASE) {
sample = (int32_t)(phase * TEST_TONE_PEAK / TEST_TONE_QUARTER_PHASE);
} else if (phase < TEST_TONE_QUARTER_PHASE * 2u) {
sample = (int32_t)((TEST_TONE_QUARTER_PHASE * 2u - phase) *
TEST_TONE_PEAK / TEST_TONE_QUARTER_PHASE);
} else if (phase < TEST_TONE_QUARTER_PHASE * 3u) {
sample = -(int32_t)((phase - TEST_TONE_QUARTER_PHASE * 2u) *
TEST_TONE_PEAK / TEST_TONE_QUARTER_PHASE);
} else {
sample = -(int32_t)((TEST_TONE_PHASE_MODULUS - phase) *
TEST_TONE_PEAK / TEST_TONE_QUARTER_PHASE);
}
return (int16_t)sample;
}
// Fills contract and audible buffers without requiring floating-point support.
static void fill_test_frames(void) {
uint32_t phase = 0;
for (uint32_t frame = 0; frame < TEST_TONE_FRAMES; frame++) {
int16_t sample = tone_sample(phase);
gFrames[frame * 2u] = sample;
gFrames[frame * 2u + 1u] = sample;
phase += TEST_TONE_PHASE_STEP;
if (phase >= TEST_TONE_PHASE_MODULUS)
phase -= TEST_TONE_PHASE_MODULUS;
}
phase = 0;
for (uint32_t frame = 0; frame < TEST_AUDIBLE_FRAMES; frame++) {
int32_t sample = tone_sample(phase);
uint32_t gain = TEST_AUDIBLE_RAMP_FRAMES;
if (frame < gain)
gain = frame;
uint32_t remaining = TEST_AUDIBLE_FRAMES - 1u - frame;
if (remaining < gain)
gain = remaining;
sample = sample * (int32_t)gain / (int32_t)TEST_AUDIBLE_RAMP_FRAMES;
gAudibleFrames[frame * 2u] = (int16_t)sample;
gAudibleFrames[frame * 2u + 1u] = (int16_t)sample;
phase += TEST_TONE_PHASE_STEP;
if (phase >= TEST_TONE_PHASE_MODULUS)
phase -= TEST_TONE_PHASE_MODULUS;
}
}
// Queries one stream with the required public structure size.
static SYS_AUDIO_STREAM_INFO query_stream(uint32_t stream) {
SYS_AUDIO_STREAM_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
KSTATUS status = SysAudioQueryStream(stream, &info);
if (status != NTLSTATUS_SUCCESS)
fail_status("query stream", status);
if (info.Version != SYS_AUDIO_ABI_VERSION)
fail("unexpected audio ABI version");
return info;
}
// Applies one valid stream volume, pan, and mute setting.
static void set_stream_mix(uint32_t stream,
uint32_t volumeQ16,
int32_t panQ16,
uint32_t muted) {
SYS_AUDIO_SET_VOLUME_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.StreamHandle = stream;
request.Muted = muted;
request.VolumeQ16 = volumeQ16;
request.PanQ16 = panQ16;
KSTATUS status = SysAudioSetVolume(&request);
if (status != NTLSTATUS_SUCCESS)
fail_status("set stream mix", status);
}
// Selects the available default endpoint, or the first available endpoint.
static const SYS_AUDIO_ENDPOINT_INFO* select_endpoint(const SYS_AUDIO_ENDPOINT_INFO* endpoints,
uint32_t count) {
const SYS_AUDIO_ENDPOINT_INFO* fallback = 0;
for (uint32_t index = 0; index < count; index++) {
const SYS_AUDIO_ENDPOINT_INFO* endpoint = &endpoints[index];
if (endpoint->Size != sizeof(*endpoint) ||
endpoint->Version != SYS_AUDIO_ABI_VERSION ||
endpoint->State != SysAudioEndpointReady ||
(endpoint->Flags & SYS_AUDIO_ENDPOINT_FLAG_AVAILABLE) == 0)
continue;
if (!fallback) fallback = endpoint;
if (endpoint->Flags & SYS_AUDIO_ENDPOINT_FLAG_DEFAULT)
return endpoint;
}
return fallback;
}
// Queries one endpoint by its stable identifier.
static SYS_AUDIO_ENDPOINT_INFO query_endpoint(uint64_t endpointId) {
SYS_AUDIO_ENDPOINT_INFO endpoints[SYS_AUDIO_MAX_ENDPOINTS];
uint32_t count = 0;
SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Endpoints = endpoints;
request.Capacity = SYS_AUDIO_MAX_ENDPOINTS;
request.CountOut = &count;
KSTATUS status = SysAudioEnumerateEndpoints(&request);
if (status != NTLSTATUS_SUCCESS)
fail_status("query endpoint", status);
for (uint32_t index = 0; index < count; index++) {
if (endpoints[index].EndpointId == endpointId)
return endpoints[index];
}
fail("endpoint disappeared during control test");
return endpoints[0];
}
// Verifies endpoint master volume, mute, validation, and restoration.
static void test_master_volume(const SYS_AUDIO_ENDPOINT_INFO* endpoint) {
SYS_AUDIO_SET_MASTER_VOLUME_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.EndpointId = endpoint->EndpointId;
request.Muted = 1u;
request.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY / 2u;
KSTATUS status = SysAudioSetMasterVolume(&request);
if (status != NTLSTATUS_SUCCESS)
fail_status("set master volume", status);
SYS_AUDIO_ENDPOINT_INFO updated = query_endpoint(endpoint->EndpointId);
if (updated.MasterVolumeQ16 != SYS_AUDIO_GAIN_Q16_UNITY / 2u ||
updated.MasterMuted != 1u)
fail("master volume snapshot mismatch");
request.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY + 1u;
status = SysAudioSetMasterVolume(&request);
if (status != NTLSTATUS_INVALID_PARAMETER)
fail_status("reject master volume above unity", status);
request.Muted = endpoint->MasterMuted;
request.VolumeQ16 = endpoint->MasterVolumeQ16;
status = SysAudioSetMasterVolume(&request);
if (status != NTLSTATUS_SUCCESS)
fail_status("restore master volume", status);
}
// Opens one stream format and checks the expected status.
static uint32_t open_stream_format(uint64_t endpointId,
uint32_t sampleRate,
uint16_t channelCount,
uint16_t sampleFormat,
KSTATUS expectedStatus) {
uint32_t stream = 0;
SYS_AUDIO_OPEN_STREAM_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.EndpointId = endpointId;
request.Format.SampleRate = sampleRate;
request.Format.ChannelCount = channelCount;
request.Format.SampleFormat = sampleFormat;
request.StreamHandleOut = &stream;
KSTATUS status = SysAudioOpenStream(&request);
if (status != expectedStatus)
fail_status("open stream format", status);
if (status == NTLSTATUS_SUCCESS && !stream)
fail("open returned a null handle");
if (status != NTLSTATUS_SUCCESS && stream)
fail("rejected format returned a handle");
return stream;
}
// Proves the complete accepted and rejected source-format surface.
static void test_format_contract(uint64_t endpointId) {
static const uint32_t rates[] = { 11025u, 22050u, 44100u, 48000u };
for (uint32_t rateIndex = 0; rateIndex < sizeof(rates) / sizeof(rates[0]); rateIndex++) {
for (uint16_t channels = 1; channels <= 2; channels++) {
uint32_t stream = open_stream_format(endpointId,
rates[rateIndex],
channels,
SysAudioSampleS16,
NTLSTATUS_SUCCESS);
KSTATUS status = SysCloseHandle(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("close format stream", status);
}
}
(void)open_stream_format(endpointId, 48000u, 2, 2, NTLSTATUS_NOT_SUPPORTED);
(void)open_stream_format(endpointId, 32000u, 2, SysAudioSampleS16, NTLSTATUS_NOT_SUPPORTED);
(void)open_stream_format(endpointId, 48000u, 3, SysAudioSampleS16, NTLSTATUS_NOT_SUPPORTED);
}
// Opens the canonical test stream.
static uint32_t open_stream(uint64_t endpointId) {
return open_stream_format(endpointId,
48000u,
2,
SysAudioSampleS16,
NTLSTATUS_SUCCESS);
}
// Writes one sample run and returns the accepted frame count.
static uint32_t write_samples(uint32_t stream,
const int16_t* frames,
uint32_t frameCount,
KSTATUS expectedStatus) {
uint32_t written = 0xFFFFFFFFu;
SYS_AUDIO_WRITE_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.StreamHandle = stream;
request.FrameCount = frameCount;
request.Frames = frameCount ? frames : 0;
request.WrittenFramesOut = &written;
KSTATUS status = SysAudioWrite(&request);
if (status != expectedStatus) fail_status("write stream", status);
if (status != NTLSTATUS_SUCCESS && written != 0)
fail("failed write reported accepted frames");
return written;
}
// Writes one contract-tone range and returns the accepted frame count.
static uint32_t write_frames(uint32_t stream,
uint32_t frameOffset,
uint32_t frameCount,
KSTATUS expectedStatus) {
if (frameOffset > TEST_TONE_FRAMES ||
frameCount > TEST_TONE_FRAMES - frameOffset)
fail("test write range exceeded the tone buffer");
return write_samples(stream,
frameCount ? gFrames + (size_t)frameOffset * 2u : 0,
frameCount,
expectedStatus);
}
// Arms one exact set of stream progress targets.
static void arm_wait(uint32_t stream,
uint32_t mask,
uint32_t writableFrames,
uint64_t consumedFrames,
uint64_t presentedFrames,
uint64_t drainedGeneration,
uint64_t discontinuityGeneration) {
SYS_AUDIO_ARM_WAIT_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.StreamHandle = stream;
request.Mask = mask;
request.WritableFrames = writableFrames;
request.ConsumedFrames = consumedFrames;
request.PresentedFrames = presentedFrames;
request.DrainedGeneration = drainedGeneration;
request.DiscontinuityGeneration = discontinuityGeneration;
KSTATUS status = SysAudioArmWait(&request);
if (status != NTLSTATUS_SUCCESS)
fail_status("arm wait", status);
}
// Waits once and converts timeout or alert completion into a focused failure.
static void wait_stream(uint32_t stream, const char* operation) {
KSTATUS status = SysWaitSingle(stream, SYS_WAIT_FLAG_NONE, TEST_WAIT_MILLISECONDS);
if (status != NTLSTATUS_SUCCESS)
fail_status(operation, status);
}
// Proves that closing the final handle retires an active stream with queued periods.
static void test_active_close_rundown(uint64_t endpointId) {
for (uint32_t iteration = 0;
iteration < TEST_CLOSE_RUNDOWN_ITERATIONS;
iteration++) {
uint32_t stream = open_stream(endpointId);
gOpenStream = stream;
set_stream_mix(stream,
SYS_AUDIO_GAIN_Q16_UNITY,
SYS_AUDIO_PAN_Q16_CENTER,
1u);
uint32_t written = write_frames(stream,
0,
SYS_AUDIO_STREAM_CAPACITY_FRAMES,
NTLSTATUS_SUCCESS);
if (written != SYS_AUDIO_STREAM_CAPACITY_FRAMES)
fail("active-close prefill was partial");
SYS_AUDIO_STREAM_INFO before = query_stream(stream);
arm_wait(stream,
SYS_AUDIO_WAIT_CONSUMED |
SYS_AUDIO_WAIT_FAILED |
SYS_AUDIO_WAIT_DISCONTINUITY,
0,
before.FramesConsumed + TEST_EXTRA_FRAMES,
before.FramesPresented,
before.DrainedGeneration,
before.DiscontinuityGeneration);
KSTATUS status = SysAudioStart(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("active-close start", status);
wait_stream(stream, "active-close consumed wait");
SYS_AUDIO_STREAM_INFO active = query_stream(stream);
if (active.State == SysAudioStreamFailed)
fail_status("active-close stream failure", active.FailureStatus);
if (active.DiscontinuityGeneration != before.DiscontinuityGeneration ||
active.FramesConsumed < before.FramesConsumed + TEST_EXTRA_FRAMES)
fail("active-close progress mismatch");
status = SysCloseHandle(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("active-close final handle", status);
gOpenStream = 0;
status = SysSleep(TEST_CLOSE_RUNDOWN_SLEEP_MILLISECONDS);
if (status != NTLSTATUS_SUCCESS)
fail_status("active-close rundown sleep", status);
}
}
// Plays one bounded audible tone without depending on real-time user refills.
static void play_continuous_tone(uint32_t stream) {
KSTATUS status = SysAudioReset(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("audible reset", status);
set_stream_mix(stream,
SYS_AUDIO_GAIN_Q16_UNITY,
SYS_AUDIO_PAN_Q16_CENTER,
0u);
SYS_AUDIO_STREAM_INFO baseline = query_stream(stream);
uint32_t submitted = write_samples(stream,
gAudibleFrames,
TEST_AUDIBLE_FRAMES,
NTLSTATUS_SUCCESS);
if (submitted != TEST_AUDIBLE_FRAMES)
fail("audible prefill was partial");
status = SysAudioStart(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("audible start", status);
status = SysAudioDrain(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("audible drain", status);
print("audio_stream_smoke: audible 440 Hz tone begin\n");
arm_wait(stream,
SYS_AUDIO_WAIT_DRAINED |
SYS_AUDIO_WAIT_FAILED |
SYS_AUDIO_WAIT_DISCONTINUITY,
0,
baseline.FramesConsumed,
baseline.FramesPresented,
baseline.DrainedGeneration,
baseline.DiscontinuityGeneration);
wait_stream(stream, "audible drained wait");
SYS_AUDIO_STREAM_INFO drained = query_stream(stream);
if (drained.State == SysAudioStreamFailed)
fail_status("audible stream failure", drained.FailureStatus);
if (drained.State != SysAudioStreamStopped ||
drained.DrainedGeneration != baseline.DrainedGeneration + 1u ||
drained.DiscontinuityGeneration != baseline.DiscontinuityGeneration ||
drained.FramesSubmitted != TEST_AUDIBLE_FRAMES ||
drained.FramesConsumed != TEST_AUDIBLE_FRAMES ||
drained.FramesPresented != TEST_AUDIBLE_FRAMES ||
drained.BufferedFrames != 0 ||
drained.Underruns != 0 ||
drained.SilenceFramesInserted != 0)
fail("audible tone completion mismatch");
print("audio_stream_smoke: audible 440 Hz tone complete\n");
}
// Runs the complete native stream contract against one endpoint.
static void test_stream(const SYS_AUDIO_ENDPOINT_INFO* endpoint) {
test_format_contract(endpoint->EndpointId);
test_active_close_rundown(endpoint->EndpointId);
uint32_t stream = open_stream(endpoint->EndpointId);
gOpenStream = stream;
KSTATUS status;
SYS_AUDIO_STREAM_INFO info = query_stream(stream);
if (info.State != SysAudioStreamCreated ||
info.CapacityFrames != SYS_AUDIO_STREAM_CAPACITY_FRAMES ||
info.Format.SampleFormat != SysAudioSampleS16)
fail("initial stream snapshot mismatch");
SYS_AUDIO_SET_VOLUME_REQUEST volume;
memset(&volume, 0, sizeof(volume));
volume.Size = sizeof(volume);
volume.StreamHandle = stream;
volume.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY / 2u;
volume.PanQ16 = SYS_AUDIO_PAN_Q16_CENTER;
status = SysAudioSetVolume(&volume);
if (status != NTLSTATUS_SUCCESS)
fail_status("set volume", status);
info = query_stream(stream);
if (info.VolumeQ16 != SYS_AUDIO_GAIN_Q16_UNITY / 2u ||
info.PanQ16 != SYS_AUDIO_PAN_Q16_CENTER || info.Muted)
fail("volume snapshot mismatch");
volume.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY + 1u;
status = SysAudioSetVolume(&volume);
if (status != NTLSTATUS_INVALID_PARAMETER)
fail_status("reject volume above unity", status);
volume.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY / 2u;
volume.PanQ16 = SYS_AUDIO_PAN_Q16_RIGHT + 1;
status = SysAudioSetVolume(&volume);
if (status != NTLSTATUS_INVALID_PARAMETER)
fail_status("reject pan outside range", status);
volume.VolumeQ16 = SYS_AUDIO_GAIN_Q16_UNITY;
volume.PanQ16 = SYS_AUDIO_PAN_Q16_CENTER;
volume.Muted = 1u;
status = SysAudioSetVolume(&volume);
if (status != NTLSTATUS_SUCCESS)
fail_status("mute control stream", status);
uint32_t written = write_frames(stream, 0, 0, NTLSTATUS_SUCCESS);
if (written != 0) fail("zero-length write accepted frames");
written = write_frames(stream,
0,
SYS_AUDIO_STREAM_CAPACITY_FRAMES + TEST_EXTRA_FRAMES,
NTLSTATUS_SUCCESS);
if (written != SYS_AUDIO_STREAM_CAPACITY_FRAMES)
fail("partial write did not fill exactly one stream ring");
written = write_frames(stream, 0, 1, NTLSTATUS_WOULD_BLOCK);
if (written != 0) fail("full-ring write accepted a frame");
info = query_stream(stream);
arm_wait(stream,
SYS_AUDIO_WAIT_WRITABLE |
SYS_AUDIO_WAIT_CONSUMED |
SYS_AUDIO_WAIT_FAILED |
SYS_AUDIO_WAIT_DISCONTINUITY,
TEST_EXTRA_FRAMES,
info.FramesConsumed + TEST_EXTRA_FRAMES,
0,
info.DrainedGeneration,
info.DiscontinuityGeneration);
status = SysAudioStart(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("start", status);
wait_stream(stream, "writable wait");
SYS_AUDIO_STREAM_INFO progressed = query_stream(stream);
if (progressed.State == SysAudioStreamFailed)
fail_status("stream failure", progressed.FailureStatus);
if (progressed.DiscontinuityGeneration != info.DiscontinuityGeneration)
fail("unexpected discontinuity before writable target");
if (progressed.WritableFrames < TEST_EXTRA_FRAMES ||
progressed.FramesConsumed < info.FramesConsumed + TEST_EXTRA_FRAMES)
fail("writable or consumed wait did not observe its target");
status = SysAudioPause(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("pause", status);
if (query_stream(stream).State != SysAudioStreamPaused)
fail("pause state mismatch");
status = SysAudioResume(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("resume", status);
uint32_t submitted = SYS_AUDIO_STREAM_CAPACITY_FRAMES;
written = write_frames(stream, submitted, TEST_EXTRA_FRAMES, NTLSTATUS_SUCCESS);
if (written != TEST_EXTRA_FRAMES)
fail("resume refill was partial");
submitted += written;
while (submitted < TEST_TONE_FRAMES) {
SYS_AUDIO_STREAM_INFO refill = query_stream(stream);
if (refill.State == SysAudioStreamFailed)
fail_status("stream failure", refill.FailureStatus);
if (refill.DiscontinuityGeneration != info.DiscontinuityGeneration)
fail("unexpected discontinuity during muted control refill");
uint32_t remaining = TEST_TONE_FRAMES - submitted;
uint32_t refillFrames = remaining < TEST_REFILL_FRAMES
? remaining
: TEST_REFILL_FRAMES;
if (refill.WritableFrames < refillFrames) {
arm_wait(stream,
SYS_AUDIO_WAIT_WRITABLE |
SYS_AUDIO_WAIT_FAILED |
SYS_AUDIO_WAIT_DISCONTINUITY,
refillFrames,
refill.FramesConsumed,
refill.FramesPresented,
refill.DrainedGeneration,
info.DiscontinuityGeneration);
wait_stream(stream, "muted control refill wait");
continue;
}
written = write_frames(stream, submitted, refillFrames, NTLSTATUS_SUCCESS);
if (written != refillFrames)
fail("muted control refill was partial despite reserved capacity");
submitted += written;
}
SYS_AUDIO_STREAM_INFO beforeDrain = query_stream(stream);
if (beforeDrain.FramesSubmitted != TEST_TONE_FRAMES)
fail("muted control frame submission mismatch");
uint64_t presentedTarget = beforeDrain.FramesSubmitted;
status = SysAudioDrain(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("drain", status);
arm_wait(stream,
SYS_AUDIO_WAIT_PRESENTED |
SYS_AUDIO_WAIT_FAILED |
SYS_AUDIO_WAIT_DISCONTINUITY,
0,
beforeDrain.FramesConsumed,
presentedTarget,
beforeDrain.DrainedGeneration,
beforeDrain.DiscontinuityGeneration);
wait_stream(stream, "presentation wait");
SYS_AUDIO_STREAM_INFO presented = query_stream(stream);
if (presented.State == SysAudioStreamFailed)
fail_status("stream failure", presented.FailureStatus);
if (presented.DiscontinuityGeneration != beforeDrain.DiscontinuityGeneration)
fail("unexpected discontinuity before presentation target");
if (presented.FramesPresented < presentedTarget)
fail("presented wait did not observe its target");
if (presented.State != SysAudioStreamStopped) {
arm_wait(stream,
SYS_AUDIO_WAIT_DRAINED |
SYS_AUDIO_WAIT_FAILED |
SYS_AUDIO_WAIT_DISCONTINUITY,
0,
presented.FramesConsumed,
presented.FramesPresented,
beforeDrain.DrainedGeneration,
beforeDrain.DiscontinuityGeneration);
wait_stream(stream, "drained wait");
}
SYS_AUDIO_STREAM_INFO drained = query_stream(stream);
if (drained.State == SysAudioStreamFailed)
fail_status("stream failure", drained.FailureStatus);
if (drained.DiscontinuityGeneration != beforeDrain.DiscontinuityGeneration)
fail("unexpected discontinuity during drain");
if (drained.State != SysAudioStreamStopped ||
drained.DrainedGeneration != beforeDrain.DrainedGeneration + 1u ||
drained.BufferedFrames != 0 ||
drained.FramesPresented < presentedTarget ||
drained.FramesPresented + drained.FramesDiscarded < drained.FramesSubmitted)
fail("drained stream snapshot mismatch");
status = SysAudioReset(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("reset", status);
SYS_AUDIO_STREAM_INFO reset = query_stream(stream);
if (reset.State != SysAudioStreamStopped || reset.FramesSubmitted != 0 ||
reset.FramesConsumed != 0 || reset.FramesPresented != 0 ||
reset.DrainedGeneration != drained.DrainedGeneration)
fail("reset stream snapshot mismatch");
written = write_frames(stream, 0, 128, NTLSTATUS_SUCCESS);
if (written != 128) fail("stop test write was partial");
status = SysAudioStart(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("restart", status);
SYS_AUDIO_STREAM_INFO beforeStop = query_stream(stream);
arm_wait(stream,
SYS_AUDIO_WAIT_STOPPED |
SYS_AUDIO_WAIT_FAILED |
SYS_AUDIO_WAIT_DISCONTINUITY,
0,
beforeStop.FramesConsumed,
beforeStop.FramesPresented,
beforeStop.DrainedGeneration,
beforeStop.DiscontinuityGeneration);
status = SysAudioStop(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("stop", status);
wait_stream(stream, "stopped wait");
if (query_stream(stream).State != SysAudioStreamStopped)
fail("stop state mismatch");
play_continuous_tone(stream);
status = SysCloseHandle(stream);
if (status != NTLSTATUS_SUCCESS)
fail_status("close stream", status);
gOpenStream = 0;
status = SysSleep(TEST_IDLE_QUIESCE_MILLISECONDS);
if (status != NTLSTATUS_SUCCESS)
fail_status("endpoint quiesce sleep", status);
}
// Executes the native audio syscall smoke test.
int main(void) {
fill_test_frames();
print("audio_stream_smoke: begin native render contract test\n");
uint32_t count = 0;
SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST countRequest;
memset(&countRequest, 0, sizeof(countRequest));
countRequest.Size = sizeof(countRequest);
countRequest.CountOut = &count;
KSTATUS status = SysAudioEnumerateEndpoints(&countRequest);
if (status != NTLSTATUS_SUCCESS && status != NTLSTATUS_BUFFER_TOO_SMALL)
fail_status("count endpoints", status);
if (!count) {
print("audio_stream_smoke: SKIP no render endpoint\n");
exit_now(0);
}
if (count > SYS_AUDIO_MAX_ENDPOINTS)
fail("endpoint count exceeded ABI maximum");
SYS_AUDIO_ENDPOINT_INFO endpoints[SYS_AUDIO_MAX_ENDPOINTS];
memset(endpoints, 0, sizeof(endpoints));
SYS_AUDIO_ENUMERATE_ENDPOINTS_REQUEST enumerate;
memset(&enumerate, 0, sizeof(enumerate));
enumerate.Size = sizeof(enumerate);
enumerate.Endpoints = endpoints;
enumerate.Capacity = SYS_AUDIO_MAX_ENDPOINTS;
enumerate.CountOut = &count;
status = SysAudioEnumerateEndpoints(&enumerate);
if (status != NTLSTATUS_SUCCESS)
fail_status("enumerate endpoints", status);
const SYS_AUDIO_ENDPOINT_INFO* endpoint = select_endpoint(endpoints, count);
if (!endpoint) {
print("audio_stream_smoke: SKIP no available render endpoint\n");
exit_now(0);
}
print("audio_stream_smoke: endpoint=");
print_u64(endpoint->EndpointId);
print("\n");
test_master_volume(endpoint);
test_stream(endpoint);
print("audio_stream_smoke: PASS endpoint=");
print_u64(endpoint->EndpointId);
print("\n");
exit_now(0);
}