You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
surroundize/freesurround_wrapper.cpp

167 lines
7.8 KiB
C++

#include "freesurround_wrapper.hpp"
FreesurroundWrapper::FreesurroundWrapper()
: channel_setup{cs_5point1}, block_size{4096},
circular_wrap(90), shift(0), depth(1), focus(0),
front_separation(1), rear_separation(1),
enable_lfe(false), low_cutoff(40), high_cutoff(90)
{ Recreate(); }
void FreesurroundWrapper::Recreate()
{
dec = {cs_5point1, block_size};
dec.circular_wrap(circular_wrap);
dec.shift(shift);
dec.depth(depth);
dec.focus(focus);
dec.front_separation(front_separation);
dec.rear_separation(rear_separation);
dec.bass_redirection(enable_lfe);
dec.low_cutoff(low_cutoff);
dec.high_cutoff(high_cutoff);
}
std::span<const float> FreesurroundWrapper::Decode(
float rate, std::span<const float> in)
{
if (in.size() != 2*block_size) throw std::runtime_error("Bad size");
// Note: this will calculate cutoff * block_size * rate internally. With small
// block sizes this breaks down, for example with default hi_cut=90 but
// block_size=512 and 48KHz sample rate, 90*512/48000 = 0.96 which is < 1 so
// no LFE will be generated! If you can't have at least 2048 block size with
// 48kHz input, you probably shouldn't bother with LFE generation here.
auto mul = 2 * rate;
dec.low_cutoff(low_cutoff * mul);
dec.high_cutoff(high_cutoff * mul);
auto n_out = dec.num_channels(channel_setup);
return {dec.decode(in.data()), n_out*block_size};
}
std::vector<Option> FreesurroundWrapper::GetOptions()
{
return {
{
"channel_setup", "SETUP",
"The output channel setup. One of: legacy, stereo, 3stereo, 5stereo, 4.1, 5.1, 6.1, 7.1, 7.1_panorama, 7.1_tricenter, 8.1, 9.1_densepanorama, 9.1_wrap, 11.1_densewrap, 13.1_totalwrap, 16.1. Default: 5.1.",
[this](std::string_view sv)
{
/**/ if (sv == "legacy") channel_setup = cs_legacy;
else if (sv == "stereo") channel_setup = cs_stereo;
else if (sv == "3stereo") channel_setup = cs_3stereo;
else if (sv == "5stereo") channel_setup = cs_5stereo;
else if (sv == "4.1") channel_setup = cs_4point1;
else if (sv == "5.1") channel_setup = cs_5point1;
else if (sv == "6.1") channel_setup = cs_6point1;
else if (sv == "7.1") channel_setup = cs_7point1;
else if (sv == "7.1_panorama") channel_setup = cs_7point1_panorama;
else if (sv == "7.1_tricenter") channel_setup = cs_7point1_tricenter;
else if (sv == "8.1") channel_setup = cs_8point1;
else if (sv == "9.1_densepanorama") channel_setup = cs_9point1_densepanorama;
else if (sv == "9.1_wrap") channel_setup = cs_9point1_wrap;
else if (sv == "11.1_densewrap") channel_setup = cs_11point1_densewrap;
else if (sv == "13.1_totalwrap") channel_setup = cs_13point1_totalwrap;
else if (sv == "16.1") channel_setup = cs_16point1;
else throw std::runtime_error("Invalid channel setup " + std::string{sv});
Recreate();
}
},
{
"block_size", "INT",
"Granularity at which data is processed. Must be a power of two and should correspond to ca. 100ms worth of single-channel samples (default is 4096, suitable for 44.1kHz or 48kHz data). Do not make it shorter than 50ms or longer than 200ms since the granularity at which locations are decoded changes with this.",
[this](std::string_view sv)
{
auto bs = FromString<unsigned>(sv);
if (bs < 16 || bs & (bs-1)) throw std::runtime_error("Invalid block size");
block_size = bs;
Recreate();
}
},
{
"circular_wrap", "FLOAT",
"Allows to wrap the soundfield around the listener in a circular manner. Determines the angle of the frontal sound stage relative to the listener, in degrees. A setting of 90° corresponds to standard surround decoding, 180° stretches the front stage from ear to ear, 270° wraps it around most of the head. The side and rear content of the sound field is compressed accordingly behind the listerer. (default: 90, range: [0°..360°])",
[this](std::string_view sv)
{ dec.circular_wrap(circular_wrap = FromString<float>(sv)); },
},
{
"shift", "FLOAT",
"Allows to shift the soundfield forward or backward.\nValue range: [-1.0..+1.0]. 0 is no offset, positive values move the sound forward, negative values move it backwards. (default: 0)",
[this](std::string_view sv)
{ dec.shift(shift = FromString<float>(sv)); },
},
{
"depth", "FLOAT",
"Allows to scale the soundfield backwards.\nValue range: [0.0..+5.0] -- 0 is all compressed to the front, 1 is no change, 5 is scaled 5x backwards (default: 1)",
[this](std::string_view sv)
{ dec.depth(depth = FromString<float>(sv)); },
},
{
"focus", "FLOAT",
"Allows to control the localization (i.e., focality) of sources.\nValue range: [-1.0..+1.0] -- 0 means unchanged, positive means more localized, negative means more ambient (default: 0)",
[this](std::string_view sv)
{ dec.focus(focus = FromString<float>(sv)); },
},
{
"front_separation", "FLOAT",
"Set the front stereo separation.\nValue range: [0.0..inf] -- 1.0 is default, 0.0 is mono.",
[this](std::string_view sv)
{ dec.front_separation(front_separation = FromString<float>(sv)); },
},
{
"rear_separation", "FLOAT",
"Set the rear stereo separation.\nValue range: [0.0..inf] -- 1.0 is default, 0.0 is mono.",
[this](std::string_view sv)
{ dec.rear_separation(rear_separation = FromString<float>(sv)); },
},
{
"enable_lfe", "BOOL",
"Enable/disable LFE channel (default: false = disabled)",
[this](std::string_view sv)
{ dec.bass_redirection(enable_lfe = FromString<bool>(sv)); },
},
{
"low_cutoff", "FLOAT",
"Set the lower end of the transition band, in Hz (default: 40).",
[this](std::string_view sv) { low_cutoff = FromString<float>(sv); },
},
{
"high_cutoff", "FLOAT",
"Set the upper end of the transition band, in Hz (default: 90).",
[this](std::string_view sv) { high_cutoff = FromString<float>(sv); },
},
};
}
std::vector<Channel::E> FreesurroundWrapper::GetChannels() const
{
auto n = dec.num_channels(channel_setup);
std::vector<Channel::E> res;
res.reserve(n);
for (unsigned i = 0; i < n; ++i)
switch (dec.channel_at(channel_setup, i))
{
case ci_front_left: res.push_back(Channel::FRONT_LEFT); break;
case ci_front_center_left: res.push_back(Channel::FRONT_LEFT_CENTER); break;
case ci_front_center: res.push_back(Channel::FRONT_CENTER); break;
case ci_front_center_right: res.push_back(Channel::FRONT_RIGHT_CENTER); break;
case ci_front_right: res.push_back(Channel::FRONT_RIGHT); break;
case ci_side_front_left: res.push_back(Channel::SIDE_FRONT_LEFT); break;
case ci_side_front_right: res.push_back(Channel::SIDE_FRONT_RIGHT); break;
case ci_side_center_left: res.push_back(Channel::SIDE_LEFT); break;
case ci_side_center_right: res.push_back(Channel::SIDE_RIGHT); break;
case ci_side_back_left: res.push_back(Channel::SIDE_BACK_LEFT); break;
case ci_side_back_right: res.push_back(Channel::SIDE_BACK_RIGHT); break;
case ci_back_left: res.push_back(Channel::REAR_LEFT); break;
case ci_back_center_left: res.push_back(Channel::REAR_LEFT_CENTER); break;
case ci_back_center: res.push_back(Channel::REAR_CENTER); break;
case ci_back_center_right: res.push_back(Channel::REAR_RIGHT_CENTER); break;
case ci_back_right: res.push_back(Channel::REAR_RIGHT); break;
case ci_lfe: res.push_back(Channel::LFE); break;
default: abort();
}
return res;
}