Audacity 3.2.0
DynamicRangeProcessorHistory.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 DynamicRangeProcessorHistory.cpp
7
8 Matthieu Hodgkinson
9
10**********************************************************************/
13#include <algorithm>
14#include <cassert>
15#include <iterator>
16
18 : mSampleRate { sampleRate }
19{
20}
21
23 const std::vector<DynamicRangeProcessorOutputPacket>& packets)
24{
25 if (packets.empty())
26 return;
27
28 if (!mFirstPacketFirstSampleIndex.has_value())
29 mFirstPacketFirstSampleIndex = packets.front().indexOfFirstSample;
30
31 const int numNewPackets = packets.size();
32 const auto lastPacketTime = !mSegments.empty() && !mSegments[0].empty() ?
33 std::make_optional(mSegments[0].back().time) :
34 std::nullopt;
35
36 const auto firstPacketToInsertIt =
37 std::find_if(packets.begin(), packets.end(), [&](const auto& packet) {
38 return !lastPacketTime.has_value() ||
39 GetPacketTime(packet) > *lastPacketTime;
40 });
41
42 if (firstPacketToInsertIt == packets.end())
43 return;
44
45 // Some packets can go lost in the transmission from audio to main thread.
46 const auto isContinuous = mExpectedNextPacketFirstSampleIndex.has_value() &&
47 firstPacketToInsertIt->indexOfFirstSample ==
49 if (mSegments.empty() || mBeginNewSegment || !isContinuous)
50 {
51 mSegments.emplace_back();
52 mBeginNewSegment = false;
53 }
55 packets.back().indexOfFirstSample + packets.back().numSamples;
56
57 auto& lastSegment = mSegments.back();
58
59 std::transform(
60 firstPacketToInsertIt, packets.end(), std::back_inserter(lastSegment),
61 [&](const auto& packet) -> Packet {
62 const auto t = GetPacketTime(packet);
63 return { t, packet.targetCompressionDb, packet.actualCompressionDb,
64 packet.inputDb, packet.outputDb };
65 });
66
67 // Clean up older packets.
68 // Algorithmically it's not completely correct to only do this for the oldest
69 // segment, but in practice, `Push` is called much more often than
70 // `BeginNewSegment`, so a simpler implementation is sufficient.
71 const auto lastTime = lastSegment.back().time;
72 auto& firstSegment = mSegments.front();
73 const auto it = std::find_if(
74 firstSegment.begin(), firstSegment.end(),
75 [lastTime](const Packet& packet) {
76 // Extend a little bit the time window, to avoid the extremities of a
77 // display to tremble.
78 return lastTime - packet.time < maxTimeSeconds + 1.f;
79 });
80 firstSegment.erase(firstSegment.begin(), it);
81
82 if (firstSegment.empty())
83 mSegments.erase(mSegments.begin());
84}
85
87{
88 mBeginNewSegment = true;
89}
90
91const std::vector<DynamicRangeProcessorHistory::Segment>&
93{
94 return mSegments;
95}
96
98{
99 return std::all_of(
100 mSegments.begin(), mSegments.end(),
101 [](const auto& segment) { return segment.empty(); });
102}
103
105 const DynamicRangeProcessorOutputPacket& packet) const
106{
107 assert(mFirstPacketFirstSampleIndex.has_value());
108 return (packet.indexOfFirstSample -
109 mFirstPacketFirstSampleIndex.value_or(0)) /
111}
void Push(const std::vector< DynamicRangeProcessorOutputPacket > &packets)
const std::vector< Segment > & GetSegments() const
std::optional< long long > mFirstPacketFirstSampleIndex
float GetPacketTime(const DynamicRangeProcessorOutputPacket &packet) const
std::optional< long long > mExpectedNextPacketFirstSampleIndex