mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-25 03:49:42 +00:00
Bug 929009 - Support mono configurations in WASAPI backend. r=padenot
This commit is contained in:
parent
94e949b282
commit
1b0e7b4249
@ -4,6 +4,7 @@
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
#undef NDEBUG
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include "config.h"
|
||||
#endif
|
||||
@ -135,10 +136,8 @@ struct cubeb_stream
|
||||
uint32_t leftover_frame_size;
|
||||
float * leftover_frames_buffer;
|
||||
/* Buffer used to downmix or upmix to the number of channels the mixer has.
|
||||
* its size is |buffer_frame_count * bytes_per_frame * mixer_channels|. */
|
||||
* its size is |frames_to_bytes_before_mix(buffer_frame_count)|. */
|
||||
float * mix_buffer;
|
||||
/* Number of bytes per frame. Prefer to use frames_to_bytes_before_mix. */
|
||||
uint8_t bytes_per_frame;
|
||||
/* True if the stream is draining. */
|
||||
bool draining;
|
||||
};
|
||||
@ -161,7 +160,7 @@ void
|
||||
mono_to_stereo(T * in, long insamples, T * out)
|
||||
{
|
||||
int j = 0;
|
||||
for (int i = 0; i < insamples; i++, j+=2) {
|
||||
for (int i = 0; i < insamples; ++i, j += 2) {
|
||||
out[j] = out[j + 1] = in[i];
|
||||
}
|
||||
}
|
||||
@ -170,6 +169,7 @@ template<typename T>
|
||||
void
|
||||
upmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channels)
|
||||
{
|
||||
assert(out_channels >= in_channels);
|
||||
/* If we are playing a mono stream over stereo speakers, copy the data over. */
|
||||
if (in_channels == 1 && out_channels == 2) {
|
||||
mono_to_stereo(in, inframes, out);
|
||||
@ -177,14 +177,11 @@ upmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channels)
|
||||
}
|
||||
/* Otherwise, put silence in other channels. */
|
||||
long out_index = 0;
|
||||
for (long i = 0; i < inframes * in_channels; i+=in_channels) {
|
||||
for (int j = 0; j < in_channels; j++) {
|
||||
for (long i = 0; i < inframes * in_channels; i += in_channels) {
|
||||
for (int j = 0; j < in_channels; ++j) {
|
||||
out[out_index + j] = in[i + j];
|
||||
if (in_channels == 1) {
|
||||
out[out_index + j + 1] = in[i + j];
|
||||
}
|
||||
}
|
||||
for (int j = in_channels; j < out_channels; j++) {
|
||||
for (int j = in_channels; j < out_channels; ++j) {
|
||||
out[out_index + j] = 0.0;
|
||||
}
|
||||
out_index += out_channels;
|
||||
@ -193,21 +190,25 @@ upmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channels)
|
||||
|
||||
template<typename T>
|
||||
void
|
||||
downmix_to_stereo(T * in, long inframes, T * out, int32_t in_channels)
|
||||
downmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channels)
|
||||
{
|
||||
assert(in_channels >= out_channels);
|
||||
/* We could use a downmix matrix here, applying mixing weight based on the
|
||||
* channel, but directsound and winmm simply drop the channels that cannot be
|
||||
* rendered by the hardware, so we do the same for consistency. */
|
||||
for (int32_t i = 0; i < inframes; i++) {
|
||||
out[i * 2] = in[i * in_channels];
|
||||
out[i * 2 + 1] = in[i * in_channels + 1];
|
||||
long out_index = 0;
|
||||
for (long i = 0; i < inframes * in_channels; i += in_channels) {
|
||||
for (int j = 0; j < out_channels; ++j) {
|
||||
out[out_index + j] = in[i + j];
|
||||
}
|
||||
out_index += out_channels;
|
||||
}
|
||||
}
|
||||
|
||||
/* This returns the size of a frame in the stream,
|
||||
* before the eventual upmix occurs. */
|
||||
static size_t
|
||||
frame_to_bytes_before_mix(cubeb_stream * stm, size_t frames)
|
||||
frames_to_bytes_before_mix(cubeb_stream * stm, size_t frames)
|
||||
{
|
||||
size_t stream_frame_size = stm->stream_params.channels * sizeof(float);
|
||||
return stream_frame_size * frames;
|
||||
@ -227,7 +228,7 @@ refill_with_resampling(cubeb_stream * stm, float * data, long frames_needed)
|
||||
long frame_requested = before_resampling - stm->leftover_frame_count;
|
||||
|
||||
size_t leftover_bytes =
|
||||
frame_to_bytes_before_mix(stm, stm->leftover_frame_count);
|
||||
frames_to_bytes_before_mix(stm, stm->leftover_frame_count);
|
||||
|
||||
/* Copy the previous leftover frames to the front of the buffer. */
|
||||
memcpy(stm->resampling_src_buffer, stm->leftover_frames_buffer, leftover_bytes);
|
||||
@ -261,11 +262,11 @@ refill_with_resampling(cubeb_stream * stm, float * data, long frames_needed)
|
||||
/* Copy the leftover frames to buffer for the next time. */
|
||||
stm->leftover_frame_count = before_resampling - in_frames;
|
||||
size_t unresampled_bytes =
|
||||
frame_to_bytes_before_mix(stm, stm->leftover_frame_count);
|
||||
frames_to_bytes_before_mix(stm, stm->leftover_frame_count);
|
||||
|
||||
uint8_t * leftover_frames_start =
|
||||
reinterpret_cast<uint8_t *>(stm->resampling_src_buffer);
|
||||
leftover_frames_start += frame_to_bytes_before_mix(stm, in_frames);
|
||||
leftover_frames_start += frames_to_bytes_before_mix(stm, in_frames);
|
||||
|
||||
assert(stm->leftover_frame_count <= stm->leftover_frame_size);
|
||||
memcpy(stm->leftover_frames_buffer, leftover_frames_start, unresampled_bytes);
|
||||
@ -278,8 +279,8 @@ refill_with_resampling(cubeb_stream * stm, float * data, long frames_needed)
|
||||
upmix(resample_dest, out_frames, data,
|
||||
stm->stream_params.channels, stm->mix_params.channels);
|
||||
} else if (should_downmix(stm)) {
|
||||
downmix_to_stereo(resample_dest, out_frames, data,
|
||||
stm->stream_params.channels);
|
||||
downmix(resample_dest, out_frames, data,
|
||||
stm->stream_params.channels, stm->mix_params.channels);
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,7 +297,7 @@ refill(cubeb_stream * stm, float * data, long frames_needed)
|
||||
}
|
||||
|
||||
long got = stm->data_callback(stm, stm->user_ptr, dest, frames_needed);
|
||||
|
||||
assert(got <= frames_needed);
|
||||
if (got != frames_needed) {
|
||||
LOG("draining.");
|
||||
stm->draining = true;
|
||||
@ -305,8 +306,9 @@ refill(cubeb_stream * stm, float * data, long frames_needed)
|
||||
if (should_upmix(stm)) {
|
||||
upmix(dest, got, data,
|
||||
stm->stream_params.channels, stm->mix_params.channels);
|
||||
} else {
|
||||
downmix_to_stereo(dest, got, data, stm->stream_params.channels);
|
||||
} else if (should_downmix(stm)) {
|
||||
downmix(dest, got, data,
|
||||
stm->stream_params.channels, stm->mix_params.channels);
|
||||
}
|
||||
}
|
||||
|
||||
@ -363,6 +365,7 @@ wasapi_stream_render_loop(LPVOID stream)
|
||||
is_playing = false;
|
||||
continue;
|
||||
}
|
||||
assert(padding <= stm->buffer_frame_count);
|
||||
|
||||
if (stm->draining) {
|
||||
if (padding == 0) {
|
||||
@ -603,7 +606,7 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub
|
||||
{
|
||||
/* Common case: the hardware is stereo. Up-mixing and down-mixing will be
|
||||
* handled in the callback. */
|
||||
if ((*mix_format)->nChannels == 2) {
|
||||
if ((*mix_format)->nChannels <= 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -647,8 +650,10 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub
|
||||
|
||||
if (hr == S_FALSE) {
|
||||
/* Not supported, but WASAPI gives us a suggestion. Use it, and handle the
|
||||
* eventual upmix/downmix ourselve */
|
||||
* eventual upmix/downmix ourselves */
|
||||
LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
|
||||
WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
|
||||
assert(closest_pcm->SubFormat == format_pcm->SubFormat);
|
||||
CoTaskMemFree(*mix_format);
|
||||
*mix_format = closest;
|
||||
} else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) {
|
||||
@ -721,9 +726,6 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
* and the format the stream we want to play uses. */
|
||||
stm->client->GetMixFormat(&mix_format);
|
||||
|
||||
/* this is the number of bytes per frame after the eventual upmix. */
|
||||
stm->bytes_per_frame = static_cast<uint8_t>(mix_format->nBlockAlign);
|
||||
|
||||
handle_channel_layout(stm, &mix_format, &stream_params);
|
||||
|
||||
/* Shared mode WASAPI always supports float32 sample format, so this
|
||||
@ -757,7 +759,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
* that is, the samples not consumed by the resampler that we will end up
|
||||
* using next time the render callback is called. */
|
||||
stm->leftover_frame_size = static_cast<uint32_t>(ceilf(1 / resampling_rate * 2) + 1);
|
||||
stm->leftover_frames_buffer = (float *)malloc(frame_to_bytes_before_mix(stm, stm->leftover_frame_size));
|
||||
stm->leftover_frames_buffer = (float *)malloc(frames_to_bytes_before_mix(stm, stm->leftover_frame_size));
|
||||
|
||||
stm->refill_function = &refill_with_resampling;
|
||||
} else {
|
||||
@ -787,10 +789,8 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
assert(stm->mix_params.channels >= 2);
|
||||
|
||||
if (should_upmix(stm) || should_downmix(stm)) {
|
||||
stm->mix_buffer = (float *) malloc(frame_to_bytes_before_mix(stm, stm->buffer_frame_count));
|
||||
stm->mix_buffer = (float *) malloc(frames_to_bytes_before_mix(stm, stm->buffer_frame_count));
|
||||
}
|
||||
|
||||
/* If we are going to resample, we will end up needing a buffer
|
||||
@ -799,7 +799,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
* factor and the channel layout into account. */
|
||||
if (stm->resampler) {
|
||||
size_t frames_needed = static_cast<size_t>(frame_count_at_rate(stm->buffer_frame_count, resampling_rate));
|
||||
stm->resampling_src_buffer = (float *)malloc(frame_to_bytes_before_mix(stm, frames_needed));
|
||||
stm->resampling_src_buffer = (float *)malloc(frames_to_bytes_before_mix(stm, frames_needed));
|
||||
}
|
||||
|
||||
hr = stm->client->SetEventHandle(stm->refill_event);
|
||||
|
Loading…
x
Reference in New Issue
Block a user