| 225 | Mixer::~Mixer() = default; |
| 226 | |
| 227 | std::pair<bool, sampleFormat> |
| 228 | Mixer::NeedsDither(bool needsDither, double rate) const |
| 229 | { |
| 230 | // This will accumulate the widest effective format of any input |
| 231 | // clip |
| 232 | auto widestEffectiveFormat = narrowestSampleFormat; |
| 233 | |
| 234 | // needsDither may already be given as true. |
| 235 | // There are many other possible disqualifiers for the avoidance of dither. |
| 236 | if (std::any_of(mSources.begin(), mSources.end(), |
| 237 | std::mem_fn(&MixerSource::VariableRates)) |
| 238 | ) |
| 239 | // We will call MixVariableRates(), so we need nontrivial resampling |
| 240 | needsDither = true; |
| 241 | |
| 242 | for (const auto &input : mSources) { |
| 243 | auto &sequence = input.GetSequence(); |
| 244 | |
| 245 | if (sequence.GetRate() != rate) |
| 246 | // Also leads to MixVariableRates(), needs nontrivial resampling |
| 247 | needsDither = true; |
| 248 | else if (mApplyVolume == ApplyVolume::Mixdown && |
| 249 | !mHasMixerSpec && |
| 250 | sequence.NChannels() > 1 && mNumChannels == 1) |
| 251 | { |
| 252 | needsDither = true; |
| 253 | } |
| 254 | else if (mApplyVolume != ApplyVolume::Discard) { |
| 255 | /// TODO: more-than-two-channels |
| 256 | for (auto c : {0, 1}) { |
| 257 | const auto volume = sequence.GetChannelVolume(c); |
| 258 | if (!(volume == 0.0 || volume == 1.0)) |
| 259 | // Fractional volume may be applied even in MixSameRate |
| 260 | needsDither = true; |
| 261 | } |
| 262 | } |
| 263 | // Examine all tracks. (This ignores the time bounds for the mixer. |
| 264 | // If it did not, we might avoid dither in more cases. But if we fix |
| 265 | // that, remember that some mixers change their time bounds after |
| 266 | // construction, as when scrubbing.) |
| 267 | if (!sequence.HasTrivialEnvelope()) |
| 268 | // Varying or non-unit volume may be applied even in MixSameRate |
| 269 | needsDither = true; |
| 270 | auto effectiveFormat = sequence.WidestEffectiveFormat(); |
| 271 | if (effectiveFormat > mFormat) |
| 272 | // Real, not just nominal, precision loss would happen in at |
| 273 | // least one clip |
| 274 | needsDither = true; |
| 275 | widestEffectiveFormat = |
| 276 | std::max(widestEffectiveFormat, effectiveFormat); |
| 277 | } |
| 278 | |
| 279 | if (needsDither) |
| 280 | // Results will be dithered to width mFormat |
| 281 | return { true, mFormat }; |
| 282 | else { |
| 283 | // Results will not be dithered |
| 284 | assert(widestEffectiveFormat <= mFormat); |
no test coverage detected