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 tempTrack = wt->EmptyCopy();
82 auto iter = tempTrack->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 (tempTrack) {
107 TrackSpectrumTransformer::PostProcess(*tempTrack, processed);
108 // Take the output track and insert it in place of the original
109 // sample data
110 // TODO make this correct in case start or end of spectral data in
111 // the channels differs
112 wt->ClearAndPaste(t0, t0 + tLen, *tempTrack, true, false);
113 }
114 }
115
116 if (applyCount) {
118 XO("Applied effect to selection"),
119 XO("Applied effect to selection"));
121 }
122
123 return applyCount > 0;
124}
125
127 long long int startSC, int hopSize, double threshold, int targetFreqBin)
128{
129 Setting setting;
130 setting.mNeedOutput = false;
131 Worker worker{ nullptr, setting };
132
133 return worker.ProcessSnapping(
134 channel, startSC, hopSize, setting.mWindowSize, threshold, targetFreqBin);
135}
136
138 long long int startSC,
139 int hopSize,
140 double threshold,
141 int targetFreqBin)
142 {
143 Setting setting;
144 setting.mNeedOutput = false;
145 Worker worker{ nullptr, setting };
146
147 return worker.ProcessOvertones(wc, startSC, hopSize, setting.mWindowSize, threshold, targetFreqBin);
148 }
149
151 WaveChannel *pChannel, const Setting &setting
152) : TrackSpectrumTransformer{ pChannel,
153 setting.mNeedOutput, setting.mInWindowType, setting.mOutWindowType,
154 setting.mWindowSize, setting.mStepsPerWindow,
155 setting.mLeadingPadding, setting.mTrailingPadding
156 }
157// Work members
158{
159}
160
162
165}
168}
169
171 const std::shared_ptr<SpectralData> &pSpectralData)
172{
173 mpSpectralData = pSpectralData;
174 const auto hopSize = mpSpectralData->GetHopSize();
175 const auto startSample = mpSpectralData->GetStartSample();
176 // Correct the first hop num, because SpectrumTransformer will send
177 // a few initial windows that overlay the range only partially
178 mStartHopNum = startSample / hopSize - (mStepsPerWindow - 1);
179 mWindowCount = 0;
180 return TrackSpectrumTransformer::Process(Processor, channel, 1,
181 mpSpectralData->GetCorrectedStartSample(), mpSpectralData->GetLength());
182}
183
185 long long startSC, int hopSize, size_t winSize, double threshold,
186 int targetFreqBin)
187{
188 mSnapThreshold = threshold;
189 mSnapTargetFreqBin = targetFreqBin;
190 mSnapSamplingRate = channel.GetTrack().GetRate();
191
192 startSC = std::max(static_cast<long long>(0), startSC - 2 * hopSize);
193 // The calculated frequency peak will be stored in mReturnFreq
194 if (!TrackSpectrumTransformer::Process(SnappingProcessor, channel,
195 1, startSC, winSize))
196 return 0;
197
198 return mSnapReturnFreqBin;
199}
200
202 const WaveChannel &channel, long long startSC, int hopSize, size_t winSize,
203 double threshold, int targetFreqBin)
204{
205 mOvertonesThreshold = threshold;
206 mSnapTargetFreqBin = targetFreqBin;
207 mSnapSamplingRate = channel.GetTrack().GetRate();
208
209 startSC = std::max(static_cast<long long>(0), startSC - 2 * hopSize);
210 // The calculated multiple frequency peaks will be stored in mOvertonesTargetFreqBin
212 OvertonesProcessor, channel, 1, startSC, winSize);
213 return move( mOvertonesTargetFreqBin );
214 }
215
217 auto &worker = static_cast<Worker &>(transformer);
218 // Compute power spectrum in the newest window
219 {
220 MyWindow &record = worker.NthWindow(0);
221 float *pSpectrum = &record.mSpectrums[0];
222 const double dc = record.mRealFFTs[0];
223 *pSpectrum++ = dc * dc;
224 float *pReal = &record.mRealFFTs[1], *pImag = &record.mImagFFTs[1];
225 for (size_t nn = worker.mSpectrumSize - 2; nn--;) {
226 const double re = *pReal++, im = *pImag++;
227 *pSpectrum++ = re * re + im * im;
228 }
229 const double nyquist = record.mImagFFTs[0];
230 *pSpectrum = nyquist * nyquist;
231
232 const double &sr = worker.mSnapSamplingRate;
233 const double nyquistRate = sr / 2;
234 const double &threshold = worker.mSnapThreshold;
235 const double &spectrumSize = worker.mSpectrumSize;
236 const int &targetBin = worker.mSnapTargetFreqBin;
237
238 int binBound = spectrumSize * threshold;
239 float maxValue = std::numeric_limits<float>::min();
240
241 // Skip the first and last bin
242 for(int i = -binBound; i < binBound; i++){
243 int idx = std::clamp(targetBin + i, 0, static_cast<int>(spectrumSize - 1));
244 if(record.mSpectrums[idx] > maxValue){
245 maxValue = record.mSpectrums[idx];
246 // Update the return frequency
247 worker.mSnapReturnFreqBin = idx;
248 }
249 }
250 }
251
252 return true;
253}
254
256 auto &worker = static_cast<Worker &>(transformer);
257 // Compute power spectrum in the newest window
258 {
259 MyWindow &record = worker.NthWindow(0);
260 float *pSpectrum = &record.mSpectrums[0];
261 const double dc = record.mRealFFTs[0];
262 *pSpectrum++ = dc * dc;
263 float *pReal = &record.mRealFFTs[1], *pImag = &record.mImagFFTs[1];
264 for (size_t nn = worker.mSpectrumSize - 2; nn--;) {
265 const double re = *pReal++, im = *pImag++;
266 *pSpectrum++ = re * re + im * im;
267 }
268 const double nyquist = record.mImagFFTs[0];
269 *pSpectrum = nyquist * nyquist;
270
271 const double &spectrumSize = worker.mSpectrumSize;
272 const int &targetBin = worker.mSnapTargetFreqBin;
273
274 float targetValue = record.mSpectrums[targetBin];
275
276 double fundamental = targetBin;
277 int overtone = 2, binNum = 0;
278 pSpectrum = &record.mSpectrums[0];
279 while ( fundamental >= 1 &&
280 ( binNum = lrint( fundamental * overtone ) ) < spectrumSize) {
281 // Examine a few bins each way up and down
282 constexpr int tolerance = 3;
283 auto begin = pSpectrum + std::max( 0, binNum - (tolerance + 1) );
284 auto end = pSpectrum +
285 std::min<size_t>( spectrumSize, binNum + (tolerance + 1) + 1 );
286 auto peak = std::max_element( begin, end );
287
288 // Abandon if the peak is too far up or down
289 if ( peak == begin || peak == end - 1 )
290 break;
291
292 int newBin = peak - pSpectrum;
293 worker.mOvertonesTargetFreqBin.push_back(newBin);
294 // Correct the estimate of the fundamental
295 fundamental = double(newBin) / overtone++;
296 }
297 }
298 return true;
299}
300
302{
303 auto &worker = static_cast<Worker &>(transformer);
304 // Compute power spectrum in the newest window
305 {
306 MyWindow &record = worker.NthWindow(0);
307 float *pSpectrum = &record.mSpectrums[0];
308 const double dc = record.mRealFFTs[0];
309 *pSpectrum++ = dc * dc;
310 float *pReal = &record.mRealFFTs[1], *pImag = &record.mImagFFTs[1];
311 for (size_t nn = worker.mSpectrumSize - 2; nn--;) {
312 const double re = *pReal++, im = *pImag++;
313 *pSpectrum++ = re * re + im * im;
314 }
315 const double nyquist = record.mImagFFTs[0];
316 *pSpectrum = nyquist * nyquist;
317 }
318
319 worker.ApplyEffectToSelection();
320 return true;
321}
322
324 auto &record = NthWindow(0);
325
326 for(auto &spectralDataMap: mpSpectralData->dataHistory){
327 // For all added frequency
328 for(const int &freqBin: spectralDataMap[mStartHopNum]){
329 record.mRealFFTs[freqBin] = 0;
330 record.mImagFFTs[freqBin] = 0;
331 }
332 }
333
334 mWindowCount++;
335 mStartHopNum ++;
336 return true;
337}
338
340-> std::unique_ptr<Window>
341{
342 return std::make_unique<MyWindow>(windowSize);
343}
344
346
347}
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(WaveChannel &wc, 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:314
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:841
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
double GetRate() const override
Definition: WaveTrack.cpp:821
#define lrint(dbl)
Definition: float_cast.h:169
const std::shared_ptr< SpectralData > FindSpectralData(Channel *pChannel)
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
const char * begin(const char *str) noexcept
Definition: StringUtils.h:101