Temperaments: load from csv file in qrc ressources

Module Temperaments is fully functionnal
This commit is contained in:
Louis-Joseph Fournier 2016-01-03 22:35:08 +01:00
parent 8a43de5b5b
commit 986f736b68
10 changed files with 160 additions and 45 deletions

View file

@ -21,4 +21,5 @@ HEADERS += \
src/scale/Temperaments.hpp src/scale/Temperaments.hpp
RESOURCES += \ RESOURCES += \
qml/desktop.qrc qml/desktop.qrc \
data/temperaments.qrc

View file

@ -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 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 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 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 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 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 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 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 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 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

1 Werckmeister III Equal 263.40 261.63 277.50 277.18 294.33 293.66 312.18 311.13 330.00 329.63 351.21 349.23 369.99 393.77 392.00 416.24 415.30 440.00 468.27 466.16 495.00 493.88
1 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
2 Werckmeister III Werckmeister III 263.40 263.40 277.50 277.50 294.33 294.33 312.18 312.18 330.00 330.00 351.21 351.21 369.99 393.77 393.77 416.24 416.24 440.00 468.27 468.27 495.00 495.00
3 Kirnberger I Kirnberger I 264.00 264.00 278.12 278.12 297.00 297.00 312.89 312.89 330.00 330.00 352.00 352.00 371.25 396.00 396.00 417.19 417.19 440.00 469.33 469.33 495.00 495.00
4 Kirnberger II Kirnberger II 262.36 262.36 276.40 276.40 295.16 295.16 310.95 310.95 327.95 327.95 349.81 349.81 368.94 393.54 393.54 414.59 414.59 440.00 466.42 466.42 491.93 491.93
7 Kelletat Kelletat 262.82 262.82 276.97 276.97 294.33 294.33 311.49 311.49 328.65 328.65 350.43 350.43 369.29 393.81 393.81 415.32 415.32 440.00 467.24 467.24 492.39 492.39
8 Kellner Kellner 262.87 262.87 276.93 276.93 294.13 294.13 311.55 311.55 329.11 329.11 350.49 350.49 369.24 393.24 393.24 415.40 415.40 440.00 467.32 467.32 493.66 493.66
9 Billeter Billeter 262.40 262.40 276.71 276.71 294.50 294.50 311.29 311.29 328.70 328.70 350.20 350.20 368.95 393.10 393.10 415.07 415.07 440.00 466.93 466.93 492.50 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
10 Lehman 1/6 Pyth. Lehman 1/6 Pyth. 262.53 262.53 277.82 277.82 294.00 294.00 311.85 311.85 329.25 329.25 350.85 350.85 370.42 392.90 392.90 416.27 416.27 440.00 467.24 467.24 493.88 493.88
11 Lehman 1/6 Synt. Lehman 1/6 Synt. 262.35 262.35 277.89 277.89 293.94 293.94 311.92 311.92 329.32 329.32 350.52 350.52 370.51 392.72 392.72 416.36 416.36 440.00 467.35 467.35 494.00 494.00
12 Fritz (1756 - Equal Beat) Fritz (1756 - Equal Beat) 261.76 261.76 277.33 277.33 293.73 293.73 311.25 311.25 329.70 329.70 349.41 349.41 370.17 392.04 392.04 415.39 415.39 440.00 466.27 466.27 493.96 493.96

5
data/temperaments.qrc Normal file
View file

@ -0,0 +1,5 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource prefix="/data/">
<file>Temperaments.csv</file>
</qresource>
</RCC>

View file

@ -7,7 +7,8 @@ DEFINES += TARGET=\""$(TARGET")\"
# PKGCONFIG += libpulse # PKGCONFIG += libpulse
RESOURCES += \ RESOURCES += \
qml/sailfish.qrc qml/sailfish.qrc \
data/temperaments.qrc
SOURCES += \ SOURCES += \
src/sailfish.cpp \ src/sailfish.cpp \

View file

@ -67,7 +67,14 @@ Tuner::Tuner()
cross = new ZeroCross<int16_t>(cross_config); cross = new ZeroCross<int16_t>(cross_config);
scale = new Scale(); scale = new Scale();
temperaments = new Temperaments(":/data");
if (temperaments->SetTemperament(0)) {
scale->SetNotesFrequencies(temperaments->NotesFrequencies());
}
else {
scale->ConstructEqualTemperament(); scale->ConstructEqualTemperament();
}
settings.setCodec("audio/PCM"); settings.setCodec("audio/PCM");
settings.setChannelCount(1); settings.setChannelCount(1);
@ -269,6 +276,24 @@ bool Tuner::GetFound()
return found; 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 /// Set a filename to record raw audio stream
void Tuner::set_record(const char *f) void Tuner::set_record(const char *f)

View file

@ -23,6 +23,7 @@
#include "audio/LinearFilter.hpp" #include "audio/LinearFilter.hpp"
#include "audio/ZeroCross.hpp" #include "audio/ZeroCross.hpp"
#include "scale/Scale.hpp" #include "scale/Scale.hpp"
#include "scale/Temperaments.hpp"
class Tuner : public QObject { class Tuner : public QObject {
Q_OBJECT Q_OBJECT
@ -33,6 +34,8 @@ class Tuner : public QObject {
Q_PROPERTY(int octave READ GetOctave NOTIFY octaveChanged) Q_PROPERTY(int octave READ GetOctave NOTIFY octaveChanged)
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(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: private:
QAudioRecorder *recorder; QAudioRecorder *recorder;
@ -42,6 +45,7 @@ class Tuner : public QObject {
LinearFilter<int16_t> *high_filter; LinearFilter<int16_t> *high_filter;
ZeroCross<int16_t> *cross; ZeroCross<int16_t> *cross;
Scale *scale; Scale *scale;
Temperaments *temperaments;
static const char *filename_record; static const char *filename_record;
std::ofstream file_record; std::ofstream file_record;
@ -92,6 +96,9 @@ class Tuner : public QObject {
double GetDeviation(); double GetDeviation();
bool GetFound(); bool GetFound();
const char* GetNoteName(); const char* GetNoteName();
unsigned int GetTemperamentIndex();
void SetTemperamentIndex(unsigned int idx);
QStringList GetTemperamentList() const;
/// analyse a file for debug /// analyse a file for debug
static void analyse_file(const char *filename); static void analyse_file(const char *filename);
@ -106,4 +113,5 @@ class Tuner : public QObject {
void octaveChanged(); void octaveChanged();
void deviationChanged(); void deviationChanged();
void foundChanged(); void foundChanged();
void temperamentChanged();
}; };

View file

@ -41,7 +41,10 @@ Scale::Scale()
void Scale::updateScale() void Scale::updateScale()
{ {
actualFactor = actualLa / defaultLa; 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[0] = actualNoteFreq[0] * pow(halfToneFactor(0, -1),0.5);
actualRange[1] = actualNoteFreq[nbNote - 1] * pow(halfToneFactor(nbNote - 1, 1), 0.5); actualRange[1] = actualNoteFreq[nbNote - 1] * pow(halfToneFactor(nbNote - 1, 1), 0.5);
@ -116,7 +119,7 @@ double Scale::GetLa()
return actualLa; return actualLa;
} }
void Scale::SetNotesFrequencies(double freq[nbNote]) void Scale::SetNotesFrequencies(const double freq[nbNote])
{ {
memcpy(noteFreq, freq, sizeof(double) * nbNote); memcpy(noteFreq, freq, sizeof(double) * nbNote);
updateScale(); updateScale();

View file

@ -52,7 +52,7 @@ class Scale {
void ConstructEqualTemperament(); void ConstructEqualTemperament();
/// Set notes frequencies from a temperament /// Set notes frequencies from a temperament
void SetNotesFrequencies(double freq[nbNote]); void SetNotesFrequencies(const double freq[nbNote]);
double GetLa(); double GetLa();
void SetLa(double la); void SetLa(double la);

View file

@ -15,59 +15,128 @@
* *
*/ */
#include <fstream> #include <QDir>
#include <iostream> #include <QDebug>
#include <dirent.h>
#include <sys/stat.h>
#include "Temperaments.hpp" #include "Temperaments.hpp"
using namespace std; using namespace std;
Temperaments::Temperaments(const char * dirname) : current(0) Temperaments::Temperaments(const QString & dirname) : current(0)
{ {
GetDir(dirname); GetDir(dirname);
} }
void Temperaments::GetDir(const char * dirname) void Temperaments::GetDir(const QString & dirname)
{ {
DIR *pdir = opendir(dirname); QDir dir(dirname);
struct dirent *pent = NULL; if (!dir.exists()) {
struct stat filestat; qDebug() << __func__ << "dir doesn't exist: " << dirname;
string filepath;
if (!pdir) {
cerr << __func__ << ": impossible to open dir " << dirname << endl;
return; return;
} }
while((pent = readdir(pdir))) { dir.setFilter(QDir::Files);
// get only files QStringList files = dir.entryList();
if (pent->d_name[0] == '.') continue; for (auto &f : files) CheckFile(dirname + "/" + f);
filepath = dirname;
filepath += "/";
filepath += pent->d_name;
if (stat(filepath.c_str(), &filestat )) continue;
if (S_ISDIR( filestat.st_mode )) continue;
CheckFile(filepath); qDebug() << list.size() << "temperaments";
}
} }
void Temperaments::CheckFile(string filename) void Temperaments::CheckFile(const QString & filename)
{ {
cout << __func__ << " " << filename << endl; 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});
}
} }
string Temperaments::GetCurrentName() const bool Temperaments::CheckoutTemperament(const temp_t &temp)
{
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;
}
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 ""; if (current >= list.size()) return "";
return list[current].name; return list[current].name;
} }
string Temperaments::GetName(unsigned int n) const QStringList Temperaments::GetNames() const
{ {
if (n < list.size()) return ""; QStringList ret;
return list[n].name; for (auto & t : list) ret << t.name;
return ret;
} }
const double * Temperaments::NotesFrequencies() const const double * Temperaments::NotesFrequencies() const

View file

@ -18,7 +18,7 @@
#ifndef __TEMPERAMENTS_HPP #ifndef __TEMPERAMENTS_HPP
#define __TEMPERAMENTS_HPP #define __TEMPERAMENTS_HPP
#include <string> #include <QString>
#include <vector> #include <vector>
/** /**
@ -31,8 +31,8 @@ class Temperaments {
private: private:
/// describe internal a temperament /// describe internal a temperament
struct temp_t { struct temp_t {
std::string name; ///< temperament name QString name; ///< temperament name
std::string file; ///< file name QString file; ///< file name
int seek; ///< position in file int seek; ///< position in file
}; };
@ -42,21 +42,24 @@ class Temperaments {
std::vector<temp_t> list; std::vector<temp_t> list;
double notes[nb_notes]; double notes[nb_notes];
void GetDir(const char *dirname); void GetDir(const QString & dirname);
void CheckFile(std::string filename); void CheckFile(const QString & filename);
void GetTemperament(const temp_t temperament); bool CheckoutTemperament(const temp_t & temperament);
public: public:
/// constructor with dir name to find data files /// constructor with dir name to find data files
Temperaments(const char * dirname); Temperaments(const QString & dirname);
~Temperaments(); ~Temperaments();
/// set current temperament /// 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 /// get the name of current temperament
std::string GetCurrentName() const; QString GetCurrentName() const;
/// get the name of temperament number n /// get the name of temperament number n
std::string GetName(unsigned int n) const; QStringList GetNames() const;
/// get notes frequencies as double[nb_notes] /// get notes frequencies as double[nb_notes]
const double * NotesFrequencies() const; const double * NotesFrequencies() const;
}; };