--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -218,25 +218,27 @@ typedef struct {
typedef struct {
uint32_t count; /**< Device count in collection. */
cubeb_device_info * device[1]; /**< Array of pointers to device info. */
} cubeb_device_collection;
/** User supplied data callback.
@param stream
@param user_ptr
- @param buffer
+ @param input_buffer
+ @param output_buffer
@param nframes
@retval Number of frames written to buffer, which must equal nframes except
at end of stream.
@retval CUBEB_ERROR on error, in which case the data callback will stop
and the stream will enter a shutdown state. */
typedef long (* cubeb_data_callback)(cubeb_stream * stream,
void * user_ptr,
- void * buffer,
+ void * input_buffer,
+ void * output_buffer,
long nframes);
/** User supplied state callback.
@param stream
@param user_ptr
@param state */
typedef void (* cubeb_state_callback)(cubeb_stream * stream,
void * user_ptr,
@@ -314,17 +316,18 @@ void cubeb_destroy(cubeb * context);
@param state_callback
@param user_ptr
@retval CUBEB_OK
@retval CUBEB_ERROR
@retval CUBEB_ERROR_INVALID_FORMAT */
int cubeb_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
- cubeb_stream_params stream_params,
+ cubeb_stream_params* input_stream_params,
+ cubeb_stream_params* output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr);
/** Destroy a stream.
@param stream */
void cubeb_stream_destroy(cubeb_stream * stream);
--- a/media/libcubeb/src/cubeb-internal.h
+++ b/media/libcubeb/src/cubeb-internal.h
@@ -18,17 +18,19 @@ struct cubeb_ops {
int (* get_min_latency)(cubeb * context,
cubeb_stream_params params,
uint32_t * latency_ms);
int (* get_preferred_sample_rate)(cubeb * context, uint32_t * rate);
int (* enumerate_devices)(cubeb * context, cubeb_device_type type,
cubeb_device_collection ** collection);
void (* destroy)(cubeb * context);
int (* stream_init)(cubeb * context, cubeb_stream ** stream, char const * stream_name,
- cubeb_stream_params stream_params, unsigned int latency,
+ cubeb_stream_params * input_stream_params,
+ cubeb_stream_params * output_stream_params,
+ unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr);
void (* stream_destroy)(cubeb_stream * stream);
int (* stream_start)(cubeb_stream * stream);
int (* stream_stop)(cubeb_stream * stream);
int (* stream_get_position)(cubeb_stream * stream, uint64_t * position);
int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
--- a/media/libcubeb/src/cubeb.c
+++ b/media/libcubeb/src/cubeb.c
@@ -57,24 +57,29 @@ int opensl_init(cubeb ** context, char c
#if defined(USE_AUDIOTRACK)
int audiotrack_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_KAI)
int kai_init(cubeb ** context, char const * context_name);
#endif
int
-validate_stream_params(cubeb_stream_params stream_params)
+validate_stream_params(cubeb_stream_params * input_stream_params,
+ cubeb_stream_params * output_stream_params)
{
- if (stream_params.rate < 1000 || stream_params.rate > 192000 ||
- stream_params.channels < 1 || stream_params.channels > 8) {
+ // Rate and sample format must be the same for input and output.
+ if (output_stream_params->rate < 1000 || output_stream_params->rate > 192000 ||
+ output_stream_params->channels < 1 || output_stream_params->channels > 8 ||
+ (input_stream_params && input_stream_params &&
+ (input_stream_params->rate != output_stream_params->rate ||
+ input_stream_params->format != output_stream_params->format))) {
return CUBEB_ERROR_INVALID_FORMAT;
}
- switch (stream_params.format) {
+ switch (output_stream_params->format) {
case CUBEB_SAMPLE_S16LE:
case CUBEB_SAMPLE_S16BE:
case CUBEB_SAMPLE_FLOAT32LE:
case CUBEB_SAMPLE_FLOAT32BE:
return CUBEB_OK;
}
return CUBEB_ERROR_INVALID_FORMAT;
@@ -213,34 +218,36 @@ cubeb_destroy(cubeb * context)
return;
}
context->ops->destroy(context);
}
int
cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
- cubeb_stream_params stream_params, unsigned int latency,
+ cubeb_stream_params * input_stream_params,
+ cubeb_stream_params * output_stream_params,
+ unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
{
int r;
if (!context || !stream) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
- if ((r = validate_stream_params(stream_params)) != CUBEB_OK ||
+ if ((r = validate_stream_params(input_stream_params, output_stream_params)) != CUBEB_OK ||
(r = validate_latency(latency)) != CUBEB_OK) {
return r;
}
return context->ops->stream_init(context, stream, stream_name,
- stream_params, latency,
+ input_stream_params, output_stream_params, latency,
data_callback,
state_callback,
user_ptr);
}
void
cubeb_stream_destroy(cubeb_stream * stream)
{
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -136,16 +136,38 @@ public:
private:
CRITICAL_SECTION critical_section;
#ifdef DEBUG
DWORD owner;
#endif
};
+template <typename T>
+class auto_ptr {
+public:
+ auto_ptr(T * ptr)
+ : ptr_(ptr)
+ {
+ // printf("alloc %p\n", ptr);
+ }
+ ~auto_ptr() {
+ // printf("delete %p\n", ptr_);
+ delete ptr_;
+ }
+ T*& operator*() {
+ return ptr_;
+ }
+ T* get() {
+ return ptr_;
+ }
+private:
+ T * ptr_;
+};
+
struct auto_lock {
auto_lock(owned_critical_section * lock)
: lock(lock)
{
lock->enter();
}
~auto_lock()
{
@@ -201,38 +223,50 @@ struct cubeb
the two function pointers we need. */
HMODULE mmcss_module;
set_mm_thread_characteristics_function set_mm_thread_characteristics;
revert_mm_thread_characteristics_function revert_mm_thread_characteristics;
};
class wasapi_endpoint_notification_client;
+/* We have three possible callbacks we can use with a stream:
+ * - input only
+ * - output only
+ * - synchronized input and output
+ *
+ * Returns true when we should continue to play, false otherwise.
+ */
+typedef bool (*wasapi_refill_callback)(cubeb_stream * stm);
+
struct cubeb_stream
{
cubeb * context;
/* Mixer pameters. We need to convert the input stream to this
- samplerate/channel layout, as WASAPI * does not resample nor upmix
+ samplerate/channel layout, as WASAPI does not resample nor upmix
itself. */
- cubeb_stream_params mix_params;
- cubeb_stream_params stream_params;
+ cubeb_stream_params input_mix_params;
+ cubeb_stream_params output_mix_params;
+ cubeb_stream_params input_stream_params;
+ cubeb_stream_params output_stream_params;
/* The latency initially requested for this stream. */
unsigned latency;
cubeb_state_callback state_callback;
cubeb_data_callback data_callback;
+ wasapi_refill_callback refill_callback;
void * user_ptr;
/* Lifetime considerations:
- client, render_client, audio_clock and audio_stream_volume are interface
pointer to the IAudioClient.
- The lifetime for device_enumerator and notification_client, resampler,
mix_buffer are the same as the cubeb_stream instance. */
/* Main handle on the WASAPI stream. */
- IAudioClient * client;
+ IAudioClient * output_client;
/* Interface pointer to use the event-driven interface. */
IAudioRenderClient * render_client;
/* Interface pointer to use the volume facilities. */
IAudioStreamVolume * audio_stream_volume;
/* Interface pointer to use the stream audio clock. */
IAudioClock * audio_clock;
/* Frames written to the stream since it was opened. Reset on device
change. Uses mix_params.rate. */
@@ -245,36 +279,45 @@ struct cubeb_stream
UINT64 prev_position;
/* Device enumerator to be able to be notified when the default
device change. */
IMMDeviceEnumerator * device_enumerator;
/* Device notification client, to be able to be notified when the default
audio device changes and route the audio to the new default audio output
device */
wasapi_endpoint_notification_client * notification_client;
+ /* Main andle to the WASAPI capture stream. */
+ IAudioClient * input_client;
+ /* Interface to use the event driven capture interface */
+ IAudioCaptureClient * capture_client;
+
/* This event is set by the stream_stop and stream_destroy
function, so the render loop can exit properly. */
HANDLE shutdown_event;
/* Set by OnDefaultDeviceChanged when a stream reconfiguration is required.
The reconfiguration is handled by the render loop thread. */
HANDLE reconfigure_event;
/* This is set by WASAPI when we should refill the stream. */
HANDLE refill_event;
+ /* This is set by WASAPI when we should read from the input stream. In
+ * practice, we read from the input stream in the output callback. */
+ HANDLE input_available_event;
/* Each cubeb_stream has its own thread. */
HANDLE thread;
/* The lock protects all members that are touched by the render thread or
change during a device reset, including: audio_clock, audio_stream_volume,
client, frames_written, mix_params, total_frames_written, prev_position. */
owned_critical_section * stream_reset_lock;
- /* Maximum number of frames we can be requested in a callback. */
- uint32_t buffer_frame_count;
+ /* Maximum number of frames that can be requested in a callback. */
+ uint32_t input_buffer_frame_count;
+ uint32_t output_buffer_frame_count;
/* Resampler instance. Resampling will only happen if necessary. */
cubeb_resampler * resampler;
/* Buffer used to downmix or upmix to the number of channels the mixer has.
- its size is |frames_to_bytes_before_mix(buffer_frame_count)|. */
+ its size is |frames_to_bytes_before_mix(output_buffer_frame_count)|. */
float * mix_buffer;
/* Stream volume. Set via stream_set_volume and used to reset volume on
device changes. */
float volume;
/* True if the stream is draining. */
bool draining;
};
@@ -372,28 +415,38 @@ private:
/* refcount for this instance, necessary to implement MSCOM semantics. */
LONG ref_count;
HANDLE reconfigure_event;
};
namespace {
bool should_upmix(cubeb_stream * stream)
{
- return stream->mix_params.channels > stream->stream_params.channels;
+ return stream->output_mix_params.channels > stream->output_stream_params.channels;
}
bool should_downmix(cubeb_stream * stream)
{
- return stream->mix_params.channels < stream->stream_params.channels;
+ return stream->output_mix_params.channels < stream->output_stream_params.channels;
}
double stream_to_mix_samplerate_ratio(cubeb_stream * stream)
{
stream->stream_reset_lock->assert_current_thread_owns();
- return double(stream->stream_params.rate) / stream->mix_params.rate;
+ return double(stream->output_stream_params.rate) / stream->output_mix_params.rate;
+}
+
+bool has_input(cubeb_stream * stm)
+{
+ return stm->input_stream_params.rate != 0;
+}
+
+bool has_output(cubeb_stream * stm)
+{
+ return stm->output_stream_params.rate != 0;
}
/* Upmix function, copies a mono channel into L and R */
template<typename T>
void
mono_to_stereo(T * in, long insamples, T * out, int32_t out_channels)
{
for (int i = 0, j = 0; i < insamples; ++i, j += out_channels) {
@@ -451,33 +504,39 @@ downmix(T * in, long inframes, T * out,
}
}
/* This returns the size of a frame in the stream, before the eventual upmix
occurs. */
static size_t
frames_to_bytes_before_mix(cubeb_stream * stm, size_t frames)
{
- size_t stream_frame_size = stm->stream_params.channels * sizeof(float);
+ size_t stream_frame_size = stm->output_stream_params.channels * sizeof(float);
return stream_frame_size * frames;
}
+/* This function handles the processing of the input and output audio,
+ * converting it to rate and channel layout specified at initialization.
+ * It then calls the data callback. */
long
-refill(cubeb_stream * stm, float * data, long frames_needed)
+refill(cubeb_stream * stm, float * input_buffer, float * output_buffer, long frames_needed)
{
/* If we need to upmix after resampling, resample into the mix buffer to
avoid a copy. */
float * dest;
if (should_upmix(stm) || should_downmix(stm)) {
dest = stm->mix_buffer;
} else {
- dest = data;
+ dest = output_buffer;
}
- long out_frames = cubeb_resampler_fill(stm->resampler, dest, frames_needed);
+ long out_frames = cubeb_resampler_fill(stm->resampler,
+ input_buffer,
+ dest,
+ frames_needed);
/* TODO: Report out_frames < 0 as an error via the API. */
XASSERT(out_frames >= 0);
{
auto_lock lock(stm->stream_reset_lock);
stm->frames_written += out_frames;
}
@@ -487,26 +546,217 @@ refill(cubeb_stream * stm, float * data,
stm->draining = true;
}
/* If this is not true, there will be glitches.
It is alright to have produced less frames if we are draining, though. */
XASSERT(out_frames == frames_needed || stm->draining);
if (should_upmix(stm)) {
- upmix(dest, out_frames, data,
- stm->stream_params.channels, stm->mix_params.channels);
+ upmix(dest, out_frames, output_buffer,
+ stm->output_stream_params.channels, stm->output_mix_params.channels);
} else if (should_downmix(stm)) {
- downmix(dest, out_frames, data,
- stm->stream_params.channels, stm->mix_params.channels);
+ downmix(dest, out_frames, output_buffer,
+ stm->output_stream_params.channels, stm->output_mix_params.channels);
}
return out_frames;
}
+/**
+ * This function gets input data from a input device, and pass it along with an
+ * output buffer to the callback. */
+bool
+refill_callback_duplex(cubeb_stream * stm)
+{
+ UINT32 padding_out, padding_in;
+ HRESULT hr;
+
+ hr = stm->output_client->GetCurrentPadding(&padding_out);
+ if (FAILED(hr)) {
+ LOG("Failed to get padding: %x\n", hr);
+ return false;
+ }
+ XASSERT(padding_out <= stm->output_buffer_frame_count);
+ hr = stm->input_client->GetCurrentPadding(&padding_in);
+ if (FAILED(hr)) {
+ LOG("Failed to get padding\n");
+ return false;
+ }
+ XASSERT(padding_in <= stm->input_buffer_frame_count);
+
+ if (stm->draining) {
+ if (padding_out == 0) {
+ stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+ }
+ return false;
+ }
+
+ long available_output = stm->output_buffer_frame_count - padding_out;
+ UINT32 total_available_input = padding_in;
+
+ if (available_output == 0) {
+ return 0;
+ }
+
+ BYTE * output_buffer;
+ auto_ptr<BYTE> input_buffer(new BYTE[available_output * sizeof(float) * stm->input_stream_params.channels]);
+
+ BYTE * tmp_input_buffer = NULL;
+ DWORD flags;
+ UINT64 dev_pos;
+ UINT32 next;
+ hr = stm->render_client->GetBuffer(available_output, &output_buffer);
+ if (FAILED(hr)) {
+ LOG("cannot get render buffer\n");
+ return false;
+ }
+ uint32_t offset = 0;
+
+ if (total_available_input < available_output) {
+ uint32_t bytes = (available_output - total_available_input) * sizeof(float) * stm->input_stream_params.channels;
+ memset(input_buffer.get(), 0, bytes);
+ offset += bytes;
+ printf("DROPPING %d.\n", total_available_input);
+ total_available_input = available_output;
+ } else {
+ total_available_input = available_output;
+ }
+
+ while (offset != total_available_input * sizeof(float) * stm->input_stream_params.channels) {
+ hr = stm->capture_client->GetNextPacketSize(&next);
+ if (FAILED(hr)) {
+ LOG("cannot get next packet size: %x\n", hr);
+ return false;
+ }
+ // printf("next packet size:%u\n", next);
+ UINT32 available_input;
+
+ hr = stm->capture_client->GetBuffer(&tmp_input_buffer, &available_input, &flags, &dev_pos, NULL);
+ if (FAILED(hr)) {
+ LOG("GetBuffer failed for capture: %x\n", hr);
+ return false;
+ }
+ XASSERT(available_input == next);
+ if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
+ memset(input_buffer.get() + offset, 0, available_input * sizeof(float) * stm->input_stream_params.channels);
+ } else {
+ memcpy(input_buffer.get() + offset, tmp_input_buffer, available_input * sizeof(float) * stm->input_stream_params.channels);
+ }
+ offset += available_input * sizeof(float) * stm->input_stream_params.channels;
+ hr = stm->capture_client->ReleaseBuffer(available_input);
+ if (FAILED(hr)) {
+ LOG("FAILED to release intput buffer");
+ return false;
+ }
+ }
+ // it's ok to have the first input not being a full buffer, because of input latency.
+ // then, there should be an equal amount of frames.
+ XASSERT(!total_available_input || total_available_input == available_output);
+
+ refill(stm, reinterpret_cast<float *>(input_buffer.get()), reinterpret_cast<float *>(output_buffer), available_output);
+
+ hr = stm->render_client->ReleaseBuffer(available_output, 0);
+
+
+ if (FAILED(hr)) {
+ LOG("failed to release buffer: %x\n", hr);
+ return false;
+ }
+ return true;
+}
+
+bool
+refill_callback_input(cubeb_stream * stm)
+{
+ /* padding here has a meaning inverse than from the output case, it's the
+ * number of frames we can read. */
+ UINT32 padding_input;
+ HRESULT hr;
+ DWORD flags;
+ UINT64 dev_pos;
+
+ hr = stm->input_client->GetCurrentPadding(&padding_input);
+ if (FAILED(hr)) {
+ LOG("Failed to get padding: %x\n", hr);
+ return false;
+ }
+ XASSERT(padding_input <= stm->input_buffer_frame_count);
+
+ /* Maybe that can happen on the first couple callbacks because of latency. */
+ if (padding_input == 0) {
+ printf("no data available.\n");
+ return true;
+ }
+
+ BYTE * data;
+ UINT32 available_input;
+ hr = stm->capture_client->GetBuffer(&data, &available_input, &flags, &dev_pos, NULL);
+ if (SUCCEEDED(hr)) {
+ long read = refill(stm, reinterpret_cast<float*>(data), nullptr, available_input);
+ XASSERT(read == available_input || stm->draining);
+
+ hr = stm->capture_client->ReleaseBuffer(read);
+ if (FAILED(hr)) {
+ LOG("failed to release buffer: %x\n", hr);
+ return false;
+ }
+ } else {
+ LOG("failed to get buffer: %x\n", hr);
+ return false;
+ }
+ return true;
+ return true;
+}
+
+bool
+refill_callback_output(cubeb_stream * stm)
+{
+ UINT32 padding;
+ HRESULT hr;
+
+ hr = stm->output_client->GetCurrentPadding(&padding);
+ if (FAILED(hr)) {
+ LOG("Failed to get padding: %x\n", hr);
+ return false;
+ }
+ XASSERT(padding <= stm->output_buffer_frame_count);
+
+ if (stm->draining) {
+ if (padding == 0) {
+ stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
+ return false;
+ }
+ return true;
+ }
+
+ long available = stm->output_buffer_frame_count - padding;
+
+ if (available == 0) {
+ return true;
+ }
+
+ BYTE * data;
+ hr = stm->render_client->GetBuffer(available, &data);
+ if (SUCCEEDED(hr)) {
+ long wrote = refill(stm, nullptr, reinterpret_cast<float *>(data), available);
+ XASSERT(wrote == available || stm->draining);
+
+ hr = stm->render_client->ReleaseBuffer(wrote, 0);
+ if (FAILED(hr)) {
+ LOG("failed to release buffer: %x\n", hr);
+ return false;
+ }
+ } else {
+ LOG("failed to get buffer: %x\n", hr);
+ return false;
+ }
+ return true;
+}
+
static unsigned int __stdcall
wasapi_stream_render_loop(LPVOID stream)
{
cubeb_stream * stm = static_cast<cubeb_stream *>(stream);
bool is_playing = true;
HANDLE wait_array[3] = {stm->shutdown_event, stm->reconfigure_event, stm->refill_event};
HANDLE mmcss_handle = NULL;
@@ -550,75 +800,46 @@ wasapi_stream_render_loop(LPVOID stream)
shutdown. */
if (stm->draining) {
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
}
continue;
}
case WAIT_OBJECT_0 + 1: { /* reconfigure */
/* Close the stream */
- stm->client->Stop();
+ if (stm->output_client) {
+ stm->output_client->Stop();
+ }
+ if (stm->input_client) {
+ stm->input_client->Stop();
+ }
{
auto_lock lock(stm->stream_reset_lock);
close_wasapi_stream(stm);
/* Reopen a stream and start it immediately. This will automatically pick the
new default device for this role. */
int r = setup_wasapi_stream(stm);
if (r != CUBEB_OK) {
/* Don't destroy the stream here, since we expect the caller to do
so after the error has propagated via the state callback. */
is_playing = false;
hr = E_FAIL;
continue;
}
}
- stm->client->Start();
+ if (stm->output_client) {
+ stm->output_client->Start();
+ }
+ if (stm->input_client) {
+ stm->input_client->Start();
+ }
break;
}
- case WAIT_OBJECT_0 + 2: { /* refill */
- UINT32 padding;
-
- hr = stm->client->GetCurrentPadding(&padding);
- if (FAILED(hr)) {
- LOG("Failed to get padding: %x\n", hr);
- is_playing = false;
- continue;
- }
- XASSERT(padding <= stm->buffer_frame_count);
-
- if (stm->draining) {
- if (padding == 0) {
- stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
- is_playing = false;
- }
- continue;
- }
-
- long available = stm->buffer_frame_count - padding;
-
- if (available == 0) {
- continue;
- }
-
- BYTE * data;
- hr = stm->render_client->GetBuffer(available, &data);
- if (SUCCEEDED(hr)) {
- long wrote = refill(stm, reinterpret_cast<float *>(data), available);
- XASSERT(wrote == available || stm->draining);
-
- hr = stm->render_client->ReleaseBuffer(wrote, 0);
- if (FAILED(hr)) {
- LOG("failed to release buffer: %x\n", hr);
- is_playing = false;
- }
- } else {
- LOG("failed to get buffer: %x\n", hr);
- is_playing = false;
- }
- }
+ case WAIT_OBJECT_0 + 2: /* refill */
+ is_playing = stm->refill_callback(stm);
break;
case WAIT_TIMEOUT:
XASSERT(stm->shutdown_event == wait_array[0]);
if (++timeout_count >= timeout_limit) {
is_playing = false;
hr = E_FAIL;
}
break;
@@ -681,31 +902,31 @@ HRESULT unregister_notification_client(c
stm->device_enumerator->UnregisterEndpointNotificationCallback(stm->notification_client);
SafeRelease(stm->notification_client);
SafeRelease(stm->device_enumerator);
return S_OK;
}
-HRESULT get_default_endpoint(IMMDevice ** device)
+HRESULT get_default_endpoint(IMMDevice ** device, EDataFlow direction)
{
IMMDeviceEnumerator * enumerator;
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&enumerator));
if (FAILED(hr)) {
LOG("Could not get device enumerator: %x\n", hr);
return hr;
}
/* eMultimedia is okay for now ("Music, movies, narration, [...]").
We will need to change this when we distinguish streams by use-case, other
possible values being eConsole ("Games, system notification sounds [...]")
and eCommunication ("Voice communication"). */
- hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, device);
+ hr = enumerator->GetDefaultAudioEndpoint(direction, eMultimedia, device);
if (FAILED(hr)) {
LOG("Could not get default audio endpoint: %x\n", hr);
SafeRelease(enumerator);
return hr;
}
SafeRelease(enumerator);
@@ -734,17 +955,17 @@ current_stream_delay(cubeb_stream * stm)
UINT64 pos;
hr = stm->audio_clock->GetPosition(&pos, NULL);
if (FAILED(hr)) {
LOG("GetPosition failed: %x\n", hr);
return 0;
}
double cur_pos = static_cast<double>(pos) / freq;
- double max_pos = static_cast<double>(stm->frames_written) / stm->mix_params.rate;
+ double max_pos = static_cast<double>(stm->frames_written) / stm->output_mix_params.rate;
double delay = max_pos - cur_pos;
XASSERT(delay >= 0);
return delay;
}
int
stream_set_volume(cubeb_stream * stm, float volume)
@@ -790,17 +1011,17 @@ int wasapi_init(cubeb ** context, char c
if (!com.ok()) {
return CUBEB_ERROR;
}
/* We don't use the device yet, but need to make sure we can initialize one
so that this backend is not incorrectly enabled on platforms that don't
support WASAPI. */
IMMDevice * device;
- hr = get_default_endpoint(&device);
+ hr = get_default_endpoint(&device, eRender);
if (FAILED(hr)) {
LOG("Could not get device: %x\n", hr);
return CUBEB_ERROR;
}
SafeRelease(device);
cubeb * ctx = (cubeb *)calloc(1, sizeof(cubeb));
if (!ctx) {
@@ -882,17 +1103,17 @@ wasapi_get_max_channel_count(cubeb * ctx
auto_com com;
if (!com.ok()) {
return CUBEB_ERROR;
}
XASSERT(ctx && max_channels);
IMMDevice * device;
- hr = get_default_endpoint(&device);
+ hr = get_default_endpoint(&device, eRender);
if (FAILED(hr)) {
return CUBEB_ERROR;
}
hr = device->Activate(__uuidof(IAudioClient),
CLSCTX_INPROC_SERVER,
NULL, (void **)&client);
SafeRelease(device);
@@ -925,17 +1146,17 @@ wasapi_get_min_latency(cubeb * ctx, cube
return CUBEB_ERROR;
}
if (params.format != CUBEB_SAMPLE_FLOAT32NE) {
return CUBEB_ERROR_INVALID_FORMAT;
}
IMMDevice * device;
- hr = get_default_endpoint(&device);
+ hr = get_default_endpoint(&device, eRender);
if (FAILED(hr)) {
LOG("Could not get default endpoint: %x\n", hr);
return CUBEB_ERROR;
}
hr = device->Activate(__uuidof(IAudioClient),
CLSCTX_INPROC_SERVER,
NULL, (void **)&client);
@@ -972,17 +1193,17 @@ wasapi_get_preferred_sample_rate(cubeb *
IAudioClient * client;
WAVEFORMATEX * mix_format;
auto_com com;
if (!com.ok()) {
return CUBEB_ERROR;
}
IMMDevice * device;
- hr = get_default_endpoint(&device);
+ hr = get_default_endpoint(&device, eRender);
if (FAILED(hr)) {
return CUBEB_ERROR;
}
hr = device->Activate(__uuidof(IAudioClient),
CLSCTX_INPROC_SERVER,
NULL, (void **)&client);
SafeRelease(device);
@@ -1048,19 +1269,19 @@ handle_channel_layout(cubeb_stream * stm
(*mix_format)->nBlockAlign = ((*mix_format)->wBitsPerSample * (*mix_format)->nChannels) / 8;
(*mix_format)->nAvgBytesPerSec = (*mix_format)->nSamplesPerSec * (*mix_format)->nBlockAlign;
format_pcm->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
(*mix_format)->wBitsPerSample = 32;
format_pcm->Samples.wValidBitsPerSample = (*mix_format)->wBitsPerSample;
/* Check if wasapi will accept our channel layout request. */
WAVEFORMATEX * closest;
- HRESULT hr = stm->client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
- *mix_format,
- &closest);
+ HRESULT hr = stm->output_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
+ *mix_format,
+ &closest);
if (hr == S_FALSE) {
/* Not supported, but WASAPI gives us a suggestion. Use it, and handle the
eventual upmix/downmix ourselves */
LOG("Using WASAPI suggested format: channels: %d\n", closest->nChannels);
WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
XASSERT(closest_pcm->SubFormat == format_pcm->SubFormat);
CoTaskMemFree(*mix_format);
@@ -1085,154 +1306,249 @@ int setup_wasapi_stream(cubeb_stream * s
stm->stream_reset_lock->assert_current_thread_owns();
auto_com com;
if (!com.ok()) {
return CUBEB_ERROR;
}
- XASSERT(!stm->client && "WASAPI stream already setup, close it first.");
+ XASSERT(!stm->output_client && "WASAPI stream already setup, close it first.");
- hr = get_default_endpoint(&device);
- if (FAILED(hr)) {
- LOG("Could not get default endpoint, error: %x\n", hr);
- return CUBEB_ERROR;
- }
+ if (has_input(stm)) {
+ hr = get_default_endpoint(&device, eCapture);
+ if (FAILED(hr)) {
+ LOG("Could not get default endpoint, error: %x\n", hr);
+ return CUBEB_ERROR;
+ }
- /* Get a client. We will get all other interfaces we need from
- this pointer. */
- hr = device->Activate(__uuidof(IAudioClient),
- CLSCTX_INPROC_SERVER,
- NULL, (void **)&stm->client);
- SafeRelease(device);
- if (FAILED(hr)) {
- LOG("Could not activate the device to get an audio client: error: %x\n", hr);
- return CUBEB_ERROR;
- }
+ /* Get a client. We will get all other interfaces we need from
+ * this pointer. */
+ hr = device->Activate(__uuidof(IAudioClient),
+ CLSCTX_INPROC_SERVER,
+ NULL, (void **)&stm->input_client);
+ SafeRelease(device);
+ if (FAILED(hr)) {
+ LOG("Could not activate the device to get an audio client: error: %x\n", hr);
+ return CUBEB_ERROR;
+ }
+
+ /* We have to distinguish between the format the mixer uses,
+ * and the format the stream we want to play uses. */
+ hr = stm->input_client->GetMixFormat(&mix_format);
+ if (FAILED(hr)) {
+ LOG("Could not fetch current mix format from the audio client: error: %x\n", hr);
+ return CUBEB_ERROR;
+ }
+
+ handle_channel_layout(stm, &mix_format, &stm->input_stream_params);
+
+ /* Shared mode WASAPI always supports float32 sample format, so this
+ * is safe. */
+ stm->input_mix_params.format = CUBEB_SAMPLE_FLOAT32NE;
+ stm->input_mix_params.rate = mix_format->nSamplesPerSec;
+ stm->input_mix_params.channels = mix_format->nChannels;
- /* We have to distinguish between the format the mixer uses,
- and the format the stream we want to play uses. */
- hr = stm->client->GetMixFormat(&mix_format);
- if (FAILED(hr)) {
- LOG("Could not fetch current mix format from the audio client: error: %x\n", hr);
- return CUBEB_ERROR;
- }
+ hr = stm->input_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
+ AUDCLNT_STREAMFLAGS_NOPERSIST,
+ ms_to_hns(stm->latency),
+ 0,
+ mix_format,
+ NULL);
- handle_channel_layout(stm, &mix_format, &stm->stream_params);
+ if (FAILED(hr)) {
+ LOG("Unable to initialize audio client: %x.\n", hr);
+ return CUBEB_ERROR;
+ }
- /* Shared mode WASAPI always supports float32 sample format, so this
- is safe. */
- stm->mix_params.format = CUBEB_SAMPLE_FLOAT32NE;
- stm->mix_params.rate = mix_format->nSamplesPerSec;
- stm->mix_params.channels = mix_format->nChannels;
+ hr = stm->input_client->GetBufferSize(&stm->input_buffer_frame_count);
+ if (FAILED(hr)) {
+ LOG("Could not get the buffer size from the client %x.\n", hr);
+ return CUBEB_ERROR;
+ }
+
+ if (should_upmix(stm) || should_downmix(stm)) {
+ stm->mix_buffer = (float *)malloc(frames_to_bytes_before_mix(stm, stm->input_buffer_frame_count));
+ }
- hr = stm->client->Initialize(AUDCLNT_SHAREMODE_SHARED,
- AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
- AUDCLNT_STREAMFLAGS_NOPERSIST,
- ms_to_hns(stm->latency),
- 0,
- mix_format,
- NULL);
- CoTaskMemFree(mix_format);
- if (FAILED(hr)) {
- LOG("Unable to initialize audio client: %x\n", hr);
- return CUBEB_ERROR;
+ hr = stm->input_client->SetEventHandle(stm->input_available_event);
+ if (FAILED(hr)) {
+ LOG("Could set the event handle for the input client %x.\n", hr);
+ return CUBEB_ERROR;
+ }
+
+ hr = stm->input_client->GetService(__uuidof(IAudioCaptureClient),
+ (void **)&stm->capture_client);
+ if (FAILED(hr)) {
+ LOG("Could not get the capture client %x.\n", hr);
+ return CUBEB_ERROR;
+ }
}
- hr = stm->client->GetBufferSize(&stm->buffer_frame_count);
- if (FAILED(hr)) {
- LOG("Could not get the buffer size from the client: %x\n", hr);
- return CUBEB_ERROR;
- }
+ if (has_output(stm)) {
+ hr = get_default_endpoint(&device, eRender);
+ if (FAILED(hr)) {
+ LOG("Could not get default endpoint, error: %x\n", hr);
+ return CUBEB_ERROR;
+ }
+
+ /* Get a client. We will get all other interfaces we need from
+ this pointer. */
+ hr = device->Activate(__uuidof(IAudioClient),
+ CLSCTX_INPROC_SERVER,
+ NULL, (void **)&stm->output_client);
+ SafeRelease(device);
+ if (FAILED(hr)) {
+ LOG("Could not activate the device to get an audio client: error: %x\n", hr);
+ return CUBEB_ERROR;
+ }
- if (should_upmix(stm) || should_downmix(stm)) {
- stm->mix_buffer = (float *) malloc(frames_to_bytes_before_mix(stm, stm->buffer_frame_count));
- }
+ /* We have to distinguish between the format the mixer uses,
+ * and the format the stream we want to play uses. */
+ hr = stm->output_client->GetMixFormat(&mix_format);
+ if (FAILED(hr)) {
+ LOG("Could not fetch current mix format from the audio client: error: %x\n", hr);
+ return CUBEB_ERROR;
+ }
+
+ handle_channel_layout(stm, &mix_format, &stm->output_stream_params);
+
+ /* Shared mode WASAPI always supports float32 sample format, so this
+ * is safe. */
+ stm->output_mix_params.format = CUBEB_SAMPLE_FLOAT32NE;
+ stm->output_mix_params.rate = mix_format->nSamplesPerSec;
+ stm->output_mix_params.channels = mix_format->nChannels;
+
+
+ hr = stm->output_client->Initialize(AUDCLNT_SHAREMODE_SHARED,
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
+ AUDCLNT_STREAMFLAGS_NOPERSIST,
+ ms_to_hns(stm->latency),
+ 0,
+ mix_format,
+ NULL);
+
+ CoTaskMemFree(mix_format);
+
+ if (FAILED(hr)) {
+ LOG("Unable to initialize audio client: %x.\n", hr);
+ return CUBEB_ERROR;
+ }
- hr = stm->client->SetEventHandle(stm->refill_event);
- if (FAILED(hr)) {
- LOG("Could set the event handle for the client: %x\n", hr);
- return CUBEB_ERROR;
- }
+ hr = stm->output_client->GetBufferSize(&stm->output_buffer_frame_count);
+ if (FAILED(hr)) {
+ LOG("Could not get the buffer size from the client %x.\n", hr);
+ return CUBEB_ERROR;
+ }
+
+ if (should_upmix(stm) || should_downmix(stm)) {
+ stm->mix_buffer = (float *)malloc(frames_to_bytes_before_mix(stm, stm->output_buffer_frame_count));
+ }
+
+ hr = stm->output_client->SetEventHandle(stm->refill_event);
+ if (FAILED(hr)) {
+ LOG("Could set the event handle for the client %x.\n", hr);
+ return CUBEB_ERROR;
+ }
+
+ hr = stm->output_client->GetService(__uuidof(IAudioRenderClient), (void **)&stm->render_client);
+ if (FAILED(hr)) {
+ LOG("Could not get the render client %x.\n", hr);
+ return CUBEB_ERROR;
+ }
+
- hr = stm->client->GetService(__uuidof(IAudioRenderClient),
- (void **)&stm->render_client);
- if (FAILED(hr)) {
- LOG("Could not get the render client: %x\n", hr);
- return CUBEB_ERROR;
+ hr = stm->output_client->GetService(__uuidof(IAudioStreamVolume),
+ (void **)&stm->audio_stream_volume);
+ if (FAILED(hr)) {
+ LOG("Could not get the IAudioStreamVolume: %x\n", hr);
+ return CUBEB_ERROR;
+ }
+
+ XASSERT(stm->frames_written == 0);
+ hr = stm->output_client->GetService(__uuidof(IAudioClock),
+ (void **)&stm->audio_clock);
+ if (FAILED(hr)) {
+ LOG("Could not get the IAudioClock: %x\n", hr);
+ return CUBEB_ERROR;
+ }
+
+ /* Restore the stream volume over a device change. */
+ if (stream_set_volume(stm, stm->volume) != CUBEB_OK) {
+ return CUBEB_ERROR;
+ }
+
+ /* If we are playing a mono stream, we only resample one channel,
+ and copy it over, so we are always resampling the number
+ of channels of the stream, not the number of channels
+ that WASAPI wants. */
+ stm->resampler = cubeb_resampler_create(stm, stm->output_stream_params,
+ stm->output_mix_params.rate,
+ stm->data_callback,
+ stm->output_buffer_frame_count,
+ stm->user_ptr,
+ CUBEB_RESAMPLER_QUALITY_DESKTOP);
+ if (!stm->resampler) {
+ LOG("Could not get a resampler\n");
+ return CUBEB_ERROR;
+ }
+
}
- hr = stm->client->GetService(__uuidof(IAudioStreamVolume),
- (void **)&stm->audio_stream_volume);
- if (FAILED(hr)) {
- LOG("Could not get the IAudioStreamVolume: %x\n", hr);
- return CUBEB_ERROR;
- }
-
- XASSERT(stm->frames_written == 0);
- hr = stm->client->GetService(__uuidof(IAudioClock),
- (void **)&stm->audio_clock);
- if (FAILED(hr)) {
- LOG("Could not get the IAudioClock: %x\n", hr);
- return CUBEB_ERROR;
- }
+ XASSERT(has_input(stm) || has_output(stm));
- /* Restore the stream volume over a device change. */
- if (stream_set_volume(stm, stm->volume) != CUBEB_OK) {
- return CUBEB_ERROR;
- }
-
- /* If we are playing a mono stream, we only resample one channel,
- and copy it over, so we are always resampling the number
- of channels of the stream, not the number of channels
- that WASAPI wants. */
- stm->resampler = cubeb_resampler_create(stm, stm->stream_params,
- stm->mix_params.rate,
- stm->data_callback,
- stm->buffer_frame_count,
- stm->user_ptr,
- CUBEB_RESAMPLER_QUALITY_DESKTOP);
- if (!stm->resampler) {
- LOG("Could not get a resampler\n");
- return CUBEB_ERROR;
+ if (has_input(stm) && has_output(stm)) {
+ stm->refill_callback = refill_callback_duplex;
+ } else if (has_input(stm)) {
+ stm->refill_callback = refill_callback_input;
+ } else if (has_output(stm)) {
+ stm->refill_callback = refill_callback_output;
}
return CUBEB_OK;
}
int
wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
- char const * stream_name, cubeb_stream_params stream_params,
+ char const * stream_name,
+ cubeb_stream_params * input_stream_params,
+ cubeb_stream_params * output_stream_params,
unsigned int latency, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
HRESULT hr;
int rv;
auto_com com;
if (!com.ok()) {
return CUBEB_ERROR;
}
XASSERT(context && stream);
- if (stream_params.format != CUBEB_SAMPLE_FLOAT32NE) {
+ if (output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE) {
return CUBEB_ERROR_INVALID_FORMAT;
}
cubeb_stream * stm = (cubeb_stream *)calloc(1, sizeof(cubeb_stream));
XASSERT(stm);
stm->context = context;
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
- stm->stream_params = stream_params;
+ stm->output_stream_params = *output_stream_params;
stm->draining = false;
+ if (input_stream_params) {
+ stm->input_stream_params = *input_stream_params;
+ }
+ if (output_stream_params) {
+ stm->output_stream_params = *output_stream_params;
+ }
stm->latency = latency;
stm->volume = 1.0;
stm->stream_reset_lock = new owned_critical_section();
stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL);
if (!stm->reconfigure_event) {
LOG("Can't create the reconfigure event, error: %x\n", GetLastError());
@@ -1242,16 +1558,25 @@ wasapi_stream_init(cubeb * context, cube
stm->refill_event = CreateEvent(NULL, 0, 0, NULL);
if (!stm->refill_event) {
LOG("Can't create the refill event, error: %x\n", GetLastError());
wasapi_stream_destroy(stm);
return CUBEB_ERROR;
}
+ if (input_stream_params) {
+ stm->input_available_event = CreateEvent(NULL, 0, 0, NULL);
+ if (!stm->input_available_event) {
+ LOG("Can't create the input available event , error: %x\n", GetLastError());
+ wasapi_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
+ }
+
{
/* Locking here is not strictly necessary, because we don't have a
notification client that can reset the stream yet, but it lets us
assert that the lock is held in the function. */
auto_lock lock(stm->stream_reset_lock);
rv = setup_wasapi_stream(stm);
}
if (rv != CUBEB_OK) {
@@ -1272,18 +1597,20 @@ wasapi_stream_init(cubeb * context, cube
}
void close_wasapi_stream(cubeb_stream * stm)
{
XASSERT(stm);
stm->stream_reset_lock->assert_current_thread_owns();
- SafeRelease(stm->client);
- stm->client = NULL;
+ SafeRelease(stm->output_client);
+ stm->output_client = NULL;
+ SafeRelease(stm->input_client);
+ stm->capture_client = NULL;
SafeRelease(stm->render_client);
stm->render_client = NULL;
SafeRelease(stm->audio_stream_volume);
stm->audio_stream_volume = NULL;
SafeRelease(stm->audio_clock);
@@ -1305,16 +1632,17 @@ void wasapi_stream_destroy(cubeb_stream
XASSERT(stm);
unregister_notification_client(stm);
stop_and_join_render_thread(stm);
SafeRelease(stm->reconfigure_event);
SafeRelease(stm->refill_event);
+ SafeRelease(stm->input_available_event);
{
auto_lock lock(stm->stream_reset_lock);
close_wasapi_stream(stm);
}
delete stm->stream_reset_lock;
@@ -1322,46 +1650,54 @@ void wasapi_stream_destroy(cubeb_stream
}
int wasapi_stream_start(cubeb_stream * stm)
{
auto_lock lock(stm->stream_reset_lock);
XASSERT(stm && !stm->thread && !stm->shutdown_event);
- if (!stm->client) {
+ if (!stm->output_client) {
return CUBEB_ERROR;
}
- HRESULT hr = stm->client->Start();
+ HRESULT hr = stm->output_client->Start();
if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
LOG("audioclient invalid device, reconfiguring\n", hr);
BOOL ok = ResetEvent(stm->reconfigure_event);
if (!ok) {
LOG("resetting reconfig event failed: %x\n", GetLastError());
}
close_wasapi_stream(stm);
int r = setup_wasapi_stream(stm);
if (r != CUBEB_OK) {
LOG("reconfigure failed\n");
return r;
}
- HRESULT hr = stm->client->Start();
+ HRESULT hr = stm->output_client->Start();
if (FAILED(hr)) {
LOG("could not start the stream after reconfig: %x\n", hr);
return CUBEB_ERROR;
}
} else if (FAILED(hr)) {
LOG("could not start the stream.\n");
return CUBEB_ERROR;
}
+ if (stm->input_client) {
+ HRESULT rv = stm->input_client->Start();
+ if (FAILED(rv)) {
+ printf("Could not start capture.\n");
+ return CUBEB_ERROR;
+ }
+ }
+
stm->shutdown_event = CreateEvent(NULL, 0, 0, NULL);
if (!stm->shutdown_event) {
LOG("Can't create the shutdown event, error: %x\n", GetLastError());
return CUBEB_ERROR;
}
stm->thread = (HANDLE) _beginthreadex(NULL, 256 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
if (stm->thread == NULL) {
@@ -1376,39 +1712,44 @@ int wasapi_stream_start(cubeb_stream * s
int wasapi_stream_stop(cubeb_stream * stm)
{
XASSERT(stm);
{
auto_lock lock(stm->stream_reset_lock);
- if (stm->client) {
- HRESULT hr = stm->client->Stop();
+ if (stm->output_client) {
+ HRESULT hr = stm->output_client->Stop();
if (FAILED(hr)) {
LOG("could not stop AudioClient\n");
return CUBEB_ERROR;
}
}
+ if (stm->input_client) {
+ stm->input_client->Stop();
+ }
+
+
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
}
stop_and_join_render_thread(stm);
return CUBEB_OK;
}
int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
XASSERT(stm && position);
auto_lock lock(stm->stream_reset_lock);
/* Calculate how far behind the current stream head the playback cursor is. */
- uint64_t stream_delay = current_stream_delay(stm) * stm->stream_params.rate;
+ uint64_t stream_delay = current_stream_delay(stm) * stm->output_stream_params.rate;
/* Calculate the logical stream head in frames at the stream sample rate. */
uint64_t max_pos = stm->total_frames_written +
round(stm->frames_written * stream_to_mix_samplerate_ratio(stm));
*position = max_pos;
if (stream_delay <= *position) {
*position -= stream_delay;
@@ -1425,27 +1766,27 @@ int wasapi_stream_get_position(cubeb_str
int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
XASSERT(stm && latency);
auto_lock lock(stm->stream_reset_lock);
/* The GetStreamLatency method only works if the
AudioClient has been initialized. */
- if (!stm->client) {
+ if (!stm->output_client) {
return CUBEB_ERROR;
}
REFERENCE_TIME latency_hns;
- HRESULT hr = stm->client->GetStreamLatency(&latency_hns);
+ HRESULT hr = stm->output_client->GetStreamLatency(&latency_hns);
if (FAILED(hr)) {
return CUBEB_ERROR;
}
double latency_s = hns_to_s(latency_hns);
- *latency = static_cast<uint32_t>(latency_s * stm->stream_params.rate);
+ *latency = static_cast<uint32_t>(latency_s * stm->output_stream_params.rate);
return CUBEB_OK;
}
int wasapi_stream_set_volume(cubeb_stream * stm, float volume)
{
auto_lock lock(stm->stream_reset_lock);
--- a/media/libcubeb/src/cubeb_winmm.c
+++ b/media/libcubeb/src/cubeb_winmm.c
@@ -174,17 +174,17 @@ winmm_refill_stream(cubeb_stream * stm)
hdr = winmm_get_next_buffer(stm);
wanted = (DWORD) stm->buffer_size / bytes_per_frame(stm->params);
/* It is assumed that the caller is holding this lock. It must be dropped
during the callback to avoid deadlocks. */
LeaveCriticalSection(&stm->lock);
- got = stm->data_callback(stm, stm->user_ptr, hdr->lpData, wanted);
+ got = stm->data_callback(stm, stm->user_ptr, NULL, hdr->lpData, wanted);
EnterCriticalSection(&stm->lock);
if (got < 0) {
LeaveCriticalSection(&stm->lock);
/* XXX handle this case */
XASSERT(0);
return;
} else if (got < wanted) {
stm->draining = 1;
--- a/media/libcubeb/tests/test_audio.cpp
+++ b/media/libcubeb/tests/test_audio.cpp
@@ -155,17 +155,17 @@ int run_test(int num_channels, int sampl
params.channels = num_channels;
synth = synth_create(params.channels, params.rate);
if (synth == NULL) {
fprintf(stderr, "Out of memory\n");
goto cleanup;
}
- r = cubeb_stream_init(ctx, &stream, "test tone", params,
+ r = cubeb_stream_init(ctx, &stream, "test tone", nullptr, ¶ms,
100, is_float ? data_cb_float : data_cb_short, state_cb, synth);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
goto cleanup;
}
cubeb_stream_start(stream);
delay(200);
@@ -207,17 +207,17 @@ int run_panning_volume_test(int is_float
params.channels = 2;
synth = synth_create(params.channels, params.rate);
if (synth == NULL) {
fprintf(stderr, "Out of memory\n");
goto cleanup;
}
- r = cubeb_stream_init(ctx, &stream, "test tone", params,
+ r = cubeb_stream_init(ctx, &stream, "test tone", nullptr, ¶ms,
100, is_float ? data_cb_float : data_cb_short, state_cb, synth);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
goto cleanup;
}
fprintf(stderr, "Testing: volume\n");
for(int i=0;i <= 4; ++i)
--- a/media/libcubeb/tests/test_sanity.cpp
+++ b/media/libcubeb/tests/test_sanity.cpp
@@ -35,23 +35,23 @@
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
#endif
static int dummy;
static uint64_t total_frames_written;
static int delay_callback;
static long
-test_data_callback(cubeb_stream * stm, void * user_ptr, void * p, long nframes)
+test_data_callback(cubeb_stream * stm, void * user_ptr, void * inputbuffer, void * outputbuffer, long nframes)
{
- assert(stm && user_ptr == &dummy && p && nframes > 0);
+ assert(stm && user_ptr == &dummy && nframes > 0);
#if (defined(_WIN32) || defined(__WIN32__))
- memset(p, 0, nframes * sizeof(float));
+ memset(outputbuffer, 0, nframes * sizeof(float));
#else
- memset(p, 0, nframes * sizeof(short));
+ memset(outputbuffer, 0, nframes * sizeof(short));
#endif
total_frames_written += nframes;
if (delay_callback) {
delay(10);
}
return nframes;
}
@@ -153,17 +153,17 @@ test_init_destroy_stream(void)
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
- r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
+ r = cubeb_stream_init(ctx, &stream, "test", nullptr, ¶ms, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
END_TEST;
}
@@ -182,17 +182,17 @@ test_init_destroy_multiple_streams(void)
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
- r = cubeb_stream_init(ctx, &stream[i], "test", params, STREAM_LATENCY,
+ r = cubeb_stream_init(ctx, &stream[i], "test", NULL, ¶ms, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0);
assert(stream[i]);
}
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
cubeb_stream_destroy(stream[i]);
}
@@ -214,17 +214,17 @@ test_configure_stream(void)
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = 2; // panning
- r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
+ r = cubeb_stream_init(ctx, &stream, "test", NULL, ¶ms, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
r = cubeb_stream_set_volume(stream, 1.0f);
assert(r == 0 || r == CUBEB_ERROR_NOT_SUPPORTED);
r = cubeb_stream_set_panning(stream, 0.0f);
assert(r == 0 || r == CUBEB_ERROR_NOT_SUPPORTED);
@@ -248,17 +248,17 @@ test_init_start_stop_destroy_multiple_st
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
- r = cubeb_stream_init(ctx, &stream[i], "test", params, STREAM_LATENCY,
+ r = cubeb_stream_init(ctx, &stream[i], "test", NULL, ¶ms, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0);
assert(stream[i]);
if (early) {
r = cubeb_stream_start(stream[i]);
assert(r == 0);
}
}
@@ -312,17 +312,17 @@ test_init_destroy_multiple_contexts_and_
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
r = cubeb_init(&ctx[i], "test_sanity");
assert(r == 0 && ctx[i]);
for (j = 0; j < streams_per_ctx; ++j) {
- r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test", params, STREAM_LATENCY,
+ r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test", NULL, ¶ms, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0);
assert(stream[i * streams_per_ctx + j]);
}
}
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
for (j = 0; j < streams_per_ctx; ++j) {
@@ -347,17 +347,17 @@ test_basic_stream_operations(void)
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
- r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
+ r = cubeb_stream_init(ctx, &stream, "test", NULL, ¶ms, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
/* position and volume before stream has started */
r = cubeb_stream_get_position(stream, &position);
assert(r == 0 && position == 0);
r = cubeb_stream_start(stream);
@@ -396,17 +396,17 @@ test_stream_position(void)
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
- r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
+ r = cubeb_stream_init(ctx, &stream, "test", NULL, ¶ms, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
/* stream position should not advance before starting playback */
r = cubeb_stream_get_position(stream, &position);
assert(r == 0 && position == 0);
delay(500);
@@ -469,26 +469,26 @@ test_stream_position(void)
END_TEST;
}
static int do_drain;
static int got_drain;
static long
-test_drain_data_callback(cubeb_stream * stm, void * user_ptr, void * p, long nframes)
+test_drain_data_callback(cubeb_stream * stm, void * user_ptr, void * inputbuffer, void * outputbuffer, long nframes)
{
- assert(stm && user_ptr == &dummy && p && nframes > 0);
+ assert(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
if (do_drain == 1) {
do_drain = 2;
return 0;
}
/* once drain has started, callback must never be called again */
assert(do_drain != 2);
- memset(p, 0, nframes * sizeof(short));
+ memset(outputbuffer, 0, nframes * sizeof(short));
total_frames_written += nframes;
return nframes;
}
void
test_drain_state_callback(cubeb_stream * stm, void * user_ptr, cubeb_state state)
{
if (state == CUBEB_STATE_DRAINED) {
@@ -512,17 +512,17 @@ test_drain(void)
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
- r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
+ r = cubeb_stream_init(ctx, &stream, "test", NULL, ¶ms, STREAM_LATENCY,
test_drain_data_callback, test_drain_state_callback, &dummy);
assert(r == 0 && stream);
r = cubeb_stream_start(stream);
assert(r == 0);
delay(500);
--- a/media/libcubeb/tests/test_tone.cpp
+++ b/media/libcubeb/tests/test_tone.cpp
@@ -29,23 +29,23 @@
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
#endif
/* store the phase of the generated waveform */
struct cb_user_data {
long position;
};
-long data_cb(cubeb_stream *stream, void *user, void *buffer, long nframes)
+long data_cb(cubeb_stream *stream, void *user, void *inputbuffer, void * outputbuffer, long nframes)
{
struct cb_user_data *u = (struct cb_user_data *)user;
#if (defined(_WIN32) || defined(__WIN32__))
- float *b = (float *)buffer;
+ float *b = (float *)outputbuffer;
#else
- short *b = (short *)buffer;
+ short *b = (short *)outputbuffer;
#endif
float t1, t2;
int i;
if (stream == NULL || u == NULL)
return CUBEB_ERROR;
/* generate our test tone on the fly */
@@ -122,17 +122,17 @@ int main(int argc, char *argv[])
user_data = (struct cb_user_data *) malloc(sizeof(*user_data));
if (user_data == NULL) {
fprintf(stderr, "Error allocating user data\n");
return CUBEB_ERROR;
}
user_data->position = 0;
- r = cubeb_stream_init(ctx, &stream, "Cubeb tone (mono)", params,
+ r = cubeb_stream_init(ctx, &stream, "Cubeb tone (mono)", nullptr, ¶ms,
250, data_cb, state_cb, user_data);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream\n");
return r;
}
cubeb_stream_start(stream);
delay(500);