Tuner: worker thread for analyse (don't work)

This commit is contained in:
Louis-Joseph Fournier 2016-01-05 00:17:20 +01:00
parent e75e79bb2d
commit 5a341979df
12 changed files with 507 additions and 299 deletions

View file

@ -1,13 +1,14 @@
QT += qml quick gui multimedia dbus QT += qml quick gui multimedia dbus
TARGET = Tuner TARGET = Tuner
CONFIG += c++11 CONFIG += c++11 debug
# PKGCONFIG += libpulse # PKGCONFIG += libpulse
SOURCES += \ SOURCES += \
src/desktop.cpp \ src/desktop.cpp \
src/Tuner.cpp \ src/Tuner.cpp \
src/TunerWorker.cpp \
src/audio/LinearFilter.cpp \ src/audio/LinearFilter.cpp \
src/audio/ZeroCross.cpp \ src/audio/ZeroCross.cpp \
src/scale/Scale.cpp \ src/scale/Scale.cpp \
@ -15,6 +16,7 @@ SOURCES += \
HEADERS += \ HEADERS += \
src/Tuner.hpp \ src/Tuner.hpp \
src/TunerWorker.hpp \
src/audio/LinearFilter.hpp \ src/audio/LinearFilter.hpp \
src/audio/ZeroCross.hpp \ src/audio/ZeroCross.hpp \
src/scale/Scale.hpp \ src/scale/Scale.hpp \

View file

@ -13,6 +13,7 @@ RESOURCES += \
SOURCES += \ SOURCES += \
src/sailfish.cpp \ src/sailfish.cpp \
src/Tuner.cpp \ src/Tuner.cpp \
src/TunerWorker.cpp \
src/audio/LinearFilter.cpp \ src/audio/LinearFilter.cpp \
src/audio/ZeroCross.cpp \ src/audio/ZeroCross.cpp \
src/scale/Scale.cpp \ src/scale/Scale.cpp \
@ -20,6 +21,7 @@ SOURCES += \
HEADERS += \ HEADERS += \
src/Tuner.hpp \ src/Tuner.hpp \
src/TunerWorker.hpp \
src/audio/LinearFilter.hpp \ src/audio/LinearFilter.hpp \
src/audio/ZeroCross.hpp \ src/audio/ZeroCross.hpp \
src/scale/Scale.hpp \ src/scale/Scale.hpp \

View file

@ -19,229 +19,57 @@
#include <stdint.h> #include <stdint.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QString>
#include <QStringList>
#include <QUrl> #include <QUrl>
#include <QDBusConnection>
#include <QDBusInterface>
#include <iostream> #include <iostream>
#include <fstream>
#include "Tuner.hpp" #include "Tuner.hpp"
using namespace std; using namespace std;
// high 10hz / 16k
static double a10[] = { 1 , -2.99214602, 2.98432286, -0.99217678 };
static double b10[] = { 0.99608071, -2.98824212, 2.98824212, -0.99608071 };
const char * Tuner::filename_record = NULL;
// function to prevent screen blank
static void blank_prevent(bool prevent)
{
cerr << __func__ << endl;
QDBusConnection system = QDBusConnection::connectToBus(QDBusConnection::SystemBus, "system");
QDBusInterface interface("com.nokia.mce", "/com/nokia/mce/request", "com.nokia.mce.request", system);
if (prevent) {
interface.call(QLatin1String("req_display_blanking_pause"));
} else {
interface.call(QLatin1String("req_display_cancel_blanking_pause"));
}
}
Tuner::Tuner() Tuner::Tuner()
{ {
running = false; running = false;
freq = deviation = 0; freq = deviation = 0;
note = octave = 0; note = octave = 0;
found = false; found = false;
count_found = count_not_found = 0; la = Scale::defaultLa;
nb_sample_running = 0;
note_found = octave_found = -1;
if (filename_record) file_record.open(filename_record); worker = new TunerWorker();
high_filter = new LinearFilter<int16_t>(3, a10, b10);
ZeroCross<int16_t>::Config cross_config({rate, defaultNbFrame, defaultFreqMin, defaultFreqMax});
cross = new ZeroCross<int16_t>(cross_config);
scale = new Scale();
temperaments = new Temperaments(":/data"); temperaments = new Temperaments(":/data");
if (temperaments->SetTemperament(0)) { if (temperaments->SetTemperament(0)) {
scale->SetNotesFrequencies(temperaments->NotesFrequencies()); worker->SetNotesFrequencies(temperaments->NotesFrequencies());
}
else {
scale->ConstructEqualTemperament();
} }
settings.setCodec("audio/PCM"); worker->moveToThread(&workerThread);
settings.setChannelCount(1);
settings.setSampleRate(rate);
recorder = new QAudioRecorder(this); connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
recorder->setAudioInput("pulseaudio:"); connect(&workerThread, &QThread::started, worker, &TunerWorker::Entry);
recorder->setAudioSettings(settings); connect(this, &Tuner::quit, worker, &TunerWorker::Quit, Qt::DirectConnection);
QUrl url = QString("/dev/null"); connect(this, &Tuner::start, worker, &TunerWorker::Start);
recorder->setOutputLocation(url); connect(this, &Tuner::stop, worker, &TunerWorker::Stop);
connect(this, &Tuner::setNotesFrequencies, worker, &TunerWorker::SetNotesFrequencies);
connect(this, &Tuner::setLa, worker, &TunerWorker::SetLa);
probe = new QAudioProbe(this); connect(worker, &TunerWorker::resultFound, this, &Tuner::ResultFound);
connect(probe, SIGNAL(audioBufferProbed(QAudioBuffer)), this, SLOT(AudioCb(QAudioBuffer))); connect(worker, &TunerWorker::resultNotFound, this, &Tuner::ResultNotFound);
probe->setSource(recorder);
workerThread.start();
} }
Tuner::~Tuner() Tuner::~Tuner()
{ {
if (filename_record && file_record.is_open()) file_record.close(); quit();
delete high_filter; // workerThread.quit();
delete cross; workerThread.wait(10);
delete recorder;
delete probe;
delete scale;
delete temperaments; delete temperaments;
} }
void Tuner::Start()
{
cerr << __func__ << endl;
count_found = count_not_found = 0;
nb_sample_running = 0;
note_found = octave_found = -1;
ResetDeviation();
blank_prevent(true);
high_filter->Clear();
cross->Clear();
recorder->record();
running = true;
runningChanged();
}
void Tuner::Stop()
{
cerr << __func__ << endl;
running = false;
recorder->stop();
runningChanged();
blank_prevent(false);
}
void Tuner::ComputeFrame(int16_t v)
{
v = (*high_filter)(v);
(*cross)(v);
}
void Tuner::AudioCb(const QAudioBuffer &buffer)
{
const int16_t *ptr = buffer.constData<int16_t>();
const int nbFrame = buffer.sampleCount();
AudioAnalyse(ptr, nbFrame);
}
void Tuner::ResetDeviation()
{
// reset deviation values
nb_deviation = 0;
deviation_start = 0;
deviation_sum = 0;
}
void Tuner::UpdateDeviation(double d)
{
if (nb_deviation == nbDeviationValues) {
deviation_sum -= deviation_values[deviation_start];
deviation_start = (deviation_start + 1) % nbDeviationValues;
nb_deviation--;
}
deviation_values[(deviation_start + nb_deviation) % nbDeviationValues] = d;
nb_deviation++;
deviation_sum += d;
}
void Tuner::SetFound(int n, int o, double d)
{
if (n != note_found || o != octave_found) {
note_found = n;
octave_found = o;
count_found = 0;
SetNotFound();
ResetDeviation();
UpdateDeviation(d);
}
else if (count_found++ >= nbConfirm) {
count_not_found = 0;
if (!found) {
found = true;
foundChanged();
}
if (note != n) {
note = n;
noteChanged();
}
if (octave != o) {
octave = o;
octaveChanged();
}
UpdateDeviation(d);
deviation = deviation_sum / nb_deviation;
deviationChanged();
}
else {
UpdateDeviation(d);
}
}
void Tuner::SetNotFound()
{
if (count_not_found++ >= nbDefect) {
count_found = 0;
if (found) {
found = false;
ResetDeviation();
foundChanged();
}
}
}
void Tuner::AudioAnalyse(const int16_t *ptr, int nb_frame)
{
nb_sample_running += nb_frame;
// record in file is needed
if (filename_record && file_record.is_open()) file_record.write((char*) ptr, nb_frame * sizeof(int16_t));
// compute every audio frame
while (nb_frame--) ComputeFrame(*ptr++);
// update frequency
if (freq != cross->Freq()) {
freq = cross->Freq();
freqChanged();
}
// find note, octave, deviation
if (freq) {
int n, o = 0;
double d = 0;
n = scale->FindNote(freq, o, d);
SetFound(n, o, d);
}
else { // no freq
SetNotFound();
}
// prevent screen blanking
if (nb_sample_running >= nbSamplePreventRunning && running) {
nb_sample_running = 0;
blank_prevent(true);
}
}
bool Tuner::GetRunning() bool Tuner::GetRunning()
{ {
return running; return running;
@ -251,8 +79,11 @@ void Tuner::SetRunning(bool r)
{ {
if (running == r) return; if (running == r) return;
if (r) Start(); running = r;
else Stop(); if (r) start();
else stop();
runningChanged();
} }
double Tuner::GetFreq() double Tuner::GetFreq()
@ -275,16 +106,23 @@ int Tuner::GetOctave()
return octave; return octave;
} }
const char* Tuner::GetNoteName()
{
return scale->NoteName(note);
}
bool Tuner::GetFound() bool Tuner::GetFound()
{ {
return found; return found;
} }
void Tuner::SetLa(double la)
{
this->la = la;
setLa(la);
laChanged();
}
double Tuner::GetLa()
{
return la;
}
unsigned int Tuner::GetTemperamentIndex() unsigned int Tuner::GetTemperamentIndex()
{ {
return temperaments->GetCurrentIndex(); return temperaments->GetCurrentIndex();
@ -293,7 +131,7 @@ unsigned int Tuner::GetTemperamentIndex()
void Tuner::SetTemperamentIndex(unsigned int idx) void Tuner::SetTemperamentIndex(unsigned int idx)
{ {
if (temperaments->SetTemperament(idx)) { if (temperaments->SetTemperament(idx)) {
scale->SetNotesFrequencies(temperaments->NotesFrequencies()); setNotesFrequencies(temperaments->NotesFrequencies());
temperamentChanged(); temperamentChanged();
} }
} }
@ -303,35 +141,23 @@ QStringList Tuner::GetTemperamentList() const
return temperaments->GetNames(); return temperaments->GetNames();
} }
/// Set a filename to record raw audio stream void Tuner::ResultFound(int note, int octave, double deviation, double freq)
void Tuner::set_record(const char *f)
{ {
filename_record = f; this->note = note;
} this->octave = octave;
this->deviation = deviation;
this->freq = freq;
resultChanged();
/// analyse a file (static function) if (!found) {
void Tuner::analyse_file(const char *filename) foundChanged();
{ found = true;
cout << "analyse file " << filename << endl;
ifstream fin;
fin.open(filename);
const int nb_frame = 1024;
Tuner *tuner = new Tuner();
int16_t buffer[nb_frame];
while (1) {
fin.read((char*) buffer, sizeof(buffer));
tuner->AudioAnalyse(buffer, sizeof(buffer) >> 1);
cout << tuner->GetFreq() << " ";
if (tuner->GetFound())
cout << tuner->GetNoteName() << " " << tuner->GetOctave() << " " << tuner->GetDeviation();
cout << endl;
if (fin.eof()) break;
} }
fin.close(); }
delete tuner;
void Tuner::ResultNotFound(double freq)
{
this->freq = freq;
found = false;
foundChanged();
} }

View file

@ -15,79 +15,37 @@
* *
*/ */
#include <QAudioRecorder> #ifndef _TUNER_HPP
#include <QAudioProbe> #define _TUNER_HPP
#include <fstream> #include "TunerWorker.hpp"
#include "audio/LinearFilter.hpp"
#include "audio/ZeroCross.hpp"
#include "scale/Scale.hpp"
#include "scale/Temperaments.hpp" #include "scale/Temperaments.hpp"
class Tuner : public QObject { class Tuner : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool running READ GetRunning WRITE SetRunning NOTIFY runningChanged) Q_PROPERTY(bool running READ GetRunning WRITE SetRunning NOTIFY runningChanged)
Q_PROPERTY(double freq READ GetFreq NOTIFY freqChanged) Q_PROPERTY(double freq READ GetFreq NOTIFY resultChanged)
Q_PROPERTY(double deviation READ GetDeviation NOTIFY deviationChanged) Q_PROPERTY(double deviation READ GetDeviation NOTIFY resultChanged)
Q_PROPERTY(int note READ GetNote NOTIFY noteChanged) Q_PROPERTY(int note READ GetNote NOTIFY resultChanged)
Q_PROPERTY(int octave READ GetOctave NOTIFY octaveChanged) Q_PROPERTY(int octave READ GetOctave NOTIFY resultChanged)
Q_PROPERTY(bool found READ GetFound NOTIFY foundChanged) Q_PROPERTY(bool found READ GetFound NOTIFY foundChanged)
Q_PROPERTY(QString noteName READ GetNoteName NOTIFY noteNameChanged)
Q_PROPERTY(int temperament_idx READ GetTemperamentIndex WRITE SetTemperamentIndex NOTIFY temperamentChanged) Q_PROPERTY(int temperament_idx READ GetTemperamentIndex WRITE SetTemperamentIndex NOTIFY temperamentChanged)
Q_PROPERTY(QStringList temperament_list READ GetTemperamentList) Q_PROPERTY(QStringList temperament_list READ GetTemperamentList)
Q_PROPERTY(double la READ GetLa WRITE SetLa NOTIFY laChanged)
private: private:
QAudioRecorder *recorder;
QAudioEncoderSettings settings;
QAudioProbe *probe;
LinearFilter<int16_t> *high_filter;
ZeroCross<int16_t> *cross;
Scale *scale;
Temperaments *temperaments; Temperaments *temperaments;
static const char *filename_record; TunerWorker *worker;
std::ofstream file_record; QThread workerThread;
bool running, found; bool running, found;
double freq, deviation; double freq, deviation, la;
int note, octave, nb_sample_running; int note, octave;
int note_found, octave_found, count_found, count_not_found;
int nb_deviation, deviation_start;
double deviation_sum;
static const int rate = 16000;
static const int defaultNbFrame = 1024;
static const int defaultFreqMin = 50;
static const int defaultFreqMax = 2000;
static const int nbSamplePreventRunning = rate * 40; // 40 seconds
/// number of analyses to confirm a note
static const int nbConfirm = 3;
/// number of analyses to drop a note
static const int nbDefect = 20;
/// number of deviation values for average
static const int nbDeviationValues = 8;
double deviation_values[nbDeviationValues];
inline void ComputeFrame(int16_t v);
void SetFound(int note, int octave, double deviation);
void SetNotFound();
void ResetDeviation();
void UpdateDeviation(double d);
private slots:
void AudioCb(const QAudioBuffer &buffer);
public: public:
Tuner(); Tuner();
~Tuner(); ~Tuner();
void Start();
void Stop();
void AudioAnalyse(const int16_t *buffer, int size);
bool GetRunning(); bool GetRunning();
void SetRunning(bool r); void SetRunning(bool r);
double GetFreq(); double GetFreq();
@ -95,23 +53,29 @@ class Tuner : public QObject {
int GetOctave(); int GetOctave();
double GetDeviation(); double GetDeviation();
bool GetFound(); bool GetFound();
const char* GetNoteName();
unsigned int GetTemperamentIndex(); unsigned int GetTemperamentIndex();
void SetTemperamentIndex(unsigned int idx); void SetTemperamentIndex(unsigned int idx);
QStringList GetTemperamentList() const; QStringList GetTemperamentList() const;
double GetLa();
void SetLa(double la);
/// analyse a file for debug public slots:
static void analyse_file(const char *filename); void ResultFound(int note, int octave, double deviation, double frequency);
/// write a file with raw audio void ResultNotFound(double freq);
static void set_record(const char *filename_record);
signals: signals:
void runningChanged(); void runningChanged();
void freqChanged();
void noteChanged();
void noteNameChanged();
void octaveChanged();
void deviationChanged();
void foundChanged(); void foundChanged();
void temperamentChanged(); void temperamentChanged();
void laChanged();
void resultChanged();
// signals to worker
void quit();
void start();
void stop();
void setNotesFrequencies(const double *freq);
void setLa(double la_freq);
}; };
#endif

