Temperaments: load from csv file in qrc ressources
Module Temperaments is fully functionnal
This commit is contained in:
parent
8a43de5b5b
commit
986f736b68
10 changed files with 160 additions and 45 deletions
|
@ -21,4 +21,5 @@ HEADERS += \
|
|||
src/scale/Temperaments.hpp
|
||||
|
||||
RESOURCES += \
|
||||
qml/desktop.qrc
|
||||
qml/desktop.qrc \
|
||||
data/temperaments.qrc
|
||||
|
|
|
@ -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
|
||||
|
|
|
5
data/temperaments.qrc
Normal file
5
data/temperaments.qrc
Normal file
|
@ -0,0 +1,5 @@
|
|||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource prefix="/data/">
|
||||
<file>Temperaments.csv</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -7,7 +7,8 @@ DEFINES += TARGET=\""$(TARGET")\"
|
|||
# PKGCONFIG += libpulse
|
||||
|
||||
RESOURCES += \
|
||||
qml/sailfish.qrc
|
||||
qml/sailfish.qrc \
|
||||
data/temperaments.qrc
|
||||
|
||||
SOURCES += \
|
||||
src/sailfish.cpp \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue