File: DynamicRangeProcessorHistory.cpp

package info (click to toggle)
audacity 3.7.7%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 134,800 kB
  • sloc: cpp: 366,277; ansic: 198,323; lisp: 7,761; sh: 3,414; python: 1,501; xml: 1,385; perl: 854; makefile: 125
file content (111 lines) | stat: -rw-r--r-- 3,738 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/*  SPDX-License-Identifier: GPL-2.0-or-later */
/*!********************************************************************

  Audacity: A Digital Audio Editor

  DynamicRangeProcessorHistory.cpp

  Matthieu Hodgkinson

**********************************************************************/
#include "DynamicRangeProcessorHistory.h"
#include "DynamicRangeProcessorTypes.h"
#include <algorithm>
#include <cassert>
#include <iterator>

DynamicRangeProcessorHistory::DynamicRangeProcessorHistory(double sampleRate)
    : mSampleRate { sampleRate }
{
}

void DynamicRangeProcessorHistory::Push(
   const std::vector<DynamicRangeProcessorOutputPacket>& packets)
{
   if (packets.empty())
      return;

   if (!mFirstPacketFirstSampleIndex.has_value())
      mFirstPacketFirstSampleIndex = packets.front().indexOfFirstSample;

   const int numNewPackets = packets.size();
   const auto lastPacketTime = !mSegments.empty() && !mSegments[0].empty() ?
                                  std::make_optional(mSegments[0].back().time) :
                                  std::nullopt;

   const auto firstPacketToInsertIt =
      std::find_if(packets.begin(), packets.end(), [&](const auto& packet) {
         return !lastPacketTime.has_value() ||
                GetPacketTime(packet) > *lastPacketTime;
      });

   if (firstPacketToInsertIt == packets.end())
      return;

   // Some packets can go lost in the transmission from audio to main thread.
   const auto isContinuous = mExpectedNextPacketFirstSampleIndex.has_value() &&
                             firstPacketToInsertIt->indexOfFirstSample ==
                                *mExpectedNextPacketFirstSampleIndex;
   if (mSegments.empty() || mBeginNewSegment || !isContinuous)
   {
      mSegments.emplace_back();
      mBeginNewSegment = false;
   }
   mExpectedNextPacketFirstSampleIndex =
      packets.back().indexOfFirstSample + packets.back().numSamples;

   auto& lastSegment = mSegments.back();

   std::transform(
      firstPacketToInsertIt, packets.end(), std::back_inserter(lastSegment),
      [&](const auto& packet) -> Packet {
         const auto t = GetPacketTime(packet);
         return { t, packet.targetCompressionDb, packet.actualCompressionDb,
                  packet.inputDb, packet.outputDb };
      });

   // Clean up older packets.
   // Algorithmically it's not completely correct to only do this for the oldest
   // segment, but in practice, `Push` is called much more often than
   // `BeginNewSegment`, so a simpler implementation is sufficient.
   const auto lastTime = lastSegment.back().time;
   auto& firstSegment = mSegments.front();
   const auto it = std::find_if(
      firstSegment.begin(), firstSegment.end(),
      [lastTime](const Packet& packet) {
         // Extend a little bit the time window, to avoid the extremities of a
         // display to tremble.
         return lastTime - packet.time < maxTimeSeconds + 1.f;
      });
   firstSegment.erase(firstSegment.begin(), it);

   if (firstSegment.empty())
      mSegments.erase(mSegments.begin());
}

void DynamicRangeProcessorHistory::BeginNewSegment()
{
   mBeginNewSegment = true;
}

const std::vector<DynamicRangeProcessorHistory::Segment>&
DynamicRangeProcessorHistory::GetSegments() const
{
   return mSegments;
}

bool DynamicRangeProcessorHistory::IsEmpty() const
{
   return std::all_of(
      mSegments.begin(), mSegments.end(),
      [](const auto& segment) { return segment.empty(); });
}

float DynamicRangeProcessorHistory::GetPacketTime(
   const DynamicRangeProcessorOutputPacket& packet) const
{
   assert(mFirstPacketFirstSampleIndex.has_value());
   return (packet.indexOfFirstSample -
           mFirstPacketFirstSampleIndex.value_or(0)) /
          mSampleRate;
}