297
src/TunerWorker.cpp Normal file
View file

@ -0,0 +1,297 @@
/* Copyright 2016 (C) Louis-Joseph Fournier
* louisjoseph.fournier@gmail.com
*
* This file is part of SailTuner.
*
* SailTuner is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SailTuner is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <QDBusConnection>
#include <QDBusInterface>
#include <iostream>
#include <fstream>
#include "TunerWorker.hpp"
using namespace std;
// high 10hz / 16k
static double a10[] = { 1 , -2.99214602, 2.98432286, -0.99217678 };
static double b10[] = { 0.99608071, -2.98824212, 2.98824212, -0.99608071 };
const char * TunerWorker::filename_record = NULL;
/// function to prevent screen blank on Sailfish OS
static void blank_prevent(bool prevent)
{
cerr << __func__ << endl;
QDBusConnection system = QDBusConnection::connectToBus(QDBusConnection::SystemBus, "system");
QDBusInterface interface("com.nokia.mce", "/com/nokia/mce/request", "com.nokia.mce.request", system);
if (prevent) {
interface.call(QLatin1String("req_display_blanking_pause"));
} else {
interface.call(QLatin1String("req_display_cancel_blanking_pause"));
}
}
TunerWorker::TunerWorker() :
high_filter(NULL),
cross(NULL),
scale(NULL),
running(false),
quit(false),
la_to_update(0),
freq_to_update(NULL)
{
// part of reset
found = false;
count_found = count_not_found = 0;
nb_sample_running = 0;
note_found = octave_found = -1;
ResetDeviation();
}
TunerWorker::~TunerWorker()
{
if (filename_record && file_record.is_open()) file_record.close();
if (high_filter) delete high_filter;
if (cross) delete cross;
if (scale) delete scale;
}
/// reset analyse values
void TunerWorker::Reset()
{
found = false;
count_found = count_not_found = 0;
nb_sample_running = 0;
note_found = octave_found = -1;
ResetDeviation();
blank_prevent(true);
high_filter->Clear();
cross->Clear();
}
void TunerWorker::Start()
{
cerr << __func__ << endl;
mutex.lock();
running = true;
condition.wakeOne();
mutex.unlock();
}
void TunerWorker::Stop()
{
mutex.lock();
running = false;
mutex.unlock();
}
void TunerWorker::Quit()
{
mutex.lock();
running = false;
quit = true;
condition.wakeOne();
mutex.unlock();
}
void TunerWorker::SetNotesFrequencies(const double *notes_freq)
{
mutex.lock();
freq_to_update = notes_freq;
mutex.unlock();
}
void TunerWorker::SetLa(double la)
{
mutex.lock();
la_to_update = la;
mutex.unlock();
}
void TunerWorker::ComputeFrame(int16_t v)
{
v = (*high_filter)(v);
(*cross)(v);
}
void TunerWorker::ResetDeviation()
{
// reset deviation values
nb_deviation = 0;
deviation_start = 0;
deviation_sum = 0;
}
void TunerWorker::UpdateDeviation(double d)
{
if (nb_deviation == nbDeviationValues) {
deviation_sum -= deviation_values[deviation_start];
deviation_start = (deviation_start + 1) % nbDeviationValues;
nb_deviation--;
}
deviation_values[(deviation_start + nb_deviation) % nbDeviationValues] = d;
nb_deviation++;
deviation_sum += d;
}
void TunerWorker::SetFound(int n, int o, double d)
{
if (n != note_found || o != octave_found) {
note_found = n;
octave_found = o;
count_found = 0;
SetNotFound();
ResetDeviation();
UpdateDeviation(d);
}
else if (count_found++ >= nbConfirm) {
found = true;
count_not_found = 0;
UpdateDeviation(d);
resultFound(n, o, deviation_sum / nb_deviation, cross->Freq());
}
else {
UpdateDeviation(d);
}
}
void TunerWorker::SetNotFound()
{
if (count_not_found++ >= nbDefect) {
count_found = 0;
if (found) {
found = false;
ResetDeviation();
resultNotFound(cross->Freq());
}
}
}
void TunerWorker::AudioAnalyse(const int16_t *ptr, int nb_frame)
{
nb_sample_running += nb_frame;
// record in file is needed
if (filename_record && file_record.is_open()) file_record.write((char*) ptr, nb_frame * sizeof(int16_t));
// compute every audio frame
while (nb_frame--) ComputeFrame(*ptr++);
// find note, octave, deviation
if (cross->Freq()) {
int n, o = 0;
double d = 0;
n = scale->FindNote(cross->Freq(), o, d);
SetFound(n, o, d);
}
else { // no freq
SetNotFound();
}
// prevent screen blanking
if (nb_sample_running >= nbSamplePreventRunning && running) {
nb_sample_running = 0;
blank_prevent(true);
}
}
void TunerWorker::Entry()
{
// initialisations
if (filename_record) file_record.open(filename_record);
high_filter = new LinearFilter<int16_t>(3, a10, b10);
ZeroCross<int16_t>::Config cross_config({rate, defaultNbFrame, defaultFreqMin, defaultFreqMax});
cross = new ZeroCross<int16_t>(cross_config);
scale = new Scale();
while (1) {
// wait for running
mutex.lock();
if (!running) {
blank_prevent(false);
while (!running && !quit) condition.wait(&mutex);
// reset operations on start
if (!quit) Reset();
}
if (quit) {
mutex.unlock();
break;
}
// update config
if (la_to_update) {
scale->SetLa(la_to_update);
la_to_update = 0;
}
if (freq_to_update) {
scale->SetNotesFrequencies(freq_to_update);
freq_to_update = NULL;
}
mutex.unlock();
std::cout << __func__ << " do job" << std::endl;
}
}
/// Set a filename to record raw audio stream
void TunerWorker::set_record(const char *f)
{
filename_record = f;
}
/// for analyse_file console logs
static void display_results(int note, int octave, double deviation, double frequency)
{
cout << frequency << " " << Scale::NoteName(note) << " " << octave << " " << deviation << endl;
}
static void display_no_results(double freq)
{
cout << freq << endl;
}
/// analyse a file (static function)
void TunerWorker::analyse_file(const char *filename)
{
cout << "analyse file " << filename << endl;
ifstream fin;
fin.open(filename);
const int nb_frame = 1024;
TunerWorker *tuner = new TunerWorker();
int16_t buffer[nb_frame];
connect(tuner, &TunerWorker::resultFound, NULL, display_results);
connect(tuner, &TunerWorker::resultNotFound, NULL, display_no_results);
while (1) {
fin.read((char*) buffer, sizeof(buffer));
tuner->AudioAnalyse(buffer, sizeof(buffer) >> 1);
// cout << "." << endl;
if (fin.eof()) break;
}
fin.close();
delete tuner;
}

106
src/TunerWorker.hpp Normal file
View file

@ -0,0 +1,106 @@
/* Copyright 2016 (C) Louis-Joseph Fournier
* louisjoseph.fournier@gmail.com
*
* This file is part of SailTuner.
*
* SailTuner is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SailTuner is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _TUNER_WORKER_HPP
#define _TUNER_WORKER_HPP
#include <QObject>
#include <QMutex>
#include <QThread>
#include <QWaitCondition>
#include <fstream>
#include "audio/LinearFilter.hpp"
#include "audio/ZeroCross.hpp"
#include "scale/Scale.hpp"
/**
* Worker class to work with audio datas
*
* Implements the pitch algorithm
* and note finding
*/
class TunerWorker : public QObject {
Q_OBJECT
private:
static const int rate = 16000;
static const int defaultNbFrame = 1024;
static const int defaultFreqMin = 50;
static const int defaultFreqMax = 2000;
static const int nbSamplePreventRunning = rate * 40; // 40 seconds
/// number of analyses to confirm a note
static const int nbConfirm = 3;
/// number of analyses to drop a note
static const int nbDefect = 20;
/// number of deviation values for average
static const int nbDeviationValues = 8;
static const char *filename_record;
LinearFilter<int16_t> *high_filter;
ZeroCross<int16_t> *cross;
Scale *scale;
std::ofstream file_record;
QMutex mutex;
QWaitCondition condition;
bool running, found, quit;
int nb_sample_running;
int note_found, octave_found, count_found, count_not_found;
int nb_deviation, deviation_start;
double deviation_sum;
double deviation_values[nbDeviationValues];
// to update vars
double la_to_update;
const double *freq_to_update;
inline void ComputeFrame(int16_t v);
void SetFound(int note, int octave, double deviation);
void SetNotFound();
void Reset();
void ResetDeviation();
void UpdateDeviation(double d);
void AudioAnalyse(const int16_t *buffer, int size);
public:
/// analyse a file for debug
static void analyse_file(const char *filename);
/// write a file with raw audio
static void set_record(const char *filename_record);
/// constructor
TunerWorker();
~TunerWorker();
public slots:
void Start();
void Stop();
void SetNotesFrequencies(const double *notes_freq);
void SetLa(double la);
void Entry();
void Quit();
signals:
void resultFound(int note, int octave, double deviation, double frequency);
void resultNotFound(double freq);
};
#endif

View file

@ -27,7 +27,7 @@
Q_DECL_EXPORT int main(int argc, char* argv[]) Q_DECL_EXPORT int main(int argc, char* argv[])
{ {
if (argc == 2) { if (argc == 2) {
Tuner::analyse_file(argv[1]); TunerWorker::analyse_file(argv[1]);
return 0; return 0;
} }

View file

@ -45,7 +45,7 @@ class Main {
Q_DECL_EXPORT int main(int argc, char* argv[]) Q_DECL_EXPORT int main(int argc, char* argv[])
{ {
if (argc == 2) { if (argc == 2) {
Tuner::analyse_file(argv[1]); TunerWorker::analyse_file(argv[1]);
return 0; return 0;
} }
else if (argc == 3 && strcmp(argv[1], "record") == 0) { else if (argc == 3 && strcmp(argv[1], "record") == 0) {

View file

@ -36,6 +36,7 @@ const char * Scale::NoteName(int note)
Scale::Scale() Scale::Scale()
{ {
actualLa = defaultLa; actualLa = defaultLa;
freq_setted = false;
} }
void Scale::updateScale() void Scale::updateScale()
@ -80,6 +81,10 @@ int Scale::findOctave(double &freq)
int Scale::FindNote(double freq, int &octave, double &deviation) int Scale::FindNote(double freq, int &octave, double &deviation)
{ {
assert (freq > 0); assert (freq > 0);
if (!freq_setted) {
std::cerr << "Scale " << __func__ << ": notes not setted" << std::endl;
ConstructEqualTemperament();
}
int note = 0; int note = 0;
octave = findOctave(freq); octave = findOctave(freq);
@ -111,6 +116,7 @@ void Scale::ConstructEqualTemperament()
noteFreq[i] = defaultLa * pow(2, (double) (i - la) / 12); noteFreq[i] = defaultLa * pow(2, (double) (i - la) / 12);
//std::cerr << noteFreq[i] << std::endl; //std::cerr << noteFreq[i] << std::endl;
} }
freq_setted = true;
updateScale(); updateScale();
} }
@ -121,6 +127,8 @@ double Scale::GetLa()
void Scale::SetNotesFrequencies(const double freq[nbNote]) void Scale::SetNotesFrequencies(const double freq[nbNote])
{ {
std::cout << __func__ << std::endl;
freq_setted = true;
memcpy(noteFreq, freq, sizeof(double) * nbNote); memcpy(noteFreq, freq, sizeof(double) * nbNote);
updateScale(); updateScale();
} }

View file

@ -22,16 +22,18 @@
* Note recognition within a temperament * Note recognition within a temperament
*/ */
class Scale { class Scale {
public:
static const int defaultLa = 440;
private: private:
static const int nbNote = 12; static const int nbNote = 12;
static const int cmpOctave = 4; static const int cmpOctave = 4;
static const int defaultLa = 440;
static const char *noteNames[nbNote]; static const char *noteNames[nbNote];
double noteFreq[nbNote], actualNoteFreq[nbNote]; double noteFreq[nbNote], actualNoteFreq[nbNote];
double actualLa, actualFactor; double actualLa, actualFactor;
double actualRange[2]; // freq range for default octave double actualRange[2]; // freq range for default octave
bool freq_setted;
/// update scale after temperament or default la change /// update scale after temperament or default la change
void updateScale(); void updateScale();

View file

@ -69,7 +69,7 @@ void Temperaments::CheckFile(const QString & filename)
} }
// add temperament // add temperament
name = line.left(offset); name = line.left(offset);
qDebug() << " -> add temperament" << name; //qDebug() << " -> add temperament" << name;
list.push_back(temp_t{name, filename, f_pos}); list.push_back(temp_t{name, filename, f_pos});
} }
} }

View file

@ -19,6 +19,7 @@
#define __TEMPERAMENTS_HPP #define __TEMPERAMENTS_HPP
#include <QString> #include <QString>
#include <QStringList>
#include <vector> #include <vector>
/** /**