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"
59 mAudioRecorder(recorder),
64 mAnalyzerRole(ROLE_IDLE)
77 #if CONFIG_OPTIMIZE_FFT
103 switch (m->getType())
109 auto mpf(std::static_pointer_cast<MessageProjectFile>(m));
110 mPiano = &mpf->getPiano();
122 auto mfkr(std::static_pointer_cast<MessageKeySelectionChanged>(m));
130 auto mmc(std::static_pointer_cast<MessageModeChanged>(m));
131 switch (mmc->getMode())
194 const double timeAtHighest = 0.5;
195 const double timeAtLowest = 3;
196 const double time = (timeAtHighest - timeAtLowest) * globalKey / 88 + timeAtLowest;
264 EptAssert(
mPowerspectrum,
"powerspectrum is accessed after while loop, be sure it is a valid pointer initially");
283 if (packet.size() > 0)
292 LogW(
"Audio buffer size in SignalAnalyzer reached.");
304 bool dataInBuffer =
false;
329 LogI(
"Recording complete, total FFT size = %d.",static_cast<int>(
mPowerspectrum->fft.size()));
347 LogI(
"Detected key: %d", keynumber);
361 LogI(
"Final key could not be found. Cancel analysis.");
368 LogD(
"Final detected key does not match the selected key. Cancel analysis.");
379 MessageHandler::send<MessageNewFFTCalculated>(result.first);
383 MessageHandler::send<MessageFinalKey>(keynumber, result.second);
384 MessageHandler::send<MessagePreliminaryKey>(-1,0);
389 std::shared_ptr<Key> key = std::make_shared<Key>(
mPiano->
getKey(keynumber));
393 MessageHandler::send<MessageNewFFTCalculated>(result->error);
396 key->setTunedFrequency(result->detectedFrequency);
397 MessageHandler::send<MessageFinalKey>(keynumber, key);
399 result->overpullInCents = key->getOverpull();
400 MessageHandler::send<MessageTuningDeviation>(result);
425 #if CONFIG_ENABLE_XMGRACE
426 std::cout <<
"SignalAnalyzer: Writing xmgrace files" << std::endl;
434 #endif // CONFIG_ENABLE_XMGRACE
450 for (
int keynumber=0; keynumber<K; ++keynumber)
454 double change = overpull-currentoverpull;
455 if (fabs(change) >= 0.1 or (currentoverpull!=0 and overpull==0))
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);
463 MessageHandler::send<MessageFinalKey>(keynumber, key);
490 uint N=(uint)signal.size();
495 const double dcBias = std::accumulate(signal.begin(), signal.end(), 0.0) / N;
499 const double a=10.8828*f0/sr;
501 for (
auto &s : signal)
507 follow += a*(s-follow);
515 N=(uint)signal.size();
518 uint blocksize = std::min(N,sr/5);
521 for (uint i=0; i<blocksize; i++) E0+=signal[i]*signal[i];
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);
538 for (uint i=0; i<blocksize; i++) {
539 signal[i] *= (double)i/blocksize;
540 signal[N-i-1] *= (double)i/blocksize;
542 return N/log(E0/E3)/sr;
547 for (
size_t n = 0; n < N; ++n) {
577 std::shared_ptr<FFTPolygon> polygon = std::make_shared<FFTPolygon>();
580 MessageHandler::send<MessageNewFFTCalculated>
600 MessageHandler::sendUnique<MessageKeySelectionChanged>(identifiedKey,
mPiano->
getKeyPtr(identifiedKey));
623 powerspectrum.clear();
625 powerspectrum.push_back (c.real()*c.real()+c.imag()*c.imag());
649 int nullcnt=0, maxcnt=0, mincnt=0;
650 double maxamp=0, minamp=0;
651 for (
auto &y : signal)
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++;
659 const int threshold = signal.size()/50;
660 if (maxcnt+mincnt > threshold)
662 LogW(
"SignalAnalyzer: High-amplitude clipping detected");
665 else if (nullcnt>threshold)
667 LogW(
"SignalAnalyzer: Highly intermittent signal detected (lot of zero amplitudes)");
687 const double fmin = 25;
688 const double fmax = 6000;
689 const double cents = 10;
690 const double factor = pow(2.0,cents/2400);
692 int fftsize = powerspec.size();
693 EptAssert(fftsize>0,
"powerspectum has to be non-empty");
695 auto q = [fftsize,samplingrate] (
double f) {
return 2*fftsize*f/samplingrate; };
697 double qs1 = q(fmin/factor);
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))
703 double qs2 = q(f*factor);
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;
711 q1=q2; qs1=qs2; leftarea=rightarea;
713 EptAssert(ymax>0,
"power should be nonzero");
714 for (
auto &p : poly) p.second /= ymax;
738 auto cmp = [](
const std::pair<int,int>& p1,
const std::pair<int,int>& p2)
739 {
return p1.second < p2.second; };
756 if (keyIndex >= 0 and keyIndex < mPiano->getKeyboard().getNumberOfKeys())
768 MessageHandler::send<MessagePreliminaryKey>(keyIndex,frequency);
779 std::ofstream os(filename);
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;
793 std::ofstream os(filename);
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;
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.
std::mutex mKeyCountStatisticsMutex
Corresponding mutex.
int identifySelectedKey()
identify final key
std::vector< PCMDataType > PacketType
Type definition of a PCM packet (vector of PCM values).
A new fft was calculated.
Abstract adapter class for recording audio signals.
Versatile timer for the entropy tuner.
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.
virtual void keyRecognized(int keyIndex, double frequency) overridefinal
const Key * getKeyPtr(int i) const
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.
void recordPostprocessing()
Process the singal after recording has finsihed.
std::vector< FFTComplexType > FFTComplexVector
Message that a change was made with the current project file.
void waitUntil(int64_t milliseconds, int interval_ms=5)
Wait in idle mode for a certain minimum time span since last reset.
FFTDataPointer mPowerspectrum
the last recorded powerspectrum
std::map< double, double > FFTPolygon
Type for a frequency-to-intensity map for graphics.
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.
static void send(Args &&...args)
short function for creating and sending a message
std::mutex mDataBufferMutex
The data buffer might change its size during recording and key selection, lock it.
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.
The final fft was calculated.
static void setThreadName(std::string s)
Specify the name of the thread.
Mode for recording the piano keys.
CircularBuffer< FFTWType > mDataBuffer
Local audio buffer.
Message that a key has been selected.
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.
void createPolygon(const FFTWVector &powerspec, FFTPolygon &poly) const
Create a polygon for drawing.
const Keyboard & getKeyboard() const
analysis of the signal started
void wait(int milliseconds)
Wait in idle mode without consuming CPU time.
virtual void stop() override
Stop the thread.
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.
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.
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
void recordSignal()
Record signal and perform FFT's in regular intervals.
OverpullEstimator mOverpull
Instance of the overpull estimator.
void WriteSignal(std::string filename, const FFTWVector &signal)
FrequencyDetectionResult detectFrequencyOfKnownKey(FFTDataPointer finalFFT, const Piano *piano, const Key &key, int keyIndex)
FFTAnalyzer::detectFrequencyOfKnownKey.
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.
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
void cutSilence(PacketType &packet)
Cut trailing silence.
int getNumberOfKeys() const
void clear()
Clear the buffer, keeping its maximal size.
analysis of the signal ended
double getOverpull() const
Get overpull in cents.
std::shared_ptr< FrequencyDetectionResultStruct > FrequencyDetectionResult
void reset()
Reset the timer and remember the current time locally.
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
void init()
Initializes the SignalAnalyzer and its components.
void workerFunction() overridefinal
Thread function of the SignalAnalyzer.
const Key & getKey(int i) const