Audacity 3.2.0
SpectralDataManager.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 SpectralDataManager.cpp
6
7 Edward Hui
8
9*******************************************************************//*******************************************************************/
15
16#include <iostream>
17#include "FFT.h"
18#include "ProjectHistory.h"
19#include "SpectralDataManager.h"
20#include "WaveTrack.h"
21
23
25
29 size_t mWindowSize = 2048;
30 unsigned mStepsPerWindow = 4;
31 bool mLeadingPadding = true;
32 bool mTrailingPadding = true;
33 bool mNeedOutput = true;
34};
35
36namespace {
37const std::shared_ptr<SpectralData> FindSpectralData(Channel *pChannel)
38{
39 auto &view = ChannelView::Get(*pChannel);
40 if (auto waveChannelViewPtr = dynamic_cast<WaveChannelView*>(&view)){
41 for (const auto &subViewPtr : waveChannelViewPtr->GetAllSubViews()){
42 if (subViewPtr->IsSpectral()) {
43 auto sView =
44 std::static_pointer_cast<SpectrumView>(subViewPtr).get();
45 const auto pData = sView->GetSpectralData();
46 if (!pData->dataHistory.empty()) {
47 return pData;
48 }
49 }
50 }
51 }
52 return {};
53}
54}
55
58 int applyCount = 0;
59 Setting setting;
60 for (auto wt : tracks.Any<WaveTrack>()) {
61 using Type = long long;
62 Type startSample{ std::numeric_limits<Type>::max() };
63 Type endSample{ std::numeric_limits<Type>::min() };
64 for (auto pChannel : wt->Channels()) {
65 if (const auto pData = FindSpectralData(pChannel.get())) {
66 const auto &hopSize = pData->GetHopSize();
67 auto start = pData->GetStartSample();
68 endSample = std::max(endSample, pData->GetEndSample());
69
70 // Correct the start of range so that the first full window is
71 // centered at that position
72 start = std::max(static_cast<long long>(0), start - 2 * hopSize);
73 startSample = std::min(startSample, start);
74 }
75 }
76 if (startSample >= endSample)
77 continue;
78 const auto t0 = wt->LongSamplesToTime(startSample);
79 const auto len = endSample - startSample;
80 const auto tLen = wt->LongSamplesToTime(len);
81 auto tempList = wt->WideEmptyCopy();
82 auto iter = (*tempList->Any<WaveTrack>().begin())->Channels().begin();
83 long long processed{};
84 for (auto pChannel : wt->Channels()) {
85 Worker worker{ (*iter++).get(), setting };
86 auto &view = ChannelView::Get(*pChannel);
87
88 if (auto waveChannelViewPtr = dynamic_cast<WaveChannelView*>(&view)){
89 for (const auto &subViewPtr : waveChannelViewPtr->GetAllSubViews()){
90 if (!subViewPtr->IsSpectral())
91 continue;
92 auto sView = std::static_pointer_cast<SpectrumView>(subViewPtr).get();
93 auto pSpectralData = sView->GetSpectralData();
94
95 if (!pSpectralData->dataHistory.empty()) {
96 // TODO make this correct in case start or end of spectral data in
97 // the channels differs
98 processed = std::max(processed, pSpectralData->GetLength());
99 worker.Process(*pChannel, pSpectralData);
100 applyCount += static_cast<int>(pSpectralData->dataHistory.size());
101 pSpectralData->clearAllData();
102 }
103 }
104 }
105 }
106 if (!tempList->empty()) {
107 const auto pTrack = *tempList->Any<WaveTrack>().begin();
108 TrackSpectrumTransformer::PostProcess(*pTrack, processed);
109 // Take the output track and insert it in place of the original
110 // sample data
111 // TODO make this correct in case start or end of spectral data in
112 // the channels differs
113 wt->ClearAndPaste(t0, t0 + tLen, *tempList, true, false);
114 }
115 }
116
117 if (applyCount) {
119 XO("Applied effect to selection"),
120 XO("Applied effect to selection"));
122 }
123
124 return applyCount > 0;
125}
126
128 long long int startSC, int hopSize, double threshold, int targetFreqBin)
129{
130 Setting setting;
131 setting.mNeedOutput = false;
132 Worker worker{ nullptr, setting };
133
134 return worker.ProcessSnapping(
135 channel, startSC, hopSize, setting.mWindowSize, threshold, targetFreqBin);
136}
137
139 long long int startSC,
140 int hopSize,
141 double threshold,
142 int targetFreqBin)
143 {
144 Setting setting;
145 setting.mNeedOutput = false;
146 Worker worker{ nullptr, setting };
147
148 return worker.ProcessOvertones(*wt, startSC, hopSize, setting.mWindowSize, threshold, targetFreqBin);
149 }
150
152 WaveChannel *pChannel, const Setting &setting
153) : TrackSpectrumTransformer{ pChannel,
154 setting.mNeedOutput, setting.mInWindowType, setting.mOutWindowType,
155 setting.mWindowSize, setting.mStepsPerWindow,
156 setting.mLeadingPadding, setting.mTrailingPadding
157 }
158// Work members
159{
160}
161
163
166}
169}
170
172 const std::shared_ptr<SpectralData> &pSpectralData)
173{
174 mpSpectralData = pSpectralData;
175 const auto hopSize = mpSpectralData->GetHopSize();
176 const auto startSample = mpSpectralData->GetStartSample();
177 // Correct the first hop num, because SpectrumTransformer will send
178 // a few initial windows that overlay the range only partially
179 mStartHopNum = startSample / hopSize - (mStepsPerWindow - 1);
180 mWindowCount = 0;
181 return TrackSpectrumTransformer::Process(Processor, channel, 1,
182 mpSpectralData->GetCorrectedStartSample(), mpSpectralData->GetLength());
183}
184
186 long long startSC, int hopSize, size_t winSize, double threshold,
187 int targetFreqBin)
188{
189 mSnapThreshold = threshold;
190 mSnapTargetFreqBin = targetFreqBin;
191 mSnapSamplingRate = channel.GetTrack().GetRate();
192
193 startSC = std::max(static_cast<long long>(0), startSC - 2 * hopSize);
194 // The calculated frequency peak will be stored in mReturnFreq
195 if (!TrackSpectrumTransformer::Process(SnappingProcessor, channel,
196 1, startSC, winSize))
197 return 0;
198
199 return mSnapReturnFreqBin;
200}
201
203 const WaveChannel &channel, long long startSC, int hopSize, size_t winSize,
204 double threshold, int targetFreqBin)
205{
206 mOvertonesThreshold = threshold;
207 mSnapTargetFreqBin = targetFreqBin;
208 mSnapSamplingRate = channel.GetTrack().GetRate();
209
210 startSC = std::max(static_cast<long long>(0), startSC - 2 * hopSize);
211 // The calculated multiple frequency peaks will be stored in mOvertonesTargetFreqBin
213 OvertonesProcessor, channel, 1, startSC, winSize);
214 return move( mOvertonesTargetFreqBin );
215 }
216
218 auto &worker = static_cast<Worker &>(transformer);
219 // Compute power spectrum in the newest window
220 {
221 MyWindow &record = worker.NthWindow(0);
222 float *pSpectrum = &record.mSpectrums[0];
223 const double dc = record.mRealFFTs[0];
224 *pSpectrum++ = dc * dc;
225 float *pReal = &record.mRealFFTs[1], *pImag = &record.mImagFFTs[1];
226 for (size_t nn = worker.mSpectrumSize - 2; nn--;) {
227 const double re = *pReal++, im = *pImag++;
228 *pSpectrum++ = re * re + im * im;
229 }
230 const double nyquist = record.mImagFFTs[0];
231 *pSpectrum = nyquist * nyquist;
232
233 const double &sr = worker.mSnapSamplingRate;
234 const double nyquistRate = sr / 2;
235 const double &threshold = worker.mSnapThreshold;
236 const double &spectrumSize = worker.mSpectrumSize;
237 const int &targetBin = worker.mSnapTargetFreqBin;
238
239 int binBound = spectrumSize * threshold;
240 float maxValue = std::numeric_limits<float>::min();
241
242 // Skip the first and last bin
243 for(int i = -binBound; i < binBound; i++){
244 int idx = std::clamp(targetBin + i, 0, static_cast<int>(spectrumSize - 1));
245 if(record.mSpectrums[idx] > maxValue){
246 maxValue = record.mSpectrums[idx];
247 // Update the return frequency
248 worker.mSnapReturnFreqBin = idx;
249 }
250 }
251 }
252
253 return true;
254}
255
257 auto &worker = static_cast<Worker &>(transformer);
258 // Compute power spectrum in the newest window
259 {
260 MyWindow &record = worker.NthWindow(0);
261 float *pSpectrum = &record.mSpectrums[0];
262 const double dc = record.mRealFFTs[0];
263 *pSpectrum++ = dc * dc;
264 float *pReal = &record.mRealFFTs[1], *pImag = &record.mImagFFTs[1];
265 for (size_t nn = worker.mSpectrumSize - 2; nn--;) {
266 const double re = *pReal++, im = *pImag++;
267 *pSpectrum++ = re * re + im * im;
268 }
269 const double nyquist = record.mImagFFTs[0];
270 *pSpectrum = nyquist * nyquist;
271
272 const double &spectrumSize = worker.mSpectrumSize;
273 const int &targetBin = worker.mSnapTargetFreqBin;
274
275 float targetValue = record.mSpectrums[targetBin];
276
277 double fundamental = targetBin;
278 int overtone = 2, binNum = 0;
279 pSpectrum = &record.mSpectrums[0];
280 while ( fundamental >= 1 &&
281 ( binNum = lrint( fundamental * overtone ) ) < spectrumSize) {
282 // Examine a few bins each way up and down
283 constexpr int tolerance = 3;
284 auto begin = pSpectrum + std::max( 0, binNum - (tolerance + 1) );
285 auto end = pSpectrum +
286 std::min<size_t>( spectrumSize, binNum + (tolerance + 1) + 1 );
287 auto peak = std::max_element( begin, end );
288
289 // Abandon if the peak is too far up or down
290 if ( peak == begin || peak == end - 1 )
291 break;
292
293 int newBin = peak - pSpectrum;
294 worker.mOvertonesTargetFreqBin.push_back(newBin);
295 // Correct the estimate of the fundamental
296 fundamental = double(newBin) / overtone++;
297 }
298 }
299 return true;
300}
301
303{
304 auto &worker = static_cast<Worker &>(transformer);
305 // Compute power spectrum in the newest window
306 {
307 MyWindow &record = worker.NthWindow(0);
308 float *pSpectrum = &record.mSpectrums[0];
309 const double dc = record.mRealFFTs[0];
310 *pSpectrum++ = dc * dc;
311 float *pReal = &record.mRealFFTs[1], *pImag = &record.mImagFFTs[1];
312 for (size_t nn = worker.mSpectrumSize - 2; nn--;) {
313 const double re = *pReal++, im = *pImag++;
314 *pSpectrum++ = re * re + im * im;
315 }
316 const double nyquist = record.mImagFFTs[0];
317 *pSpectrum = nyquist * nyquist;
318 }
319
320 worker.ApplyEffectToSelection();
321 return true;
322}
323
325 auto &record = NthWindow(0);
326
327 for(auto &spectralDataMap: mpSpectralData->dataHistory){
328 // For all added frequency
329 for(const int &freqBin: spectralDataMap[mStartHopNum]){
330 record.mRealFFTs[freqBin] = 0;
331 record.mImagFFTs[freqBin] = 0;
332 }
333 }
334
335 mWindowCount++;
336 mStartHopNum ++;
337 return true;
338}
339
341-> std::unique_ptr<Window>
342{
343 return std::make_unique<MyWindow>(windowSize);
344}
345
347
348}
int min(int a, int b)
eWindowFunctions
Definition: FFT.h:110
@ eWinFuncHann
Definition: FFT.h:114
XO("Cut/Copy/Paste")
const auto tracks
const auto project
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static ChannelView & Get(Channel &channel)
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
Worker(WaveChannel *pChannel, const Setting &setting)
std::vector< int > ProcessOvertones(const WaveChannel &channel, long long int startSC, int hopSize, size_t winSize, double threshold, int targetFreqBin)
bool DoFinish() override
Called after the last call to ProcessWindow().
bool DoStart() override
Called before any calls to ProcessWindow.
std::unique_ptr< Window > NewWindow(size_t windowSize) override
Allocates a window to place in the queue.
int ProcessSnapping(const WaveChannel &channel, long long int startSC, int hopSize, size_t winSize, double threshold, int targetFreqBin)
bool Process(const WaveChannel &channel, const std::shared_ptr< SpectralData > &sDataPtr)
static bool OvertonesProcessor(SpectrumTransformer &transformer)
static bool Processor(SpectrumTransformer &transformer)
static bool SnappingProcessor(SpectrumTransformer &transformer)
static std::vector< int > FindHighestFrequencyBins(WaveTrack *wt, long long int startSC, int hopSize, double threshold, int targetFreqBin)
static bool ProcessTracks(AudacityProject &project)
static int FindFrequencySnappingBin(const WaveChannel &channel, long long startSC, int hopSize, double threshold, int targetFreqBin)
A class that transforms a portion of a wave track (preserving duration) by applying Fourier transform...
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:347
Subclass of SpectrumTransformer that rewrites a track.
bool DoFinish() override
Called after the last call to ProcessWindow().
static bool PostProcess(WaveTrack &outputTrack, sampleCount len)
Final flush and trimming of tail samples.
bool DoStart() override
Called before any calls to ProcessWindow.
bool Process(const WindowProcessor &processor, const WaveChannel &channel, size_t queueLength, sampleCount start, sampleCount len)
Invokes Start(), ProcessSamples(), and Finish()
WaveTrack & GetTrack()
Definition: WaveTrack.h:1205
A Track that contains audio waveform data.
Definition: WaveTrack.h:222
double GetRate() const override
Definition: WaveTrack.cpp:1102
#define lrint(dbl)
Definition: float_cast.h:169
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
const std::shared_ptr< SpectralData > FindSpectralData(Channel *pChannel)