Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions include/cubeb/cubeb.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,11 +220,16 @@ enum {

/** Miscellaneous stream preferences. */
typedef enum {
CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */
CUBEB_STREAM_PREF_LOOPBACK = 0x01 /**< Request a loopback stream. Should be
specified on the input params and an
output device to loopback from should
be passed in place of an input device. */
CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */
CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be
specified on the input params and an
output device to loopback from should
be passed in place of an input device. */
CUBEB_STREAM_PREF_EXCLUSIVE = 0x02 /**< (Windows / WASAPI only) Request that
this stream is run in exclusive mode
which results in lower latency but
doesn't allow other applications to
use the same device */
} cubeb_stream_prefs;

/** Stream format initialization parameters. */
Expand Down
5 changes: 5 additions & 0 deletions src/cubeb.c
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,11 @@ cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
return r;
}

if ((output_stream_params && output_stream_params->prefs) & CUBEB_STREAM_PREF_EXCLUSIVE || (input_stream_params && input_stream_params->prefs & CUBEB_STREAM_PREF_EXCLUSIVE)) {
if (strcmp(cubeb_get_backend_id(context), "wasapi") != 0)
return CUBEB_ERROR_NOT_SUPPORTED;
}

r = context->ops->stream_init(context, stream, stream_name,
input_device,
input_stream_params,
Expand Down
106 changes: 92 additions & 14 deletions src/cubeb_wasapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,9 +524,20 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,

int wasapi_stream_reset_default_device(cubeb_stream * stm);

/* Helper for making get_input_buffer work in exclusive mode */
HRESULT get_next_packet_size(cubeb_stream * stm, PUINT32 next)
{
if (stm->input_stream_params.prefs & CUBEB_STREAM_PREF_EXCLUSIVE) {
*next = stm->input_buffer_frame_count;
return S_OK;
} else {
return stm->capture_client->GetNextPacketSize(next);
}
}

/* This helper grabs all the frames available from a capture client, put them in
* linear_input_buffer. linear_input_buffer should be cleared before the
* callback exits. This helper does not work with exclusive mode streams. */
* callback exits. */
bool get_input_buffer(cubeb_stream * stm)
{
XASSERT(has_input(stm));
Expand All @@ -543,9 +554,9 @@ bool get_input_buffer(cubeb_stream * stm)
// single packet each time. However, if we're pulling from the stream we may
// need to grab multiple packets worth of frames that have accumulated (so
// need a loop).
for (hr = stm->capture_client->GetNextPacketSize(&next);
for (hr = get_next_packet_size(stm, &next);
next > 0;
hr = stm->capture_client->GetNextPacketSize(&next)) {
hr = get_next_packet_size(stm, &next)) {
if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
// Application can recover from this error. More info
// https://msdn.microsoft.com/en-us/library/windows/desktop/dd316605(v=vs.85).aspx
Expand Down Expand Up @@ -619,7 +630,11 @@ bool get_input_buffer(cubeb_stream * stm)
LOG("FAILED to release intput buffer");
return false;
}
offset += input_stream_samples;

offset += input_stream_samples;

if (stm->input_stream_params.prefs & CUBEB_STREAM_PREF_EXCLUSIVE)
break;
}

XASSERT(stm->linear_input_buffer->length() >= offset);
Expand Down Expand Up @@ -1297,9 +1312,13 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
return CUBEB_ERROR;
}

/* The second parameter is for exclusive mode, that we don't use. */
REFERENCE_TIME default_period;
hr = client->GetDevicePeriod(&default_period, NULL);

if (params.prefs & CUBEB_STREAM_PREF_EXCLUSIVE)
hr = client->GetDevicePeriod(NULL, &default_period);
else
hr = client->GetDevicePeriod(&default_period, NULL);

if (FAILED(hr)) {
LOG("Could not get device period: %lx", hr);
return CUBEB_ERROR;
Expand Down Expand Up @@ -1391,7 +1410,7 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction, com_heap_ptr<WAV

/* Check if wasapi will accept our channel layout request. */
WAVEFORMATEX * closest;
HRESULT hr = audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
HRESULT hr = audio_client->IsFormatSupported(stream_params->prefs & CUBEB_STREAM_PREF_EXCLUSIVE ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
mix_format.get(),
&closest);
if (hr == S_FALSE) {
Expand Down Expand Up @@ -1518,11 +1537,27 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
mix_params->channels = mix_format->nChannels;
mix_params->layout = mask_to_channel_layout(mix_format.get());

LOG("Setup requested=[f=%d r=%u c=%u l=%u] mix=[f=%d r=%u c=%u l=%u]",
stream_params->format, stream_params->rate, stream_params->channels,
stream_params->layout,
mix_params->format, mix_params->rate, mix_params->channels,
mix_params->layout);
if (mix_params->layout == CUBEB_LAYOUT_UNDEFINED) {
LOG("Stream using undefined layout! Any mixing may be unpredictable!\n");
} else if (mix_format->nChannels != mix_params->channels) {
// The CUBEB_CHANNEL_LAYOUT_MAPS[mix_params->layout].channels may be
// different from the mix_params->channels. 6 channel ouput with stereo
// layout is acceptable in Windows. If this happens, it should not downmix
// audio according to layout.
LOG("Channel count is different from the layout standard!\n");
}


if (stream_params->prefs & CUBEB_STREAM_PREF_EXCLUSIVE)
LOG("Setup requested=[f=%d r=%u c=%u] mix=[f=%d r=%u c=%u]",
stream_params->format, stream_params->rate, stream_params->channels,
mix_params->format, mix_params->rate, mix_params->channels);
else
LOG("Setup requested=[f=%d r=%u c=%u l=%u] mix=[f=%d r=%u c=%u l=%u]",
stream_params->format, stream_params->rate, stream_params->channels,
stream_params->layout,
mix_params->format, mix_params->rate, mix_params->channels,
mix_params->layout);

DWORD flags = AUDCLNT_STREAMFLAGS_NOPERSIST;

Expand All @@ -1534,12 +1569,51 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
}

hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
hr = audio_client->Initialize(stream_params->prefs & CUBEB_STREAM_PREF_EXCLUSIVE ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
flags,
frames_to_hns(stm, stm->latency),
0,
stream_params->prefs & CUBEB_STREAM_PREF_EXCLUSIVE ? frames_to_hns(stm, stm->latency) : 0,
mix_format.get(),
NULL);

// Try to realign the buffer size
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
{
LOG("Buffer size misaligned, trying to realign");

audio_client.reset();

hr = device->Activate(__uuidof(IAudioClient),
CLSCTX_INPROC_SERVER,
NULL, audio_client.receive_vpp());

if (FAILED(hr)) {
LOG("Unable to reactivate audio client for %s: %lx", DIRECTION_NAME, hr);
return CUBEB_ERROR;
}

REFERENCE_TIME realigned_time = frames_to_hns(stm, stm->latency) * *buffer_frame_count / mix_format->nSamplesPerSec + 0.5;


hr = audio_client->Initialize(stream_params->prefs & CUBEB_STREAM_PREF_EXCLUSIVE ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
flags,
realigned_time,
stream_params->prefs & CUBEB_STREAM_PREF_EXCLUSIVE ? realigned_time : 0,
mix_format.get(),
NULL);

if (FAILED(hr)) {
LOG("Unable to initialize realigned audio client for %s: %lx.", DIRECTION_NAME, hr);
return CUBEB_ERROR;
}
}

if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT)
{
LOG("The requested format is not supported by the current device.");
return CUBEB_ERROR_INVALID_FORMAT;
}

if (FAILED(hr)) {
LOG("Unable to initialize audio client for %s: %lx.", DIRECTION_NAME, hr);
return CUBEB_ERROR;
Expand Down Expand Up @@ -2105,6 +2179,10 @@ int wasapi_stream_set_volume(cubeb_stream * stm, float volume)
return CUBEB_ERROR;
}

// Exclusive mode doesn't support setting the volume
if (stm->output_stream_params.prefs & CUBEB_STREAM_PREF_EXCLUSIVE)
return CUBEB_ERROR_NOT_SUPPORTED;

if (stream_set_volume(stm, volume) != CUBEB_OK) {
return CUBEB_ERROR;
}
Expand Down
6 changes: 6 additions & 0 deletions test/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#endif
#include <objbase.h>
#include <windows.h>
#include <objbase.h>
#else
#include <unistd.h>
#endif
Expand Down Expand Up @@ -93,6 +94,11 @@ void print_log(const char * msg, ...)
* override. */
int common_init(cubeb ** ctx, char const * ctx_name)
{
#ifdef _WIN32
// Exclusive mode needs this
CoInitialize(NULL);
#endif

int r;
char const * backend;
char const * ctx_backend;
Expand Down
21 changes: 18 additions & 3 deletions test/test_audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ int supports_channel_count(string backend_id, int nchannels)
(backend_id != "opensl" && backend_id != "audiotrack");
}

int run_test(int num_channels, int sampling_rate, int is_float)
int run_test(int num_channels, int sampling_rate, int is_float, bool exclusive=false)
{
int r = CUBEB_OK;

Expand Down Expand Up @@ -123,13 +123,18 @@ int run_test(int num_channels, int sampling_rate, int is_float)
params.rate = sampling_rate;
params.channels = num_channels;
params.layout = CUBEB_LAYOUT_UNDEFINED;
params.prefs = CUBEB_STREAM_PREF_NONE;
params.prefs = exclusive ? CUBEB_STREAM_PREF_EXCLUSIVE : CUBEB_STREAM_PREF_NONE;

synth_state synth(params.channels, params.rate);

cubeb_stream *stream = NULL;
r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
4096, is_float ? &data_cb<float> : &data_cb<short>, state_cb_audio, &synth);
if (r == CUBEB_ERROR_INVALID_FORMAT)
{
fprintf(stderr, "Format not supported. Not treating as a failure.\n");
return CUBEB_OK;
}
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
return r;
Expand Down Expand Up @@ -172,14 +177,19 @@ int run_panning_volume_test(int is_float)
params.rate = 44100;
params.channels = 2;
params.layout = CUBEB_LAYOUT_STEREO;
params.prefs = CUBEB_STREAM_PREF_NONE;
params.prefs = CUBEB_STREAM_PREF_EXCLUSIVE;

synth_state synth(params.channels, params.rate);

cubeb_stream *stream = NULL;
r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
4096, is_float ? &data_cb<float> : &data_cb<short>,
state_cb_audio, &synth);
if (r == CUBEB_ERROR_INVALID_FORMAT)
{
fprintf(stderr, "Format not supported. Not treating as a failure.\n");
return CUBEB_OK;
}
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
return r;
Expand Down Expand Up @@ -248,6 +258,11 @@ TEST(cubeb, run_channel_rate_test)
fprintf(stderr, "--------------------------\n");
ASSERT_EQ(run_test(channels, freq, 0), CUBEB_OK);
ASSERT_EQ(run_test(channels, freq, 1), CUBEB_OK);
#ifdef _WIN32
// Run exclusive mdoe tests on Windows
ASSERT_EQ(run_test(channels, freq, 0, true), CUBEB_OK);
ASSERT_EQ(run_test(channels, freq, 1, true), CUBEB_OK);
#endif
}
}
}