mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 21:55:36 +00:00
WASAPI: sine wave example works with raw device
This commit is contained in:
parent
dd7a6a8bbc
commit
90fa377c99
18
README.md
18
README.md
|
@ -242,7 +242,23 @@ cmake .. -DCMAKE_TOOLCHAIN_FILE=/path/to/mxe/usr/x86_64-w64-mingw32.static/share
|
||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Running the Tests
|
### Testing
|
||||||
|
|
||||||
|
For each backend, do the following:
|
||||||
|
|
||||||
|
0. Run the unit tests: `./unit_tests`. To see test coverage, install lcov, run
|
||||||
|
`make coverage`, and then view `coverage/index.html` in a browser.
|
||||||
|
0. Run the example `./sio_list_devices` and make sure it does not crash, and
|
||||||
|
the output looks good. If valgrind is available, use it.
|
||||||
|
0. Run `./sio_list_devices --watch` and make sure it detects when you plug and
|
||||||
|
unplug a USB microphone.
|
||||||
|
0. Run `./sio_sine` and make sure you hear a sine wave. For backends with raw
|
||||||
|
devices, run `./sio_sine --device id` (where 'id' is a device id you got
|
||||||
|
from `sio_list_devices` and make sure you hear a sine wave.
|
||||||
|
0. Run `./underflow` and read the testing instructions that it prints.
|
||||||
|
0. Run `./sio_microphone` and ensure that it is both recording and playing
|
||||||
|
back correctly. If possible use the `--in-device` and `--out-device`
|
||||||
|
parameters to test a USB microphone in raw mode.
|
||||||
|
|
||||||
```
|
```
|
||||||
make test
|
make test
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
static void panic(const char *format, ...) {
|
static void panic(const char *format, ...) {
|
||||||
|
@ -28,11 +29,36 @@ static int usage(char *exe) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const float PI = 3.1415926535f;
|
static void write_sample_s16ne(char *ptr, double sample) {
|
||||||
static float seconds_offset = 0.0f;
|
int16_t *buf = (int16_t *)ptr;
|
||||||
|
double range = (double)INT16_MAX - (double)INT16_MIN;
|
||||||
|
double val = sample * range / 2.0;
|
||||||
|
*buf = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_sample_s32ne(char *ptr, double sample) {
|
||||||
|
int32_t *buf = (int32_t *)ptr;
|
||||||
|
double range = (double)INT32_MAX - (double)INT32_MIN;
|
||||||
|
double val = sample * range / 2.0;
|
||||||
|
*buf = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_sample_float32ne(char *ptr, double sample) {
|
||||||
|
float *buf = (float *)ptr;
|
||||||
|
*buf = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_sample_float64ne(char *ptr, double sample) {
|
||||||
|
double *buf = (double *)ptr;
|
||||||
|
*buf = sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void (*write_sample)(char *ptr, double sample);
|
||||||
|
static const double PI = 3.14159265358979323846264338328;
|
||||||
|
static double seconds_offset = 0.0;
|
||||||
static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) {
|
static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) {
|
||||||
float float_sample_rate = outstream->sample_rate;
|
double float_sample_rate = outstream->sample_rate;
|
||||||
float seconds_per_frame = 1.0f / float_sample_rate;
|
double seconds_per_frame = 1.0f / float_sample_rate;
|
||||||
struct SoundIoChannelArea *areas;
|
struct SoundIoChannelArea *areas;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -48,13 +74,13 @@ static void write_callback(struct SoundIoOutStream *outstream, int frame_count_m
|
||||||
|
|
||||||
const struct SoundIoChannelLayout *layout = &outstream->layout;
|
const struct SoundIoChannelLayout *layout = &outstream->layout;
|
||||||
|
|
||||||
float pitch = 440.0f;
|
double pitch = 440.0;
|
||||||
float radians_per_second = pitch * 2.0f * PI;
|
double radians_per_second = pitch * 2.0 * PI;
|
||||||
for (int frame = 0; frame < frame_count; frame += 1) {
|
for (int frame = 0; frame < frame_count; frame += 1) {
|
||||||
float sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second);
|
double sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second);
|
||||||
for (int channel = 0; channel < layout->channel_count; channel += 1) {
|
for (int channel = 0; channel < layout->channel_count; channel += 1) {
|
||||||
float *ptr = (float*)(areas[channel].ptr + areas[channel].step * frame);
|
write_sample(areas[channel].ptr, sample);
|
||||||
*ptr = sample;
|
areas[channel].ptr += areas[channel].step;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
seconds_offset += seconds_per_frame * frame_count;
|
seconds_offset += seconds_per_frame * frame_count;
|
||||||
|
@ -158,15 +184,32 @@ int main(int argc, char **argv) {
|
||||||
fprintf(stderr, "Output device: %s\n", device->name);
|
fprintf(stderr, "Output device: %s\n", device->name);
|
||||||
|
|
||||||
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
|
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
|
||||||
outstream->format = SoundIoFormatFloat32NE;
|
|
||||||
outstream->write_callback = write_callback;
|
outstream->write_callback = write_callback;
|
||||||
outstream->underflow_callback = underflow_callback;
|
outstream->underflow_callback = underflow_callback;
|
||||||
|
|
||||||
|
if (soundio_device_supports_format(device, SoundIoFormatFloat32NE)) {
|
||||||
|
outstream->format = SoundIoFormatFloat32NE;
|
||||||
|
write_sample = write_sample_float32ne;
|
||||||
|
} else if (soundio_device_supports_format(device, SoundIoFormatFloat64NE)) {
|
||||||
|
outstream->format = SoundIoFormatFloat64NE;
|
||||||
|
write_sample = write_sample_float64ne;
|
||||||
|
} else if (soundio_device_supports_format(device, SoundIoFormatS32NE)) {
|
||||||
|
outstream->format = SoundIoFormatS32NE;
|
||||||
|
write_sample = write_sample_s32ne;
|
||||||
|
} else if (soundio_device_supports_format(device, SoundIoFormatS16NE)) {
|
||||||
|
outstream->format = SoundIoFormatS16NE;
|
||||||
|
write_sample = write_sample_s16ne;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "No suitable device format available.\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
if ((err = soundio_outstream_open(outstream))) {
|
if ((err = soundio_outstream_open(outstream))) {
|
||||||
fprintf(stderr, "unable to open device: %s", soundio_strerror(err));
|
fprintf(stderr, "unable to open device: %s", soundio_strerror(err));
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (outstream->layout_error)
|
if (outstream->layout_error)
|
||||||
fprintf(stderr, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error));
|
fprintf(stderr, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error));
|
||||||
|
|
||||||
|
|
|
@ -454,9 +454,6 @@ int soundio_outstream_open(struct SoundIoOutStream *outstream) {
|
||||||
if (device->aim != SoundIoDeviceAimOutput)
|
if (device->aim != SoundIoDeviceAimOutput)
|
||||||
return SoundIoErrorInvalid;
|
return SoundIoErrorInvalid;
|
||||||
|
|
||||||
if (outstream->format <= SoundIoFormatInvalid)
|
|
||||||
return SoundIoErrorInvalid;
|
|
||||||
|
|
||||||
if (device->probe_error)
|
if (device->probe_error)
|
||||||
return device->probe_error;
|
return device->probe_error;
|
||||||
|
|
||||||
|
@ -465,6 +462,9 @@ int soundio_outstream_open(struct SoundIoOutStream *outstream) {
|
||||||
SoundIoFormatFloat32NE : device->formats[0];
|
SoundIoFormatFloat32NE : device->formats[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (outstream->format <= SoundIoFormatInvalid)
|
||||||
|
return SoundIoErrorInvalid;
|
||||||
|
|
||||||
if (!outstream->layout.channel_count) {
|
if (!outstream->layout.channel_count) {
|
||||||
const SoundIoChannelLayout *stereo = soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdStereo);
|
const SoundIoChannelLayout *stereo = soundio_channel_layout_get_builtin(SoundIoChannelLayoutIdStereo);
|
||||||
outstream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
|
outstream->layout = soundio_device_supports_layout(device, stereo) ? *stereo : device->layouts[0];
|
||||||
|
|
107
src/wasapi.cpp
107
src/wasapi.cpp
|
@ -18,8 +18,6 @@ const static GUID SOUNDIO_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {
|
||||||
const static GUID SOUNDIO_KSDATAFORMAT_SUBTYPE_PCM = {
|
const static GUID SOUNDIO_KSDATAFORMAT_SUBTYPE_PCM = {
|
||||||
0x00000001,0x0000,0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
0x00000001,0x0000,0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
||||||
|
|
||||||
const static DWORD SOUNDIO_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM = 0x80000000;
|
|
||||||
|
|
||||||
// Adding more common sample rates helps the heuristics; feel free to do that.
|
// Adding more common sample rates helps the heuristics; feel free to do that.
|
||||||
static int test_sample_rates[] = {
|
static int test_sample_rates[] = {
|
||||||
8000,
|
8000,
|
||||||
|
@ -64,6 +62,27 @@ static SoundIoChannelLayoutId test_layouts[] = {
|
||||||
SoundIoChannelLayoutId5Point1Back,
|
SoundIoChannelLayoutId5Point1Back,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
static void print_possible_err_codes() {
|
||||||
|
fprintf(stderr, "AUDCLNT_E_ALREADY_INITIALIZED: %ld\n", AUDCLNT_E_ALREADY_INITIALIZED);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_WRONG_ENDPOINT_TYPE: %ld\n", AUDCLNT_E_WRONG_ENDPOINT_TYPE);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: %ld\n", AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_BUFFER_SIZE_ERROR: %ld\n", AUDCLNT_E_BUFFER_SIZE_ERROR);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_CPUUSAGE_EXCEEDED: %ld\n", AUDCLNT_E_CPUUSAGE_EXCEEDED);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_DEVICE_INVALIDATED: %ld\n", AUDCLNT_E_DEVICE_INVALIDATED);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_DEVICE_IN_USE: %ld\n", AUDCLNT_E_DEVICE_IN_USE);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_ENDPOINT_CREATE_FAILED: %ld\n", AUDCLNT_E_ENDPOINT_CREATE_FAILED);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_INVALID_DEVICE_PERIOD: %ld\n", AUDCLNT_E_INVALID_DEVICE_PERIOD);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_UNSUPPORTED_FORMAT: %ld\n", AUDCLNT_E_UNSUPPORTED_FORMAT);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: %ld\n", AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: %ld\n", AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL);
|
||||||
|
fprintf(stderr, "AUDCLNT_E_SERVICE_NOT_RUNNING: %ld\n", AUDCLNT_E_SERVICE_NOT_RUNNING);
|
||||||
|
fprintf(stderr, "E_POINTER: %ld\n", E_POINTER);
|
||||||
|
fprintf(stderr, "E_INVALIDARG: %ld\n", E_INVALIDARG);
|
||||||
|
fprintf(stderr, "E_OUTOFMEMORY: %ld\n", E_OUTOFMEMORY);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// converts a windows wide string to a UTF-8 encoded char *
|
// converts a windows wide string to a UTF-8 encoded char *
|
||||||
// Possible errors:
|
// Possible errors:
|
||||||
// * SoundIoErrorNoMem
|
// * SoundIoErrorNoMem
|
||||||
|
@ -611,6 +630,7 @@ static int refresh_devices(SoundIoPrivate *si) {
|
||||||
rd.device_shared->ref_count = 1;
|
rd.device_shared->ref_count = 1;
|
||||||
rd.device_shared->soundio = soundio;
|
rd.device_shared->soundio = soundio;
|
||||||
rd.device_shared->is_raw = false;
|
rd.device_shared->is_raw = false;
|
||||||
|
rd.device_shared->software_latency_max = 2.0;
|
||||||
|
|
||||||
SoundIoDevicePrivate *dev_raw = allocate<SoundIoDevicePrivate>(1);
|
SoundIoDevicePrivate *dev_raw = allocate<SoundIoDevicePrivate>(1);
|
||||||
if (!dev_raw) {
|
if (!dev_raw) {
|
||||||
|
@ -624,6 +644,7 @@ static int refresh_devices(SoundIoPrivate *si) {
|
||||||
rd.device_raw->ref_count = 1;
|
rd.device_raw->ref_count = 1;
|
||||||
rd.device_raw->soundio = soundio;
|
rd.device_raw->soundio = soundio;
|
||||||
rd.device_raw->is_raw = true;
|
rd.device_raw->is_raw = true;
|
||||||
|
rd.device_raw->software_latency_max = 0.5;
|
||||||
|
|
||||||
int device_id_len;
|
int device_id_len;
|
||||||
if ((err = from_lpwstr(rd.lpwstr, &rd.device_shared->id, &device_id_len))) {
|
if ((err = from_lpwstr(rd.lpwstr, &rd.device_shared->id, &device_id_len))) {
|
||||||
|
@ -657,7 +678,10 @@ static int refresh_devices(SoundIoPrivate *si) {
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
dev_w_shared->period_duration = from_reference_time(default_device_period);
|
dev_w_shared->period_duration = from_reference_time(default_device_period);
|
||||||
|
rd.device_shared->software_latency_current = dev_w_shared->period_duration;
|
||||||
|
|
||||||
dev_w_raw->period_duration = from_reference_time(min_device_period);
|
dev_w_raw->period_duration = from_reference_time(min_device_period);
|
||||||
|
rd.device_raw->software_latency_min = dev_w_raw->period_duration * 2;
|
||||||
|
|
||||||
|
|
||||||
if (rd.endpoint) {
|
if (rd.endpoint) {
|
||||||
|
@ -954,9 +978,14 @@ static void outstream_destroy_wasapi(struct SoundIoPrivate *si, struct SoundIoOu
|
||||||
|
|
||||||
if (osw->thread) {
|
if (osw->thread) {
|
||||||
osw->thread_exit_flag.clear();
|
osw->thread_exit_flag.clear();
|
||||||
soundio_os_mutex_lock(osw->mutex);
|
if (osw->is_raw) {
|
||||||
soundio_os_cond_signal(osw->cond, osw->mutex);
|
if (osw->h_event)
|
||||||
soundio_os_mutex_unlock(osw->mutex);
|
SetEvent(osw->h_event);
|
||||||
|
} else {
|
||||||
|
soundio_os_mutex_lock(osw->mutex);
|
||||||
|
soundio_os_cond_signal(osw->cond, osw->mutex);
|
||||||
|
soundio_os_mutex_unlock(osw->mutex);
|
||||||
|
}
|
||||||
soundio_os_thread_destroy(osw->thread);
|
soundio_os_thread_destroy(osw->thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -966,6 +995,8 @@ static void outstream_destroy_wasapi(struct SoundIoPrivate *si, struct SoundIoOu
|
||||||
IUnknown_Release(osw->audio_clock_adjustment);
|
IUnknown_Release(osw->audio_clock_adjustment);
|
||||||
if (osw->audio_client)
|
if (osw->audio_client)
|
||||||
IUnknown_Release(osw->audio_client);
|
IUnknown_Release(osw->audio_client);
|
||||||
|
if (osw->h_event)
|
||||||
|
CloseHandle(osw->h_event);
|
||||||
|
|
||||||
soundio_os_cond_destroy(osw->cond);
|
soundio_os_cond_destroy(osw->cond);
|
||||||
soundio_os_mutex_destroy(osw->mutex);
|
soundio_os_mutex_destroy(osw->mutex);
|
||||||
|
@ -1029,7 +1060,6 @@ static int outstream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStr
|
||||||
CoTaskMemFree(mix_format);
|
CoTaskMemFree(mix_format);
|
||||||
mix_format = nullptr;
|
mix_format = nullptr;
|
||||||
osw->need_resample = (wave_format.Format.nSamplesPerSec != (DWORD)outstream->sample_rate);
|
osw->need_resample = (wave_format.Format.nSamplesPerSec != (DWORD)outstream->sample_rate);
|
||||||
// TODO do we need SOUNDIO_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM ?
|
|
||||||
flags = osw->need_resample ? AUDCLNT_STREAMFLAGS_RATEADJUST : 0;
|
flags = osw->need_resample ? AUDCLNT_STREAMFLAGS_RATEADJUST : 0;
|
||||||
share_mode = AUDCLNT_SHAREMODE_SHARED;
|
share_mode = AUDCLNT_SHAREMODE_SHARED;
|
||||||
periodicity = 0;
|
periodicity = 0;
|
||||||
|
@ -1065,22 +1095,35 @@ static int outstream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStr
|
||||||
CoTaskMemFree(mix_format);
|
CoTaskMemFree(mix_format);
|
||||||
mix_format = nullptr;
|
mix_format = nullptr;
|
||||||
osw->need_resample = (wave_format.Format.nSamplesPerSec != (DWORD)outstream->sample_rate);
|
osw->need_resample = (wave_format.Format.nSamplesPerSec != (DWORD)outstream->sample_rate);
|
||||||
// TODO do we need SOUNDIO_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM ?
|
|
||||||
flags = osw->need_resample ? AUDCLNT_STREAMFLAGS_RATEADJUST : 0;
|
flags = osw->need_resample ? AUDCLNT_STREAMFLAGS_RATEADJUST : 0;
|
||||||
to_wave_format_layout(&outstream->layout, &wave_format);
|
to_wave_format_layout(&outstream->layout, &wave_format);
|
||||||
to_wave_format_format(outstream->format, &wave_format);
|
to_wave_format_format(outstream->format, &wave_format);
|
||||||
complete_wave_format_data(&wave_format);
|
complete_wave_format_data(&wave_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_duration = to_reference_time(osw->buffer_frame_count / outstream->sample_rate);
|
buffer_duration = to_reference_time(osw->buffer_frame_count / (double)outstream->sample_rate);
|
||||||
if (osw->is_raw)
|
if (osw->is_raw)
|
||||||
periodicity = buffer_duration;
|
periodicity = buffer_duration;
|
||||||
if (FAILED(hr = IAudioClient_Initialize(osw->audio_client, share_mode, flags,
|
if (FAILED(hr = IAudioClient_Initialize(osw->audio_client, share_mode, flags,
|
||||||
buffer_duration, periodicity, (WAVEFORMATEX*)&wave_format, nullptr)))
|
buffer_duration, periodicity, (WAVEFORMATEX*)&wave_format, nullptr)))
|
||||||
{
|
{
|
||||||
outstream_destroy_wasapi(si, os);
|
if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) {
|
||||||
return SoundIoErrorOpeningDevice;
|
outstream_destroy_wasapi(si, os);
|
||||||
|
return SoundIoErrorIncompatibleDevice;
|
||||||
|
} else if (hr == E_OUTOFMEMORY) {
|
||||||
|
outstream_destroy_wasapi(si, os);
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
} else {
|
||||||
|
outstream_destroy_wasapi(si, os);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) {
|
||||||
|
outstream_destroy_wasapi(si, os);
|
||||||
|
return SoundIoErrorIncompatibleDevice;
|
||||||
|
} else if (hr == E_OUTOFMEMORY) {
|
||||||
|
outstream_destroy_wasapi(si, os);
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
} else {
|
} else {
|
||||||
outstream_destroy_wasapi(si, os);
|
outstream_destroy_wasapi(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
@ -1093,7 +1136,15 @@ static int outstream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStr
|
||||||
outstream->software_latency = osw->buffer_frame_count / (double)outstream->sample_rate;
|
outstream->software_latency = osw->buffer_frame_count / (double)outstream->sample_rate;
|
||||||
|
|
||||||
if (osw->is_raw) {
|
if (osw->is_raw) {
|
||||||
soundio_panic("TODO event callback");
|
osw->h_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||||
|
if (!osw->h_event) {
|
||||||
|
outstream_destroy_wasapi(si, os);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
if (FAILED(hr = IAudioClient_SetEventHandle(osw->audio_client, osw->h_event))) {
|
||||||
|
outstream_destroy_wasapi(si, os);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
} else if (osw->need_resample) {
|
} else if (osw->need_resample) {
|
||||||
if (FAILED(hr = IAudioClient_GetService(osw->audio_client, IID_IAudioClockAdjustment,
|
if (FAILED(hr = IAudioClient_GetService(osw->audio_client, IID_IAudioClockAdjustment,
|
||||||
(void**)&osw->audio_clock_adjustment)))
|
(void**)&osw->audio_clock_adjustment)))
|
||||||
|
@ -1181,16 +1232,40 @@ void outstream_shared_run(void *arg) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void outstream_raw_run(void *arg) {
|
||||||
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *) arg;
|
||||||
|
SoundIoOutStreamWasapi *osw = &os->backend_data.wasapi;
|
||||||
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
|
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
outstream->write_callback(outstream, osw->buffer_frame_count, osw->buffer_frame_count);
|
||||||
|
|
||||||
|
if (FAILED(hr = IAudioClient_Start(osw->audio_client))) {
|
||||||
|
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
WaitForSingleObject(osw->h_event, INFINITE);
|
||||||
|
if (!osw->thread_exit_flag.test_and_set())
|
||||||
|
return;
|
||||||
|
|
||||||
|
outstream->write_callback(outstream, osw->buffer_frame_count, osw->buffer_frame_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int outstream_start_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
static int outstream_start_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStreamPrivate *os) {
|
||||||
SoundIoOutStreamWasapi *osw = &os->backend_data.wasapi;
|
SoundIoOutStreamWasapi *osw = &os->backend_data.wasapi;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (osw->is_raw) {
|
assert(!osw->thread);
|
||||||
soundio_panic("TODO start raw");
|
osw->thread_exit_flag.test_and_set();
|
||||||
} else {
|
|
||||||
assert(!osw->thread);
|
|
||||||
|
|
||||||
osw->thread_exit_flag.test_and_set();
|
if (osw->is_raw) {
|
||||||
|
if ((err = soundio_os_thread_create(outstream_raw_run, os, true, &osw->thread)))
|
||||||
|
return err;
|
||||||
|
} else {
|
||||||
if ((err = soundio_os_thread_create(outstream_shared_run, os, true, &osw->thread)))
|
if ((err = soundio_os_thread_create(outstream_shared_run, os, true, &osw->thread)))
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ struct SoundIoOutStreamWasapi {
|
||||||
int writable_frame_count;
|
int writable_frame_count;
|
||||||
UINT32 buffer_frame_count;
|
UINT32 buffer_frame_count;
|
||||||
int write_frame_count;
|
int write_frame_count;
|
||||||
|
HANDLE h_event;
|
||||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,8 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
fprintf(stderr, "You should hear a sine wave for 3 seconds, then some period of silence or glitches,\n"
|
fprintf(stderr, "You should hear a sine wave for 3 seconds, then some period of silence or glitches,\n"
|
||||||
"then you should see at least one buffer underflow message, then hear a sine\n"
|
"then you should see at least one buffer underflow message, then hear a sine\n"
|
||||||
"wave for 3 seconds, then the program should exit successfully.\n");
|
"wave for 3 seconds, then the program should exit successfully.\n"
|
||||||
|
"WASAPI does not report buffer underflows.\n");
|
||||||
|
|
||||||
if (!(soundio = soundio_create()))
|
if (!(soundio = soundio_create()))
|
||||||
panic("out of memory");
|
panic("out of memory");
|
||||||
|
|
Loading…
Reference in a new issue