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/)
- [ALSA](http://www.alsa-project.org/)
- [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)
* Exposes both raw devices and shared devices. Raw devices give you the best
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`
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
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
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

View file

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

View file

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

View file

@ -415,6 +415,9 @@ static int outstream_open_jack(struct SoundIoPrivate *si, struct SoundIoOutStrea
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
if (!outstream->name)
outstream->name = "SoundIoOutStream";
outstream->software_latency = device->software_latency_current;
osj->period_size = sij->period_size;
@ -617,6 +620,9 @@ static int instream_open_jack(struct SoundIoPrivate *si, struct SoundIoInStreamP
if (sij->is_shutdown)
return SoundIoErrorBackendDisconnected;
if (!instream->name)
instream->name = "SoundIoInStream";
instream->software_latency = device->software_latency_current;
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)
return SoundIoErrorIncompatibleBackend;
if (!outstream->name)
outstream->name = "SoundIoOutStream";
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
ospa->stream_ready.store(false);
@ -883,6 +886,8 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
return SoundIoErrorInvalid;
if ((unsigned)instream->layout.channel_count > PA_CHANNELS_MAX)
return SoundIoErrorIncompatibleBackend;
if (!instream->name)
instream->name = "SoundIoInStream";
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
ispa->stream_ready = false;

View file

@ -473,9 +473,6 @@ int soundio_outstream_open(struct SoundIoOutStream *outstream) {
if (!outstream->sample_rate)
outstream->sample_rate = soundio_device_nearest_sample_rate(device, 48000);
if (!outstream->name)
outstream->name = "SoundIoOutStream";
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)outstream;
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);
@ -559,9 +556,6 @@ int soundio_instream_open(struct SoundIoInStream *instream) {
if (!instream->sample_rate)
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_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;
}
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) {
layout->channel_count = 0;
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)
IUnknown_Release(osw->audio_render_client);
if (osw->audio_session_control)
IUnknown_Release(osw->audio_session_control);
if (osw->audio_clock_adjustment)
IUnknown_Release(osw->audio_clock_adjustment);
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_mutex_destroy(osw->mutex);
free(osw->stream_name);
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,
(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,
(void **)&isw->audio_capture_client)))
{

View file

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