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

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
RESOURCES += \
qml/sailfish.qrc
qml/sailfish.qrc \
data/temperaments.qrc
SOURCES += \
src/sailfish.cpp \

View file

@ -67,7 +67,14 @@ Tuner::Tuner()
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();
}
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)

View file

@ -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<int16_t> *high_filter;
ZeroCross<int16_t> *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();
};

View file

@ -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();

View file

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

View file

@ -15,59 +15,128 @@
*
*/
#include <fstream>
#include <iostream>
#include <dirent.h>
#include <sys/stat.h>
#include <QDir>
#include <QDebug>
#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

View file

@ -18,7 +18,7 @@
#ifndef __TEMPERAMENTS_HPP
#define __TEMPERAMENTS_HPP
#include <string>
#include <QString>
#include <vector>
/**
@ -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<temp_t> 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;
};