Audacity  2.2.2
Equalization48x.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  EffectEqualization.cpp
6 
7  Andrew Hallendorff
8 
9 *******************************************************************//****************************************************************/
15 
16 #include "../Audacity.h"
17 #include "../Experimental.h"
18 #ifdef EXPERIMENTAL_EQ_SSE_THREADED
19 #include "../MemoryX.h"
20 #include "../Project.h"
21 #include "Equalization.h"
22 #include "../WaveTrack.h"
23 #include "../float_cast.h"
24 #include <vector>
25 
26 #include <wx/dcmemory.h>
27 #include <wx/event.h>
28 #include <wx/string.h>
29 
30 #if wxUSE_TOOLTIPS
31 #include <wx/tooltip.h>
32 #endif
33 #include <wx/utils.h>
34 
35 #include <math.h>
36 
37 #include "Equalization48x.h"
38 #include "../RealFFTf.h"
39 #include "../RealFFTf48x.h"
40 
41 #ifndef USE_SSE2
42 #define USE_SSE2
43 #endif
44 
45 #include <stdlib.h>
46 
47 #ifdef __WXMSW__
48 #include <malloc.h>
49 #endif
50 
51 #include <stdio.h>
52 #include <math.h>
53 #include <emmintrin.h>
54 
55 #ifdef _WIN32
56 
57 // Windows
58 #include <intrin.h>
59 #define cpuid __cpuid
60 
61 #else
62 
63 // GCC Inline Assembly
64 void cpuid(int CPUInfo[4],int InfoType){
65  __asm__ __volatile__ (
66  "cpuid":
67  "=a" (CPUInfo[0]),
68  "=b" (CPUInfo[1]),
69  "=c" (CPUInfo[2]),
70  "=d" (CPUInfo[3]) :
71  "a" (InfoType)
72  );
73 }
74 
75 #endif
76 
77 bool sMathCapsInitialized = false;
78 
79 MathCaps sMathCaps;
80 
81 // dirty switcher
82 int sMathPath=MATH_FUNCTION_SSE|MATH_FUNCTION_THREADED;
83 
84 void EffectEqualization48x::SetMathPath(int mathPath) { sMathPath=mathPath; };
85 
86 int EffectEqualization48x::GetMathPath() { return sMathPath; };
87 
88 void EffectEqualization48x::AddMathPathOption(int mathPath) { sMathPath|=mathPath; };
89 
90 void EffectEqualization48x::RemoveMathPathOption(int mathPath) { sMathPath&=~mathPath; };
91 
92 MathCaps *EffectEqualization48x::GetMathCaps()
93 {
94  if(!sMathCapsInitialized)
95  {
96  sMathCapsInitialized=true;
97  sMathCaps.x64 = false;
98  sMathCaps.MMX = false;
99  sMathCaps.SSE = false;
100  sMathCaps.SSE2 = false;
101  sMathCaps.SSE3 = false;
102  sMathCaps.SSSE3 = false;
103  sMathCaps.SSE41 = false;
104  sMathCaps.SSE42 = false;
105  sMathCaps.SSE4a = false;
106  sMathCaps.AVX = false;
107  sMathCaps.XOP = false;
108  sMathCaps.FMA3 = false;
109  sMathCaps.FMA4 = false;
110 
111  int info[4];
112  cpuid(info, 0);
113  int nIds = info[0];
114 
115  cpuid(info, 0x80000000);
116  int nExIds = info[0];
117 
118  // Detect Instruction Set
119  if (nIds >= 1){
120  cpuid(info,0x00000001);
121  sMathCaps.MMX = (info[3] & ((int)1 << 23)) != 0;
122  sMathCaps.SSE = (info[3] & ((int)1 << 25)) != 0;
123  sMathCaps.SSE2 = (info[3] & ((int)1 << 26)) != 0;
124  sMathCaps.SSE3 = (info[2] & ((int)1 << 0)) != 0;
125 
126  sMathCaps.SSSE3 = (info[2] & ((int)1 << 9)) != 0;
127  sMathCaps.SSE41 = (info[2] & ((int)1 << 19)) != 0;
128  sMathCaps.SSE42 = (info[2] & ((int)1 << 20)) != 0;
129 
130  sMathCaps.AVX = (info[2] & ((int)1 << 28)) != 0;
131  sMathCaps.FMA3 = (info[2] & ((int)1 << 12)) != 0;
132  }
133 
134  if (nExIds >= 0x80000001){
135  cpuid(info,0x80000001);
136  sMathCaps.x64 = (info[3] & ((int)1 << 29)) != 0;
137  sMathCaps.SSE4a = (info[2] & ((int)1 << 6)) != 0;
138  sMathCaps.FMA4 = (info[2] & ((int)1 << 16)) != 0;
139  sMathCaps.XOP = (info[2] & ((int)1 << 11)) != 0;
140  }
141  if(sMathCaps.SSE)
142  sMathPath=MATH_FUNCTION_SSE|MATH_FUNCTION_THREADED; // we are starting on.
143  }
144  return &sMathCaps;
145 };
146 
147 void * malloc_simd(const size_t size)
148 {
149 #if defined WIN32 // WIN32
150  return _aligned_malloc(size, 16);
151 #elif defined __linux__ // Linux
152  return memalign (16, size);
153 #elif defined __MACH__ // Mac OS X
154  return malloc(size);
155 #else // other (use valloc for page-aligned memory)
156  return valloc(size);
157 #endif
158 }
159 
160 void free_simd::operator() (void* mem) const
161 {
162 #if defined WIN32 // WIN32
163  _aligned_free(mem);
164 #else
165  free(mem);
166 #endif
167 }
168 
169 EffectEqualization48x::EffectEqualization48x():
170  mThreadCount(0),mFilterSize(0),mWindowSize(0),mBlockSize(0),mWorkerDataCount(0),mBlocksPerBuffer(20),
171  mScratchBufferSize(0),mSubBufferSize(0),mThreaded(false),
172  mBenching(false),mBufferCount(0)
173 {
174 }
175 
176 EffectEqualization48x::~EffectEqualization48x()
177 {
178 }
179 
180 bool EffectEqualization48x::AllocateBuffersWorkers(int nThreads)
181 {
182  if(mBigBuffer)
183  FreeBuffersWorkers();
184  mFilterSize=(mEffectEqualization->mM-1)&(~15); // 4000 !!! Filter MUST BE QUAD WORD ALIGNED !!!!
185  mWindowSize=mEffectEqualization->windowSize;
186  wxASSERT(mFilterSize < mWindowSize);
187  mBlockSize=mWindowSize-mFilterSize; // 12,384
188  auto threadCount = wxThread::GetCPUCount();
189  mThreaded = (nThreads > 0 && threadCount > 0);
190  if(mThreaded)
191  {
192  mThreadCount = threadCount;
193  mWorkerDataCount=mThreadCount+2; // 2 extra slots (maybe double later)
194  } else {
195  mWorkerDataCount=1;
196  mThreadCount=0;
197  }
198 #ifdef __AVX_ENABLED
199  mBufferCount=sMathPath&MATH_FUNCTION_AVX?8:4;
200 #else
201  mBufferCount=4;
202 #endif
203  // we're skewing the data by one block to allow for 1/4 block intersections.
204  // this will remove the disparity in data at the intersections of the runs
205 
206  // The nice magic allocation
207  // megabyte - 3 windows - 4 overlaping buffers - filter
208  // 2^20 = 1,048,576 - 3 * 2^14 (16,384) - ((4 * 20) - 3) * 12,384 - 4000
209  // 1,048,576 - 49,152 - 953,568 - 4000 = 41,856 (leftover)
210 
211  mScratchBufferSize=mWindowSize*3*sizeof(float)*mBufferCount; // 3 window size blocks of instruction size
212  mSubBufferSize=mBlockSize*(mBufferCount*(mBlocksPerBuffer-1)); // we are going to do a full block overlap
213  mBigBuffer.reset( (float *)malloc_simd(sizeof(float) * (mSubBufferSize + mFilterSize + mScratchBufferSize) * mWorkerDataCount) ); // we run over by filtersize
214  // fill the bufferInfo
215  mBufferInfo.reinit(mWorkerDataCount);
216  for(int i=0;i<mWorkerDataCount;i++) {
217  mBufferInfo[i].mFftWindowSize=mWindowSize;
218  mBufferInfo[i].mFftFilterSize=mFilterSize;
219  mBufferInfo[i].mBufferLength=mBlockSize*mBlocksPerBuffer;
220  mBufferInfo[i].mContiguousBufferSize=mSubBufferSize;
221  mBufferInfo[i].mScratchBuffer=&mBigBuffer[(mSubBufferSize+mScratchBufferSize)*i+mSubBufferSize];
222  for(int j=0;j<mBufferCount;j++)
223  mBufferInfo[i].mBufferDest[j]=mBufferInfo[i].mBufferSouce[j]=&mBigBuffer[j*(mBufferInfo[i].mBufferLength-mBlockSize)+(mSubBufferSize+mScratchBufferSize)*i];
224  }
225  if(mThreadCount) {
226  // start the workers
227  mDataMutex.IsOk();
228  mEQWorkers.reinit(mThreadCount);
229  for(int i=0;i<mThreadCount;i++) {
230  mEQWorkers[i].SetData( mBufferInfo.get(), mWorkerDataCount, &mDataMutex, this);
231  mEQWorkers[i].Create();
232  mEQWorkers[i].Run();
233  }
234  }
235  return true;
236 }
237 
238 bool EffectEqualization48x::FreeBuffersWorkers()
239 {
240  if(mThreaded) {
241  for(int i=0;i<mThreadCount;i++) { // tell all the workers to exit
242  mEQWorkers[i].ExitLoop();
243  }
244  for(int i=0;i<mThreadCount;i++) {
245  mEQWorkers[i].Wait();
246  }
247  mEQWorkers.reset(); // kill the workers ( go directly to jail)
248  mThreadCount=0;
249  mWorkerDataCount=0;
250  }
251  mBufferInfo.reset();
252  mBigBuffer.reset();
253  return true;
254 }
255 
256 
257 #pragma warning(push)
258 // Disable the unreachable code warning in MSVC, for this function.
259 #pragma warning(disable: 4702)
260 bool EffectEqualization48x::RunFunctionSelect(int flags, int count, WaveTrack * track, sampleCount start, sampleCount len)
261 {
262  // deal with tables here
263  flags&=~(MATH_FUNCTION_BITREVERSE_TABLE|MATH_FUNCTION_SIN_COS_TABLE); // clear out the table flags
264  switch (flags)
265  {
266  case MATH_FUNCTION_SSE:
267  return ProcessOne4x(count, track, start, len);
268  break;
269  case MATH_FUNCTION_SSE|MATH_FUNCTION_THREADED:
270  return ProcessOne1x4xThreaded(count, track, start, len);
271  break;
272  case MATH_FUNCTION_THREADED:
273  case MATH_FUNCTION_THREADED|MATH_FUNCTION_SEGMENTED_CODE:
274  return ProcessOne1x4xThreaded(count, track, start, len, 1);
275  break;
276  case MATH_FUNCTION_SEGMENTED_CODE:
277  return ProcessOne1x(count, track, start, len);
278  break;
279  default:
280  return !mEffectEqualization->ProcessOne(count, track, start, len);
281  break;
282  }
283  return false;
284 }
285 #pragma warning(pop)
286 
287 bool EffectEqualization48x::Process(EffectEqualization* effectEqualization)
288 {
289  mEffectEqualization=effectEqualization;
290 // return TrackCompare(); // used for debugging data
291  mEffectEqualization->CopyInputTracks(); // Set up mOutputTracks.
292  bool bBreakLoop = false;
293 
294  TableUsage(sMathPath);
295  if(sMathPath) // !!! Filter MUST BE QUAD WORD ALIGNED !!!!
296  mEffectEqualization->mM=(mEffectEqualization->mM&(~15))+1;
297  AllocateBuffersWorkers(sMathPath&MATH_FUNCTION_THREADED);
298  auto cleanup = finally( [&] { FreeBuffersWorkers(); } );
299  SelectedTrackListOfKindIterator iter(Track::Wave, mEffectEqualization->mOutputTracks.get());
300  WaveTrack *track = (WaveTrack *) iter.First();
301  int count = 0;
302  while (track) {
303  double trackStart = track->GetStartTime();
304  double trackEnd = track->GetEndTime();
305  double t0 = mEffectEqualization->mT0 < trackStart? trackStart: mEffectEqualization->mT0;
306  double t1 = mEffectEqualization->mT1 > trackEnd? trackEnd: mEffectEqualization->mT1;
307 
308  if (t1 > t0) {
309  auto start = track->TimeToLongSamples(t0);
310  auto end = track->TimeToLongSamples(t1);
311  auto len = end - start;
312  bBreakLoop=RunFunctionSelect(sMathPath, count, track, start, len);
313  if( bBreakLoop )
314  break;
315  }
316  track = (WaveTrack *) iter.Next();
317  count++;
318  }
319 
320  mEffectEqualization->ReplaceProcessedTracks(!bBreakLoop);
321  return !bBreakLoop;
322 }
323 
324 bool EffectEqualization48x::TrackCompare()
325 {
326  mEffectEqualization->CopyInputTracks(); // Set up mOutputTracks.
327  bool bBreakLoop = false;
328 
329  TableUsage(sMathPath);
330  if(sMathPath) // !!! Filter MUST BE QUAD WORD ALIGNED !!!!
331  mEffectEqualization->mM=(mEffectEqualization->mM&(~15))+1;
332  AllocateBuffersWorkers(sMathPath&MATH_FUNCTION_THREADED);
333  auto cleanup = finally( [&] { FreeBuffersWorkers(); } );
334  // Reset map
335  // PRL: These two maps aren't really used
336  std::vector<Track*> SecondIMap;
337  std::vector<Track*> SecondOMap;
338  SecondIMap.clear();
339  SecondOMap.clear();
340 
341  TrackList SecondOutputTracks;
342 
343  //iterate over tracks of type trackType (All types if Track::All)
344  TrackListOfKindIterator aIt(mEffectEqualization->mOutputTracksType, mEffectEqualization->mTracks);
345 
346  for (Track *aTrack = aIt.First(); aTrack; aTrack = aIt.Next()) {
347 
348  // Include selected tracks, plus sync-lock selected tracks for Track::All.
349  if (aTrack->GetSelected() ||
350  (mEffectEqualization->mOutputTracksType == Track::All && aTrack->IsSyncLockSelected()))
351  {
352  auto o = aTrack->Duplicate();
353  SecondIMap.push_back(aTrack);
354  SecondIMap.push_back(o.get());
355  SecondOutputTracks.Add(std::move(o));
356  }
357  }
358 
359  for(int i=0;i<2;i++) {
361  (Track::Wave, i
362  ? mEffectEqualization->mOutputTracks.get()
363  : &SecondOutputTracks);
364  i?sMathPath=sMathPath:sMathPath=0;
365  WaveTrack *track = (WaveTrack *) iter.First();
366  int count = 0;
367  while (track) {
368  double trackStart = track->GetStartTime();
369  double trackEnd = track->GetEndTime();
370  double t0 = mEffectEqualization->mT0 < trackStart? trackStart: mEffectEqualization->mT0;
371  double t1 = mEffectEqualization->mT1 > trackEnd? trackEnd: mEffectEqualization->mT1;
372 
373  if (t1 > t0) {
374  auto start = track->TimeToLongSamples(t0);
375  auto end = track->TimeToLongSamples(t1);
376  auto len = end - start;
377  bBreakLoop=RunFunctionSelect(sMathPath, count, track, start, len);
378  if( bBreakLoop )
379  break;
380  }
381  track = (WaveTrack *) iter.Next();
382  count++;
383  }
384  }
386  iter(Track::Wave, mEffectEqualization->mOutputTracks.get());
387  SelectedTrackListOfKindIterator iter2(Track::Wave, &SecondOutputTracks);
388  WaveTrack *track = (WaveTrack *) iter.First();
389  WaveTrack *track2 = (WaveTrack *) iter2.First();
390  while (track) {
391  double trackStart = track->GetStartTime();
392  double trackEnd = track->GetEndTime();
393  double t0 = mEffectEqualization->mT0 < trackStart? trackStart: mEffectEqualization->mT0;
394  double t1 = mEffectEqualization->mT1 > trackEnd? trackEnd: mEffectEqualization->mT1;
395 
396  if (t1 > t0) {
397  auto start = track->TimeToLongSamples(t0);
398  auto end = track->TimeToLongSamples(t1);
399  auto len = end - start;
400  DeltaTrack(track, track2, start, len);
401  }
402  track = (WaveTrack *) iter.Next();
403  track2 = (WaveTrack *) iter2.Next();
404  }
405  mEffectEqualization->ReplaceProcessedTracks(!bBreakLoop);
406  return bBreakLoop; // return !bBreakLoop ?
407 }
408 
409 bool EffectEqualization48x::DeltaTrack(WaveTrack * t, WaveTrack * t2, sampleCount start, sampleCount len)
410 {
411 
412  auto trackBlockSize = t->GetMaxBlockSize();
413 
414  Floats buffer1{ trackBlockSize };
415  Floats buffer2{ trackBlockSize };
416 
418  auto output=p->GetTrackFactory()->NewWaveTrack(floatSample, t->GetRate());
419  auto originalLen = len;
420  auto currentSample = start;
421 
422  while(len > 0) {
423  auto curretLength = limitSampleBufferSize(trackBlockSize, len);
424  t->Get((samplePtr)buffer1.get(), floatSample, currentSample, curretLength);
425  t2->Get((samplePtr)buffer2.get(), floatSample, currentSample, curretLength);
426  for(decltype(curretLength) i=0;i<curretLength;i++)
427  buffer1[i]-=buffer2[i];
428  output->Append((samplePtr)buffer1.get(), floatSample, curretLength);
429  currentSample+=curretLength;
430  len-=curretLength;
431  }
432  output->Flush();
433  len=originalLen;
434  ProcessTail(t, output.get(), start, len);
435  return true;
436 }
437 
438 bool EffectEqualization48x::Benchmark(EffectEqualization* effectEqualization)
439 {
440  mEffectEqualization=effectEqualization;
441  mEffectEqualization->CopyInputTracks(); // Set up mOutputTracks.
442  bool bBreakLoop = false;
443 
444  TableUsage(sMathPath);
445  if(sMathPath) // !!! Filter MUST BE QUAD WORD ALIGNED !!!!
446  mEffectEqualization->mM=(mEffectEqualization->mM&(~15))+1;
447  AllocateBuffersWorkers(MATH_FUNCTION_THREADED);
448  auto cleanup = finally( [&] { FreeBuffersWorkers(); } );
450  iter(Track::Wave, mEffectEqualization->mOutputTracks.get());
451  long times[] = { 0,0,0,0,0 };
452  wxStopWatch timer;
453  mBenching=true;
454  for(int i=0;i<5 && !bBreakLoop;i++) {
455  int localMathPath;
456  switch(i) {
457  case 0: localMathPath=MATH_FUNCTION_SSE|MATH_FUNCTION_THREADED;
458  if(!sMathCaps.SSE)
459  localMathPath=-1;
460  break;
461  case 1: localMathPath=MATH_FUNCTION_SSE;
462  if(!sMathCaps.SSE)
463  localMathPath=-1;
464  break;
465  case 2: localMathPath=MATH_FUNCTION_SEGMENTED_CODE;
466  break;
467  case 3: localMathPath=MATH_FUNCTION_THREADED|MATH_FUNCTION_SEGMENTED_CODE;
468  break;
469  case 4: localMathPath=0;
470  break;
471  default: localMathPath=-1;
472  }
473  if(localMathPath>=0) {
474  timer.Start();
475  WaveTrack *track = (WaveTrack *) iter.First();
476  int count = 0;
477  while (track) {
478  double trackStart = track->GetStartTime();
479  double trackEnd = track->GetEndTime();
480  double t0 = mEffectEqualization->mT0 < trackStart? trackStart: mEffectEqualization->mT0;
481  double t1 = mEffectEqualization->mT1 > trackEnd? trackEnd: mEffectEqualization->mT1;
482 
483  if (t1 > t0) {
484  auto start = track->TimeToLongSamples(t0);
485  auto end = track->TimeToLongSamples(t1);
486  auto len = end - start;
487  bBreakLoop=RunFunctionSelect( localMathPath, count, track, start, len);
488  if( bBreakLoop )
489  break;
490  }
491  track = (WaveTrack *) iter.Next();
492  count++;
493  }
494  times[i]=timer.Time();
495  }
496  }
497  mBenching=false;
498  bBreakLoop=false;
499  mEffectEqualization->ReplaceProcessedTracks(bBreakLoop);
500 
501  wxTimeSpan tsSSEThreaded(0, 0, 0, times[0]);
502  wxTimeSpan tsSSE(0, 0, 0, times[1]);
503  wxTimeSpan tsDefaultEnhanced(0, 0, 0, times[2]);
504  wxTimeSpan tsDefaultThreaded(0, 0, 0, times[3]);
505  wxTimeSpan tsDefault(0, 0, 0, times[4]);
506 
507  Effect::MessageBox(wxString::Format(_("Benchmark times:\nOriginal: %s\nDefault Segmented: %s\nDefault Threaded: %s\nSSE: %s\nSSE Threaded: %s\n"),tsDefault.Format(wxT("%M:%S.%l")),
508  tsDefaultEnhanced.Format(wxT("%M:%S.%l")), tsDefaultThreaded.Format(wxT("%M:%S.%l")),tsSSE.Format(wxT("%M:%S.%l")),tsSSEThreaded.Format(wxT("%M:%S.%l"))));
509  return bBreakLoop; // return !bBreakLoop ?
510 }
511 
512 bool EffectEqualization48x::ProcessTail(WaveTrack * t, WaveTrack * output, sampleCount start, sampleCount len)
513 {
514  // double offsetT0 = t->LongSamplesToTime(offset);
515  double lenT = t->LongSamplesToTime(len);
516  // 'start' is the sample offset in 't', the passed in track
517  // 'startT' is the equivalent time value
518  // 'output' starts at zero
519  double startT = t->LongSamplesToTime(start);
520 
521  //output has one waveclip for the total length, even though
522  //t might have whitespace seperating multiple clips
523  //we want to maintain the original clip structure, so
524  //only paste the intersections of the NEW clip.
525 
526  //Find the bits of clips that need replacing
527  std::vector<std::pair<double, double> > clipStartEndTimes;
528  std::vector<std::pair<double, double> > clipRealStartEndTimes; //the above may be truncated due to a clip being partially selected
529  for (const auto &clip: t->GetClips())
530  {
531  double clipStartT;
532  double clipEndT;
533 
534  clipStartT = clip->GetStartTime();
535  clipEndT = clip->GetEndTime();
536  if( clipEndT <= startT )
537  continue; // clip is not within selection
538  if( clipStartT >= startT + lenT )
539  continue; // clip is not within selection
540 
541  //save the actual clip start/end so that we can rejoin them after we paste.
542  clipRealStartEndTimes.push_back(std::pair<double,double>(clipStartT,clipEndT));
543 
544  if( clipStartT < startT ) // does selection cover the whole clip?
545  clipStartT = startT; // don't copy all the NEW clip
546  if( clipEndT > startT + lenT ) // does selection cover the whole clip?
547  clipEndT = startT + lenT; // don't copy all the NEW clip
548 
549  //save them
550  clipStartEndTimes.push_back(std::pair<double,double>(clipStartT,clipEndT));
551  }
552  //now go thru and replace the old clips with NEW
553  for(unsigned int i=0;i<clipStartEndTimes.size();i++)
554  {
555  //remove the old audio and get the NEW
556  t->Clear(clipStartEndTimes[i].first,clipStartEndTimes[i].second);
557  // output->Copy(clipStartEndTimes[i].first-startT+offsetT0,clipStartEndTimes[i].second-startT+offsetT0, &toClipOutput);
558  auto toClipOutput = output->Copy(clipStartEndTimes[i].first-startT, clipStartEndTimes[i].second-startT);
559  //put the processed audio in
560  t->Paste(clipStartEndTimes[i].first, toClipOutput.get());
561  //if the clip was only partially selected, the Paste will have created a split line. Join is needed to take care of this
562  //This is not true when the selection is fully contained within one clip (second half of conditional)
563  if( (clipRealStartEndTimes[i].first != clipStartEndTimes[i].first ||
564  clipRealStartEndTimes[i].second != clipStartEndTimes[i].second) &&
565  !(clipRealStartEndTimes[i].first <= startT &&
566  clipRealStartEndTimes[i].second >= startT+lenT) )
567  t->Join(clipRealStartEndTimes[i].first,clipRealStartEndTimes[i].second);
568  }
569  return true;
570 }
571 
572 bool EffectEqualization48x::ProcessBuffer(fft_type *sourceBuffer, fft_type *destBuffer, size_t bufferLength)
573 {
574  BufferInfo bufferInfo;
575  bufferInfo.mContiguousBufferSize=bufferLength;
576  bufferInfo.mBufferSouce[0]=sourceBuffer;
577  bufferInfo.mBufferDest[0]=destBuffer;
578  bufferInfo.mScratchBuffer=&sourceBuffer[mSubBufferSize];
579  return ProcessBuffer1x(&bufferInfo);
580 }
581 
582 bool EffectEqualization48x::ProcessBuffer1x(BufferInfo *bufferInfo)
583 {
584  int bufferCount=bufferInfo->mContiguousBufferSize?1:4;
585  for(int bufferIndex=0;bufferIndex<bufferCount;bufferIndex++)
586  {
587  auto bufferLength=bufferInfo->mBufferLength;
588  if(bufferInfo->mContiguousBufferSize)
589  bufferLength=bufferInfo->mContiguousBufferSize;
590 
591  auto blockCount=bufferLength/mBlockSize;
592  auto lastBlockSize=bufferLength%mBlockSize;
593  if(lastBlockSize)
594  blockCount++;
595 
596  float *workBuffer=bufferInfo->mScratchBuffer; // all scratch buffers are at the end
597  float *scratchBuffer=&workBuffer[mWindowSize*2]; // all scratch buffers are at the end
598  float *sourceBuffer=bufferInfo->mBufferSouce[bufferIndex];
599  float *destBuffer=bufferInfo->mBufferDest[bufferIndex];
600  for(size_t runx=0;runx<blockCount;runx++)
601  {
602  float *currentBuffer=&workBuffer[mWindowSize*(runx&1)];
603  for(int i=0;i<mBlockSize;i++)
604  currentBuffer[i]=sourceBuffer[i];
605  sourceBuffer+=mBlockSize;
606  float *currentFilter=&currentBuffer[mBlockSize];
607  for(int i=0;i<mFilterSize;i++)
608  currentFilter[i]=0;
609 // mEffectEqualization->Filter(mWindowSize, currentBuffer);
610  Filter1x(mWindowSize, currentBuffer, scratchBuffer);
611  float *writeEnd=currentBuffer+mBlockSize;
612  if(runx==blockCount)
613  writeEnd=currentBuffer+(lastBlockSize+mFilterSize);
614  if(runx) {
615  float *lastOverrun=&workBuffer[mWindowSize*((runx+1)&1)+mBlockSize];
616  for(int j=0;j<mFilterSize;j++)
617  *destBuffer++= *currentBuffer++ + *lastOverrun++;
618  } else
619  currentBuffer+=mFilterSize>>1; // this will skip the first filterSize on the first run
620  while(currentBuffer<writeEnd)
621  *destBuffer++ = *currentBuffer++;
622  }
623  }
624  return true;
625 }
626 
627 bool EffectEqualization48x::ProcessOne1x(int count, WaveTrack * t,
628  sampleCount start, sampleCount len)
629 {
630  //sampleCount blockCount=len/mBlockSize;
631 
632  auto trackBlockSize = t->GetMaxBlockSize();
633 
635  auto output = p->GetTrackFactory()->NewWaveTrack(floatSample, t->GetRate());
636 
637  mEffectEqualization->TrackProgress(count, 0.0);
638  int subBufferSize=mBufferCount==8?(mSubBufferSize>>1):mSubBufferSize; // half the buffers if avx is active
639  auto bigRuns=len/(subBufferSize-mBlockSize);
640  int trackBlocksPerBig=subBufferSize/trackBlockSize;
641  int trackLeftovers=subBufferSize-trackBlocksPerBig*trackBlockSize;
642  size_t singleProcessLength;
643  if(bigRuns == 0)
644  singleProcessLength = len.as_size_t();
645  else
646  singleProcessLength=(mFilterSize>>1)*bigRuns + len%(bigRuns*(subBufferSize-mBlockSize));
647  auto currentSample=start;
648  bool bBreakLoop = false;
649  for(int bigRun=0;bigRun<bigRuns;bigRun++)
650  {
651  // fill the buffer
652  for(int i=0;i<trackBlocksPerBig;i++) {
653  t->Get((samplePtr)&mBigBuffer[i*trackBlockSize], floatSample, currentSample, trackBlockSize);
654  currentSample+=trackBlockSize;
655  }
656  if(trackLeftovers) {
657  t->Get((samplePtr)&mBigBuffer[trackBlocksPerBig*trackBlockSize], floatSample, currentSample, trackLeftovers);
658  currentSample+=trackLeftovers;
659  }
660  currentSample-=mBlockSize+(mFilterSize>>1);
661 
662  ProcessBuffer1x(mBufferInfo.get());
663  bBreakLoop=mEffectEqualization->TrackProgress(count, (double)(bigRun)/bigRuns.as_double());
664  if( bBreakLoop )
665  break;
666  output->Append((samplePtr)&mBigBuffer[(bigRun?mBlockSize:0)+(mFilterSize>>1)], floatSample, subBufferSize-((bigRun?mBlockSize:0)+(mFilterSize>>1)));
667  }
668  if(singleProcessLength && !bBreakLoop) {
669  t->Get((samplePtr)mBigBuffer.get(), floatSample, currentSample, singleProcessLength+mBlockSize+(mFilterSize>>1));
670  ProcessBuffer(mBigBuffer.get(), mBigBuffer.get(), singleProcessLength+mBlockSize+(mFilterSize>>1));
671  output->Append((samplePtr)&mBigBuffer[bigRuns > 0 ? mBlockSize : 0], floatSample, singleProcessLength+mBlockSize+(mFilterSize>>1));
672  }
673  output->Flush();
674  if(!bBreakLoop)
675  ProcessTail(t, output.get(), start, len);
676  return bBreakLoop;
677 }
678 
679 void EffectEqualization48x::Filter1x(size_t len,
680  float *buffer, float *scratchBuffer)
681 {
682  int i;
683  float real, imag;
684  // Apply FFT
685  RealFFTf1x(buffer, mEffectEqualization->hFFT.get());
686 
687  // Apply filter
688  // DC component is purely real
689 
690  float filterFuncR, filterFuncI;
691  filterFuncR = mEffectEqualization->mFilterFuncR[0];
692  scratchBuffer[0] = buffer[0] * filterFuncR;
693  auto halfLength = (len / 2);
694 
695  bool useBitReverseTable=sMathPath&1;
696 
697  for(i = 1; i < halfLength; i++)
698  {
699  if(useBitReverseTable) {
700  real=buffer[mEffectEqualization->hFFT->BitReversed[i] ];
701  imag=buffer[mEffectEqualization->hFFT->BitReversed[i]+1];
702  } else {
703  int bitReversed=SmallRB(i,mEffectEqualization->hFFT->pow2Bits);
704  real=buffer[bitReversed];
705  imag=buffer[bitReversed+1];
706  }
707  filterFuncR=mEffectEqualization->mFilterFuncR[i];
708  filterFuncI=mEffectEqualization->mFilterFuncI[i];
709 
710  scratchBuffer[2*i ] = real*filterFuncR - imag*filterFuncI;
711  scratchBuffer[2*i+1] = real*filterFuncI + imag*filterFuncR;
712  }
713  // Fs/2 component is purely real
714  filterFuncR=mEffectEqualization->mFilterFuncR[halfLength];
715  scratchBuffer[1] = buffer[1] * filterFuncR;
716 
717  // Inverse FFT and normalization
718  InverseRealFFTf1x(scratchBuffer, mEffectEqualization->hFFT.get());
719  ReorderToTime1x(mEffectEqualization->hFFT.get(), scratchBuffer, buffer);
720 }
721 
722 bool EffectEqualization48x::ProcessBuffer4x(BufferInfo *bufferInfo)
723 {
724  // length must be a factor of window size for 4x processing.
725  if(bufferInfo->mBufferLength%mBlockSize)
726  return false;
727 
728  auto blockCount=bufferInfo->mBufferLength/mBlockSize;
729 
730  __m128 *readBlocks[4]; // some temps so we dont destroy the vars in the struct
731  __m128 *writeBlocks[4];
732  for(int i=0;i<4;i++) {
733  readBlocks[i]=(__m128 *)bufferInfo->mBufferSouce[i];
734  writeBlocks[i]=(__m128 *)bufferInfo->mBufferDest[i];
735  }
736 
737  __m128 *swizzledBuffer128=(__m128 *)bufferInfo->mScratchBuffer;
738  __m128 *scratchBuffer=&swizzledBuffer128[mWindowSize*2];
739 
740  for(size_t run4x=0;run4x<blockCount;run4x++)
741  {
742  // swizzle the data to the swizzle buffer
743  __m128 *currentSwizzledBlock=&swizzledBuffer128[mWindowSize*(run4x&1)];
744  for(int i=0,j=0;j<mBlockSize;i++,j+=4) {
745  __m128 tmp0 = _mm_shuffle_ps(readBlocks[0][i], readBlocks[1][i], _MM_SHUFFLE(1,0,1,0));
746  __m128 tmp1 = _mm_shuffle_ps(readBlocks[0][i], readBlocks[1][i], _MM_SHUFFLE(3,2,3,2));
747  __m128 tmp2 = _mm_shuffle_ps(readBlocks[2][i], readBlocks[3][i], _MM_SHUFFLE(1,0,1,0));
748  __m128 tmp3 = _mm_shuffle_ps(readBlocks[2][i], readBlocks[3][i], _MM_SHUFFLE(3,2,3,2));
749  currentSwizzledBlock[j] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(2,0,2,0));
750  currentSwizzledBlock[j+1] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(3,1,3,1));
751  currentSwizzledBlock[j+2] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(2,0,2,0));
752  currentSwizzledBlock[j+3] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(3,1,3,1));
753  }
754  __m128 *thisOverrun128=&currentSwizzledBlock[mBlockSize];
755  for(int i=0;i<mFilterSize;i++)
756  thisOverrun128[i]=_mm_set1_ps(0.0);
757  Filter4x(mWindowSize, (float *)currentSwizzledBlock, (float *)scratchBuffer);
758  int writeStart=0, writeToStart=0; // note readStart is where the read data is written
759  int writeEnd=mBlockSize;
760  if(run4x) {
761  // maybe later swizzle add and write in one
762  __m128 *lastOverrun128=&swizzledBuffer128[mWindowSize*((run4x+1)&1)+mBlockSize];
763  // add and swizzle data + filter
764  for(int i=0,j=0;j<mFilterSize;i++,j+=4) {
765  __m128 tmps0 = _mm_add_ps(currentSwizzledBlock[j], lastOverrun128[j]);
766  __m128 tmps1 = _mm_add_ps(currentSwizzledBlock[j+1], lastOverrun128[j+1]);
767  __m128 tmps2 = _mm_add_ps(currentSwizzledBlock[j+2], lastOverrun128[j+2]);
768  __m128 tmps3 = _mm_add_ps(currentSwizzledBlock[j+3], lastOverrun128[j+3]);
769  __m128 tmp0 = _mm_shuffle_ps(tmps1, tmps0, _MM_SHUFFLE(0,1,0,1));
770  __m128 tmp1 = _mm_shuffle_ps(tmps1, tmps0, _MM_SHUFFLE(2,3,2,3));
771  __m128 tmp2 = _mm_shuffle_ps(tmps3, tmps2, _MM_SHUFFLE(0,1,0,1));
772  __m128 tmp3 = _mm_shuffle_ps(tmps3, tmps2, _MM_SHUFFLE(2,3,2,3));
773  writeBlocks[0][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(1,3,1,3));
774  writeBlocks[1][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(0,2,0,2));
775  writeBlocks[2][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(1,3,1,3));
776  writeBlocks[3][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(0,2,0,2));
777  }
778  writeStart=mFilterSize;
779  writeToStart=mFilterSize>>2;
780  // swizzle it back.
781  for(int i=writeToStart,j=writeStart;j<writeEnd;i++,j+=4) {
782  __m128 tmp0 = _mm_shuffle_ps(currentSwizzledBlock[j+1], currentSwizzledBlock[j], _MM_SHUFFLE(0,1,0,1));
783  __m128 tmp1 = _mm_shuffle_ps(currentSwizzledBlock[j+1], currentSwizzledBlock[j], _MM_SHUFFLE(2,3,2,3));
784  __m128 tmp2 = _mm_shuffle_ps(currentSwizzledBlock[j+3], currentSwizzledBlock[j+2], _MM_SHUFFLE(0,1,0,1));
785  __m128 tmp3 = _mm_shuffle_ps(currentSwizzledBlock[j+3], currentSwizzledBlock[j+2], _MM_SHUFFLE(2,3,2,3));
786  writeBlocks[0][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(1,3,1,3));
787  writeBlocks[1][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(0,2,0,2));
788  writeBlocks[2][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(1,3,1,3));
789  writeBlocks[3][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(0,2,0,2));
790  }
791  } else {
792  // swizzle it back. We overlap one block so we only write the first block on the first run
793  writeStart=0;
794  writeToStart=0;
795  for(int i=writeToStart,j=writeStart;j<writeEnd;i++,j+=4) {
796  __m128 tmp0 = _mm_shuffle_ps(currentSwizzledBlock[j+1], currentSwizzledBlock[j], _MM_SHUFFLE(0,1,0,1));
797  __m128 tmp2 = _mm_shuffle_ps(currentSwizzledBlock[j+3], currentSwizzledBlock[j+2], _MM_SHUFFLE(0,1,0,1));
798  writeBlocks[0][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(1,3,1,3));
799  }
800  }
801  for(int i=0;i<4;i++) { // shift each block
802  readBlocks[i]+=mBlockSize>>2; // these are 128b pointers, each window is 1/4 blockSize for those
803  writeBlocks[i]+=mBlockSize>>2;
804  }
805  }
806  return true;
807 }
808 
809 bool EffectEqualization48x::ProcessOne4x(int count, WaveTrack * t,
810  sampleCount start, sampleCount len)
811 {
812  int subBufferSize=mBufferCount==8?(mSubBufferSize>>1):mSubBufferSize; // half the buffers if avx is active
813 
814  if(len<subBufferSize) // it's not worth 4x processing do a regular process
815  return ProcessOne1x(count, t, start, len);
816 
817  auto trackBlockSize = t->GetMaxBlockSize();
818 
820  auto output = p->GetTrackFactory()->NewWaveTrack(floatSample, t->GetRate());
821 
822  mEffectEqualization->TrackProgress(count, 0.0);
823  auto bigRuns = len/(subBufferSize-mBlockSize);
824  int trackBlocksPerBig=subBufferSize/trackBlockSize;
825  int trackLeftovers=subBufferSize-trackBlocksPerBig*trackBlockSize;
826  size_t singleProcessLength=(mFilterSize>>1)*bigRuns + len%(bigRuns*(subBufferSize-mBlockSize));
827  auto currentSample=start;
828 
829  bool bBreakLoop = false;
830  for(int bigRun=0;bigRun<bigRuns;bigRun++)
831  {
832  // fill the buffer
833  for(int i=0;i<trackBlocksPerBig;i++) {
834  t->Get((samplePtr)&mBigBuffer[i*trackBlockSize], floatSample, currentSample, trackBlockSize);
835  currentSample+=trackBlockSize;
836  }
837  if(trackLeftovers) {
838  t->Get((samplePtr)&mBigBuffer[trackBlocksPerBig*trackBlockSize], floatSample, currentSample, trackLeftovers);
839  currentSample+=trackLeftovers;
840  }
841  currentSample-=mBlockSize+(mFilterSize>>1);
842 
843  ProcessBuffer4x(mBufferInfo.get());
844  bBreakLoop=mEffectEqualization->TrackProgress(count, (double)(bigRun)/bigRuns.as_double());
845  if( bBreakLoop )
846  break;
847  output->Append((samplePtr)&mBigBuffer[(bigRun?mBlockSize:0)+(mFilterSize>>1)], floatSample, subBufferSize-((bigRun?mBlockSize:0)+(mFilterSize>>1)));
848  }
849  if(singleProcessLength && !bBreakLoop) {
850  t->Get((samplePtr)mBigBuffer.get(), floatSample, currentSample, singleProcessLength+mBlockSize+(mFilterSize>>1));
851  ProcessBuffer(mBigBuffer.get(), mBigBuffer.get(), singleProcessLength+mBlockSize+(mFilterSize>>1));
852  output->Append((samplePtr)&mBigBuffer[bigRuns > 0 ? mBlockSize : 0], floatSample, singleProcessLength+mBlockSize+(mFilterSize>>1));
853 // output->Append((samplePtr)&mBigBuffer[bigRuns?mBlockSize:0], floatSample, singleProcessLength);
854  }
855  output->Flush();
856  if(!bBreakLoop)
857  ProcessTail(t, output.get(), start, len);
858  return bBreakLoop;
859 }
860 
861 void *EQWorker::Entry()
862 {
863  while(!mExitLoop) {
864  int i = 0;
865  {
866  wxMutexLocker locker( mMutex );
867  for(; i < mBufferInfoCount; i++) {
868  if(mBufferInfoList[i].mBufferStatus==BufferReady) { // we found an unlocked ready buffer
869  mBufferInfoList[i].mBufferStatus=BufferBusy; // we own it now
870  break;
871  }
872  }
873  }
874  if ( i < mBufferInfoCount ) {
875  switch (mProcessingType)
876  {
877  case 1:
878  mEffectEqualization48x->ProcessBuffer1x(&mBufferInfoList[i]);
879  break;
880  case 4:
881  mEffectEqualization48x->ProcessBuffer4x(&mBufferInfoList[i]);
882  break;
883  }
884  mBufferInfoList[i].mBufferStatus=BufferDone; // we're done
885  }
886  }
887  return NULL;
888 }
889 
890 bool EffectEqualization48x::ProcessOne1x4xThreaded(int count, WaveTrack * t,
891  sampleCount start, sampleCount len, int processingType)
892 {
893  int subBufferSize=mBufferCount==8?(mSubBufferSize>>1):mSubBufferSize; // half the buffers if avx is active
894 
895  sampleCount blockCount=len/mBlockSize;
896 
897  if(blockCount<16) // it's not worth 4x processing do a regular process
898  return ProcessOne4x(count, t, start, len);
899  if(mThreadCount<=0 || blockCount<256) // dont do it without cores or big data
900  return ProcessOne4x(count, t, start, len);
901 
902  for(int i=0;i<mThreadCount;i++)
903  mEQWorkers[i].mProcessingType=processingType;
904 
906  auto output = p->GetTrackFactory()->NewWaveTrack(floatSample, t->GetRate());
907 
908  auto trackBlockSize = t->GetMaxBlockSize();
909  mEffectEqualization->TrackProgress(count, 0.0);
910  auto bigRuns = len/(subBufferSize-mBlockSize);
911  int trackBlocksPerBig=subBufferSize/trackBlockSize;
912  int trackLeftovers=subBufferSize-trackBlocksPerBig*trackBlockSize;
913  size_t singleProcessLength=(mFilterSize>>1)*bigRuns + len%(bigRuns*(subBufferSize-mBlockSize));
914  auto currentSample=start;
915 
916  int bigBlocksRead=mWorkerDataCount, bigBlocksWritten=0;
917 
918  // fill the first workerDataCount buffers we checked above and there is at least this data
919  auto maxPreFill = bigRuns < mWorkerDataCount ? bigRuns : mWorkerDataCount;
920  for(int i=0;i<maxPreFill;i++)
921  {
922  // fill the buffer
923  for(int j=0;j<trackBlocksPerBig;j++) {
924  t->Get((samplePtr)&mBufferInfo[i].mBufferSouce[0][j*trackBlockSize], floatSample, currentSample, trackBlockSize);
925  currentSample+=trackBlockSize;
926  }
927  if(trackLeftovers) {
928  t->Get((samplePtr)&mBufferInfo[i].mBufferSouce[0][trackBlocksPerBig*trackBlockSize], floatSample, currentSample, trackLeftovers);
929  currentSample+=trackLeftovers;
930  }
931  currentSample-=mBlockSize+(mFilterSize>>1);
932  mBufferInfo[i].mBufferStatus=BufferReady; // free for grabbin
933  }
934  int currentIndex=0;
935  bool bBreakLoop = false;
936  while(bigBlocksWritten<bigRuns && !bBreakLoop) {
937  bBreakLoop=mEffectEqualization->TrackProgress(count, (double)(bigBlocksWritten)/bigRuns.as_double());
938  if( bBreakLoop )
939  break;
940  wxMutexLocker locker( mDataMutex ); // Get in line for data
941  // process as many blocks as we can
942  while((mBufferInfo[currentIndex].mBufferStatus==BufferDone) && (bigBlocksWritten<bigRuns)) { // data is ours
943  output->Append((samplePtr)&mBufferInfo[currentIndex].mBufferDest[0][(bigBlocksWritten?mBlockSize:0)+(mFilterSize>>1)], floatSample, subBufferSize-((bigBlocksWritten?mBlockSize:0)+(mFilterSize>>1)));
944  bigBlocksWritten++;
945  if(bigBlocksRead<bigRuns) {
946  // fill the buffer
947  for(int j=0;j<trackBlocksPerBig;j++) {
948  t->Get((samplePtr)&mBufferInfo[currentIndex].mBufferSouce[0][j*trackBlockSize], floatSample, currentSample, trackBlockSize);
949  currentSample+=trackBlockSize;
950  }
951  if(trackLeftovers) {
952  t->Get((samplePtr)&mBufferInfo[currentIndex].mBufferSouce[0][trackBlocksPerBig*trackBlockSize], floatSample, currentSample, trackLeftovers);
953  currentSample+=trackLeftovers;
954  }
955  currentSample-=mBlockSize+(mFilterSize>>1);
956  mBufferInfo[currentIndex].mBufferStatus=BufferReady; // free for grabbin
957  bigBlocksRead++;
958  } else mBufferInfo[currentIndex].mBufferStatus=BufferEmpty; // this is completely unecessary
959  currentIndex=(currentIndex+1)%mWorkerDataCount;
960  }
961  }
962  if(singleProcessLength && !bBreakLoop) {
963  t->Get((samplePtr)mBigBuffer.get(), floatSample, currentSample, singleProcessLength+mBlockSize+(mFilterSize>>1));
964  ProcessBuffer(mBigBuffer.get(), mBigBuffer.get(), singleProcessLength+mBlockSize+(mFilterSize>>1));
965  output->Append((samplePtr)&mBigBuffer[mBlockSize], floatSample, singleProcessLength+mBlockSize+(mFilterSize>>1));
966  }
967  output->Flush();
968  if(!bBreakLoop)
969  ProcessTail(t, output.get(), start, len);
970  return bBreakLoop;
971 }
972 
973 void EffectEqualization48x::Filter4x(size_t len,
974  float *buffer, float *scratchBuffer)
975 {
976  int i;
977  __m128 real128, imag128;
978  // Apply FFT
979  RealFFTf4x(buffer, mEffectEqualization->hFFT.get());
980 
981  // Apply filter
982  // DC component is purely real
983  __m128 *localFFTBuffer=(__m128 *)scratchBuffer;
984  __m128 *localBuffer=(__m128 *)buffer;
985 
986  __m128 filterFuncR, filterFuncI;
987  filterFuncR = _mm_set1_ps(mEffectEqualization->mFilterFuncR[0]);
988  localFFTBuffer[0] = _mm_mul_ps(localBuffer[0], filterFuncR);
989  auto halfLength = (len / 2);
990 
991  bool useBitReverseTable = sMathPath & 1;
992 
993  for(i = 1; i < halfLength; i++)
994  {
995  if(useBitReverseTable) {
996  real128=localBuffer[mEffectEqualization->hFFT->BitReversed[i] ];
997  imag128=localBuffer[mEffectEqualization->hFFT->BitReversed[i]+1];
998  } else {
999  int bitReversed=SmallRB(i,mEffectEqualization->hFFT->pow2Bits);
1000  real128=localBuffer[bitReversed];
1001  imag128=localBuffer[bitReversed+1];
1002  }
1003  filterFuncR=_mm_set1_ps(mEffectEqualization->mFilterFuncR[i]);
1004  filterFuncI=_mm_set1_ps(mEffectEqualization->mFilterFuncI[i]);
1005  localFFTBuffer[2*i ] = _mm_sub_ps( _mm_mul_ps(real128, filterFuncR), _mm_mul_ps(imag128, filterFuncI));
1006  localFFTBuffer[2*i+1] = _mm_add_ps( _mm_mul_ps(real128, filterFuncI), _mm_mul_ps(imag128, filterFuncR));
1007  }
1008  // Fs/2 component is purely real
1009  filterFuncR=_mm_set1_ps(mEffectEqualization->mFilterFuncR[halfLength]);
1010  localFFTBuffer[1] = _mm_mul_ps(localBuffer[1], filterFuncR);
1011 
1012  // Inverse FFT and normalization
1013  InverseRealFFTf4x(scratchBuffer, mEffectEqualization->hFFT.get());
1014  ReorderToTime4x(mEffectEqualization->hFFT.get(), scratchBuffer, buffer);
1015 }
1016 
1017 #ifdef __AVX_ENABLED
1018 
1019 // note although written it has not been tested
1020 
1021 bool EffectEqualization48x::ProcessBuffer8x(BufferInfo *bufferInfo)
1022 {
1023  // length must be a factor of window size for 4x processing.
1024  if(bufferInfo->mBufferLength%mBlockSize || mBufferCount!=8)
1025  return false;
1026 
1027  auto blockCount=bufferInfo->mBufferLength/mBlockSize;
1028 
1029  __m128 *readBlocks[8]; // some temps so we dont destroy the vars in the struct
1030  __m128 *writeBlocks[8];
1031  for(int i=0;i<8;i++) {
1032  readBlocks[i]=(__m128 *)bufferInfo->mBufferSouce[i];
1033  writeBlocks[i]=(__m128 *)bufferInfo->mBufferDest[i];
1034  }
1035 
1036  __m128 *swizzledBuffer128=(__m128 *)bufferInfo->mScratchBuffer;
1037  __m128 *scratchBuffer=&swizzledBuffer128[mWindowSize*4];
1038 
1039  int doubleFilter=mFilterSize<<1;
1040  int doubleWindow=mWindowSize<<1;
1041  int doubleBlock=mBlockSize<<1;
1042  for(int run4x=0;run4x<blockCount;run4x++)
1043  {
1044  // swizzle the data to the swizzle buffer
1045  __m128 *currentSwizzledBlock=&swizzledBuffer128[doubleWindow*(run4x&1)];
1046  for(int i=0,j=0;j<doubleBlock;i++,j+=8) { // mBlockSize or doubleBlock???
1047  __m128 tmp0 = _mm_shuffle_ps(readBlocks[0][i], readBlocks[1][i], _MM_SHUFFLE(1,0,1,0));
1048  __m128 tmp1 = _mm_shuffle_ps(readBlocks[0][i], readBlocks[1][i], _MM_SHUFFLE(3,2,3,2));
1049  __m128 tmp2 = _mm_shuffle_ps(readBlocks[2][i], readBlocks[3][i], _MM_SHUFFLE(1,0,1,0));
1050  __m128 tmp3 = _mm_shuffle_ps(readBlocks[2][i], readBlocks[3][i], _MM_SHUFFLE(3,2,3,2));
1051  currentSwizzledBlock[j] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(2,0,2,0));
1052  currentSwizzledBlock[j+2] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(3,1,3,1));
1053  currentSwizzledBlock[j+4] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(2,0,2,0));
1054  currentSwizzledBlock[j+6] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(3,1,3,1));
1055  tmp0 = _mm_shuffle_ps(readBlocks[4][i], readBlocks[5][i], _MM_SHUFFLE(1,0,1,0));
1056  tmp1 = _mm_shuffle_ps(readBlocks[4][i], readBlocks[5][i], _MM_SHUFFLE(3,2,3,2));
1057  tmp2 = _mm_shuffle_ps(readBlocks[6][i], readBlocks[7][i], _MM_SHUFFLE(1,0,1,0));
1058  tmp3 = _mm_shuffle_ps(readBlocks[6][i], readBlocks[7][i], _MM_SHUFFLE(3,2,3,2));
1059  currentSwizzledBlock[j+1] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(2,0,2,0));
1060  currentSwizzledBlock[j+3] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(3,1,3,1));
1061  currentSwizzledBlock[j+5] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(2,0,2,0));
1062  currentSwizzledBlock[j+7] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(3,1,3,1));
1063  }
1064  __m128 *thisOverrun128=&currentSwizzledBlock[doubleBlock];
1065  for(int i=0;i<doubleFilter;i++)
1066  thisOverrun128[i]=_mm_set1_ps(0.0);
1067  Filter8x(mWindowSize, (float *)currentSwizzledBlock, (float *)scratchBuffer);
1068  int writeStart=0, writeToStart=0; // note readStart is where the read data is written
1069  int writeEnd=doubleBlock;
1070  if(run4x) {
1071  // maybe later swizzle add and write in one
1072  __m128 *lastOverrun128=&swizzledBuffer128[doubleWindow*((run4x+1)&1)+doubleBlock];
1073  // add and swizzle data + filter
1074  for(int i=0,j=0;j<doubleFilter;i++,j+=8) {
1075  __m128 tmps0 = _mm_add_ps(currentSwizzledBlock[j], lastOverrun128[j]);
1076  __m128 tmps1 = _mm_add_ps(currentSwizzledBlock[j+2], lastOverrun128[j+2]);
1077  __m128 tmps2 = _mm_add_ps(currentSwizzledBlock[j+4], lastOverrun128[j+4]);
1078  __m128 tmps3 = _mm_add_ps(currentSwizzledBlock[j+6], lastOverrun128[j+6]);
1079  __m128 tmp0 = _mm_shuffle_ps(tmps1, tmps0, _MM_SHUFFLE(0,1,0,1));
1080  __m128 tmp1 = _mm_shuffle_ps(tmps1, tmps0, _MM_SHUFFLE(2,3,2,3));
1081  __m128 tmp2 = _mm_shuffle_ps(tmps3, tmps2, _MM_SHUFFLE(0,1,0,1));
1082  __m128 tmp3 = _mm_shuffle_ps(tmps3, tmps2, _MM_SHUFFLE(2,3,2,3));
1083  writeBlocks[0][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(1,3,1,3));
1084  writeBlocks[1][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(0,2,0,2));
1085  writeBlocks[2][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(1,3,1,3));
1086  writeBlocks[3][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(0,2,0,2));
1087  tmps0 = _mm_add_ps(currentSwizzledBlock[j+1], lastOverrun128[j+1]);
1088  tmps1 = _mm_add_ps(currentSwizzledBlock[j+3], lastOverrun128[j+3]);
1089  tmps2 = _mm_add_ps(currentSwizzledBlock[j+5], lastOverrun128[j+5]);
1090  tmps3 = _mm_add_ps(currentSwizzledBlock[j+7], lastOverrun128[j+7]);
1091  tmp0 = _mm_shuffle_ps(tmps1, tmps0, _MM_SHUFFLE(0,1,0,1));
1092  tmp1 = _mm_shuffle_ps(tmps1, tmps0, _MM_SHUFFLE(2,3,2,3));
1093  tmp2 = _mm_shuffle_ps(tmps3, tmps2, _MM_SHUFFLE(0,1,0,1));
1094  tmp3 = _mm_shuffle_ps(tmps3, tmps2, _MM_SHUFFLE(2,3,2,3));
1095  writeBlocks[4][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(1,3,1,3));
1096  writeBlocks[5][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(0,2,0,2));
1097  writeBlocks[6][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(1,3,1,3));
1098  writeBlocks[7][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(0,2,0,2));
1099  }
1100  writeStart=doubleFilter;
1101  writeToStart=mFilterSize>>2;
1102  // swizzle it back.
1103  for(int i=writeToStart,j=writeStart;j<writeEnd;i++,j+=8) {
1104  __m128 tmp0 = _mm_shuffle_ps(currentSwizzledBlock[j+2], currentSwizzledBlock[j], _MM_SHUFFLE(0,1,0,1));
1105  __m128 tmp1 = _mm_shuffle_ps(currentSwizzledBlock[j+2], currentSwizzledBlock[j], _MM_SHUFFLE(2,3,2,3));
1106  __m128 tmp2 = _mm_shuffle_ps(currentSwizzledBlock[j+6], currentSwizzledBlock[j+4], _MM_SHUFFLE(0,1,0,1));
1107  __m128 tmp3 = _mm_shuffle_ps(currentSwizzledBlock[j+6], currentSwizzledBlock[j+4], _MM_SHUFFLE(2,3,2,3));
1108  writeBlocks[0][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(1,3,1,3));
1109  writeBlocks[1][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(0,2,0,2));
1110  writeBlocks[2][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(1,3,1,3));
1111  writeBlocks[3][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(0,2,0,2));
1112  tmp0 = _mm_shuffle_ps(currentSwizzledBlock[j+3], currentSwizzledBlock[j+1], _MM_SHUFFLE(0,1,0,1));
1113  tmp1 = _mm_shuffle_ps(currentSwizzledBlock[j+3], currentSwizzledBlock[j+1], _MM_SHUFFLE(2,3,2,3));
1114  tmp2 = _mm_shuffle_ps(currentSwizzledBlock[j+7], currentSwizzledBlock[j+5], _MM_SHUFFLE(0,1,0,1));
1115  tmp3 = _mm_shuffle_ps(currentSwizzledBlock[j+7], currentSwizzledBlock[j+5], _MM_SHUFFLE(2,3,2,3));
1116  writeBlocks[4][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(1,3,1,3));
1117  writeBlocks[5][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(0,2,0,2));
1118  writeBlocks[6][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(1,3,1,3));
1119  writeBlocks[7][i] = _mm_shuffle_ps(tmp1, tmp3, _MM_SHUFFLE(0,2,0,2));
1120  }
1121  } else {
1122  // swizzle it back. We overlap one block so we only write the first block on the first run
1123  writeStart=0;
1124  writeToStart=0;
1125  for(int i=writeToStart,j=writeStart;j<writeEnd;i++,j+=8) {
1126  __m128 tmp0 = _mm_shuffle_ps(currentSwizzledBlock[j+2], currentSwizzledBlock[j], _MM_SHUFFLE(0,1,0,1));
1127  __m128 tmp2 = _mm_shuffle_ps(currentSwizzledBlock[j+6], currentSwizzledBlock[j+4], _MM_SHUFFLE(0,1,0,1));
1128  writeBlocks[0][i] = _mm_shuffle_ps(tmp0, tmp2, _MM_SHUFFLE(1,3,1,3));
1129  }
1130  }
1131  for(int i=0;i<8;i++) { // shift each block
1132  readBlocks[i]+=mBlockSize>>2; // these are 128b pointers, each window is 1/4 blockSize for those
1133  writeBlocks[i]+=mBlockSize>>2;
1134  }
1135  }
1136  return true;
1137 }
1138 
1139 bool EffectEqualization48x::ProcessOne8x(int count, WaveTrack * t,
1140  sampleCount start, sampleCount len)
1141 {
1142  sampleCount blockCount=len/mBlockSize;
1143 
1144  if(blockCount<32) // it's not worth 8x processing do a regular process
1145  return ProcessOne4x(count, t, start, len);
1146 
1147  auto trackBlockSize = t->GetMaxBlockSize();
1148 
1150  auto output = p->GetTrackFactory()->NewWaveTrack(floatSample, t->GetRate());
1151 
1152  mEffectEqualization->TrackProgress(count, 0.0);
1153  int bigRuns=len/(mSubBufferSize-mBlockSize);
1154  int trackBlocksPerBig=mSubBufferSize/trackBlockSize;
1155  int trackLeftovers=mSubBufferSize-trackBlocksPerBig*trackBlockSize;
1156  int singleProcessLength=(mFilterSize>>1)*bigRuns + len%(bigRuns*(mSubBufferSize-mBlockSize));
1157  auto currentSample=start;
1158 
1159  bool bBreakLoop = false;
1160  for(int bigRun=0;bigRun<bigRuns;bigRun++)
1161  {
1162  // fill the buffer
1163  for(int i=0;i<trackBlocksPerBig;i++) {
1164  t->Get((samplePtr)&mBigBuffer[i*trackBlockSize], floatSample, currentSample, trackBlockSize);
1165  currentSample+=trackBlockSize;
1166  }
1167  if(trackLeftovers) {
1168  t->Get((samplePtr)&mBigBuffer[trackBlocksPerBig*trackBlockSize], floatSample, currentSample, trackLeftovers);
1169  currentSample+=trackLeftovers;
1170  }
1171  currentSample-=mBlockSize+(mFilterSize>>1);
1172 
1173  ProcessBuffer4x(mBufferInfo);
1174  if (bBreakLoop=mEffectEqualization->TrackProgress(count, (double)(bigRun)/(double)bigRuns))
1175  {
1176  break;
1177  }
1178  output->Append((samplePtr)&mBigBuffer[(bigRun?mBlockSize:0)+(mFilterSize>>1)], floatSample, mSubBufferSize-((bigRun?mBlockSize:0)+(mFilterSize>>1)));
1179  }
1180  if(singleProcessLength && !bBreakLoop) {
1181  t->Get((samplePtr)mBigBuffer.get(), floatSample, currentSample, singleProcessLength+mBlockSize+(mFilterSize>>1));
1182  ProcessBuffer(mBigBuffer.get(), mBigBuffer.get(), singleProcessLength+mBlockSize+(mFilterSize>>1));
1183  output->Append((samplePtr)&mBigBuffer[mBlockSize], floatSample, singleProcessLength+mBlockSize+(mFilterSize>>1));
1184  }
1185  output->Flush();
1186  if(!bBreakLoop)
1187  ProcessTail(t, output.get(), start, len);
1188  return bBreakLoop;
1189 }
1190 
1191 bool EffectEqualization48x::ProcessOne8xThreaded(int count, WaveTrack * t,
1192  sampleCount start, sampleCount len)
1193 {
1194  sampleCount blockCount=len/mBlockSize;
1195 
1196  if(blockCount<16) // it's not worth 4x processing do a regular process
1197  return ProcessOne4x(count, t, start, len);
1198  if(mThreadCount<=0 || blockCount<256) // dont do it without cores or big data
1199  return ProcessOne4x(count, t, start, len);
1200 
1202  auto output = p->GetTrackFactory()->NewWaveTrack(floatSample, t->GetRate());
1203 
1204  auto trackBlockSize = t->GetMaxBlockSize();
1205  mEffectEqualization->TrackProgress(count, 0.0);
1206  int bigRuns=len/(mSubBufferSize-mBlockSize);
1207  int trackBlocksPerBig=mSubBufferSize/trackBlockSize;
1208  int trackLeftovers=mSubBufferSize-trackBlocksPerBig*trackBlockSize;
1209  int singleProcessLength=(mFilterSize>>1)*bigRuns + len%(bigRuns*(mSubBufferSize-mBlockSize));
1210  auto currentSample=start;
1211 
1212  int bigBlocksRead=mWorkerDataCount, bigBlocksWritten=0;
1213 
1214  // fill the first workerDataCount buffers we checked above and there is at least this data
1215  for(int i=0;i<mWorkerDataCount;i++)
1216  {
1217  // fill the buffer
1218  for(int j=0;j<trackBlocksPerBig;j++) {
1219  t->Get((samplePtr)&mBufferInfo[i].mBufferSouce[0][j*trackBlockSize], floatSample, currentSample, trackBlockSize);
1220  currentSample+=trackBlockSize;
1221  }
1222  if(trackLeftovers) {
1223  t->Get((samplePtr)&mBufferInfo[i].mBufferSouce[0][trackBlocksPerBig*trackBlockSize], floatSample, currentSample, trackLeftovers);
1224  currentSample+=trackLeftovers;
1225  }
1226  currentSample-=mBlockSize+(mFilterSize>>1);
1227  mBufferInfo[i].mBufferStatus=BufferReady; // free for grabbin
1228  }
1229  int currentIndex=0;
1230  bool bBreakLoop = false;
1231  while(bigBlocksWritten<bigRuns) {
1232  if (bBreakLoop=mEffectEqualization->TrackProgress(count, (double)(bigBlocksWritten)/(double)bigRuns))
1233  {
1234  break;
1235  }
1236  wxMutexLocker locker( mDataMutex ); // Get in line for data
1237  // process as many blocks as we can
1238  while((mBufferInfo[currentIndex].mBufferStatus==BufferDone) && (bigBlocksWritten<bigRuns)) { // data is ours
1239  output->Append((samplePtr)&mBufferInfo[currentIndex].mBufferDest[0][(bigBlocksWritten?mBlockSize:0)+(mFilterSize>>1)], floatSample, mSubBufferSize-((bigBlocksWritten?mBlockSize:0)+(mFilterSize>>1)));
1240  bigBlocksWritten++;
1241  if(bigBlocksRead<bigRuns) {
1242  // fill the buffer
1243  for(int j=0;j<trackBlocksPerBig;j++) {
1244  t->Get((samplePtr)&mBufferInfo[currentIndex].mBufferSouce[0][j*trackBlockSize], floatSample, currentSample, trackBlockSize);
1245  currentSample+=trackBlockSize;
1246  }
1247  if(trackLeftovers) {
1248  t->Get((samplePtr)&mBufferInfo[currentIndex].mBufferSouce[0][trackBlocksPerBig*trackBlockSize], floatSample, currentSample, trackLeftovers);
1249  currentSample+=trackLeftovers;
1250  }
1251  currentSample-=mBlockSize+(mFilterSize>>1);
1252  mBufferInfo[currentIndex].mBufferStatus=BufferReady; // free for grabbin
1253  bigBlocksRead++;
1254  } else mBufferInfo[currentIndex].mBufferStatus=BufferEmpty; // this is completely unecessary
1255  currentIndex=(currentIndex+1)%mWorkerDataCount;
1256  }
1257  }
1258  if(singleProcessLength && !bBreakLoop) {
1259  t->Get((samplePtr)mBigBuffer.get(), floatSample, currentSample, singleProcessLength+mBlockSize+(mFilterSize>>1));
1260  ProcessBuffer(mBigBuffer.get(), mBigBuffer.get(), singleProcessLength+mBlockSize+(mFilterSize>>1));
1261  output->Append((samplePtr)&mBigBuffer[mBlockSize], floatSample, singleProcessLength+mBlockSize+(mFilterSize>>1));
1262  }
1263  output->Flush();
1264  if(!bBreakLoop)
1265  ProcessTail(t, output.get(), start, len);
1266  return bBreakLoop;
1267 }
1268 
1269 
1270 
1271 
1272 void EffectEqualization48x::Filter8x(size_t len,
1273  float *buffer, float *scratchBuffer)
1274 {
1275  int i;
1276  __m256 real256, imag256;
1277  // Apply FFT
1278  RealFFTf8x(buffer, mEffectEqualization->hFFT);
1279 
1280  // Apply filter
1281  // DC component is purely real
1282  __m256 *localFFTBuffer=(__m256 *)scratchBuffer;
1283  __m256 *localBuffer=(__m256 *)buffer;
1284 
1285  __m256 filterFuncR, filterFuncI;
1286  filterFuncR = _mm256_set1_ps(mEffectEqualization->mFilterFuncR[0]);
1287  localFFTBuffer[0] = _mm256_mul_ps(localBuffer[0], filterFuncR);
1288  auto halfLength = (len / 2);
1289 
1290  bool useBitReverseTable = sMathPath & 1;
1291 
1292  for(i = 1; i < halfLength; i++)
1293  {
1294  if(useBitReverseTable) {
1295  real256=localBuffer[mEffectEqualization->hFFT->BitReversed[i] ];
1296  imag256=localBuffer[mEffectEqualization->hFFT->BitReversed[i]+1];
1297  } else {
1298  int bitReversed=SmallRB(i,mEffectEqualization->hFFT->pow2Bits);
1299  real256=localBuffer[bitReversed];
1300  imag256=localBuffer[bitReversed+1];
1301  }
1302  filterFuncR=_mm256_set1_ps(mEffectEqualization->mFilterFuncR[i]);
1303  filterFuncI=_mm256_set1_ps(mEffectEqualization->mFilterFuncI[i]);
1304  localFFTBuffer[2*i ] = _mm256_sub_ps( _mm256_mul_ps(real256, filterFuncR), _mm256_mul_ps(imag256, filterFuncI));
1305  localFFTBuffer[2*i+1] = _mm256_add_ps( _mm256_mul_ps(real256, filterFuncI), _mm256_mul_ps(imag256, filterFuncR));
1306  }
1307  // Fs/2 component is purely real
1308  filterFuncR=_mm256_set1_ps(mEffectEqualization->mFilterFuncR[halfLength]);
1309  localFFTBuffer[1] = _mm256_mul_ps(localBuffer[1], filterFuncR);
1310 
1311  // Inverse FFT and normalization
1312  InverseRealFFTf8x(scratchBuffer, mEffectEqualization->hFFT);
1313  ReorderToTime8x(mEffectEqualization->hFFT, scratchBuffer, buffer);
1314 }
1315 
1316 #endif
1317 
1318 #endif
A list of TrackListNode items.
Definition: Track.h:611
int MessageBox(const wxString &message, long style=DefaultMessageBoxStyle, const wxString &titleStr=wxString{})
Definition: Effect.cpp:2665
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true) const
Definition: WaveTrack.cpp:1977
void CopyInputTracks()
Definition: Effect.cpp:2039
An Effect that modifies volume in different frequency bands.
Definition: Equalization.h:98
Track * Add(std::unique_ptr< TrackKind > &&t)
Add a Track, giving it a fresh id.
Definition: Track.cpp:901
Track::Holder Copy(double t0, double t1, bool forClipboard=true) const override
Definition: WaveTrack.cpp:626
void Clear(double t0, double t1) override
Definition: WaveTrack.cpp:699
float fft_type
Definition: RealFFTf.h:8
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:158
int SmallRB(int bits, int numberBits)
void Join(double t0, double t1)
Definition: WaveTrack.cpp:1509
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
WaveClipHolders & GetClips()
Definition: WaveTrack.h:364
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:94
void TableUsage(int iMask)
void Paste(double t0, const Track *src) override
Definition: WaveTrack.cpp:1197
void ReorderToTime1x(FFTParam *hFFT, fft_type *buffer, fft_type *TimeOut, int functionType=-1)
void Append(samplePtr buffer, sampleFormat format, size_t len, unsigned int stride=1, XMLWriter *blockFileLog=NULL)
Append the sample data to the WaveTrack. You must call Flush() after the last Append.
Definition: WaveTrack.cpp:1563
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:1631
void RealFFTf4x(fft_type *, FFTParam *, int functionType=-1)
void InverseRealFFTf1x(fft_type *, FFTParam *, int functionType=-1)
std::unique_ptr< WaveTrack > NewWaveTrack(sampleFormat format=(sampleFormat) 0, double rate=0)
Definition: WaveTrack.cpp:78
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom"))), OnMoveTrack) void TrackMenuTable::OnSetName(wxCommandEvent &)
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: WaveTrack.cpp:1849
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:300
void InverseRealFFTf4x(fft_type *, FFTParam *, int functionType=-1)
void RealFFTf1x(fft_type *, FFTParam *, int functionType=-1)
double GetEndTime() const
Get the time at which the last clip in the track ends, plus recorded stuff.
Definition: WaveTrack.cpp:1879
double GetRate() const
Definition: WaveTrack.cpp:424
double LongSamplesToTime(sampleCount pos) const
Convert correctly between an number of samples and an (absolute) time in seconds. ...
Definition: WaveTrack.cpp:1854
TrackFactory * GetTrackFactory()
Definition: Project.cpp:1403
double GetStartTime() const
Get the time at which the first clip in the track starts.
Definition: WaveTrack.cpp:1859
void Flush()
Flush must be called after last Append.
Definition: WaveTrack.cpp:1656
void ReorderToTime4x(FFTParam *hFFT, fft_type *buffer, fft_type *TimeOut, int functionType=-1)