PitchDetection and TunerWorker different objects

This commit is contained in:
Louis-Joseph Fournier 2016-01-05 10:34:22 +01:00
parent 5a341979df
commit 1d857fd25d
10 changed files with 436 additions and 290 deletions

View file

@ -7,6 +7,7 @@ CONFIG += c++11 debug
SOURCES += \ SOURCES += \
src/desktop.cpp \ src/desktop.cpp \
src/PitchDetection.cpp \
src/Tuner.cpp \ src/Tuner.cpp \
src/TunerWorker.cpp \ src/TunerWorker.cpp \
src/audio/LinearFilter.cpp \ src/audio/LinearFilter.cpp \
@ -15,6 +16,7 @@ SOURCES += \
src/scale/Temperaments.cpp src/scale/Temperaments.cpp
HEADERS += \ HEADERS += \
src/PitchDetection.hpp \
src/Tuner.hpp \ src/Tuner.hpp \
src/TunerWorker.hpp \ src/TunerWorker.hpp \
src/audio/LinearFilter.hpp \ src/audio/LinearFilter.hpp \

View file

@ -12,6 +12,7 @@ RESOURCES += \
SOURCES += \ SOURCES += \
src/sailfish.cpp \ src/sailfish.cpp \
src/PitchDetection.cpp \
src/Tuner.cpp \ src/Tuner.cpp \
src/TunerWorker.cpp \ src/TunerWorker.cpp \
src/audio/LinearFilter.cpp \ src/audio/LinearFilter.cpp \
@ -20,6 +21,8 @@ SOURCES += \
src/scale/Temperaments.cpp src/scale/Temperaments.cpp
HEADERS += \ HEADERS += \
src/PitchDetection.hpp \
src/Tuner.cpp \
src/Tuner.hpp \ src/Tuner.hpp \
src/TunerWorker.hpp \ src/TunerWorker.hpp \
src/audio/LinearFilter.hpp \ src/audio/LinearFilter.hpp \

242
src/PitchDetection.cpp Normal file
View file

@ -0,0 +1,242 @@
/* 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 <iostream>
#include <fstream>
#include "PitchDetection.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 * PitchDetection::filename_record = NULL;
/// Pitch detection result methods
PitchDetection::PitchResult::PitchResult() :
found(false),
deviation(0),
frequency(0),
note(0),
octave(0)
{
}
void PitchDetection::PitchResult::Set(int n, int o, double d, double f)
{
note = n;
octave = o;
deviation = d;
frequency = f;
found = true;
}
/// Pitch detection methods
PitchDetection::PitchDetection()
{
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();
temperaments = new Temperaments(":/data");
if (temperaments->SetTemperament(0)) {
scale->SetNotesFrequencies(temperaments->NotesFrequencies());
}
else {
scale->ConstructEqualTemperament();
}
Reset();
}
PitchDetection::~PitchDetection()
{
if (filename_record && file_record.is_open()) file_record.close();
delete high_filter;
delete cross;
delete scale;
delete temperaments;
}
/// reset analyse values
void PitchDetection::Reset()
{
result.found = false;
updated = false;
count_found = count_not_found = 0;
note_found = octave_found = -1;
ResetDeviation();
high_filter->Clear();
cross->Clear();
}
void PitchDetection::ComputeFrame(int16_t v)
{
v = (*high_filter)(v);
(*cross)(v);
}
void PitchDetection::ResetDeviation()
{
// reset deviation values
nb_deviation = 0;
deviation_start = 0;
deviation_sum = 0;
}
void PitchDetection::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 PitchDetection::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;
UpdateDeviation(d);
result.Set(n, o, deviation_sum / nb_deviation, cross->Freq());
updated = true;
}
else {
UpdateDeviation(d);
}
}
void PitchDetection::SetNotFound()
{
if (count_not_found++ >= nbDefect) {
count_found = 0;
if (result.found) {
result.found = false;
ResetDeviation();
updated = true;
}
}
}
void PitchDetection::SetLa(double la)
{
scale->SetLa(la);
}
void PitchDetection::SetTemperament(int idx)
{
if (idx < 0) return;
if (temperaments->SetTemperament(idx)) {
scale->SetNotesFrequencies(temperaments->NotesFrequencies());
}
}
void PitchDetection::AudioAnalyse(const int16_t *ptr, int 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();
}
}
bool PitchDetection::GetResultUpdated(PitchResult &res)
{
if (!updated) return false;
res = this->result;
updated = false;
return true;
}
QStringList PitchDetection::GetTemperamentList() const
{
return temperaments->GetNames();
}
/// Set a filename to record raw audio stream
void PitchDetection::set_record(const char *f)
{
filename_record = f;
}
/// for analyse_file console logs
static void display_results(const PitchDetection::PitchResult &res)
{
if (res.found)
cout << res.frequency << " " << Scale::NoteName(res.note) << " " << res.octave << " " << res.deviation << endl;
else
cout << res.frequency << endl;
}
/// analyse a file (static function)
void PitchDetection::analyse_file(const char *filename)
{
cout << "analyse file " << filename << endl;
ifstream fin;
fin.open(filename);
const int nb_frame = 1024;
PitchDetection *pitch = new PitchDetection();
int16_t buffer[nb_frame];
PitchResult result;
while (1) {
fin.read((char*) buffer, sizeof(buffer));
pitch->AudioAnalyse(buffer, sizeof(buffer) >> 1);
if (pitch->GetResultUpdated(result)) display_results(result);
else cout << "." << endl;
if (fin.eof()) break;
}
fin.close();
delete pitch;
}

107
src/PitchDetection.hpp Normal file
View file

@ -0,0 +1,107 @@
/* 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 _PITCH_DETECTION_HPP
#define _PITCH_DETECTION_HPP
#include <fstream>
#include "audio/LinearFilter.hpp"
#include "audio/ZeroCross.hpp"
#include "scale/Scale.hpp"
#include "scale/Temperaments.hpp"
/**
* Pitch detection algorithm
*
* Implements the pitch algorithm
* and note finding, using audio and scale objects
*/
class PitchDetection {
public:
/**
* Structure for pitch detection result
*/
struct PitchResult {
bool found;
double deviation, frequency;
int note, octave;
PitchResult();
void Set(int n, int o, double d, double f);
};
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;
Temperaments *temperaments;
std::ofstream file_record;
PitchResult result;
bool updated;
int note_found, octave_found, count_found, count_not_found;
int nb_deviation, deviation_start;
double deviation_sum;
double deviation_values[nbDeviationValues];
void SetFound(int note, int octave, double deviation);
void SetNotFound();
void ResetDeviation();
void UpdateDeviation(double d);
/// Compute an audio sample
inline void ComputeFrame(int16_t v);
public:
PitchDetection();
~PitchDetection();
/// compute an audio buffer
void AudioAnalyse(const int16_t *buffer, int size);
/// Get results if updated
bool GetResultUpdated(PitchResult &result);
/// reset computing
void Reset();
/// Set la4 freq reference
void SetLa(double freq);
/// Set temperament
void SetTemperament(int idx);
/// Get temperament list
QStringList GetTemperamentList() const;
/// 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);
};
#endif

View file

@ -32,32 +32,24 @@ using namespace std;
Tuner::Tuner() Tuner::Tuner()
{ {
running = false; running = false;
freq = deviation = 0; temperament_idx = 0;
note = octave = 0;
found = false;
la = Scale::defaultLa; la = Scale::defaultLa;
worker = new TunerWorker(); worker = new TunerWorker();
temperaments = new Temperaments(":/data");
if (temperaments->SetTemperament(0)) {
worker->SetNotesFrequencies(temperaments->NotesFrequencies());
}
worker->moveToThread(&workerThread); worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(&workerThread, &QThread::started, worker, &TunerWorker::Entry); connect(&workerThread, &QThread::started, worker, &TunerWorker::Entry);
connect(this, &Tuner::quit, worker, &TunerWorker::Quit, Qt::DirectConnection);
connect(this, &Tuner::quit, worker, &TunerWorker::Quit, Qt::DirectConnection);
connect(this, &Tuner::start, worker, &TunerWorker::Start); connect(this, &Tuner::start, worker, &TunerWorker::Start);
connect(this, &Tuner::stop, worker, &TunerWorker::Stop); connect(this, &Tuner::stop, worker, &TunerWorker::Stop);
connect(this, &Tuner::setNotesFrequencies, worker, &TunerWorker::SetNotesFrequencies); connect(this, &Tuner::setTemperamentIndex, worker, &TunerWorker::SetTemperament);
connect(this, &Tuner::setLa, worker, &TunerWorker::SetLa); connect(this, &Tuner::setLa, worker, &TunerWorker::SetLa);
connect(worker, &TunerWorker::resultFound, this, &Tuner::ResultFound); connect(worker, &TunerWorker::resultUpdated, this, &Tuner::ResultUpdated);
connect(worker, &TunerWorker::resultNotFound, this, &Tuner::ResultNotFound); connect(worker, &TunerWorker::temperamentListUpdated, this, &Tuner::TemperamentListUpdated);
workerThread.start(); workerThread.start();
} }
@ -67,7 +59,6 @@ Tuner::~Tuner()
quit(); quit();
// workerThread.quit(); // workerThread.quit();
workerThread.wait(10); workerThread.wait(10);
delete temperaments;
} }
bool Tuner::GetRunning() bool Tuner::GetRunning()
@ -80,42 +71,42 @@ void Tuner::SetRunning(bool r)
if (running == r) return; if (running == r) return;
running = r; running = r;
if (r) start(); if (r) emit start();
else stop(); else emit stop();
runningChanged(); emit runningChanged();
} }
double Tuner::GetFreq() double Tuner::GetFreq()
{ {
return freq; return result.frequency;
} }
int Tuner::GetNote() int Tuner::GetNote()
{ {
return note; return result.note;
} }
double Tuner::GetDeviation() double Tuner::GetDeviation()
{ {
return deviation; return result.deviation;
} }
int Tuner::GetOctave() int Tuner::GetOctave()
{ {
return octave; return result.octave;
} }
bool Tuner::GetFound() bool Tuner::GetFound()
{ {
return found; return result.found;
} }
void Tuner::SetLa(double la) void Tuner::SetLa(double la)
{ {
this->la = la; this->la = la;
setLa(la); emit setLa(la);
laChanged(); emit laChanged();
} }
double Tuner::GetLa() double Tuner::GetLa()
@ -125,39 +116,33 @@ double Tuner::GetLa()
unsigned int Tuner::GetTemperamentIndex() unsigned int Tuner::GetTemperamentIndex()
{ {
return temperaments->GetCurrentIndex(); return temperament_idx;
} }
void Tuner::SetTemperamentIndex(unsigned int idx) void Tuner::SetTemperamentIndex(int idx)
{ {
if (temperaments->SetTemperament(idx)) { temperament_idx = idx;
setNotesFrequencies(temperaments->NotesFrequencies()); emit setTemperamentIndex(idx);
temperamentChanged(); emit temperamentChanged();
}
} }
QStringList Tuner::GetTemperamentList() const QStringList Tuner::GetTemperamentList() const
{ {
return temperaments->GetNames(); return temperament_list;
} }
void Tuner::ResultFound(int note, int octave, double deviation, double freq) void Tuner::ResultUpdated(const PitchDetection::PitchResult &result)
{ {
this->note = note; const bool changed = (this->result.found ^ result.found);
this->octave = octave;
this->deviation = deviation;
this->freq = freq;
resultChanged();
if (!found) { this->result = result;
foundChanged();
found = true; if (result.found) emit resultChanged();
} if (changed) emit foundChanged();
} }
void Tuner::ResultNotFound(double freq) void Tuner::TemperamentListUpdated(const QStringList &list)
{ {
this->freq = freq; temperament_list = list;
found = false; emit temperamentListChanged();
foundChanged();
} }

View file

@ -18,8 +18,10 @@
#ifndef _TUNER_HPP #ifndef _TUNER_HPP
#define _TUNER_HPP #define _TUNER_HPP
#include <QStringList>
#include "PitchDetection.hpp"
#include "TunerWorker.hpp" #include "TunerWorker.hpp"
#include "scale/Temperaments.hpp"
class Tuner : public QObject { class Tuner : public QObject {
Q_OBJECT Q_OBJECT
@ -30,17 +32,18 @@ class Tuner : public QObject {
Q_PROPERTY(int octave READ GetOctave NOTIFY resultChanged) 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(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 NOTIFY temperamentListChanged)
Q_PROPERTY(double la READ GetLa WRITE SetLa NOTIFY laChanged) Q_PROPERTY(double la READ GetLa WRITE SetLa NOTIFY laChanged)
private: private:
Temperaments *temperaments;
TunerWorker *worker; TunerWorker *worker;
QThread workerThread; QThread workerThread;
bool running, found; PitchDetection::PitchResult result;
double freq, deviation, la; QStringList temperament_list;
int note, octave; bool running;
double la;
int temperament_idx;
public: public:
Tuner(); Tuner();
@ -54,27 +57,30 @@ class Tuner : public QObject {
double GetDeviation(); double GetDeviation();
bool GetFound(); bool GetFound();
unsigned int GetTemperamentIndex(); unsigned int GetTemperamentIndex();
void SetTemperamentIndex(unsigned int idx); void SetTemperamentIndex(int idx);
QStringList GetTemperamentList() const; QStringList GetTemperamentList() const;
double GetLa(); double GetLa();
void SetLa(double la); void SetLa(double la);
public slots: public slots:
void ResultFound(int note, int octave, double deviation, double frequency); // slots from worker
void ResultNotFound(double freq); void ResultUpdated(const PitchDetection::PitchResult &result);
void TemperamentListUpdated(const QStringList &list);
signals: signals:
// signals to UI
void runningChanged(); void runningChanged();
void foundChanged(); void foundChanged();
void temperamentChanged();
void laChanged(); void laChanged();
void resultChanged(); void resultChanged();
void temperamentChanged();
void temperamentListChanged();
// signals to worker // signals to worker
void quit(); void quit();
void start(); void start();
void stop(); void stop();
void setNotesFrequencies(const double *freq); void setTemperamentIndex(int idx);
void setLa(double la_freq); void setLa(double la_freq);
}; };

View file

@ -19,18 +19,11 @@
#include <QDBusInterface> #include <QDBusInterface>
#include <iostream> #include <iostream>
#include <fstream>
#include "TunerWorker.hpp" #include "TunerWorker.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 * TunerWorker::filename_record = NULL;
/// function to prevent screen blank on Sailfish OS /// function to prevent screen blank on Sailfish OS
static void blank_prevent(bool prevent) static void blank_prevent(bool prevent)
@ -47,41 +40,16 @@ static void blank_prevent(bool prevent)
} }
TunerWorker::TunerWorker() : TunerWorker::TunerWorker() :
high_filter(NULL),
cross(NULL),
scale(NULL),
running(false), running(false),
quit(false), quit(false),
la_to_update(0), la_to_update(0),
freq_to_update(NULL) temperament_to_update(-1)
{ {
// part of reset
found = false;
count_found = count_not_found = 0;
nb_sample_running = 0;
note_found = octave_found = -1;
ResetDeviation();
} }
TunerWorker::~TunerWorker() TunerWorker::~TunerWorker()
{ {
if (filename_record && file_record.is_open()) file_record.close(); if (pitchDetection) delete pitchDetection;
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() void TunerWorker::Start()
@ -109,13 +77,6 @@ void TunerWorker::Quit()
mutex.unlock(); mutex.unlock();
} }
void TunerWorker::SetNotesFrequencies(const double *notes_freq)
{
mutex.lock();
freq_to_update = notes_freq;
mutex.unlock();
}
void TunerWorker::SetLa(double la) void TunerWorker::SetLa(double la)
{ {
mutex.lock(); mutex.lock();
@ -123,107 +84,18 @@ void TunerWorker::SetLa(double la)
mutex.unlock(); mutex.unlock();
} }
void TunerWorker::ComputeFrame(int16_t v) void TunerWorker::SetTemperament(int idx)
{ {
v = (*high_filter)(v); mutex.lock();
(*cross)(v); temperament_to_update = idx;
mutex.unlock();
} }
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() void TunerWorker::Entry()
{ {
// initialisations cerr << __func__ << endl;
if (filename_record) file_record.open(filename_record); pitchDetection = new PitchDetection();
emit temperamentListUpdated(pitchDetection->GetTemperamentList());
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) { while (1) {
// wait for running // wait for running
@ -231,8 +103,9 @@ void TunerWorker::Entry()
if (!running) { if (!running) {
blank_prevent(false); blank_prevent(false);
while (!running && !quit) condition.wait(&mutex); while (!running && !quit) condition.wait(&mutex);
cerr << "wake-up" << endl;
// reset operations on start // reset operations on start
if (!quit) Reset(); if (!quit) pitchDetection->Reset();
} }
if (quit) { if (quit) {
mutex.unlock(); mutex.unlock();
@ -240,58 +113,22 @@ void TunerWorker::Entry()
} }
// update config // update config
if (la_to_update) { if (la_to_update) {
scale->SetLa(la_to_update); pitchDetection->SetLa(la_to_update);
la_to_update = 0; la_to_update = 0;
} }
if (freq_to_update) { if (temperament_to_update != -1) {
scale->SetNotesFrequencies(freq_to_update); pitchDetection->SetTemperament(temperament_to_update);
freq_to_update = NULL; temperament_to_update = -1;
} }
mutex.unlock(); mutex.unlock();
std::cout << __func__ << " do job" << std::endl; std::cout << __func__ << " do job" << std::endl;
} }
/*
// prevent screen blanking
if (nb_sample_running >= nbSamplePreventRunning && running) {
nb_sample_running = 0;
blank_prevent(true);
}*/
} }
/// 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;
}

View file

@ -22,70 +22,32 @@
#include <QMutex> #include <QMutex>
#include <QThread> #include <QThread>
#include <QWaitCondition> #include <QWaitCondition>
#include <QStringList>
#include <fstream> #include "PitchDetection.hpp"
#include "audio/LinearFilter.hpp"
#include "audio/ZeroCross.hpp"
#include "scale/Scale.hpp"
/** /**
* Worker class to work with audio datas * Worker class to receive audio and
* do pitch detection in a thread
* *
* Implements the pitch algorithm
* and note finding
*/ */
class TunerWorker : public QObject { class TunerWorker : public QObject {
Q_OBJECT Q_OBJECT
private: 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; QMutex mutex;
QWaitCondition condition; QWaitCondition condition;
bool running, found, quit; PitchDetection *pitchDetection;
bool running, quit;
int nb_sample_running; 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 // to update vars
double la_to_update; double la_to_update;
const double *freq_to_update; int temperament_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: 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 /// constructor
TunerWorker(); TunerWorker();
~TunerWorker(); ~TunerWorker();
@ -93,14 +55,14 @@ class TunerWorker : public QObject {
public slots: public slots:
void Start(); void Start();
void Stop(); void Stop();
void SetNotesFrequencies(const double *notes_freq); void SetTemperament(int idx);
void SetLa(double la); void SetLa(double la);
void Entry(); void Entry();
void Quit(); void Quit();
signals: signals:
void resultFound(int note, int octave, double deviation, double frequency); void resultUpdated(const PitchDetection::PitchResult &result);
void resultNotFound(double freq); void temperamentListUpdated(const QStringList &list);
}; };
#endif #endif

View file

@ -120,6 +120,7 @@ template<typename A> ZeroCross<A>::ZeroCross(const Config &config)
template<typename sample_t> void ZeroCross<sample_t>::Clear() template<typename sample_t> void ZeroCross<sample_t>::Clear()
{ {
last_sample = 0;
pattern_current.time = -1; pattern_current.time = -1;
nb_frame_analysed = 0; nb_frame_analysed = 0;
freq = 0; freq = 0;

View file

@ -22,12 +22,13 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include "PitchDetection.hpp"
#include "Tuner.hpp" #include "Tuner.hpp"
Q_DECL_EXPORT int main(int argc, char* argv[]) Q_DECL_EXPORT int main(int argc, char* argv[])
{ {
if (argc == 2) { if (argc == 2) {
TunerWorker::analyse_file(argv[1]); PitchDetection::analyse_file(argv[1]);
return 0; return 0;
} }