freesurround_wrapper.cpp (8100B)
1 #include "freesurround_wrapper.hpp" 2 3 FreesurroundWrapper::FreesurroundWrapper() 4 : channel_setup{cs_5point1}, block_size{4096}, 5 circular_wrap(90), shift(0), depth(1), focus(0), 6 front_separation(1), rear_separation(1), 7 enable_lfe(false), low_cutoff(40), high_cutoff(90) 8 {} 9 10 void FreesurroundWrapper::Init(float rate) 11 { 12 if (rate < 0) return; 13 this->rate = rate; 14 dec = {cs_5point1, block_size}; 15 dec.circular_wrap(circular_wrap); 16 dec.shift(shift); 17 dec.depth(depth); 18 dec.focus(focus); 19 dec.front_separation(front_separation); 20 dec.rear_separation(rear_separation); 21 dec.bass_redirection(enable_lfe); 22 23 // Note: this will calculate cutoff * block_size * rate internally. With small 24 // block sizes this breaks down, for example with default hi_cut=90 but 25 // block_size=512 and 48KHz sample rate, 90*512/48000 = 0.96 which is < 1 so 26 // no LFE will be generated! If you can't have at least 2048 block size with 27 // 48kHz input, you probably shouldn't bother with LFE generation here. 28 auto mul = 2 * rate; 29 dec.low_cutoff(low_cutoff * mul); 30 dec.high_cutoff(high_cutoff * mul); 31 } 32 33 std::span<const float> FreesurroundWrapper::Decode(std::span<const float> in) 34 { 35 if (in.size() != 2*block_size) throw std::runtime_error("Bad size"); 36 auto n_out = dec.num_channels(channel_setup); 37 return {dec.decode(in.data()), n_out*block_size}; 38 } 39 40 std::vector<Option> FreesurroundWrapper::GetOptions() 41 { 42 return { 43 { 44 "channel_setup", "SETUP", 45 "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.", 46 [this](std::string_view sv) 47 { 48 /**/ if (sv == "legacy") channel_setup = cs_legacy; 49 else if (sv == "stereo") channel_setup = cs_stereo; 50 else if (sv == "3stereo") channel_setup = cs_3stereo; 51 else if (sv == "5stereo") channel_setup = cs_5stereo; 52 else if (sv == "4.1") channel_setup = cs_4point1; 53 else if (sv == "5.1") channel_setup = cs_5point1; 54 else if (sv == "6.1") channel_setup = cs_6point1; 55 else if (sv == "7.1") channel_setup = cs_7point1; 56 else if (sv == "7.1_panorama") channel_setup = cs_7point1_panorama; 57 else if (sv == "7.1_tricenter") channel_setup = cs_7point1_tricenter; 58 else if (sv == "8.1") channel_setup = cs_8point1; 59 else if (sv == "9.1_densepanorama") channel_setup = cs_9point1_densepanorama; 60 else if (sv == "9.1_wrap") channel_setup = cs_9point1_wrap; 61 else if (sv == "11.1_densewrap") channel_setup = cs_11point1_densewrap; 62 else if (sv == "13.1_totalwrap") channel_setup = cs_13point1_totalwrap; 63 else if (sv == "16.1") channel_setup = cs_16point1; 64 else throw std::runtime_error("Invalid channel setup " + std::string{sv}); 65 Init(rate); 66 } 67 }, 68 { 69 "block_size", "INT", 70 "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.", 71 [this](std::string_view sv) 72 { 73 auto bs = FromString<unsigned>(sv); 74 if (bs < 16 || bs & (bs-1)) throw std::runtime_error("Invalid block size"); 75 block_size = bs; 76 Init(rate); 77 } 78 }, 79 80 { 81 "circular_wrap", "FLOAT", 82 "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°])", 83 [this](std::string_view sv) 84 { dec.circular_wrap(circular_wrap = FromString<float>(sv)); }, 85 }, 86 { 87 "shift", "FLOAT", 88 "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)", 89 [this](std::string_view sv) 90 { dec.shift(shift = FromString<float>(sv)); }, 91 }, 92 { 93 "depth", "FLOAT", 94 "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)", 95 [this](std::string_view sv) 96 { dec.depth(depth = FromString<float>(sv)); }, 97 }, 98 { 99 "focus", "FLOAT", 100 "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)", 101 [this](std::string_view sv) 102 { dec.focus(focus = FromString<float>(sv)); }, 103 }, 104 105 { 106 "front_separation", "FLOAT", 107 "Set the front stereo separation.\nValue range: [0.0..inf] -- 1.0 is default, 0.0 is mono.", 108 [this](std::string_view sv) 109 { dec.front_separation(front_separation = FromString<float>(sv)); }, 110 }, 111 { 112 "rear_separation", "FLOAT", 113 "Set the rear stereo separation.\nValue range: [0.0..inf] -- 1.0 is default, 0.0 is mono.", 114 [this](std::string_view sv) 115 { dec.rear_separation(rear_separation = FromString<float>(sv)); }, 116 }, 117 118 { 119 "enable_lfe", "BOOL", 120 "Enable/disable LFE channel (default: false = disabled)", 121 [this](std::string_view sv) 122 { dec.bass_redirection(enable_lfe = FromString<bool>(sv)); }, 123 }, 124 { 125 "low_cutoff", "FLOAT", 126 "Set the lower end of the transition band, in Hz (default: 40).", 127 [this](std::string_view sv) 128 { 129 low_cutoff = FromString<float>(sv); 130 dec.low_cutoff(low_cutoff * 2 * rate); 131 }, 132 }, 133 { 134 "high_cutoff", "FLOAT", 135 "Set the upper end of the transition band, in Hz (default: 90).", 136 [this](std::string_view sv) 137 { 138 high_cutoff = FromString<float>(sv); 139 dec.high_cutoff(high_cutoff * 2 * rate); 140 }, 141 }, 142 }; 143 } 144 145 std::vector<Channel::E> FreesurroundWrapper::GetChannels() const 146 { 147 auto n = dec.num_channels(channel_setup); 148 std::vector<Channel::E> res; 149 res.reserve(n); 150 for (unsigned i = 0; i < n; ++i) 151 switch (dec.channel_at(channel_setup, i)) 152 { 153 case ci_front_left: res.push_back(Channel::FRONT_LEFT); break; 154 case ci_front_center_left: res.push_back(Channel::FRONT_LEFT_CENTER); break; 155 case ci_front_center: res.push_back(Channel::FRONT_CENTER); break; 156 case ci_front_center_right: res.push_back(Channel::FRONT_RIGHT_CENTER); break; 157 case ci_front_right: res.push_back(Channel::FRONT_RIGHT); break; 158 case ci_side_front_left: res.push_back(Channel::SIDE_FRONT_LEFT); break; 159 case ci_side_front_right: res.push_back(Channel::SIDE_FRONT_RIGHT); break; 160 case ci_side_center_left: res.push_back(Channel::SIDE_LEFT); break; 161 case ci_side_center_right: res.push_back(Channel::SIDE_RIGHT); break; 162 case ci_side_back_left: res.push_back(Channel::SIDE_BACK_LEFT); break; 163 case ci_side_back_right: res.push_back(Channel::SIDE_BACK_RIGHT); break; 164 case ci_back_left: res.push_back(Channel::REAR_LEFT); break; 165 case ci_back_center_left: res.push_back(Channel::REAR_LEFT_CENTER); break; 166 case ci_back_center: res.push_back(Channel::REAR_CENTER); break; 167 case ci_back_center_right: res.push_back(Channel::REAR_RIGHT_CENTER); break; 168 case ci_back_right: res.push_back(Channel::REAR_RIGHT); break; 169 case ci_lfe: res.push_back(Channel::LFE); break; 170 default: abort(); 171 } 172 return res; 173 }