WASAPI: use stream name for session display name

This commit is contained in:
Andrew Kelley 2015-08-24 19:52:43 -07:00
parent 1bb10e6a13
commit d1f27fad83
8 changed files with 95 additions and 24 deletions

View file

@ -24,7 +24,7 @@ behavior on every platform.
- [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/) - [PulseAudio](http://www.freedesktop.org/wiki/Software/PulseAudio/)
- [ALSA](http://www.alsa-project.org/) - [ALSA](http://www.alsa-project.org/)
- [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html) - [CoreAudio](https://developer.apple.com/library/mac/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html)
- (in progress) [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx) - [WASAPI](https://msdn.microsoft.com/en-us/library/windows/desktop/dd371455%28v=vs.85%29.aspx)
- Dummy (silence) - Dummy (silence)
* Exposes both raw devices and shared devices. Raw devices give you the best * Exposes both raw devices and shared devices. Raw devices give you the best
performance but prevent other applications from using them. Shared devices performance but prevent other applications from using them. Shared devices
@ -260,24 +260,9 @@ For each backend, do the following:
back correctly. If possible use the `--in-device` and `--out-device` back correctly. If possible use the `--in-device` and `--out-device`
parameters to test a USB microphone in raw mode. parameters to test a USB microphone in raw mode.
```
make test
```
For more detailed output:
```
make
./unit_tests
```
To see test coverage, install lcov, run `make coverage` and then
view `coverage/index.html` in a browser.
## Roadmap ## Roadmap
0. implement WASAPI (Windows) backend, get examples working 0. implement WASAPI (Windows) backend, get examples working
- set display name of output stream
- move the bulk of the `outstream_open_wasapi` code to the thread and - move the bulk of the `outstream_open_wasapi` code to the thread and
have them communicate back and forth. because the thread has to do have them communicate back and forth. because the thread has to do
weird thread-local com stuff, and all that com stuff really needs to be weird thread-local com stuff, and all that com stuff really needs to be

View file

@ -24,8 +24,13 @@ static void panic(const char *format, ...) {
} }
static int usage(char *exe) { static int usage(char *exe) {
fprintf(stderr, "Usage: %s [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi] [--device id] [--raw]\n", fprintf(stderr, "Usage: %s [options]\n"
exe); "Options:\n"
" [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n"
" [--device id]\n"
" [--raw]\n"
" [--name stream_name]\n"
, exe);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -107,6 +112,7 @@ int main(int argc, char **argv) {
enum SoundIoBackend backend = SoundIoBackendNone; enum SoundIoBackend backend = SoundIoBackendNone;
char *device_id = NULL; char *device_id = NULL;
bool raw = false; bool raw = false;
char *stream_name = NULL;
for (int i = 1; i < argc; i += 1) { for (int i = 1; i < argc; i += 1) {
char *arg = argv[i]; char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') { if (arg[0] == '-' && arg[1] == '-') {
@ -135,6 +141,8 @@ int main(int argc, char **argv) {
} }
} else if (strcmp(arg, "--device") == 0) { } else if (strcmp(arg, "--device") == 0) {
device_id = argv[i]; device_id = argv[i];
} else if (strcmp(arg, "--name") == 0) {
stream_name = argv[i];
} else { } else {
return usage(exe); return usage(exe);
} }
@ -186,6 +194,7 @@ int main(int argc, char **argv) {
struct SoundIoOutStream *outstream = soundio_outstream_create(device); struct SoundIoOutStream *outstream = soundio_outstream_create(device);
outstream->write_callback = write_callback; outstream->write_callback = write_callback;
outstream->underflow_callback = underflow_callback; outstream->underflow_callback = underflow_callback;
outstream->name = stream_name;
if (soundio_device_supports_format(device, SoundIoFormatFloat32NE)) { if (soundio_device_supports_format(device, SoundIoFormatFloat32NE)) {
outstream->format = SoundIoFormatFloat32NE; outstream->format = SoundIoFormatFloat32NE;

View file

@ -453,6 +453,7 @@ struct SoundIoOutStream {
// PulseAudio uses this for the stream name. // PulseAudio uses this for the stream name.
// JACK uses this for the client name of the client that connects when you // JACK uses this for the client name of the client that connects when you
// open the stream. // open the stream.
// WASAPI uses this for the session display name.
// Must not contain a colon (":"). // Must not contain a colon (":").
const char *name; const char *name;
@ -521,6 +522,7 @@ struct SoundIoInStream {
// PulseAudio uses this for the stream name. // PulseAudio uses this for the stream name.
// JACK uses this for the client name of the client that connects when you // JACK uses this for the client name of the client that connects when you
// open the stream. // open the stream.
// WASAPI uses this for the session display name.
// Must not contain a colon (":"). // Must not contain a colon (":").
const char *name; const char *name;

View file

@ -415,6 +415,9 @@ static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStrea
if (sij->is_shutdown) if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected; return SoundIoErrorBackendDisconnected;
if (!outstream->name)
outstream->name = "SoundIoOutStream";
outstream->software_latency = device->software_latency_current; outstream->software_latency = device->software_latency_current;
osj->period_size = sij->period_size; osj->period_size = sij->period_size;
@ -617,6 +620,9 @@ static int instream_open_jack(struct SoundIoPrivate *si, struct SoundIoInStreamP
if (sij->is_shutdown) if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected; return SoundIoErrorBackendDisconnected;
if (!instream->name)
instream->name = "SoundIoInStream";
instream->software_latency = device->software_latency_current; instream->software_latency = device->software_latency_current;
isj->period_size = sij->period_size; isj->period_size = sij->period_size;

View file

@ -672,6 +672,9 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
if ((unsigned)outstream->layout.channel_count > PA_CHANNELS_MAX) if ((unsigned)outstream->layout.channel_count > PA_CHANNELS_MAX)
return SoundIoErrorIncompatibleBackend; return SoundIoErrorIncompatibleBackend;
if (!outstream->name)
outstream->name = "SoundIoOutStream";
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
ospa->stream_ready.store(false); ospa->stream_ready.store(false);
@ -883,6 +886,8 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
return SoundIoErrorInvalid; return SoundIoErrorInvalid;
if ((unsigned)instream->layout.channel_count > PA_CHANNELS_MAX) if ((unsigned)instream->layout.channel_count > PA_CHANNELS_MAX)
return SoundIoErrorIncompatibleBackend; return SoundIoErrorIncompatibleBackend;
if (!instream->name)
instream->name = "SoundIoInStream";
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio; SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
ispa->stream_ready = false; ispa->stream_ready = false;

View file

@ -473,9 +473,6 @@ int soundio_outstream_open(struct SoundIoOutStream *outstream) {
if (!outstream->sample_rate) if (!outstream->sample_rate)
outstream->sample_rate = soundio_device_nearest_sample_rate(device, 48000); outstream->sample_rate = soundio_device_nearest_sample_rate(device, 48000);
if (!outstream->name)
outstream->name = "SoundIoOutStream";
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream; SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
outstream->bytes_per_frame = soundio_get_bytes_per_frame(outstream->format, outstream->layout.channel_count); outstream->bytes_per_frame = soundio_get_bytes_per_frame(outstream->format, outstream->layout.channel_count);
outstream->bytes_per_sample = soundio_get_bytes_per_sample(outstream->format); outstream->bytes_per_sample = soundio_get_bytes_per_sample(outstream->format);
@ -559,9 +556,6 @@ int soundio_instream_open(struct SoundIoInStream *instream) {
if (!instream->sample_rate) if (!instream->sample_rate)
instream->sample_rate = soundio_device_nearest_sample_rate(device, 48000); instream->sample_rate = soundio_device_nearest_sample_rate(device, 48000);
if (!instream->name)
instream->name = "SoundIoInStream";
instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count); instream->bytes_per_frame = soundio_get_bytes_per_frame(instream->format, instream->layout.channel_count);
instream->bytes_per_sample = soundio_get_bytes_per_sample(instream->format); instream->bytes_per_sample = soundio_get_bytes_per_sample(instream->format);

View file

@ -109,6 +109,25 @@ static int from_lpwstr(LPWSTR lpwstr, char **out_str, int *out_str_len) {
return 0; return 0;
} }
static int to_lpwstr(const char *str, int str_len, LPWSTR *out_lpwstr) {
DWORD flags = 0;
int w_len = MultiByteToWideChar(CP_UTF8, flags, str, str_len, nullptr, 0);
if (w_len <= 0)
return SoundIoErrorEncodingString;
LPWSTR buf = allocate<wchar_t>(w_len + 1);
if (!buf)
return SoundIoErrorNoMem;
if (MultiByteToWideChar(CP_UTF8, flags, str, str_len, buf, w_len) != w_len) {
free(buf);
return SoundIoErrorEncodingString;
}
*out_lpwstr = buf;
return 0;
}
static void from_channel_mask_layout(UINT channel_mask, SoundIoChannelLayout *layout) { static void from_channel_mask_layout(UINT channel_mask, SoundIoChannelLayout *layout) {
layout->channel_count = 0; layout->channel_count = 0;
if (channel_mask & SPEAKER_FRONT_LEFT) if (channel_mask & SPEAKER_FRONT_LEFT)
@ -1000,6 +1019,8 @@ static void outstream_destroy_wasapi(struct SoundIoPrivate *si, struct SoundIoOu
if (osw->audio_render_client) if (osw->audio_render_client)
IUnknown_Release(osw->audio_render_client); IUnknown_Release(osw->audio_render_client);
if (osw->audio_session_control)
IUnknown_Release(osw->audio_session_control);
if (osw->audio_clock_adjustment) if (osw->audio_clock_adjustment)
IUnknown_Release(osw->audio_clock_adjustment); IUnknown_Release(osw->audio_clock_adjustment);
if (osw->audio_client) if (osw->audio_client)
@ -1010,6 +1031,8 @@ static void outstream_destroy_wasapi(struct SoundIoPrivate *si, struct SoundIoOu
soundio_os_cond_destroy(osw->cond); soundio_os_cond_destroy(osw->cond);
soundio_os_mutex_destroy(osw->mutex); soundio_os_mutex_destroy(osw->mutex);
free(osw->stream_name);
CoUninitialize(); CoUninitialize();
} }
@ -1172,6 +1195,27 @@ static int outstream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoOutStr
} }
} }
if (outstream->name) {
if (FAILED(hr = IAudioClient_GetService(osw->audio_client, IID_IAudioSessionControl,
(void **)&osw->audio_session_control)))
{
outstream_destroy_wasapi(si, os);
return SoundIoErrorOpeningDevice;
}
int err;
if ((err = to_lpwstr(outstream->name, strlen(outstream->name), &osw->stream_name))) {
outstream_destroy_wasapi(si, os);
return err;
}
if (FAILED(hr = IAudioSessionControl_SetDisplayName(osw->audio_session_control,
osw->stream_name, nullptr)))
{
outstream_destroy_wasapi(si, os);
return SoundIoErrorOpeningDevice;
}
}
if (FAILED(hr = IAudioClient_GetService(osw->audio_client, IID_IAudioRenderClient, if (FAILED(hr = IAudioClient_GetService(osw->audio_client, IID_IAudioRenderClient,
(void **)&osw->audio_render_client))) (void **)&osw->audio_render_client)))
{ {
@ -1515,6 +1559,27 @@ static int instream_open_wasapi(struct SoundIoPrivate *si, struct SoundIoInStrea
} }
} }
if (instream->name) {
if (FAILED(hr = IAudioClient_GetService(isw->audio_client, IID_IAudioSessionControl,
(void **)&isw->audio_session_control)))
{
instream_destroy_wasapi(si, is);
return SoundIoErrorOpeningDevice;
}
int err;
if ((err = to_lpwstr(instream->name, strlen(instream->name), &isw->stream_name))) {
instream_destroy_wasapi(si, is);
return err;
}
if (FAILED(hr = IAudioSessionControl_SetDisplayName(isw->audio_session_control,
isw->stream_name, nullptr)))
{
instream_destroy_wasapi(si, is);
return SoundIoErrorOpeningDevice;
}
}
if (FAILED(hr = IAudioClient_GetService(isw->audio_client, IID_IAudioCaptureClient, if (FAILED(hr = IAudioClient_GetService(isw->audio_client, IID_IAudioCaptureClient,
(void **)&isw->audio_capture_client))) (void **)&isw->audio_capture_client)))
{ {

View file

@ -23,6 +23,7 @@
#include <mmreg.h> #include <mmreg.h>
#include <audioclient.h> #include <audioclient.h>
#include <audiosessiontypes.h> #include <audiosessiontypes.h>
#include <audiopolicy.h>
int soundio_wasapi_init(struct SoundIoPrivate *si); int soundio_wasapi_init(struct SoundIoPrivate *si);
@ -53,6 +54,8 @@ struct SoundIoOutStreamWasapi {
IAudioClient *audio_client; IAudioClient *audio_client;
IAudioClockAdjustment *audio_clock_adjustment; IAudioClockAdjustment *audio_clock_adjustment;
IAudioRenderClient *audio_render_client; IAudioRenderClient *audio_render_client;
IAudioSessionControl *audio_session_control;
LPWSTR stream_name;
bool need_resample; bool need_resample;
SoundIoOsThread *thread; SoundIoOsThread *thread;
SoundIoOsMutex *mutex; SoundIoOsMutex *mutex;
@ -70,6 +73,8 @@ struct SoundIoOutStreamWasapi {
struct SoundIoInStreamWasapi { struct SoundIoInStreamWasapi {
IAudioClient *audio_client; IAudioClient *audio_client;
IAudioCaptureClient *audio_capture_client; IAudioCaptureClient *audio_capture_client;
IAudioSessionControl *audio_session_control;
LPWSTR stream_name;
SoundIoOsThread *thread; SoundIoOsThread *thread;
SoundIoOsMutex *mutex; SoundIoOsMutex *mutex;
SoundIoOsCond *cond; SoundIoOsCond *cond;