Entropy Piano Tuner  1.1.3 (documentation not yet complete)
An open-source experimental software for piano tuning by entropy minimization
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
signalanalyzer.cpp
Go to the documentation of this file.
1 /*****************************************************************************
2  * Copyright 2015 Haye Hinrichsen, Christoph Wick
3  *
4  * This file is part of Entropy Piano Tuner.
5  *
6  * Entropy Piano Tuner is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * Entropy Piano Tuner is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along with
17  * Entropy Piano Tuner. If not, see http://www.gnu.org/licenses/.
18  *****************************************************************************/
19 
20 //=============================================================================
21 // Signal analyzer
22 //=============================================================================
23 
24 #include "signalanalyzer.h"
25 
26 #include "../system/log.h"
27 #include "../system/timer.h"
28 #include "../config.h"
29 #include "../settings.h"
30 #include "../messages/messagehandler.h"
31 #include "../messages/messageprojectfile.h"
32 #include "../messages/messagenewfftcalculated.h"
33 #include "../messages/messagefinalkey.h"
34 #include "../messages/messagemodechanged.h"
35 #include "../messages/messagepreliminarykey.h"
36 #include "../messages/messagekeyselectionchanged.h"
37 #include "../messages/messagetuningdeviation.h"
38 #include "../audio/recorder/audiorecorderadapter.h"
39 #include "../math/mathtools.h"
40 
41 #include <cmath>
42 #include <iostream>
43 #include <map>
44 #include <algorithm>
45 #include <numeric>
46 
47 //-----------------------------------------------------------------------------
48 // Constructor
49 //-----------------------------------------------------------------------------
50 
55 
57  mPiano(nullptr),
58  mDataBuffer(),
59  mAudioRecorder(recorder),
60  mRecording(false),
61  mKeyRecognizer(this),
62  mSelectedKey(-1),
63  mKeyForced(false),
64  mAnalyzerRole(ROLE_IDLE)
65 {}
66 
67 
74 
76 {
77 #if CONFIG_OPTIMIZE_FFT
78  mKeyRecognizer.init(true);
79 #else
80  mKeyRecognizer.init(false);
81 #endif
82  mKeyCountStatistics.clear();
83 }
84 
85 
86 
90 }
91 
92 //-----------------------------------------------------------------------------
93 // Message receiver and dispatcher
94 //-----------------------------------------------------------------------------
95 
100 
102 {
103  switch (m->getType())
104  {
106  {
107  // stop the thread
108  this->stop();
109  auto mpf(std::static_pointer_cast<MessageProjectFile>(m));
110  mPiano = &mpf->getPiano();
111  updateOverpull();
112  break;
113  }
114  case Message::MSG_RECORDING_STARTED: // start thread
115  start();
116  break;
118  mRecording = false; // set a flag to terminate thread
119  break;
121  {
122  auto mfkr(std::static_pointer_cast<MessageKeySelectionChanged>(m));
123  mSelectedKey = mfkr->getKeyNumber();
124  mKeyForced = mfkr->isForced();
126  break;
127  }
129  {
130  auto mmc(std::static_pointer_cast<MessageModeChanged>(m));
131  switch (mmc->getMode())
132  {
133  case MODE_RECORDING:
135  break;
136  case MODE_TUNING:
138  updateOverpull();
139  break;
140  default:
142  break;
143  }
144 
145  break;
146  }
147  default:
148  break;
149  }
150 }
151 
152 
153 //-----------------------------------------------------------------------------
154 // Function to handle role changing
155 //-----------------------------------------------------------------------------
156 
163 
165 {
166  stop();
167  mAnalyzerRole = role;
169 }
170 
171 
172 
173 
174 
176 {
177  std::lock_guard<std::mutex> lock(mDataBufferMutex);
178 
179  switch (mAnalyzerRole.load())
180  {
181  case ROLE_IDLE:
182  break;
184  {
185  // Initialize the local circular buffer which holds about a minute of data
188  break;
189  }
190  case ROLE_ROLLING_FFT:
191  {
192  // Initialize the local circular buffer which holds 0.5...3 seconds of data
193  const int globalKey = mSelectedKey + 48 - mPiano->getKeyboard().getKeyNumberOfA4();
194  const double timeAtHighest = 0.5;
195  const double timeAtLowest = 3;
196  const double time = (timeAtHighest - timeAtLowest) * globalKey / 88 + timeAtLowest;
197  mDataBuffer.resize(static_cast<size_t>(mAudioRecorder->getSamplingRate() * time));
198  break;
199  }
200  }
201  mDataBuffer.clear();
202 }
203 
204 
205 //-----------------------------------------------------------------------------
206 // Signal analyzer thread function
207 //-----------------------------------------------------------------------------
208 
217 
219 {
220  setThreadName("SignalAnalyzer");
221  // record the signal
223  {
224  recordSignal();
225 
226  // Send message
228 
229  // stop the KeyRecognizer
231 
232  // post process after recording
234 
235  // Send message
237  }
238 }
239 
240 
241 //-----------------------------------------------------------------------------
242 // Record signal
243 //-----------------------------------------------------------------------------
244 
248 
250 {
251  if (cancelThread()) {return;}
252 
253  mRecording = true;
254 
255  mDataBufferMutex.lock();
256  mDataBuffer.clear();
257  mDataBufferMutex.unlock();
258 
259  // Reset the statistics for the majority of recognized keys
260  mKeyCountStatistics.clear();
261 
262  // Create a shared pointer to a vector containing the powerspectrum
263  mPowerspectrum = std::make_shared<FFTData>();
264  EptAssert(mPowerspectrum, "powerspectrum is accessed after while loop, be sure it is a valid pointer initially");
265 
266  // Define a timer in order to send only a limited rate of FFTs.
267  Timer timer;
268 
269  // define the packed to store audio data
270  AudioBase::PacketType packet;
271 
272  // read all data from the audio recorder to clear all buffered data
273  mAudioRecorder->readAll(packet);
274 
275  // get the sampling rate
276  uint samplingrate = mAudioRecorder->getSamplingRate();
277 
278  // Loop that continuously reads the audio stream and performs FFTs
279  while (mRecording and not cancelThread())
280  {
281  timer.reset();
282  mAudioRecorder->readAll(packet); // Read audio data
283  if (packet.size() > 0)
284  {
285  // lock the data puffer if new data available during compile comutation run
286  std::lock_guard<std::mutex> lock(mDataBufferMutex);
287 
288  for (auto &d : packet) mDataBuffer.push_back(d);
289 
292  LogW("Audio buffer size in SignalAnalyzer reached.");
293  }
294  }
295 
296  // If the buffer has accumulated a certain minimum of data
297  if (mDataBuffer.size() > (samplingrate * MINIMAL_FFT_INTERVAL_IN_MILLISECONDS) / 1000)
298  {
299 
300  // Get audio data and make it suitable for analysis
302 
303  // check if there is data in the buffer
304  bool dataInBuffer = false;
305  for (auto &d : mProprocessedSignal) {
306  if (d * d > 0) {
307  // this element contains data
308  dataInBuffer = true;
309  break;
310  }
311  }
312  if (!dataInBuffer) {// no data in buffer
313  continue;
314  }
315 
316  // preprocess signal
317  signalPreprocessing(mProprocessedSignal);
319 
320  // process signal
321  signalProcessing(mProprocessedSignal, samplingrate);
322 
323  // Finally let's wait until a certain minimal time has elapsed.
325  }
326  }
327  timer.wait(50);
328  }
329  LogI("Recording complete, total FFT size = %d.",static_cast<int>(mPowerspectrum->fft.size()));
330 }
331 
332 
333 //-----------------------------------------------------------------------------
334 // Analyze final signal
335 //-----------------------------------------------------------------------------
336 
340 
342 {
344 
345  // and determine key from the key statistics
346  int keynumber = identifySelectedKey();
347  LogI("Detected key: %d", keynumber);
348 
349  // check the audio signal for possible
350  // clipping effects and unusually long strings of zero amplitudes
352 
354 
355  // if the key is forced, we use the forced key number
356  if (mKeyForced) {keynumber = mSelectedKey;}
357 
358  // check if a correct key was found
359  if (keynumber < 0)
360  {
361  LogI("Final key could not be found. Cancel analysis.");
362  return;
363  }
364 
365  // check if found key equates the keynumber
366  if (keynumber != mSelectedKey)
367  {
368  LogD("Final detected key does not match the selected key. Cancel analysis.");
369  return;
370  }
371 
372  // If the key was successfully identified start call the FFTAnalyzer
374  {
375  // returns a pair consisting of error code and key-shared-ptr
376  auto result = mFFTAnalyser.analyse(mPiano, mPowerspectrum, keynumber);
377 
378  if (result.first != FFTAnalyzerErrorTypes::ERR_NONE) // if error
379  MessageHandler::send<MessageNewFFTCalculated>(result.first);
380  else
381  {
382  // send the result
383  MessageHandler::send<MessageFinalKey>(keynumber, result.second);
384  MessageHandler::send<MessagePreliminaryKey>(-1,0);
385  }
386  }
387  else if (mAnalyzerRole == ROLE_ROLLING_FFT)
388  {
389  std::shared_ptr<Key> key = std::make_shared<Key>(mPiano->getKey(keynumber));
391 
392  if (result->error != FFTAnalyzerErrorTypes::ERR_NONE) // if error
393  MessageHandler::send<MessageNewFFTCalculated>(result->error);
394  else
395  {
396  key->setTunedFrequency(result->detectedFrequency);
397  MessageHandler::send<MessageFinalKey>(keynumber, key);
398  }
399  result->overpullInCents = key->getOverpull();
400  MessageHandler::send<MessageTuningDeviation>(result);
401  }
402 }
403 
404 //-----------------------------------------------------------------------------
405 // process the signal after recording has finished
406 //-----------------------------------------------------------------------------
407 
415 
417 {
419 
421  {
422  // do the actual fft analysis
423  analyzeSignal();
424  // Debug output for signals
425 #if CONFIG_ENABLE_XMGRACE
426  std::cout << "SignalAnalyzer: Writing xmgrace files" << std::endl;
427 
428  // this is only for development and will be removed
429  // do the actual fft analysis
431  WriteSignal("1-preprocessed-signal.dat", mProprocessedSignal);
432  WriteSignal("0-raw-signal.dat", mDataBuffer.getOrderedData());
433  WriteFFT ("2-final-fft.dat",mPowerspectrum.get()->fft);
434 #endif // CONFIG_ENABLE_XMGRACE
435 
436  // sleep a bit to wait for the synthesizer to finish and to display the quality
437  msleep(1500);
438  }
440 }
441 
442 
443 //-----------------------------------------------------------------------------
444 // Update overpulls
445 //-----------------------------------------------------------------------------
446 
448 {
449  int K = mPiano->getKeyboard().getNumberOfKeys();
450  for (int keynumber=0; keynumber<K; ++keynumber)
451  {
452  double overpull = mOverpull.getOverpull(keynumber,mPiano);
453  double currentoverpull = mPiano->getKey(keynumber).getOverpull();
454  double change = overpull-currentoverpull;
455  if (fabs(change) >= 0.1 or (currentoverpull!=0 and overpull==0))
456  {
457  // set new overpull value
458  std::shared_ptr<Key> key = std::make_shared<Key>(mPiano->getKey(keynumber));
459  double existingtune = key->getTunedFrequency();
460  if (existingtune>20 and currentoverpull!=0)
461  key->setTunedFrequency(existingtune*pow(2,change/1200.0));
462  key->setOverpull(overpull); // this informs the tuning curve window
463  MessageHandler::send<MessageFinalKey>(keynumber, key);
464  }
465  }
466 
467 }
468 
469 //-----------------------------------------------------------------------------
470 // Signal preprocessing
471 //-----------------------------------------------------------------------------
472 
485 
487 {
488 
489  const uint sr = mAudioRecorder->getSamplingRate();
490  uint N=(uint)signal.size();
491  if (N==0) return 0;
492 
493  // 1. Remove dc-Bias and cut subsonic waves
494  // For a derivation see Mathematica file in the doc folder
495  const double dcBias = std::accumulate(signal.begin(), signal.end(), 0.0) / N;
496  //for (auto &s : signal) {s -= dcBias;}
497 
498  const double f0 = 5; // Frequency to be suppressed by 50%
499  const double a=10.8828*f0/sr; // Damping factor
500  double follow=0;
501  for (auto &s : signal)
502  {
503  // remove dc-bias
504  s -= dcBias;
505 
506  // subsonic waves
507  follow += a*(s-follow);
508  s -= follow;
509  }
510 
511  // cut silence
512  mAudioRecorder->cutSilence(signal);
513 
514  // signal size may be changed
515  N=(uint)signal.size();
516 
517  // 2. Determine the initial energy of the keystroke:
518  uint blocksize = std::min(N,sr/5); // 0.2 sec
519  assert(blocksize>0);
520  double E0=0;
521  for (uint i=0; i<blocksize; i++) E0+=signal[i]*signal[i];
522  E0 *= 2.0/blocksize;
523 
525  {
526  // 3. Modify the input signal in such a way that the volume is constant
527  const double gamma=50.0/sr;
528  double E1=E0,E2=E0,E3=E0;
529  for (auto &s : signal) {
530  E1 += gamma * (s*s-E1);
531  E2 += gamma * (E1-E2);
532  E3 += gamma * (E2-E3);
533  s /= (sqrt(fabs(E3))+0.001);
534  }
535 
536  // 4. Fade in and out at the end of the buffer
537  blocksize = N/50;
538  for (uint i=0; i<blocksize; i++) {
539  signal[i] *= (double)i/blocksize;
540  signal[N-i-1] *= (double)i/blocksize;
541  }
542  return N/log(E0/E3)/sr; // return the average decay time of the envelope
543  }
544  else if (mAnalyzerRole == ROLE_ROLLING_FFT)
545  {
546  // 4. Apply a hanning window
547  for (size_t n = 0; n < N; ++n) {
548 // signal[n] *= 0.5 * (1.0 - cos((MathTools::TWO_PI * n) / (N - 1)));
549  }
550 
551  }
552 
553  return 0;
554 }
555 
556 //-----------------------------------------------------------------------------
557 // Signal preprocessing
558 //-----------------------------------------------------------------------------
559 
568 
569 void SignalAnalyzer::signalProcessing(FFTWVector &signal, int samplingrate) {
570  mPowerspectrum = std::make_shared<FFTData>();
571  mPowerspectrum->samplingRate = samplingrate;
572  PerformFFT(signal, mPowerspectrum->fft);
573  if (cancelThread()) return;
574 
575  // The FFT is too long to be plotted. Therefore, we
576  // create here a shorter polygon and transmit it by a message
577  std::shared_ptr<FFTPolygon> polygon = std::make_shared<FFTPolygon>();
578  createPolygon (mPowerspectrum->fft, *polygon.get());
579 
580  MessageHandler::send<MessageNewFFTCalculated>
583  mPowerspectrum, polygon);
584 
585  // recognize key
587 
589  analyzeSignal();
590  }
591 
592  // automatic key selection
593  if (!Settings::getSingleton().isAutomaticKeySelectionDisabled()) {
594  // if the recognizer finds a key next to the current key, change the selected key
595  // of course if the user forced the key, this should not work
596  // also select the key if there is non selected
597  int identifiedKey = identifySelectedKey();
598  if ((!mKeyForced && abs(identifiedKey - mSelectedKey) == 1 && identifiedKey != -1)
599  || mSelectedKey == -1) {
600  MessageHandler::sendUnique<MessageKeySelectionChanged>(identifiedKey, mPiano->getKeyPtr(identifiedKey));
601  }
602  }
603 }
604 
605 //-----------------------------------------------------------------------------
606 // Fast Fourier transform
607 //-----------------------------------------------------------------------------
608 
618 
619 void SignalAnalyzer::PerformFFT (FFTWVector &signal, FFTWVector &powerspectrum)
620 {
621  FFTComplexVector cvec;
622  mFFT.calculateFFT(signal,cvec);
623  powerspectrum.clear();
624  for (auto &c : cvec)
625  powerspectrum.push_back (c.real()*c.real()+c.imag()*c.imag());
626 }
627 
628 
629 //-----------------------------------------------------------------------------
630 // Clipping detector
631 //-----------------------------------------------------------------------------
632 
646 
648 {
649  int nullcnt=0, maxcnt=0, mincnt=0;
650  double maxamp=0, minamp=0;
651  for (auto &y : signal)
652  {
653  if (y>maxamp) maxamp=y;
654  else if (y>=maxamp*0.99) maxcnt++;
655  if (y<minamp) minamp=y;
656  else if (y<=minamp*0.99) mincnt++;
657  if (y==0) nullcnt++;
658  }
659  const int threshold = signal.size()/50;
660  if (maxcnt+mincnt > threshold)
661  {
662  LogW("SignalAnalyzer: High-amplitude clipping detected");
663  return true;
664  }
665  else if (nullcnt>threshold)
666  {
667  LogW("SignalAnalyzer: Highly intermittent signal detected (lot of zero amplitudes)");
668  return true;
669  }
670  return false;
671 }
672 
673 
674 //-----------------------------------------------------------------------------
675 // Create polygon for drawing
676 //-----------------------------------------------------------------------------
677 
684 
685 void SignalAnalyzer::createPolygon (const FFTWVector &powerspec, FFTPolygon &poly) const
686 {
687  const double fmin = 25;
688  const double fmax = 6000;
689  const double cents = 10;
690  const double factor = pow(2.0,cents/2400);
691 
692  int fftsize = powerspec.size();
693  EptAssert(fftsize>0,"powerspectum has to be non-empty");
694  int samplingrate = mAudioRecorder->getSamplingRate();
695  auto q = [fftsize,samplingrate] (double f) { return 2*fftsize*f/samplingrate; };
696 
697  double qs1 = q(fmin/factor);
698  int q1 = std::max<int>(0, MathTools::roundToInteger(qs1));
699  double leftarea = (q1-qs1+0.5)*powerspec[q1];
700  double ymax=0, df = samplingrate / 2 / fftsize;
701  for (double f=fmin; f<=fmax; f=std::max(f*factor*factor,f+df))
702  {
703  double qs2 = q(f*factor);
704  int q2 = std::min<int>(MathTools::roundToInteger(qs2), powerspec.size() - 1);
705  double sum=0;
706  for (int q=q1+1; q<=q2; ++q) sum += powerspec[q];
707  double rightarea = (q2-qs2+0.5)*powerspec[q2];
708  double y = sum + leftarea - rightarea;
709  if (y>ymax) ymax=y;
710  poly[f] = y;
711  q1=q2; qs1=qs2; leftarea=rightarea;
712  }
713  EptAssert(ymax>0,"power should be nonzero");
714  for (auto &p : poly) p.second /= ymax; // normalize
715 
716 }
717 
718 
719 //-----------------------------------------------------------------------------
720 // Identify the selected key
721 //-----------------------------------------------------------------------------
722 
732 
734 {
735  // Take the majority selection from KeyRecognizer
736  std::lock_guard<std::mutex> lock(mKeyCountStatisticsMutex);
737  if (mKeyCountStatistics.size()==0) return -1;
738  auto cmp = [](const std::pair<int,int>& p1, const std::pair<int,int>& p2)
739  { return p1.second < p2.second; };
740  auto max = std::max_element(mKeyCountStatistics.begin(),mKeyCountStatistics.end(),cmp);
741  if (max->second > static_cast<int>(mKeyCountStatistics.size()/2)) return max->first;
742 
743  return -1;
744 }
745 
746 //-----------------------------------------------------------------------------
747 // Callback function of the KeyRecognizer
748 //-----------------------------------------------------------------------------
749 
750 void SignalAnalyzer::keyRecognized(int keyIndex, double frequency)
751 {
752  EptAssert(mPiano, "Piano has to be set.");
753 
755  // fetch the current key from the statistics
756  if (keyIndex >= 0 and keyIndex < mPiano->getKeyboard().getNumberOfKeys())
757  {
758  std::lock_guard<std::mutex> lock(mKeyCountStatisticsMutex);
759  mKeyCountStatistics[keyIndex]++;
760  }
761  MessageHandler::send<MessagePreliminaryKey>(identifySelectedKey(),frequency);
762  } else {
763  std::lock_guard<std::mutex> lock(mKeyCountStatisticsMutex);
764  // this is the sole key
765  mKeyCountStatistics.clear();
766  mKeyCountStatistics[keyIndex]++;
767  // send it
768  MessageHandler::send<MessagePreliminaryKey>(keyIndex,frequency);
769  }
770 }
771 
772 //-----------------------------------------------------------------------------
773 // write spectrum to disk in XMGRACE-readable format
774 //-----------------------------------------------------------------------------
775 
776 
777 void SignalAnalyzer::WriteFFT (std::string filename, const FFTWVector &fft)
778 {
779  std::ofstream os(filename);
780  uint samplingrate = mAudioRecorder->getSamplingRate();
781  uint fftsize = fft.size();
782  auto qtof = [samplingrate,fftsize] (int q) { return (double)samplingrate*q/fftsize/2; };
783  os << "@g0 type logy" << std::endl;
784  os << "@ xaxis label \"FREQUENCY (HZ)\"" << std::endl;
785  os << "@ yaxis label \"INTENSITY\"" << std::endl;
786  os << "@ subtitle \"SPECTRUM OF THE RECORDED SIGNAL\"" << std::endl;
787  for (uint q=0; q<fft.size(); ++q) os << qtof(q) << "\t" << fft[q] << std::endl;
788  os.close();
789 }
790 
791 void SignalAnalyzer::WriteSignal (std::string filename, const FFTWVector &signal)
792 {
793  std::ofstream os(filename);
794  int samplingrate = mAudioRecorder->getSamplingRate();
795  os << "@ xaxis label \"TIME (s)\"" << std::endl;
796  os << "@ yaxis label \"AMPLITUDE\"" << std::endl;
797  os << "@ subtitle \"RECORDED SIGNAL\"" << std::endl;
798  for (uint i=0; i<signal.size(); ++i) os << (double)i/samplingrate << "\t" << signal[i] << std::endl;
799  os.close();
800 }
std::size_t maximum_size() const
Return actual maximal size.
bool detectClipping(FFTWVector signal)
Clipping detector.
void changeRole(AnalyzerRole role)
Changes the current role of the signal analyzer.
std::shared_ptr< Message > MessagePtr
Global type of a shared message pointer.
Definition: message.h:98
std::mutex mKeyCountStatisticsMutex
Corresponding mutex.
int identifySelectedKey()
identify final key
std::vector< PCMDataType > PacketType
Type definition of a PCM packet (vector of PCM values).
Definition: audiobase.h:51
Abstract adapter class for recording audio signals.
Versatile timer for the entropy tuner.
Definition: timer.h:39
virtual void stop()
Stop the thread.
std::vector< data_type > getOrderedData() const
Copy entire data in a time-ordered form.
#define CHECK_CANCEL_THREAD
static Settings & getSingleton()
Get a pointer to the singleton instance.
Definition: settings.cpp:46
virtual void keyRecognized(int keyIndex, double frequency) overridefinal
const Key * getKeyPtr(int i) const
Definition: piano.h:88
void msleep(double milliseconds)
Sleep function for staying idle.
static const int AUDIO_BUFFER_SIZE_IN_SECONDS
Maximal size of the audio buffer.
std::atomic< AnalyzerRole > mAnalyzerRole
void signalProcessing(FFTWVector &signal, int samplingrate)
Function for signal processing.
Performing rolling ffts in tuning mode.
AudioRecorderAdapter * mAudioRecorder
Pointer to the audio recorder.
#define LogW(...)
Definition: log.h:56
void recordPostprocessing()
Process the singal after recording has finsihed.
std::vector< FFTComplexType > FFTComplexVector
Definition: fftadapter.h:36
Message that a change was made with the current project file.
Definition: message.h:68
void waitUntil(int64_t milliseconds, int interval_ms=5)
Wait in idle mode for a certain minimum time span since last reset.
Definition: timer.cpp:112
FFTDataPointer mPowerspectrum
the last recorded powerspectrum
std::map< double, double > FFTPolygon
Type for a frequency-to-intensity map for graphics.
Definition: fftadapter.h:45
void WriteFFT(std::string filename, const FFTWVector &fft)
void init(bool optimize)
Initialization of the KeyRecognizer.
static const int MINIMAL_FFT_INTERVAL_IN_MILLISECONDS
Time interval for at most one FFT.
Message that the operation mode has changed.
Definition: message.h:65
static void send(Args &&...args)
short function for creating and sending a message
#define LogD(...)
Definition: log.h:44
std::mutex mDataBufferMutex
The data buffer might change its size during recording and key selection, lock it.
int roundToInteger(T x)
Round a floating point number to an integer.
Definition: mathtools.h:43
std::atomic< bool > mRecording
Flag indicating ongoing recording.
void handleMessage(MessagePtr m) overridefinal
Message receiver and dispatcer.
FFTAnalyzer mFFTAnalyser
Instance of the FFT analyzer.
bool mKeyForced
Is the key selection forced.
static void setThreadName(std::string s)
Specify the name of the thread.
Mode for recording the piano keys.
Definition: prerequisites.h:69
CircularBuffer< FFTWType > mDataBuffer
Local audio buffer.
Message that a key has been selected.
Definition: message.h:63
std::map< int, int > mKeyCountStatistics
Count which key is selected how often.
std::pair< FFTAnalyzerErrorTypes, std::shared_ptr< Key > > analyse(const Piano *piano, FFTDataPointer finalFFT, int finalKey)
Main analyzing function.
Definition: fftanalyzer.cpp:62
void createPolygon(const FFTWVector &powerspec, FFTPolygon &poly) const
Create a polygon for drawing.
const Keyboard & getKeyboard() const
Definition: piano.h:83
analysis of the signal started
Definition: message.h:53
void wait(int milliseconds)
Wait in idle mode without consuming CPU time.
Definition: timer.cpp:67
virtual void stop() override
Stop the thread.
#define LogI(...)
Definition: log.h:50
KeyRecognizer mKeyRecognizer
Instance of the Key recognizer.
virtual void start()
Start the thread.
void updateDataBufferSize()
void readAll(PacketType &packet)
Read all data from the internal buffer.
void analyzeSignal()
Analyze the final signal (in recording mode only)
FFT_Implementation mFFT
Instance of the Fourier transformer.
SignalAnalyzer(AudioRecorderAdapter *recorder)
Constructor of the SignalAnalyzer.
int getSamplingRate() const
Get the actual sampling rate.
Definition: audiobase.cpp:78
void resize(std::size_t maximum_size)
Resize the buffer, shrink oldest data if necessary.
std::size_t size() const
Return current buffer size.
Mode for manually tuning the piano.
Definition: prerequisites.h:71
void push_back(const data_type &data)
Append a new data element to the buffer.
void recognizeKey(bool forceRestart, const Piano *piano, FFTDataPointer fftPointer, int selectedKey, bool keyForced)
Start key recognition.
int getKeyNumberOfA4() const
Definition: keyboard.h:73
void recordSignal()
Record signal and perform FFT's in regular intervals.
OverpullEstimator mOverpull
Instance of the overpull estimator.
#define EptAssert(a, b)
Definition: eptexception.h:47
void WriteSignal(std::string filename, const FFTWVector &signal)
FrequencyDetectionResult detectFrequencyOfKnownKey(FFTDataPointer finalFFT, const Piano *piano, const Key &key, int keyIndex)
FFTAnalyzer::detectFrequencyOfKnownKey.
recording ended
Definition: message.h:52
FFTWVector mProprocessedSignal
the current signal (after preprocessing)
double getOverpull(int keynumber, const Piano *piano)
Compute the required overpull on the basis of the interaction matrix.
Definition: overpull.cpp:245
bool cancelThread() const
Cancel-flag getter method, thread-safe.
void PerformFFT(FFTWVector &signal, FFTWVector &powerspec)
Perform fast Fourier transform.
std::vector< FFTWType > FFTWVector
fftw array
Definition: fftadapter.h:43
void cutSilence(PacketType &packet)
Cut trailing silence.
int getNumberOfKeys() const
Definition: keyboard.h:72
void clear()
Clear the buffer, keeping its maximal size.
analysis of the signal ended
Definition: message.h:54
double getOverpull() const
Get overpull in cents.
Definition: key.cpp:211
std::shared_ptr< FrequencyDetectionResultStruct > FrequencyDetectionResult
void reset()
Reset the timer and remember the current time locally.
Definition: timer.cpp:50
double signalPreprocessing(FFTWVector &signal)
Function for signal preprocessing.
Recording a key stroke in recording operation mode.
int mSelectedKey
The selected key by the user.
const Piano * mPiano
Pointer to the piano.
void calculateFFT(const FFTRealVector &in, FFTComplexVector &out)
Foward FFT, mapping a real vector with size N to a complex one with size N/2+1.
keystroke recognized and recording started
Definition: message.h:51
void init()
Initializes the SignalAnalyzer and its components.
void workerFunction() overridefinal
Thread function of the SignalAnalyzer.
const Key & getKey(int i) const
Definition: piano.h:86