зеркало из https://github.com/mozilla/gecko-dev.git
Bug 899050 - Add rudimentary support for multichannel files when using WASAPI. r=kinetik
This commit is contained in:
Родитель
b2ef254383
Коммит
3ab666560e
|
@ -119,7 +119,7 @@ struct cubeb_stream
|
|||
uint32_t buffer_frame_count;
|
||||
/* Resampler instance. If this is !NULL, resampling should happen. */
|
||||
SpeexResamplerState * resampler;
|
||||
/* Buffer to resample from, into the upmix buffer or the final buffer. */
|
||||
/* Buffer to resample from, into the mix buffer or the final buffer. */
|
||||
float * resampling_src_buffer;
|
||||
/* Pointer to the function used to refill the buffer, depending
|
||||
* on the respective samplerate of the stream and the mix. */
|
||||
|
@ -128,9 +128,10 @@ struct cubeb_stream
|
|||
uint32_t leftover_frame_count;
|
||||
uint32_t leftover_frame_size;
|
||||
float * leftover_frames_buffer;
|
||||
/* upmix buffer of size |buffer_frame_count * bytes_per_frame / 2|. */
|
||||
float * upmix_buffer;
|
||||
/* Number of bytes per frame. Prefer to use frames_to_bytes_before_upmix. */
|
||||
/* 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|. */
|
||||
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;
|
||||
|
@ -139,7 +140,12 @@ struct cubeb_stream
|
|||
namespace {
|
||||
bool should_upmix(cubeb_stream * stream)
|
||||
{
|
||||
return stream->upmix_buffer;
|
||||
return stream->mix_params.channels > stream->stream_params.channels;
|
||||
}
|
||||
|
||||
bool should_downmix(cubeb_stream * stream)
|
||||
{
|
||||
return stream->mix_params.channels < stream->stream_params.channels;
|
||||
}
|
||||
|
||||
/* Upmix function, copies a mono channel in two interleaved
|
||||
|
@ -179,10 +185,23 @@ 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)
|
||||
{
|
||||
/* 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];
|
||||
}
|
||||
}
|
||||
|
||||
/* This returns the size of a frame in the stream,
|
||||
* before the eventual upmix occurs. */
|
||||
static size_t
|
||||
frame_to_bytes_before_upmix(cubeb_stream * stm, size_t frames)
|
||||
frame_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;
|
||||
|
@ -202,7 +221,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_upmix(stm, stm->leftover_frame_count);
|
||||
frame_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);
|
||||
|
@ -218,11 +237,11 @@ refill_with_resampling(cubeb_stream * stm, float * data, long frames_needed)
|
|||
uint32_t in_frames = before_resampling;
|
||||
uint32_t out_frames = frames_needed;
|
||||
|
||||
/* if we need to upmix after resampling, resample into
|
||||
* the upmix buffer to avoid a copy */
|
||||
/* If we need to upmix after resampling, resample into the mix buffer to
|
||||
* avoid a copy. */
|
||||
float * resample_dest;
|
||||
if (should_upmix(stm)) {
|
||||
resample_dest = stm->upmix_buffer;
|
||||
if (should_upmix(stm) || should_downmix(stm)) {
|
||||
resample_dest = stm->mix_buffer;
|
||||
} else {
|
||||
resample_dest = data;
|
||||
}
|
||||
|
@ -236,11 +255,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_upmix(stm, stm->leftover_frame_count);
|
||||
frame_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_upmix(stm, in_frames);
|
||||
leftover_frames_start += frame_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);
|
||||
|
@ -252,17 +271,20 @@ refill_with_resampling(cubeb_stream * stm, float * data, long frames_needed)
|
|||
if (should_upmix(stm)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
refill(cubeb_stream * stm, float * data, long frames_needed)
|
||||
{
|
||||
/* If we need to upmix after resampling, get the data into
|
||||
* the upmix buffer to avoid a copy. */
|
||||
/* If we need to upmix/downmix, get the data into the mix buffer to avoid a
|
||||
* copy, then do the processing process. */
|
||||
float * dest;
|
||||
if (should_upmix(stm)) {
|
||||
dest = stm->upmix_buffer;
|
||||
if (should_upmix(stm) || should_downmix(stm)) {
|
||||
dest = stm->mix_buffer;
|
||||
} else {
|
||||
dest = data;
|
||||
}
|
||||
|
@ -277,6 +299,8 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -514,13 +538,14 @@ void wasapi_stream_destroy(cubeb_stream * stm);
|
|||
static void
|
||||
handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cubeb_stream_params * stream_params)
|
||||
{
|
||||
/* Common case: the hardware supports stereo, and the stream is mono or
|
||||
* stereo. Easy. */
|
||||
if ((*mix_format)->nChannels == 2 &&
|
||||
stream_params->channels <= 2) {
|
||||
/* Common case: the hardware is stereo. Up-mixing and down-mixing will be
|
||||
* handled in the callback. */
|
||||
if ((*mix_format)->nChannels == 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise, the hardware supports more than two channels. */
|
||||
|
||||
/* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1],
|
||||
* so the reinterpret_cast below should be safe. In practice, this is not
|
||||
* true, and we just want to bail out and let the rest of the code find a good
|
||||
|
@ -559,7 +584,7 @@ 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 ourselve */
|
||||
* eventual upmix/downmix ourselve */
|
||||
LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
|
||||
CoTaskMemFree(*mix_format);
|
||||
*mix_format = closest;
|
||||
|
@ -596,11 +621,6 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
|||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* we don't support more that two channels for now. */
|
||||
if (stream_params.channels > 2) {
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
cubeb_stream * stm = (cubeb_stream *)calloc(1, sizeof(cubeb_stream));
|
||||
|
||||
assert(stm);
|
||||
|
@ -680,7 +700,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_upmix(stm, stm->leftover_frame_size));
|
||||
stm->leftover_frames_buffer = (float *)malloc(frame_to_bytes_before_mix(stm, stm->leftover_frame_size));
|
||||
|
||||
stm->refill_function = &refill_with_resampling;
|
||||
} else {
|
||||
|
@ -712,8 +732,8 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
|||
|
||||
assert(stm->mix_params.channels >= 2);
|
||||
|
||||
if (stm->mix_params.channels != stm->stream_params.channels) {
|
||||
stm->upmix_buffer = (float *) malloc(frame_to_bytes_before_upmix(stm, stm->buffer_frame_count));
|
||||
if (should_upmix(stm) || should_downmix(stm)) {
|
||||
stm->mix_buffer = (float *) malloc(frame_to_bytes_before_mix(stm, stm->buffer_frame_count));
|
||||
}
|
||||
|
||||
/* If we are going to resample, we will end up needing a buffer
|
||||
|
@ -722,7 +742,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_upmix(stm, frames_needed));
|
||||
stm->resampling_src_buffer = (float *)malloc(frame_to_bytes_before_mix(stm, frames_needed));
|
||||
}
|
||||
|
||||
hr = stm->client->SetEventHandle(stm->refill_event);
|
||||
|
@ -783,7 +803,7 @@ void wasapi_stream_destroy(cubeb_stream * stm)
|
|||
|
||||
free(stm->leftover_frames_buffer);
|
||||
free(stm->resampling_src_buffer);
|
||||
free(stm->upmix_buffer);
|
||||
free(stm->mix_buffer);
|
||||
free(stm);
|
||||
CoUninitialize();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче