diff --git a/Tuner.pro b/Tuner.pro index 3f30835..4a34d8d 100644 --- a/Tuner.pro +++ b/Tuner.pro @@ -21,4 +21,5 @@ HEADERS += \ src/scale/Temperaments.hpp RESOURCES += \ - qml/desktop.qrc + qml/desktop.qrc \ + data/temperaments.qrc diff --git a/data/Temperaments.csv b/data/Temperaments.csv index 5a4b6c0..df2caf3 100644 --- a/data/Temperaments.csv +++ b/data/Temperaments.csv @@ -1,3 +1,4 @@ +Equal;261.63;277.18;293.66;311.13;329.63;349.23;369.99;392.00;415.30;440.00;466.16;493.88 Werckmeister III;263.40;277.50;294.33;312.18;330.00;351.21;369.99;393.77;416.24;440.00;468.27;495.00 Kirnberger I;264.00;278.12;297.00;312.89;330.00;352.00;371.25;396.00;417.19;440.00;469.33;495.00 Kirnberger II;262.36;276.40;295.16;310.95;327.95;349.81;368.94;393.54;414.59;440.00;466.42;491.93 @@ -6,7 +7,6 @@ Kirnberger III unequal;263.10;277.18;294.50;311.82;328.88;350.80;369.98;393.80;4 Kelletat;262.82;276.97;294.33;311.49;328.65;350.43;369.29;393.81;415.32;440.00;467.24;492.39 Kellner;262.87;276.93;294.13;311.55;329.11;350.49;369.24;393.24;415.40;440.00;467.32;493.66 Billeter;262.40;276.71;294.50;311.29;328.70;350.20;368.95;393.10;415.07;440.00;466.93;492.50 -Equal temperature;261.63;277.18;293.66;311.13;329.63;349.23;369.99;392.00;415.30;440.00;466.16;493.88 Lehman 1/6 Pyth.;262.53;277.82;294.00;311.85;329.25;350.85;370.42;392.90;416.27;440.00;467.24;493.88 Lehman 1/6 Synt.;262.35;277.89;293.94;311.92;329.32;350.52;370.51;392.72;416.36;440.00;467.35;494.00 Fritz (1756 - Equal Beat);261.76;277.33;293.73;311.25;329.70;349.41;370.17;392.04;415.39;440.00;466.27;493.96 diff --git a/data/temperaments.qrc b/data/temperaments.qrc new file mode 100644 index 0000000..ebbcafa --- /dev/null +++ b/data/temperaments.qrc @@ -0,0 +1,5 @@ + + +Temperaments.csv + + diff --git a/harbour-sailtuner.pro b/harbour-sailtuner.pro index 88de359..41eadb2 100644 --- a/harbour-sailtuner.pro +++ b/harbour-sailtuner.pro @@ -7,7 +7,8 @@ DEFINES += TARGET=\""$(TARGET")\" # PKGCONFIG += libpulse RESOURCES += \ - qml/sailfish.qrc + qml/sailfish.qrc \ + data/temperaments.qrc SOURCES += \ src/sailfish.cpp \ diff --git a/src/Tuner.cpp b/src/Tuner.cpp index 3de897c..1970487 100644 --- a/src/Tuner.cpp +++ b/src/Tuner.cpp @@ -67,7 +67,14 @@ Tuner::Tuner() cross = new ZeroCross(cross_config); scale = new Scale(); - scale->ConstructEqualTemperament(); + + temperaments = new Temperaments(":/data"); + if (temperaments->SetTemperament(0)) { + scale->SetNotesFrequencies(temperaments->NotesFrequencies()); + } + else { + scale->ConstructEqualTemperament(); + } settings.setCodec("audio/PCM"); settings.setChannelCount(1); @@ -269,6 +276,24 @@ bool Tuner::GetFound() return found; } +unsigned int Tuner::GetTemperamentIndex() +{ + return temperaments->GetCurrentIndex(); +} + +void Tuner::SetTemperamentIndex(unsigned int idx) +{ + if (temperaments->SetTemperament(idx)) { + scale->SetNotesFrequencies(temperaments->NotesFrequencies()); + temperamentChanged(); + } +} + +QStringList Tuner::GetTemperamentList() const +{ + return temperaments->GetNames(); +} + /// Set a filename to record raw audio stream void Tuner::set_record(const char *f) diff --git a/src/Tuner.hpp b/src/Tuner.hpp index 4434c6f..1d0e9da 100644 --- a/src/Tuner.hpp +++ b/src/Tuner.hpp @@ -23,6 +23,7 @@ #include "audio/LinearFilter.hpp" #include "audio/ZeroCross.hpp" #include "scale/Scale.hpp" +#include "scale/Temperaments.hpp" class Tuner : public QObject { Q_OBJECT @@ -33,6 +34,8 @@ class Tuner : public QObject { Q_PROPERTY(int octave READ GetOctave NOTIFY octaveChanged) 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(QStringList temperament_list READ GetTemperamentList) private: QAudioRecorder *recorder; @@ -42,6 +45,7 @@ class Tuner : public QObject { LinearFilter *high_filter; ZeroCross *cross; Scale *scale; + Temperaments *temperaments; static const char *filename_record; std::ofstream file_record; @@ -92,6 +96,9 @@ class Tuner : public QObject { double GetDeviation(); bool GetFound(); const char* GetNoteName(); + unsigned int GetTemperamentIndex(); + void SetTemperamentIndex(unsigned int idx); + QStringList GetTemperamentList() const; /// analyse a file for debug static void analyse_file(const char *filename); @@ -106,4 +113,5 @@ class Tuner : public QObject { void octaveChanged(); void deviationChanged(); void foundChanged(); + void temperamentChanged(); }; diff --git a/src/scale/Scale.cpp b/src/scale/Scale.cpp index 6c22d27..8f21389 100644 --- a/src/scale/Scale.cpp +++ b/src/scale/Scale.cpp @@ -41,7 +41,10 @@ Scale::Scale() void Scale::updateScale() { actualFactor = actualLa / defaultLa; - for (int i = 0; i < nbNote; i++) actualNoteFreq[i] = actualFactor * noteFreq[i]; + for (int i = 0; i < nbNote; i++) { + actualNoteFreq[i] = actualFactor * noteFreq[i]; +// std::cerr << " " << noteFreq[i] << " " << actualNoteFreq[i] << std::endl; + } actualRange[0] = actualNoteFreq[0] * pow(halfToneFactor(0, -1),0.5); actualRange[1] = actualNoteFreq[nbNote - 1] * pow(halfToneFactor(nbNote - 1, 1), 0.5); @@ -116,7 +119,7 @@ double Scale::GetLa() return actualLa; } -void Scale::SetNotesFrequencies(double freq[nbNote]) +void Scale::SetNotesFrequencies(const double freq[nbNote]) { memcpy(noteFreq, freq, sizeof(double) * nbNote); updateScale(); diff --git a/src/scale/Scale.hpp b/src/scale/Scale.hpp index 5847539..f97834e 100644 --- a/src/scale/Scale.hpp +++ b/src/scale/Scale.hpp @@ -52,7 +52,7 @@ class Scale { void ConstructEqualTemperament(); /// Set notes frequencies from a temperament - void SetNotesFrequencies(double freq[nbNote]); + void SetNotesFrequencies(const double freq[nbNote]); double GetLa(); void SetLa(double la); diff --git a/src/scale/Temperaments.cpp b/src/scale/Temperaments.cpp index 38cc84a..ae0c989 100644 --- a/src/scale/Temperaments.cpp +++ b/src/scale/Temperaments.cpp @@ -15,59 +15,128 @@ * */ -#include -#include -#include -#include +#include +#include #include "Temperaments.hpp" using namespace std; -Temperaments::Temperaments(const char * dirname) : current(0) +Temperaments::Temperaments(const QString & dirname) : current(0) { GetDir(dirname); } -void Temperaments::GetDir(const char * dirname) +void Temperaments::GetDir(const QString & dirname) { - DIR *pdir = opendir(dirname); - struct dirent *pent = NULL; - struct stat filestat; - string filepath; - - if (!pdir) { - cerr << __func__ << ": impossible to open dir " << dirname << endl; + QDir dir(dirname); + if (!dir.exists()) { + qDebug() << __func__ << "dir doesn't exist: " << dirname; return; } - while((pent = readdir(pdir))) { - // get only files - if (pent->d_name[0] == '.') continue; - filepath = dirname; - filepath += "/"; - filepath += pent->d_name; - if (stat(filepath.c_str(), &filestat )) continue; - if (S_ISDIR( filestat.st_mode )) continue; + dir.setFilter(QDir::Files); + QStringList files = dir.entryList(); + for (auto &f : files) CheckFile(dirname + "/" + f); - CheckFile(filepath); + qDebug() << list.size() << "temperaments"; +} + +void Temperaments::CheckFile(const QString & filename) +{ + qDebug() << __func__ << "check " << filename; + QFile file(filename); + if (!file.exists()) { + qDebug() << __func__ << "file doesn't exist: " << filename; + return; + } + if (!file.open(QIODevice::ReadOnly)) { + qDebug() << __func__ << "cannot open file " << filename; + return; + } + QByteArray line; + QString name; + int offset, f_pos; + int n_line = 0; + while (1) { + f_pos = file.pos(); + line = file.readLine(); + if (line.count() == 0) break; + n_line++; + offset = line.indexOf(';'); + if (offset < 1) { + qDebug() << __func__ << "cannot read line " << n_line; + continue; + } + // add temperament + name = line.left(offset); + qDebug() << " -> add temperament" << name; + list.push_back(temp_t{name, filename, f_pos}); } } -void Temperaments::CheckFile(string filename) +bool Temperaments::CheckoutTemperament(const temp_t &temp) { - cout << __func__ << " " << filename << endl; + QFile file(temp.file); + if (!file.open(QIODevice::ReadOnly)) { + qDebug() << __func__ << "cannot open file " << temp.file; + return false; + } + file.seek(temp.seek); + QByteArray line = file.readLine(); + if (line.count() == 0) { + qDebug() << __func__ << "empty line" << temp.file << "pos" << temp.seek; + return false; + } + auto tab = line.trimmed().split(';'); + if (tab.count() != nb_notes + 1) { + qDebug() << __func__ << "nb element not match" << temp.file << "pos" << temp.seek << "count" << tab.count(); + return false; + } + for (int i = 1; i < nb_notes + 1; i++) { + notes[i-1] = tab[i].toDouble(); + } + qDebug() << "temperament" << temp.name << "loaded"; + return true; } -string Temperaments::GetCurrentName() const +bool Temperaments::SetTemperament(unsigned int index) +{ + if (index >= list.size()) { + qDebug() << __func__ << "index" << index << "out of range"; + return false; + } + current = index; + return CheckoutTemperament(list[current]); +} + +bool Temperaments::SetTemperament(const QString name) +{ + int idx = 0; + for (auto &t : list) { + if (t.name == name) { + return SetTemperament(idx); + } + idx++; + } + return false; +} + +unsigned int Temperaments::GetCurrentIndex() const +{ + return current; +} + +QString Temperaments::GetCurrentName() const { if (current >= list.size()) return ""; return list[current].name; } -string Temperaments::GetName(unsigned int n) const +QStringList Temperaments::GetNames() const { - if (n < list.size()) return ""; - return list[n].name; + QStringList ret; + for (auto & t : list) ret << t.name; + return ret; } const double * Temperaments::NotesFrequencies() const diff --git a/src/scale/Temperaments.hpp b/src/scale/Temperaments.hpp index e0a9827..6214eb3 100644 --- a/src/scale/Temperaments.hpp +++ b/src/scale/Temperaments.hpp @@ -18,7 +18,7 @@ #ifndef __TEMPERAMENTS_HPP #define __TEMPERAMENTS_HPP -#include +#include #include /** @@ -31,8 +31,8 @@ class Temperaments { private: /// describe internal a temperament struct temp_t { - std::string name; ///< temperament name - std::string file; ///< file name + QString name; ///< temperament name + QString file; ///< file name int seek; ///< position in file }; @@ -42,21 +42,24 @@ class Temperaments { std::vector list; double notes[nb_notes]; - void GetDir(const char *dirname); - void CheckFile(std::string filename); - void GetTemperament(const temp_t temperament); + void GetDir(const QString & dirname); + void CheckFile(const QString & filename); + bool CheckoutTemperament(const temp_t & temperament); public: /// constructor with dir name to find data files - Temperaments(const char * dirname); + Temperaments(const QString & dirname); ~Temperaments(); /// set current temperament - bool SetTemperament(std::string name); + bool SetTemperament(const QString name); + bool SetTemperament(unsigned int index); + /// get index of current temperament + unsigned int GetCurrentIndex() const; /// get the name of current temperament - std::string GetCurrentName() const; + QString GetCurrentName() const; /// get the name of temperament number n - std::string GetName(unsigned int n) const; + QStringList GetNames() const; /// get notes frequencies as double[nb_notes] const double * NotesFrequencies() const; };