serenity/Userland/Libraries/LibAudio/MultiChannel.h
kleines Filmröllchen 111fd1e5fe LibAudio: Optimize FlacLoader by avoiding allocations
- Pre-allocate and reuse sample decompression buffers. In many FLAC
  files, the amount of samples per frame is either constant or the
  largest frame will be hit within the first couple of frames. Also,
  during audio output, we need to move and combine the samples from the
  decompression buffers into the final output buffers anyways. Avoiding
  the reallocation of these large buffers provides an improvement from
  16x to 18x decode speed on strongly compressed but otherwise usual
  input.
- Leave a FIXME for a similar improvement that can be made in the
  residual decoder.
- Pre-allocate audio chunks if frame size is known.
- Use reasonable inline capacities in several places where we know the
  maximum or usual capacity needed.
2023-07-06 19:13:36 +01:00

104 lines
4.5 KiB
C++

/*
* Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Concepts.h>
#include <AK/FixedArray.h>
#include <LibAudio/Sample.h>
namespace Audio {
// Downmixes any number of channels to stereo, under the assumption that standard channel layout is followed:
// 1 channel = mono
// 2 channels = stereo (left, right)
// 3 channels = left, right, center
// 4 channels = front left/right, back left/right
// 5 channels = front left/right, center, back left/right
// 6 channels = front left/right, center, LFE, back left/right
// 7 channels = front left/right, center, LFE, back center, side left/right
// 8 channels = front left/right, center, LFE, back left/right, side left/right
// Additionally, performs sample rescaling to go from integer samples to floating-point samples.
template<ArrayLike<i64> ChannelType, ArrayLike<ChannelType> InputType>
ErrorOr<FixedArray<Sample>> downmix_surround_to_stereo(InputType const& input, float sample_scale_factor)
{
if (input.size() == 0)
return Error::from_string_view("Cannot resample from 0 channels"sv);
auto channel_count = input.size();
auto sample_count = input[0].size();
FixedArray<Sample> output = TRY(FixedArray<Sample>::create(sample_count));
// FIXME: We could figure out a better way to mix the channels, possibly spatially, but for now:
// - Center and LFE channels are added to both left and right.
// - All left channels are added together on the left, all right channels are added together on the right.
switch (channel_count) {
case 1:
for (auto i = 0u; i < sample_count; ++i)
output[i] = Sample { input[0][i] * sample_scale_factor };
break;
case 2:
for (auto i = 0u; i < sample_count; ++i)
output[i] = Sample {
input[0][i] * sample_scale_factor,
input[1][i] * sample_scale_factor
};
break;
case 3:
for (auto i = 0u; i < sample_count; ++i)
output[i] = Sample {
input[0][i] * sample_scale_factor + input[2][i] * sample_scale_factor,
input[1][i] * sample_scale_factor + input[2][i] * sample_scale_factor
};
break;
case 4:
for (auto i = 0u; i < sample_count; ++i)
output[i] = Sample {
input[0][i] * sample_scale_factor + input[2][i] * sample_scale_factor,
input[1][i] * sample_scale_factor + input[3][i] * sample_scale_factor
};
break;
case 5:
for (auto i = 0u; i < sample_count; ++i)
output[i] = Sample {
input[0][i] * sample_scale_factor + input[3][i] * sample_scale_factor + input[2][i] * sample_scale_factor,
input[1][i] * sample_scale_factor + input[4][i] * sample_scale_factor + input[2][i] * sample_scale_factor
};
break;
case 6:
for (auto i = 0u; i < sample_count; ++i) {
output[i] = Sample {
input[0][i] * sample_scale_factor + input[4][i] * sample_scale_factor + input[2][i] * sample_scale_factor + input[3][i] * sample_scale_factor,
input[1][i] * sample_scale_factor + input[5][i] * sample_scale_factor + input[2][i] * sample_scale_factor + input[3][i] * sample_scale_factor
};
}
break;
case 7:
for (auto i = 0u; i < sample_count; ++i) {
output[i] = Sample {
input[0][i] * sample_scale_factor + input[5][i] * sample_scale_factor + input[2][i] * sample_scale_factor + input[3][i] * sample_scale_factor + input[4][i] * sample_scale_factor,
input[1][i] * sample_scale_factor + input[6][i] * sample_scale_factor + input[2][i] * sample_scale_factor + input[3][i] * sample_scale_factor + input[4][i] * sample_scale_factor
};
}
break;
case 8:
for (auto i = 0u; i < sample_count; ++i) {
output[i] = Sample {
input[0][i] * sample_scale_factor + input[4][i] * sample_scale_factor + input[6][i] * sample_scale_factor + input[2][i] * sample_scale_factor + input[3][i] * sample_scale_factor,
input[1][i] * sample_scale_factor + input[5][i] * sample_scale_factor + input[7][i] * sample_scale_factor + input[2][i] * sample_scale_factor + input[3][i] * sample_scale_factor
};
}
break;
default:
return Error::from_string_view("Invalid number of channels greater than 8"sv);
}
return output;
}
}