/* top
Calcul de la transformée de Fourier rapide d'un signal audio au format PCM enregistré dans un fichier .wav
Format WAVE, RIFF, fmt échantilloné à 48000Hz
Format : Wave
File size : 2.39 MiB
Duration : 13 s 54 ms
Overall bit rate mode : Constant
Overall bit rate : 1 536 kb/s
Audio
Format : PCM
Format settings : Little / Signed
Codec ID : 1
Duration : 13 s 54 ms
Bit rate mode : Constant
Bit rate : 1 536 kb/s
Channel(s) : 2 channels
Sampling rate : 48.0 kHz
Bit depth : 16 bits
Stream size : 2.39 MiB (100%)
Programme écrit par Silicium628
ce logiciel est libre et open source
*/
/*
***** RAPPEL**** (source: https://helpx.adobe.com/fr/audition/using/digitizing-audio.html) **
Taux d’échantillonnage Niveau de qualité Plage de fréquences
11,025 Hz Faible qualité radio AM (multimédia bas de gamme) 0- 5 512 Hz
22,050 Hz Comparable à la qualité radio FM (multimédia haut de gamme) 0-11 025 Hz
32 000 Hz Meilleur que la qualité radio FM (taux de diffusion standard) 0-16 000 Hz
44 100 Hz CD 0-22 050 Hz
48 000 Hz DVD standard 0-24 000 Hz
96 000 Hz DVD Blu-ray 0-48 000 Hz
*********************************************************************************************/
/*
Le présent logiciel est paramétré pour traiter les fichiers audio ECHANTILLONES à 48000Hz (qualité DVD)
ET PAS 44.1 !!
Je le rendrai compatible avec les autres taux, mais pour l'instant il faut enregistrer
au préalable l'audio à traiter avec un taux de 48kHz
(par exemple avec Audacity, c'est facile voir en bas à gauche de sa fenêtre).
*/
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "math.h"
#include "complexe.cpp"
#include <QMessageBox>
#include <QFileDialog>
#include <QFile>
//#include <QImage>
#include <QtMultimedia/QMediaPlayer>
#include <QAudioOutput>
#include <QMouseEvent>
#include <QDateTime>
#include <QScrollBar>
/* FFT_wav */
QString version = "33.1";
/* todo:
- clic trop haut en dehors du cadre -> note MI 52 au lieu de rien
*/
int write_mode =0;
QString couleur_ecran = "#141414";
QString couleur_ligne = "#878787";
QString couleur_trace1 = "#0EA004";
QString couleur_trace2 = "#00FFFF";
QString couleur_curseur = "#FFFF00";
QString couleur_texte = "#FFFF00"; // JAUNE
QString couleur_encadrement = "#FF0000"; // ROUGE
QPen pen_ligne(QColor(couleur_ligne), 1, Qt::SolidLine);
QPen pen_trace1(QColor(couleur_trace1), 1, Qt::SolidLine);
QPen pen_trace2(QColor(couleur_trace2), 1, Qt::SolidLine);
QPen pen_curseur(QColor(couleur_curseur), 1, Qt::SolidLine);
QPen pen_reticule(QColor(couleur_ligne), 1, Qt::SolidLine);
QPen pen_encadrement(QColor(couleur_encadrement), 1, Qt::SolidLine);
char temp_entete[100]; // signal entete lu dans le fichier .wav
char temp_data[2000000]; // signal audio lu dans le fichier .wav (max 2MB)
int pas_echantillonage = 24;
Complexe ech[2048]; // nb_ech echantillons
Complexe tab_X[2048]; // nb_ech valeurs traitées
Complexe tab_W[2048];
bool tableau_w_plein = false;
uint8_t table_FREQ_midi[2000][1]; // [t][num_raie]
uint8_t table_amplitudes[2000][1];// [t][num_raie]
double facteur_compression=1.0;
double frequence1=400; // Hz
double frequence2=400; // Hz
double frequence3=100; // Hz
uint nb_etapes=10; // 10
uint nb_ech = pow (2, nb_etapes); // nombre d'échantillons = 2 puissance(nb_etapes) ->2^10 = 1024
float table_modules_FFT[1024]; // = 2^10
float x_clic_ecran, y_clic_ecran;
int nb_tics;
long T_i=0;
int n_player=1;
double seuil;
double gain =1.0;
int num_note_depart;
int num_note_jouee=0;
int nb_acc=0;
int num_note_max=0;
double memo_scroll_x=0;
QDateTime temps_0; // départ du jeu des notes
bool hamming = true;
bool bourrage_de_zeros = true;
bool second_Frq = false;
bool bz = true;
bool modul = false;
// type de mesure noté Ta/Tb (4/4, 3/2 ...)
int Ta = 4;
int Tb = 4;
//QList<int> index_midi; // position x des notes midi sur le graphe FFT (à partir de midi=52)
QList<QString> liste_couleurs;
QList<ENR_FFT> liste_ENR_FFT; // liste de "notes" détectées (maximums de FFT)
QList<NOTE> liste_NOTES;
QList<QString> noms_notes;
QList<QString> gamme_chromatique;
QFile file_wav; // fichier à analyser
QFile file_dat; // datas du TAB_FRQ
QDir currentDir;
QString base_Dir;
QString default_Dir = "/home/";
QString string_currentDir = default_Dir; // à priori; sera mis à jour lors de la lecture du fichier 'params.ini'
QString string_currentFile;
QString nom_fichier_in="";
QDataStream binStream1;
float duree; // calculée dans la fonction 'decode_entete'
float data_size; // calculée dans la fonction 'decode_entete'
bool wav_ok = false;
bool dossier_de_travail_ok = false;
bool data_nts_ok = false;
bool rapide = false;
bool etendre = true;
bool visu_notes = true;
bool visu_freq = true;
bool visu_ech_tps;
bool visu_mesures;
bool visu_duree_notes = true;
bool lecture_en_cours = false;
uint32_t offset_t;
float zoom_x =1.0;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent)
{
setupUi(this);
setWindowTitle("Transformée de Fourier Rapide FFT - version " + version);
//this->setGeometry(0,0, 1900, 1000);
setWindowState(Qt::WindowMaximized);
tabWidget_Global->setGeometry(0,0, 1920, 1280);
tabWidget_Global->setCurrentIndex(0);
//-----------------------------------------------------
// SCENE 1 - celle du haut ('Transformée de Fourier' ET 'partie échantillonée du signal')
scene1 = new QGraphicsScene(this);
scene1->setBackgroundBrush(QColor(couleur_ecran));
graphicsView1->setScene(scene1);
graphicsView1->setGeometry(0, 0, 1900, 655); //( 0, 0, 1900, 700)
graphicsView1->verticalScrollBar()->setValue(0);
calque_lignes_F_zero = new QGraphicsItemGroup();
scene1->addItem(calque_lignes_F_zero);
calque_reticule1 = new QGraphicsItemGroup();
scene1->addItem(calque_reticule1);
afficher_titre_calque("Transformée de Fourier", 0, 0, calque_reticule1);
calque_trace_FFT = new QGraphicsItemGroup();
scene1->addItem(calque_trace_FFT);
calque_trace_signal1 = new QGraphicsItemGroup();
scene1->addItem(calque_trace_signal1);
calque_curseur = new QGraphicsItemGroup();
scene1->addItem(calque_curseur);
//-----------------------------------------------------
// SCENE 2 - celle du bas ('Fichier Audio .wav')
scene2 = new QGraphicsScene(this);
QBrush QB1("#222222");
scene2->setBackgroundBrush(QB1); // couleur_ecran
graphicsView2->setScene(scene2);
graphicsView2->setGeometry(0, 660, 1900, 200); // (0, 660, 1900, 200)
calque_trace_signal2 = new QGraphicsItemGroup();
scene2->addItem(calque_trace_signal2);
calque_reticule2 = new QGraphicsItemGroup();
scene2->addItem(calque_reticule2);
afficher_titre_calque("Fichier Audio .wav", 0, -80, calque_reticule2);
calque_encadrement1 = new QGraphicsItemGroup();
scene2->addItem(calque_encadrement1);
calque_encadrement2 = new QGraphicsItemGroup();
scene2->addItem(calque_encadrement2);
//-----------------------------------------------------
// SCENE 3 - LISTE verticale NOTES sur l'onglet NOTES
scene3 = new QGraphicsScene(this);
scene3->setBackgroundBrush(QColor(couleur_ecran));
graphicsView3->setScene(scene3);
calque_gradu_TAB_FRQ = new QGraphicsItemGroup();
scene3->addItem(calque_gradu_TAB_FRQ);
//-----------------------------------------------------
// SCENE 4 - ANALYSE sur l'onglet NOTES
scene4 = new QGraphicsScene(this);
scene4->setBackgroundBrush(QColor(couleur_ecran));
graphicsView4->setScene(scene4);
// graphicsView4->setEnabled(false); // pour empêcher le scrolling (pb: l'horizontal aussi !)
// graphicsView4->setGeometry(10, 30, 1000, 690); // (10,30,1000,690)
calque_grille_mesures = new QGraphicsItemGroup();
scene4->addItem(calque_grille_mesures);
calque_TAB_FRQ = new QGraphicsItemGroup();
scene4->addItem(calque_TAB_FRQ);
afficher_titre_calque("Table Fréquences & notes", 200, 0, calque_TAB_FRQ);
calque_notes = new QGraphicsItemGroup();
scene4->addItem(calque_notes);
calque_notes_jouee = new QGraphicsItemGroup();
scene4->addItem(calque_notes_jouee);
calque_echelle_temporelle =new QGraphicsItemGroup();
scene4->addItem(calque_echelle_temporelle);
//-----------------------------------------------------
Timer1 = new QTimer(this);
connect(Timer1, SIGNAL(timeout()), this, SLOT(Tic1()));
Timer2 = new QTimer(this);
connect(Timer2, SIGNAL(timeout()), this, SLOT(Tic2()));
noms_notes<<"La"<<"Si"<<"Do"<<"Ré"<<"Mi" <<"Fa"<<"Sol"<<"--"<<"--"; // <- A B C D E F G
gamme_chromatique<<"Do"<<"Do#"<<"Ré"<<"Mib"<<"Mi"<<"Fa"<<"Fa#"<<"Sol"<<"Sol#"<<"La"<<"Sib"<<"Si";
liste_couleurs <<"#EF2929"<<"#FF5C00"<<"#FCAF3E"<<"#FFE300"<<"#BFFF00"<<"#07F64F"
<<"#16D298"<<"#16D2C4"<<"#00AEFF"<<"#1667D2"<<"#7C00FF"<<"#FF67EF"<<"#EEEEEC";
player1 = new QMediaPlayer;
audioOutput1 = new QAudioOutput(this);
player1->setAudioOutput(audioOutput1);
player2 = new QMediaPlayer;
audioOutput2 = new QAudioOutput(this);
player2->setAudioOutput(audioOutput2);
player3 = new QMediaPlayer;
audioOutput3 = new QAudioOutput(this);
player3->setAudioOutput(audioOutput3);
player4 = new QMediaPlayer;
audioOutput4 = new QAudioOutput(this);
player4->setAudioOutput(audioOutput4);
load_fichier_ini();
calcul_tableau_W();
tracer_graduations_signal();
tracer_graduations_FFT();
init_TW_1();
init_TAB_FRQ();
init_TAB_lst_notes();
init_TW_type_M();
init_Autres();
seuil = doubleSpinBox_seuil->value();
spinBox_offset->setValue(-2146); // 2146; +/- 22 pour un décalage de +/-1/4 de ton
spinBox_echx->setValue(498);
write_mode=0;
on_Bt_mode_R_clicked();
visu_mesures = false;
on_Bt_toggle_visu_mesures_clicked();
visu_ech_tps = true;
on_Bt_toggle_visu_ech_tps_clicked();
visu_notes = false;
on_Bt_toggle_visu_notes_clicked();
on_tableWidget_type_M_cellClicked(2, 0);
spinBox_zoom_2->setValue(2);
Bt_jouer_tout->setStyleSheet("background-color: rgb(200, 200, 200);");
progressBar_1->setValue(0);
}
void MainWindow::init_TAB_FRQ()
{
frame_notes->setGeometry(0,0,1920,720);
graphicsView3->setGeometry(0, 30, 100, 670);
graphicsView4->setGeometry(100, 30, 2000, 700);
graphicsView4->setEnabled(false); // empêche le recadrage auto par le contenu, mais disable le scroll manuel !
rectangle1 = new QGraphicsRectItem(0, 0, 2000, 670);
rectangle1->setPen(QColor(couleur_ligne));
calque_TAB_FRQ->addToGroup(rectangle1);
tracer_graduation_TAB_NOTES();
tracer_grille_MESURES();
}
void MainWindow::init_TAB_lst_notes()
{
tableWidget_lst_notes->setGeometry(1000, 30, 450, 201);
QStringList liste_entetes1;
liste_entetes1<<"numéro"<<"midi"<<"note"<<"n° barre"<< "temps"<< "durée" << "type";
tableWidget_lst_notes->setHorizontalHeaderLabels (liste_entetes1);
tableWidget_lst_notes->setEditTriggers(QAbstractItemView::NoEditTriggers);
}
void MainWindow::init_Autres()
{
frame_1->setGeometry(1250,865,410,131);
frame_1->setVisible(false);
frame_2->setGeometry(10,865,970,75);
frame_3->setGeometry(1000,865,661,121);
frame_5->setGeometry(0, 730, 1920, 260);
frame_6->setGeometry(10, 950, 901, 40);
Bt_efface->setGeometry(915,950,60,21);
lineEdit_fichier_FREQ->setGeometry(1250, 0, 700, 26);
Bt_efface_2->setGeometry(1165, 0, 81, 26);
progressBar_1->setGeometry(98, 4, 101, 18); //(1085, 3, 78, 18);
lineEdit_fichier->setGeometry(380, 40, 601, 26);
groupBox_2->setGeometry(380, 70, 241, 161);
groupBox_3->setGeometry(630, 70, 351, 161);
groupBox_4->setGeometry(5, 5, 5, 5); // (215, 40, 155, 191)
textEdit_ETAT->setGeometry(1480, 30, 400, 200);
Bt_prev->setGeometry(880, 702, 92, 26);
Bt_next->setGeometry(980, 702, 92, 26);
Bt_debut->setGeometry(780, 702, 92, 26);
Bt_fin->setGeometry(1080, 702, 92, 26);
tracer_gradu_temporelle_signal_entree();
tracer_graduations_signal();
}
void MainWindow::init_TW_type_M()
{
frame_4->setGeometry(210, 40, 120, 120);
tableWidget_type_M->setGeometry(5, 20, 40, 70);
lineEdit_10->setGeometry(60,30,51,26);
label_8->setGeometry(2,2,111,20);
QStringList liste_labels; // pour mettre directement dans la 1ere colonne (et pas en entêtes)
liste_labels << "2/4" << "3/4" << "4/4";
for(int n=0; n<3; n++) { tableWidget_type_M->setItem(0, n, new QTableWidgetItem (liste_labels[n])); }
}
void MainWindow::init_TW_1() // affi entete du fichier wav
{
tableWidget_1->setGeometry(700, 200, 600, 300);
Bt_close_entete->setGeometry(1280, 200, 20, 20);
tableWidget_1->setColumnCount(6);
tableWidget_1->setColumnWidth(4, 120);
tableWidget_1->setColumnWidth(5, 130);
tableWidget_1->setVisible(false);
Bt_close_entete->setVisible(false);
QStringList liste_entetes;
liste_entetes <<"FileTypeBlocID"
<<"FileSize"
<<"FileFormatID"
<<"FormatBlocID"
<<"BlocSize"
<<"AudioFormat"
<<"NbrCanaux"
<<"Frequence"
<<"ECH (BytePerSec)"
<<"BytePerBloc"
<<"BitsPerSample"
<<"DataBlocID"
<<"DataSize"
<<"durée calculée";
tableWidget_1->setVerticalHeaderLabels(liste_entetes);
}
void MainWindow::load_fichier_ini()
{
QString line;
int p1, p2, p3;
dossier_de_travail_ok = false; // à priori
QFile file1(QDir::currentPath() + "/" + "params_FFT.ini"); // dans le dossier de l'exécutable (= QDir::currentPath() )
if (file1.open(QIODevice::ReadOnly | QIODevice::Text))
{
QTextStream in(&file1);
in.reset();
while ( !in.atEnd() )
{
line = in.readLine();
if (line.at(0) !='#')
{
if ((p1 = line.indexOf("<currentDir>")) != -1)
{
p2 = line.indexOf('>',p1);
p3 = line.indexOf("</",p2);
QString s1 = line.mid(p2+1, p3-p2-1);
string_currentDir = s1;
dossier_de_travail_ok = true;
}
if ((p1 = line.indexOf("<currentFile>")) != -1)
{
p2 = line.indexOf('>',p1);
p3 = line.indexOf("</",p2);
QString s1 = line.mid(p2+1, p3-p2-1);
string_currentFile = s1;
}
}
}
file1.close();
}
else
{
QString s1 = "fichier .ini non trouvé, je le crée (dans le dossier de l'executable)";
QMessageBox msgBox; msgBox.setText(s1); msgBox.exec();
base_Dir=QDir::currentPath() + "/";
save_fichier_ini();
dossier_de_travail_ok = false;
}
lineEdit_current_dir->setText(string_currentDir);
lineEdit_fichier->setText(string_currentDir);
}
QString calcul_couleur(float x) // x = 0.0 à 1.0 ; return: rouge->jaune->vert
{
QString coul_str ="#000000";
QString coul_R_str="#000000";
QString coul_V_str="#000000";
int R, V; // rouge, vert
V= x * 255;
R= 300 - (1.2 * V);
float coxh;
// la formule suivante met le jaune en valeur (avec un cosinus hyperbolique)
coxh = 3.2 - 1.5 * cosh(2.6*x - 1.5); // merci kmplot (testez la courbe f(x) = 3.2 −1.5 * cosh(2.6x − 1.5) )
R *= coxh/1.2;
V *= coxh/1.2;
coul_R_str.setNum(R, 16); coul_R_str = "0" + coul_R_str; coul_R_str = coul_R_str.right(2);
coul_V_str.setNum(V, 16); coul_V_str = "0" + coul_V_str; coul_V_str = coul_V_str.right(2);
coul_str = coul_R_str + coul_V_str;
coul_str = "#" + coul_str + "00";
return coul_str; // de la forme "#AABB00" (rouge & vert en exa, (bleu à zéro, à traiter))
}
int MainWindow::calcul_y_note(int mid) // en pixels sur la grille
{
int h1, y;
h1 = mid-52;
y =655 - (h1 * 16); // voir 'tracer_graduation_TAB_NOTES()' pour l'espacement vertical
return y;
}
int MainWindow::calcul_hauteur_note(int mid) // de 0 à 11
{
int h1;
h1 = mid-52;
h1 -=5;
h1+=12;
while(h1>=12) {h1-=12;}
if (h1==12) {h1=0;}
return h1; // h1 = 0..11
}
QString MainWindow::nom_note(int mid)
{
QString s1;
int h1 = calcul_hauteur_note(mid);
h1-=3;
if(h1<0){h1+=12;}
if(h1>11){h1-=12;}
if((h1<0) || ( h1>= gamme_chromatique.length())) {return "-";}
s1 = gamme_chromatique[h1];
return s1;
}
float freq_mid(int midi_i) // calcule la fréquence (en Hertz) d'une note à partir de son numéro midi
{
// https://fr.wikipedia.org/wiki/MIDI_Tuning_Standard
float F, A;
A=((float)midi_i-69.0)/12.0;
F= 440 * powf(2,A);
return F;
}
double calcul_id_midi(double freq_i) // complément de la fonction précédente ( float freq_mid(int midi_i) )
{
double id;
id = 69.0 + 12.0 * log2(freq_i / 440.0);
return id;
}
int MainWindow::calcul_num_midi(double x) // en fonction de la position horizontale dans le tableau d'affichage de la FFT
{
// on va détecter la note la plus proche de la position donnée
// rappel : x=92 +(m-52) * 40.9 (voir la fonction : 'tracer_graduations_FFT()')
double dx;
double xm;
for (int m=52; m<94; m++)
{
xm= 92.0 +(m-52) * 40.9;
dx = xm-x;
if (dx<0){dx=-dx;}
if (dx < (40.9/2) )
{
encadrement_note(m);
return m;
}
}
return 0;
}
void MainWindow::play_note(int midi) // sortie audio
{
QString s1, freq_txt;
calcul_hauteur_note(midi);
s1.setNum(midi);
s1="notes/"+s1+".wav"; // fichiers audio comprenant une seule note chacun, voir dans le dossier "notes" sur le HDD
if (n_player == 1) {player1->setSource(QUrl::fromLocalFile(s1)); player1->play();}
if (n_player == 2) {player2->setSource(QUrl::fromLocalFile(s1)); player2->play();}
if (n_player == 3) {player3->setSource(QUrl::fromLocalFile(s1)); player3->play();}
if (n_player == 4) {player4->setSource(QUrl::fromLocalFile(s1)); player4->play();}
if (n_player == 5) {player5->setSource(QUrl::fromLocalFile(s1)); player5->play();}
n_player++;
if (n_player >= 5) {n_player = 1;}
}
void MainWindow::effacer_calque(QGraphicsScene *scene_i, QGraphicsItemGroup *calque_i)
{
foreach( QGraphicsItem *item, scene_i->items( calque_i->boundingRect() ) )
{if( item->group() == calque_i ) { delete item; }}
}
MainWindow::~MainWindow()
{
}
void MainWindow::Etiquette(int x, int y, QString s, QString coul_txt, QString coul_fond, QString coul_bord,QGraphicsItemGroup *calque_i )
{
// fond noir sous le texte
rect1 = new QGraphicsRectItem(x, y+4, 40, 18);
rect1->setPen(QColor(coul_bord)); // bordure
rect1->setBrush(QColor(coul_fond)); // fond
calque_i->addToGroup(rect1);
//texte
GraphicsTextItem = new QGraphicsTextItem(s);
GraphicsTextItem->setPos(x, y);
GraphicsTextItem->setDefaultTextColor(coul_txt);
calque_i->addToGroup(GraphicsTextItem);
}
void MainWindow::tracer_graduations_FFT() // sur onglet
{
double x=0;
QString s1;
int y_bas =450; // 350
// cadre
rectangle1 = new QGraphicsRectItem(0, 0, 1900, y_bas);
QPen pen1("#12FF00");
rectangle1->setPen(pen1);
calque_reticule1->addToGroup(rectangle1);
s1 = "midi :"; // numéro midi
GraphicsTextItem = new QGraphicsTextItem(s1);
GraphicsTextItem->setDefaultTextColor("#FFFFFF");
GraphicsTextItem->setPos(30, 20);
// if(x<1900)
{
calque_reticule1->addToGroup(GraphicsTextItem);
}
// positions des notes midi
x= 96;
for(int m=52; m<=94; m++)
{
ligne1 = new QGraphicsLineItem(x, 70, x, y_bas);
ligne1->setPen(pen_reticule);
calque_reticule1->addToGroup(ligne1);
s1.setNum(m); // numéro midi
GraphicsTextItem = new QGraphicsTextItem(s1);
GraphicsTextItem->setDefaultTextColor("#D3D7CF");
GraphicsTextItem->setPos(x-15, 20);
if(x<1900) {calque_reticule1->addToGroup(GraphicsTextItem); }
int h1 = calcul_hauteur_note(m);
h1-=3;
if(h1<0){h1+=12;}
if(h1>11){h1-=12;}
if((h1<0) || ( h1>= gamme_chromatique.length())) {return;}
s1 = " " + gamme_chromatique[h1]; // nom de la note
GraphicsTextItem = new QGraphicsTextItem(s1);
GraphicsTextItem->setDefaultTextColor(liste_couleurs[h1]);
GraphicsTextItem->setPos(x-18, 40);
if(x<1900) {calque_reticule1->addToGroup(GraphicsTextItem); }
x+=40.9; // espacement des graduation
}
}
float MainWindow::calcul_x_barre(int n_barre)
{
// calcul de l'abscisse (en pixel) d'une barre de mesure
float x0 = 100; // à voir !
float pas_grille = doubleSpinBox_dt->value()/2.0 * zoom_x;
float x = x0 + float(n_barre) * pas_grille;
return x;
}
void MainWindow::tracer_grille_MESURES()
{
//grille temporelle (mesures et temps forts/faibles) sur le TAB NOTES (barres verticales)
QString s1;
QString bleu_clair = "#00AAFF";
QString jaune = "#FFFF00";
QString gris_clair = "#CCCCCC";
QString gris_fonce = "#777777";
QString gris_tres_fonce = "#444444";
QString couleur_i;
QPen pen_i;
float x_i1;
int num_mesure;
effacer_calque(scene4, calque_grille_mesures);
if ( visu_mesures)
{
for(int n=0; n<=120*16; n++) //42
{
x_i1 = calcul_x_barre(n);
couleur_i = gris_tres_fonce; // à priori
if ( n%(Ta/2) == 0) { couleur_i = gris_fonce; }
if ( n%(4) == 0) { couleur_i = gris_clair; }
if ( n%(4*Ta) == 0) // 1er temps fort de la mmesure
{
// numérotation des mesures, en bas, chiffres jaunes
num_mesure = n /Ta/4;
s1.setNum(num_mesure);
Etiquette(x_i1-10, 650, s1, "#FFFF00", "#000000", "#0000FF", calque_grille_mesures);
}
//if ( n%(4*m)== 0) {couleur_i = jaune;}
pen_i.setColor(couleur_i);
ligne1 = new QGraphicsLineItem(x_i1, 0.0, x_i1, 650.0);
ligne1->setPen(pen_i);
calque_grille_mesures->addToGroup(ligne1);
if (n==0)
{
/* A REVOIR
// représentation de la durée d'une NOIRE sous la forme d'un petit rectangle horizontal
// afin de bien visualiser le type de mesures à la clef : 2/4 .. 3/4 .. 4/4 .. etc
GraphicsTextItem = new QGraphicsTextItem("noire");
GraphicsTextItem->setDefaultTextColor("#AAAAAA");
GraphicsTextItem->setPos(x_i1+1.0, 595);
calque_grille_mesures->addToGroup(GraphicsTextItem);
rect2 = new QGraphicsRectItem(x_i1+1.0, 620, (4.0 * pas_grille) -2, 5);
QBrush br2; br2.setStyle(Qt::SolidPattern); br2.setColor("#222222");rect2->setBrush(br2);
pen_i.setColor("#FFFFFF");
rect2->setPen(pen_i);
calque_grille_mesures->addToGroup(rect2);
s1 += " <- n° mesure";
GraphicsTextItem = new QGraphicsTextItem(s1);
GraphicsTextItem->setDefaultTextColor("#FFFF00"); // jaune
GraphicsTextItem->setPos(x_i1-10.0, 650);
calque_grille_mesures->addToGroup(GraphicsTextItem);
*/
}
}
}
}
void MainWindow::afficher_titre_calque(QString titre, int x, int y, QGraphicsItemGroup *calque_i)
{
GraphicsTextItem = new QGraphicsTextItem(titre);
GraphicsTextItem->setDefaultTextColor("#FFFFFF");
GraphicsTextItem->setPos(x, y);
calque_i->addToGroup(GraphicsTextItem);
}
void MainWindow::tracer_graduation_TAB_NOTES() // midi ; en colonne à gauche + noms des notes
{
// positions des notes midi
QString s1;
int y= 640; //640
for(int m=52; m<=92; m++)
{
//ligne1 = new QGraphicsLineItem(100, 0, 100, 350);
//ligne1->setPen(pen_reticule);
//calque_reticule1->addToGroup(ligne1);
s1.setNum(m); // numéro midi
GraphicsTextItem = new QGraphicsTextItem(s1);
GraphicsTextItem->setDefaultTextColor("#D3D7CF");
GraphicsTextItem->setPos(0, y);
if(y> -50) {calque_gradu_TAB_FRQ->addToGroup(GraphicsTextItem); }
int h1 = calcul_hauteur_note(m);
h1-=3;
if(h1<0){h1+=12;}
if(h1>11){h1-=12;}
if((h1<0) || ( h1>= gamme_chromatique.length())) {return;}
s1 = " " + gamme_chromatique[h1]; // nom de la note
GraphicsTextItem = new QGraphicsTextItem(s1);
GraphicsTextItem->setDefaultTextColor(liste_couleurs[h1]);
// GraphicsTextItem->setHtml("<div style='background-color:#666666;'>" + s1 + "</div>");
if (m==69) // La 440
{
rect3 = new QGraphicsRectItem(30, y+6, 40, 15);
QBrush br2;
QPen pen_i;
pen_i.setColor("#AAAAAA");
rect3->setPen(pen_i);
calque_gradu_TAB_FRQ->addToGroup(rect3);
}
GraphicsTextItem->setPos(30, y);
if(y > -50) {calque_gradu_TAB_FRQ->addToGroup(GraphicsTextItem); }
y-=16; // espacement des graduations
}
}
void MainWindow::tracer_graduations_signal() // sur le 1er onglet (Source.wav)
{
/*
midi57 = 110Hz
1/110Hz = 9ms -> delta x = 142-46 = 96px
echelle x = 96/9 = 10.6 px/ms
soit:
10ms -> 106px
*/
double x;
double nb_grad_max;
double intervalle; // séparant les graduations
QPen pen1;
// int num_x;
//QString sti;
// uint decal = 3; // leger decallage vertical pour ne pas masquer la trace
/*
rectangle1 = new QGraphicsRectItem(5, 452, 1900, 200);
pen1.setColor("#0073FF");
rectangle1->setPen(pen1);
calque_reticule1->addToGroup(rectangle1);
ligne1 = new QGraphicsLineItem(10, 475, 1900, 475); // ligne horizontale
pen1.setColor("#0073FF");
ligne1->setPen(pen1);
calque_reticule1->addToGroup(ligne1);
*/
afficher_titre_calque("Partie échantillonée du signal", 0, 452, calque_reticule1);
// lignes verticales
intervalle = 106;
nb_grad_max = 18;
for (int i=0; i<=nb_grad_max; i++)
{
x = intervalle * i;
ligne1 = new QGraphicsLineItem(x, 452, x, 780);
ligne1->setPen(pen_reticule);
/*
sti.setNum(num_x);
sti+=" ms";
GraphicsTextItem = new QGraphicsTextItem(sti);
GraphicsTextItem->setDefaultTextColor(couleur_texte);
GraphicsTextItem->setPos(x,550);
if(x<1900) {calque_reticule1->addToGroup(GraphicsTextItem); }// évite que l'écriture ne déborde du cadre a droite
*/
}
/*
// lignes horizontales
intervalle = 55;
nb_grad_max = 3;
for (i=0; i<=nb_grad_max; i++)
{
y = 300 - intervalle * i;
ligne1 = new QGraphicsLineItem(0, y, 1350, y);
ligne1->setPen(pen_reticule);
calque_reticule1->addToGroup(ligne1);
}
texte_mid = new QGraphicsTextItem("Silicium628");
texte_mid->setDefaultTextColor(couleur_signature);
texte_mid->setPos(5,5);
calque_reticule1->addToGroup(texte_mid);
*/
scene1->addItem(calque_reticule1);
}
void MainWindow::tracer_gradu_temporelle_signal_entree()
{
// GRADUATION TEMPORELLE sur le signal d'entrée, en secondes
QString s1;
int x=0;
int dy = 75;
float pas =8;
for(int n=0; n<1200; n++)
{
if (n%10 == 0) { dy = 75; }
else if (n%5 == 0) {dy = 20; }
else {dy = 10;}
x = n *pas;
ligne1 = new QGraphicsLineItem(x, -dy, x, dy);
QPen P1("#AAAAAA");
ligne1->setPen(P1);
calque_reticule2->addToGroup(ligne1);
if (n%10 == 0)
{
s1.setNum(n/10);
s1+="s";
GraphicsTextItem = new QGraphicsTextItem(s1);
GraphicsTextItem->setDefaultTextColor(couleur_texte);
GraphicsTextItem->setPos(x,55);
if(x<6000) {calque_reticule2->addToGroup(GraphicsTextItem); }//3000
}
}
}
void MainWindow::tracer_signal_complet() // totalité du fichier wav (dans le cadre du bas du 1er onglet)
{
uint8_t octet_n;
double R;
double offset_y = 0.0;
double echelle_y =2.0;
double echelle =0.074;
int x, y, memo_x, memo_y;
double min= 1000000.0;
double max=-1000000.0;
int pas = 10 * pas_echantillonage; // 240;
// le signal wav est échantilloné à l'origine à 96000 Bps (séréo 2*fois 48000 Bps)
// en ne prenant qu'un octet sur 24 on obtient 4000 Bps stéréo, c.a.d 2000 Bps mono.
QString s1;
effacer_calque(scene2, calque_trace_signal2 );
segment_trace = new QGraphicsLineItem(0, offset_y, 512, offset_y);
QPen P1("#0000FF");
segment_trace->setPen(P1); //couleur_ligne
calque_trace_signal2->addToGroup(segment_trace);
x=0;
y=offset_y;
long n_max = 80000; // (2 000 000/25 = 80 000) voir le fonction 'garnis_temp_data()'
long n;
long i=0;
int j=0;
for(n=0; n<n_max; n+=10)
{
memo_x = x;
memo_y = y;
x = echelle * n;
octet_n = temp_data[i]; // canal 1
R = octet_n - 128; // pour oter la composante continue propre au codage par des octets non signés
R *= echelle_y;
y=offset_y + R;
// recherche du mini-maxi:
if (y < min) {min = y;}
if (y > max) {max = y;}
// if (x<3000)
{
segment_trace = new QGraphicsLineItem(memo_x ,memo_y, x, y);
QPen P1("#0055FF");
segment_trace->setPen(P1);
calque_trace_signal2->addToGroup(segment_trace);
}
i++; // pour sauter le canal B (le signal dans le fichier .wav est stéréo)
i+= pas;
j++;
}
//scene1->addItem(calque_trace_signal);
s1.setNum(min);
//lineEdit_4->setText(s1);
s1.setNum(max);
// lineEdit_5->setText(s1);
}
void MainWindow::encadrement_signal(int x, int dx)
{
ligne1 = new QGraphicsLineItem(x, -80, x, 80);
ligne1->setPen(pen_encadrement);
calque_encadrement2->addToGroup(ligne1);
ligne1 = new QGraphicsLineItem(x+dx, -80, x+dx, 80);
ligne1->setPen(pen_encadrement);
calque_encadrement2->addToGroup(ligne1);
ligne1 = new QGraphicsLineItem(x, -80, x+dx, -80);
ligne1->setPen(pen_encadrement);
calque_encadrement2->addToGroup(ligne1);
ligne1 = new QGraphicsLineItem(x, 80, x+dx, 80);
ligne1->setPen(pen_encadrement);
calque_encadrement2->addToGroup(ligne1);
}
void MainWindow::encadrement_note(int n_midi) // par un rectangle
{
double x, dx, y, dy;
x=92 - 13 + ((double)n_midi-52.0) * 40.9;
dx=35;
y=16;
dy=50;
rectangle1 = new QGraphicsRectItem(x, y, dx, dy);
rectangle1->setPen(pen_encadrement);
calque_trace_FFT->addToGroup(rectangle1);
}
void MainWindow::tracer_signal_a_convertir() //partie à analyser s=f(t) (signal incident, pas le FFT); dans le cadre du haut
{
double offset_y = 575.0;
double echelle =5.0;
uint8_t octet_n;
double R;
double x, y, memo_x, memo_y;
double min= 1000000.0;
double max=-1000000.0;
QString s1;
effacer_calque(scene1,calque_trace_signal1 );
segment_trace = new QGraphicsLineItem(0, offset_y, 512, offset_y);
segment_trace->setPen(QColor(couleur_ligne));
calque_trace_signal1->addToGroup(segment_trace);
long n;
long i=0;
x=0;
y=offset_y;
int pas = 64;
for(n=0; n<nb_ech/2; n++)
{
memo_x = x;
memo_y = y;
x = echelle * n;
octet_n = temp_data[i]; // canal 1
R = octet_n - 128; // pour oter la composante continue propre au codage par des octets non signés
// R /= 10.0;
y=offset_y + R;
if (y < min) {min = y;}
if (y > max) {max = y;}
if (x<1800)
{
segment_trace = new QGraphicsLineItem(memo_x ,memo_y, x, y);
segment_trace->setPen(pen_trace1);
calque_trace_signal1->addToGroup(segment_trace);
}
i++; // pour sauter le canal B (le signal dans le fichier .wav est stéréo)
i+= pas;
}
//scene1->addItem(calque_trace_signal);
s1.setNum(min);
//lineEdit_4->setText(s1);
s1.setNum(max);
// lineEdit_5->setText(s1);
}
void MainWindow::tracer_notes_FFT() // sur onglet NOTES
{
double offset_x= spinBox_offset->value(); // ref -2146 ; +/- 22 pour un décalage de +/-1/4 de ton
double echelle_x =spinBox_echx->value(); // 498
double offset_y = 350.0;
uint n;
double module_FFT;
double x, y;
double memo_x[5]={0,0,0,0,0};
double memo_y[5]={0,0,0,0,0};
double x0, y0; // fréquence centrale trouvée comme résultat (points d'inflexions de la courbe)
double z=1.6;
//int x1=0;
//int x2=0;
int num_raie=0;
QPen pen1("#12FF00");
if(!rapide)
{
effacer_calque(scene1,calque_trace_FFT );
effacer_calque(scene1,calque_lignes_F_zero );
}
x=0;
y = offset_y;
ENR_FFT enr_i;
for(n=0; n<nb_ech; n++)
{
//qDebug() << "n" << n;
memo_x[0] = x;
memo_y[0] = y;
// x = offset_x + echelle_x * n; // echelle linéaire -> pas top
x = offset_x + echelle_x * log2((double)(n+1)); // échelle logarithmique afin que les tons soient équi-espacés
// ci-dessous 'a' est la partie réelle du complexe, et 'b' la partie imaginaire
// on calcule le module :
module_FFT = z * sqrt( tab_X[n].a * tab_X[n].a + tab_X[n].b * tab_X[n].b ); // z * racine(a²+b²)
y = module_FFT*10.0; // amplitude
y = offset_y - y; // offset et inversion du sens d'affichage because à l'écran les y croissent de haut en bas
// décale la pile memo_x
// puis mise en mémoire (empile) le dernier point
memo_x[4] = memo_x[3]; memo_y[4] = memo_y[3];
memo_x[3] = memo_x[2]; memo_y[3] = memo_y[2];
memo_x[2] = memo_x[1]; memo_y[2] = memo_y[1];
memo_x[1] = memo_x[0]; memo_y[1] = memo_y[0];
memo_x[0] = x;
memo_y[0] = y;
if((x>20) && (x<1900))
{
//y *=1.0;
if (x > -100)
{
if (!rapide)
{
segment_trace = new QGraphicsLineItem(memo_x[1] ,memo_y[1], x, y);
segment_trace->setPen(pen_trace2);
calque_trace_FFT->addToGroup(segment_trace);
}
}
// DETECTION DES NOTES
//----------------------------------------------------------------------------------
// détection direct des points hauts (points d'inflexion de la courbe)
// on pourrait calculer la dérivée de la courbe, et détecter les valeurs pour lesquelles elle s'annule
// problème : peut ne pas détecter si le sommet est un pic (discontinuité)
//----------------------------------------------------------------------------------
// Autre méthode :
// détection de 2 doublets de points de part et d'autre, montants puis descendant
// le sommet se situe donc entre les 2 doublets
// la DETECTION s'effectue ICI
// ATTENTION : la ligne qui suit détermine la stratégie utilisée et son paramétrage influe grandement
// sur la qualité du résultat.
// on doit pouvoir l'améliorer mais attention : toujours expérimenter sur des données réelles (air de musique)
// avant de valider la moindre "améloration".
//if ( (memo_y[3] - memo_y[2]) >0.5 && (memo_y[1] - memo_y[2]) >0.5 )
if ( (memo_y[3] - memo_y[2]) > 1.1 && (memo_y[1] - memo_y[2]) > 1.1 )
{
x0 = memo_x[2]; // point d'inflexion probable
y0 = memo_y[2];
if( !rapide) // pas d'affichage en mode rapide
{
ligne1 = new QGraphicsLineItem(x0, 60, x0, 350);
ligne1->setPen(pen1); // BLEU
calque_lignes_F_zero->addToGroup(ligne1);
ligne1 = new QGraphicsLineItem(x0, T_i-1, x0, T_i+1);
ligne1->setPen(pen1);
}
int m0 = 0;
m0 = calcul_num_midi(x0);
enr_i.t=T_i;
enr_i.num=num_raie;
enr_i.midi=m0;
enr_i.amplitude=y0;
// tracer sur tableau fréquences
if(num_raie<10) // 6
{
liste_ENR_FFT << enr_i; // memorisation ; donc 5 notes max pour un temps (T_i) donné
// les notes ayant même T_i seront considérées comme simultanées (accord)
tracer_1enr_sur_TAB_NOTES(enr_i);
}
num_raie++;
}
}
}
T_i++;
// T_i est ici incrémenté à chaque appel de la fonction
// sachant que chaque appel de cette fonction analyse 1024 échantillons du signal.
// et que chaque échantillon représente une durée = 0.5ms du signal d'origine
// donc T_i est (virtuellement) incrémenté toutes les 1024*0.5ms = 512ms;
// pourquoi 'virtuellement' ? parce que chaque échantillon "REPRESENTE" une durée de 0.5 ms
// mais ne "DURE PAS" 0.5ms; L'analyse s'effectue à très haute vitesse sur un signal pré-échantilloné.
// de quel chapeau sort ce 0.5ms? Voir la fontion 'void MainWindow::remplir_tableau_echantillons_signal()'
num_raie=0;
//affi_notes();
}
void MainWindow::tracer_echelle_temps_TAB_NOTES() // en secondes sur l'onglet NOTES
{
QString s1;
float x, x0, dx;
int offset_x = 80;
int y0 = 620;
float nbs;
effacer_calque(scene4, calque_echelle_temporelle);
if (visu_ech_tps == true) {y0 = 0;} // grandes lignes
for(int n=0; n<=120; n++)
{
x0 = 10.0 * doubleSpinBox_T0->value();
dx = 1000.0 * pas_echantillonage/256.0; // =93.75
x = x0 + dx * zoom_x * (float)n;
ligne1 = new QGraphicsLineItem(offset_x + x, y0-5, offset_x + x, 650);
QPen pen1;
pen1.setColor("#00FF00"); //
ligne1->setPen(pen1);
calque_echelle_temporelle->addToGroup(ligne1);
nbs = (float) n;
s1.setNum(nbs, 10, 0); // base 10, 0 décimale
s1 += "s";
Etiquette(offset_x + x, 630, s1, "#FFFFFF", "#000000", "#00FF00", calque_echelle_temporelle);
}
}
void MainWindow::tracer_1enr_sur_TAB_NOTES(ENR_FFT enr_i) // c.a.d. ayant la durée élémentaire T_i = 256 ms
{
int T_i;
//uint8_t num_raie;
uint8_t m0;
double yH, yL;
double y0, y2, dy, dy2;
double offset_y = 350.0;
QString couleur1;
T_i = zoom_x * enr_i.t;
m0 = enr_i.midi;
y0 = enr_i.amplitude;
if (checkBox_flitrer->isChecked() && (m0 != spinBox_filtre->value())) {return;}
int v; // v comme 'vertical'
v= 655 - (16 * (m0-52) );
y2 = -y0 + offset_y;
dy = y2/30.0; // 40
//dy = y2/10;
//dy=dy*dy; // pour un meilleur affichage ; A VOIR !!!!
//dy /=5.0;
dy *= gain;
if ( dy > 200) { dy = 200;} // bridage amplitude max
if(v > 655) {return;}
if (dy > seuil)
{
dy2 = dy;
if (checkBox_norm->isChecked()) {dy2=5; } // bride la hauteur des tirets
float x = 80.0 + T_i;
yH = v+dy2/2;
yL = v-dy2/2;
// rabotage pour éviter le scrolling de l'affichage:
if(yH > 655) {yH = 655;}
if(yL <0) {yH = 0;}
//ligne1 = new QGraphicsLineItem(x, yL, x, yH);
rectangle1 = new QGraphicsRectItem(x, v-dy2/2, zoom_x-2, dy2);
int h1 = calcul_hauteur_note(m0);
h1-=3;
if(h1<0){h1+=12;}
if(h1>11){h1-=12;}
couleur1 = liste_couleurs[h1];
if (checkBox_norm->isChecked()) // utilise une couleur en fonction de l'amplitude du signal
{
float dy3 = dy/ 20;
if (dy3 > 0.8) {dy3 = 0.8;}
couleur1 = calcul_couleur(dy3);
}
QPen pen1;
pen1.setColor(couleur1);
rectangle1->setPen(pen1);
rectangle1->setBrush(QColor(couleur1));
QBrush br1;
br1.setStyle(Qt::SolidPattern);
br1.setColor(couleur1);
rectangle1->setBrush(br1);
calque_TAB_FRQ->addToGroup(rectangle1);
graphicsView1->verticalScrollBar()->setValue(0);
}
}
void MainWindow::retracer_TAB_FRQ()
{
ENR_FFT enr_i;
uint16_t n_max = liste_ENR_FFT.length();
if (n_max == 0) return;
//effacer_calque(scene1,calque_trace_FFT );
tracer_graduation_TAB_NOTES();
calque_TAB_FRQ->setPos(0, 0);
for(uint16_t n=0; n<n_max-1; n++)
{
enr_i = liste_ENR_FFT[n];
tracer_1enr_sur_TAB_NOTES(enr_i);
}
float pas_grille = doubleSpinBox_dt->value()/2.0 * zoom_x;
double offset_x = 10 * doubleSpinBox_x0->value() + spinBox_d_barres->value() * pas_grille;
calque_TAB_FRQ->setPos(offset_x, 0);
}
void RAZ_tableau_echantillons()
{
uint n;
for(n=0; n < nb_ech; n++)
{
ech[n].a = 0; // partie reelle
ech[n].b = 0; // partie imaginaire
}
}
void MainWindow::remplir_tableau_echantillons_sinus()
{
// création du signal à analyser
uint n, n_max;
double R;
double frq_echantillonnage=48000; // Hz
double Te; // période d'échantillonage
Te = 1.0 / frq_echantillonnage;
Te *=15.0;
if (bz==true) {n_max = nb_ech /4; } else {n_max = nb_ech; }
for(n=0; n < n_max; n++) // nb d'échantillons
{
R= cos(2.0 * M_PI * frequence1 * n * Te );
if (modul) {R *= 1 + 0.8 * cos(2 * M_PI * frequence3 * n * Te);} // modulation d'amplitude
if (second_Frq) {R+=cos(2 * M_PI * frequence2 * n * Te); }
if (hamming) { R = R * (1- cos (2* M_PI * n / n_max ));}
R/=10;
ech[n].a = R; // partie reelle
ech[n].b = 0; // partie imaginaire
}
}
uint bit_reversal(uint num, uint nb_bits)
{
uint r = 0, i, s;
if ( num > (1<< nb_bits)) { return 0; }
for (i=0; i<nb_bits; i++)
{
s = (num & (1 << i));
if(s) { r |= (1 << ((nb_bits - 1) - i)); }
}
return r;
}
void MainWindow::bit_reverse_tableau_X()
{
// recopie les échantillons en les classant dans l'ordre 'bit reverse'
uint n,r;
for(n=0; n < nb_ech; n++) // nb d'échantillons
{
r=bit_reversal(n,nb_etapes);
tab_X[n] = ech[r];
}
}
void MainWindow::calcul_tableau_W()
{
if (tableau_w_plein == true) {return;}
// calcul et memorisation dans un tableau des twiddle factors (facteurs de rotation)
uint n;
double x;
for(n=0; n<(nb_ech/2-1); n++)
{
x=2.0*M_PI * n / nb_ech;
tab_W[n].a = cos(x); // partie reelle
tab_W[n].b = -sin(x); // partie imaginaire
}
tableau_w_plein = true;
}
void MainWindow::calcul_FFT()
{
Complexe produit; // voir la classe "Complexe" : complexe.h et complexe.ccp
uint etape, e1, e2, li, w, ci;
uint li_max=nb_ech;
e2=1;
for (etape=1; etape<=nb_etapes; etape++)
{
e1=e2; //(e1 évite d'effectuer des divisions e2/2 plus bas)
e2=e2+e2; // plus rapide que *=2
for (li=0; li<li_max; li+=1)
{
ci=li & (e2-1); // ET bit à bit
if (ci>(e1-1))
{
w=li_max/e2*(li & (e1 -1)); // ET bit à bit calcul du numéro du facteur de rotation W
produit = tab_W[w] * tab_X[li]; // le twiddle factor est lu en memoire; le produit est une mutiplication de nb complexes. Voir "complexe.cpp"
tab_X[li]=tab_X[li-e1] - produit; // concerne la ligne basse du croisillon; soustraction complexe; Voir "complexe.cpp"
tab_X[li-e1]=tab_X[li-e1] + produit; // concerne la ligne haute du croisillon; addition complexe; Voir "complexe.cpp"
}
}
}
}
void MainWindow::on_spinBox_frq_valueChanged(int arg1) // SELECTEUR DE FREQUENCE 1
{
effacer_calque(scene1,calque_trace_FFT );
frequence1 = arg1;
RAZ_tableau_echantillons();
remplir_tableau_echantillons_sinus();
tracer_signal_a_convertir();
calcul_tableau_W();
bit_reverse_tableau_X();
calcul_FFT(); // pour 1 cluster
// tracer_graduations();
tracer_notes_FFT();
}
void MainWindow::on_spinBox_frq_2_valueChanged(int arg1) // SELECTEUR DE FREQUENCE 2
{
effacer_calque(scene1,calque_trace_FFT );
frequence2 = arg1;
RAZ_tableau_echantillons();
remplir_tableau_echantillons_sinus();
tracer_signal_a_convertir();
calcul_tableau_W();
bit_reverse_tableau_X();
calcul_FFT();
// tracer_graduations();
tracer_notes_FFT();
}
void MainWindow::on_spinBox_frq_3_valueChanged(int arg1)
{
effacer_calque(scene1,calque_trace_FFT );
frequence3 = arg1;
RAZ_tableau_echantillons();
remplir_tableau_echantillons_sinus();
tracer_signal_a_convertir();
calcul_tableau_W();
bit_reverse_tableau_X();
calcul_FFT();
// tracer_graduations();
tracer_notes_FFT();
}
void MainWindow::on_checkBox1_toggled(bool checked) // HAMMING
{
effacer_calque(scene1,calque_trace_FFT );
if (checked) {hamming = true;} else {hamming = false;}
RAZ_tableau_echantillons();
remplir_tableau_echantillons_sinus();
tracer_signal_a_convertir();
calcul_tableau_W();
bit_reverse_tableau_X();
calcul_FFT();
// tracer_graduations();
tracer_notes_FFT();
}
void MainWindow::on_checkBox2_toggled(bool checked) // SECONDE FREQUENCE
{
effacer_calque(scene1,calque_trace_FFT );
if (checked) {second_Frq = true;} else {second_Frq = false;}
RAZ_tableau_echantillons();
remplir_tableau_echantillons_sinus();
tracer_signal_a_convertir();
calcul_tableau_W();
bit_reverse_tableau_X();
calcul_FFT();
// tracer_graduations();
tracer_notes_FFT();
}
void MainWindow::on_checkBox3_toggled(bool checked)
{
effacer_calque(scene1,calque_trace_FFT );
if (checked) {bz = true;} else {bz = false;}
RAZ_tableau_echantillons();
remplir_tableau_echantillons_sinus();
tracer_signal_a_convertir();
calcul_tableau_W();
bit_reverse_tableau_X();
calcul_FFT();
// tracer_graduations();
tracer_notes_FFT();
}
void MainWindow::on_checkBox4_clicked(bool checked)
{
effacer_calque(scene1,calque_trace_FFT );
if (checked) {modul = true;} else {modul = false;}
RAZ_tableau_echantillons();
remplir_tableau_echantillons_sinus();
tracer_signal_a_convertir();
calcul_tableau_W();
bit_reverse_tableau_X();
calcul_FFT();
// tracer_graduations();
tracer_notes_FFT();
}
void MainWindow::Tic1()
{
nb_tics++;
if(nb_tics > 4000)
{
Timer1->stop();
nb_tics=0;
}
else
{
spinBox_4->setValue( spinBox_4->value() +1);
Timer1->setInterval(2); // à voir
}
}
void MainWindow::Tic2()
{
int d_barre;
float dt, vts;
int itrv;
vts = doubleSpinBox_vitesse->value();
d_barre = joue_1_note_de_la_liste(); // nb de barres temporelles séparant la note avec celle qui suit
dt = 20.0 * (float)d_barre;
dt *= (100.0 / vts);
itrv = (int) dt;
Timer2->setInterval(itrv);
}
void clear_temp_data()
{
for(int i=0; i<2000000; i++)
{
temp_data[i]=0;
}
}
void MainWindow::on_Bt_load_wav_clicked()
{
open_fichier_wav(); // associe le binStream1 au fichier wav
decode_entete();
MainWindow::setCursor(Qt::WaitCursor);
liste_ENR_FFT.clear();
effacer_calque(scene1,calque_trace_signal1 );
effacer_calque(scene1, calque_trace_FFT);
clear_temp_data();
garnis_temp_data(0); // donne accès à la totalité (2MB) du fichier wav (dans le 'temp_data')
tracer_signal_complet();
MainWindow::setCursor(Qt::ArrowCursor);
tracer_gradu_temporelle_signal_entree();
frame_3->setVisible(true);
Bt_scan_auto->setVisible(true);
checkBox_rapide->setVisible(true);
}
/*
char RIFF[4]; (4 bytes)
unsigned long ChunkSize; (4 bytes)
char WAVE[4]; (4 bytes)
char fmt[4]; (4 bytes)
unsigned long Subchunk1Size; (4 bytes)
unsigned short AudioFormat; (2 octets)
unsigned short NumOfChan; (2 octets)
unsigned long SamplesPerSec; (4 bytes)
unsigned long bytesPerSec; (4 bytes)
unsigned short blockAlign; (2 octets)
unsigned short bitsPerSample; (2 octets)
char Subchunk2ID[4]; (4 bytes)
unsigned long Subchunk2Size; (4 bytes)
TOTAL 44 octets
*/
QString separ_milliers(uint n) // séparation des miliers par des espaces
{
QString s1, s1a, s1b, s1c;
s1.setNum(n);
int L= s1.length(); s1a = s1.right(3); s1b = s1.mid(L-6, 3); s1c = s1.mid(L-9, 3);
return (s1c+" "+s1b+" "+s1a);
}
void MainWindow::save_fichier_ini()
{
QFile file1(QDir::currentPath() + "/" + "params_FFT.ini"); // dans le dossier de l'exécutable
if (file1.open(QIODevice::WriteOnly | QIODevice::Text))
{
//int p1= string_currentFile.lastIndexOf('/');
//string_currentDir = string_currentFile.left(p1);
QTextStream out(&file1);
out << "# Ce fichier est crée automatiquement par le programme";
out << '\n';
out << "#";
out << '\n';
out << "# chemins:";
out << '\n';
out << "<currentDir>" << string_currentDir << "</currentDir>";
out << '\n';
out << "<currentFile>" << string_currentFile << "</currentFile>";
}
file1.close();
}
void MainWindow::open_fichier_wav()
{
// Le fichier .wav comprend un signal audio stéréo échantilloné à 48 000 octets/seconde pour chaque canal
// ce qui fait 96000 octets/s pour l'ensemble du signal stéréo
string_currentFile = QFileDialog::getOpenFileName(this, tr("Ouvrir Fichier wav..."), string_currentDir,
tr("Fichiers wav (*.wav);;All Files (*)"));
if (string_currentFile != "")
{
save_fichier_ini();
}
lineEdit_1->setText(string_currentFile);
int p1= string_currentFile.lastIndexOf('/');
string_currentDir = string_currentFile.left(p1);
lineEdit_current_dir->setText(string_currentDir);
save_fichier_ini();
file_wav.setFileName(string_currentFile);
if (!file_wav.open(QIODevice::ReadOnly))
{
wav_ok = false;
Bt_scan_auto->setStyleSheet("background-color: rgb(200, 200, 200);");
return ;
}
else
{
binStream1.setDevice(&file_wav); // accès à la totalité du fichier wav
wav_ok = true;
Bt_scan_auto->setStyleSheet("background-color: rgb(0, 255, 0);"); // vert
}
// le fichier reste ouvert pour toute la session
}
void MainWindow::decode_entete()
{
// ============ [Bloc de déclaration d'un fichier au format WAVE] ============
if (! wav_ok) {return;}
QString s3, s4, s5, fileSize1;
uint8_t x, nb_canaux;
float tot1, tot2, tot3, tot4;
binStream1.device()->seek(0); // retour de la lecture au début du fichier
binStream1.readRawData (temp_entete, 100); // lecture entete du fichier
//FileTypeBlocID
x=(uint8_t)temp_entete[0]; if ((x>64) && (x<127)) { s3 = char(x); }
x=(uint8_t)temp_entete[1]; if ((x>64) && (x<127)) { s3 += char(x); }
x=(uint8_t)temp_entete[2]; if ((x>64) && (x<127)) { s3 += char(x); }
x=(uint8_t)temp_entete[3]; if ((x>64) && (x<127)) { s3 += char(x); }
tableWidget_1->setItem(0, 0, new QTableWidgetItem (s3) ); //FileTypeBlocID
// FileSize
x=(uint8_t)temp_entete[4]; s3.setNum(x); tableWidget_1->setItem(1, 0, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[5]; s3.setNum(x); tableWidget_1->setItem(1, 1, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[6]; s3.setNum(x); tableWidget_1->setItem(1, 2, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[7]; s3.setNum(x); tableWidget_1->setItem(1, 3, new QTableWidgetItem (s3) );
tot1 = temp_entete[4] + (2<<7) * temp_entete[5] + (2<<15) * temp_entete[6] + (2<23)* temp_entete[7];
fileSize1 = separ_milliers(tot1);
tableWidget_1->setItem(1, 4, new QTableWidgetItem ("= " + fileSize1) );
// FileFormatID
x=(uint8_t)temp_entete[8]; if ((x>64) && (x<127)) { s3 = char(x); }
x=(uint8_t)temp_entete[9]; if ((x>64) && (x<127)) { s3 += char(x); }
x=(uint8_t)temp_entete[10]; if ((x>64) && (x<127)) { s3 += char(x); }
x=(uint8_t)temp_entete[11]; if ((x>64) && (x<127)) { s3 += char(x); }
tableWidget_1->setItem(2, 0, new QTableWidgetItem (s3) );
// ============ [Bloc décrivant le format audio] ==============
// FormatBlocID
x=(uint8_t)temp_entete[12]; if ((x>64) && (x<127)) { s3 = char(x); }
x=(uint8_t)temp_entete[13]; if ((x>64) && (x<127)) { s3 += char(x); }
x=(uint8_t)temp_entete[14]; if ((x>64) && (x<127)) { s3 += char(x); }
x=(uint8_t)temp_entete[15]; if ((x>64) && (x<127)) { s3 += char(x); }
tableWidget_1->setItem(3, 0, new QTableWidgetItem (s3) );
//BlocSize
x=(uint8_t)temp_entete[16]; s3.setNum(x); tableWidget_1->setItem(4, 0, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[17]; s3.setNum(x); tableWidget_1->setItem(4, 1, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[18]; s3.setNum(x); tableWidget_1->setItem(4, 2, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[19]; s3.setNum(x); tableWidget_1->setItem(4, 3, new QTableWidgetItem (s3) );
tot2 = (uint8_t)temp_entete[16] + (2<<7) * (uint8_t)temp_entete[17] + (2<<15) * (uint8_t)temp_entete[18] + (2<23)* (uint8_t)temp_entete[19];
tableWidget_1->setItem(4, 4, new QTableWidgetItem ("= " + separ_milliers(tot2)) );
//AudioFormat
x=(uint8_t)temp_entete[20]; s3.setNum(x); tableWidget_1->setItem(5, 0, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[21]; s3.setNum(x); tableWidget_1->setItem(5, 1, new QTableWidgetItem (s3) );
if ((uint8_t)temp_entete[20]==1){tableWidget_1->setItem(5, 4, new QTableWidgetItem ("PCM") );}
//NbrCanaux
nb_canaux=(uint8_t)temp_entete[22]; s3.setNum(nb_canaux); tableWidget_1->setItem(6, 0, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[23]; s3.setNum(x); tableWidget_1->setItem(6, 1, new QTableWidgetItem (s3) );
//Fréquence d'échantillonage en Hz
x=(uint8_t)temp_entete[24]; s3.setNum(x); tableWidget_1->setItem(7, 0, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[25]; s3.setNum(x); tableWidget_1->setItem(7, 1, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[26]; s3.setNum(x); tableWidget_1->setItem(7, 2, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[27]; s3.setNum(x); tableWidget_1->setItem(7, 3, new QTableWidgetItem (s3) );
tot3 = (uint8_t)temp_entete[24] + (2<<7) * (uint8_t)temp_entete[25] + (2<<15) * (uint8_t)temp_entete[26] + (2<23)* (uint8_t)temp_entete[27];
tableWidget_1->setItem(7, 4, new QTableWidgetItem ("= " + separ_milliers(tot3)) );
//BytePerSec
x=temp_entete[28]; s3.setNum(x); tableWidget_1->setItem(8, 0, new QTableWidgetItem (s3) );
x=temp_entete[29]; s3.setNum(x); tableWidget_1->setItem(8, 1, new QTableWidgetItem (s3) );
x=temp_entete[30]; s3.setNum(x); tableWidget_1->setItem(8, 2, new QTableWidgetItem (s3) );
x=temp_entete[31]; s3.setNum(x); tableWidget_1->setItem(8, 3, new QTableWidgetItem (s3) );
tot4 = (uint8_t)temp_entete[28] + (2<<7) * (uint8_t)temp_entete[29] + (2<<15) * (uint8_t)temp_entete[30] + (2<23)* (uint8_t)temp_entete[31];
s3.setNum(tot4);
tableWidget_1->setItem(8, 4, new QTableWidgetItem (s3));
//tableWidget_1->setItem(8, 4, new QTableWidgetItem ("= " + separ_milliers(tot4)) );
//BytePerBloc
x=(uint8_t)temp_entete[32]; s3.setNum(x); tableWidget_1->setItem(9, 0, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[33]; s3.setNum(x); tableWidget_1->setItem(9, 1, new QTableWidgetItem (s3) );
//BitsPerSample
x=(uint8_t)temp_entete[34]; s3.setNum(x); tableWidget_1->setItem(10, 0, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[35]; s3.setNum(x); tableWidget_1->setItem(10, 1, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[36]; if ((x>64) && (x<127)) { s3 = char(x); }
x=(uint8_t)temp_entete[37]; if ((x>64) && (x<127)) { s3 += char(x); }
x=(uint8_t)temp_entete[38]; if ((x>64) && (x<127)) { s3 += char(x); }
x=(uint8_t)temp_entete[39]; if ((x>64) && (x<127)) { s3 += char(x); }
tableWidget_1->setItem(11, 0, new QTableWidgetItem (s3) );
// DataSize
x=(uint8_t)temp_entete[40]; s3.setNum(x); tableWidget_1->setItem(12, 0, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[41]; s3.setNum(x); tableWidget_1->setItem(12, 1, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[42]; s3.setNum(x); tableWidget_1->setItem(12, 2, new QTableWidgetItem (s3) );
x=(uint8_t)temp_entete[43]; s3.setNum(x); tableWidget_1->setItem(12, 3, new QTableWidgetItem (s3) );
int tot5a = 1 * (uint8_t) temp_entete[40]; //because le temp_entête de type 'char' code avec entiers signés
int tot5b = (1<<8) * (uint8_t)temp_entete[41]; // remarque: (1<<8) = 2^8 = 256 // ne pas écrire (2<<8) !!
int tot5c = (1<<16) * (uint8_t)temp_entete[42];
int tot5d = (1<<24) * (uint8_t)temp_entete[43];
data_size = tot5a + tot5b + tot5c + tot5d;
// 21248 + 458752 = 480000
// pour le .wav LA 440 (48000, durée 10s) ->14x(2^16) + 166 * (2^8) = 960000
// 960 000 kb (stéréo) / 96 000 kb/s (stéréo)= 10s
tableWidget_1->setItem(12, 4, new QTableWidgetItem ("= " + separ_milliers(data_size)) ); // DataSize
// durée calculée
duree = data_size / tot4;
s3.setNum(duree, 'f', 3);// double n, char format = 'g', int precision = 6
tableWidget_1->setItem(13, 4, new QTableWidgetItem (s3 + " s") );
s4.setNum(tot4);
s4 = "ech : " + s3;
s4 += " Bps / 2 voies (stéréo) = ";
s5.setNum(tot4/2000);
s5 +=" kBps";
tableWidget_1->setItem(8, 5, new QTableWidgetItem (s5) );
lineEdit_6->setText("File size = " + fileSize1 + " bytes ; " + s4 + s5 + " ; durée calculée = " +s3 + "s");
if ((nb_canaux == 2) && (tot4 != 96000))
{
QMessageBox msgBox;
QString s1;
s1 = "Attention: Le taux d'échentillonage n'est pas 48 kb/s";
msgBox.setText(s1);
msgBox.exec();
}
if (nb_canaux != 2)
{
QMessageBox msgBox;
QString s1;
s1 = "Attention: Signal mono (doit être STEREO !)";
msgBox.setText(s1);
msgBox.exec();
}
}
// voir la page : https://fr.wikipedia.org/wiki/Waveform_Audio_File_Format
void MainWindow::garnis_temp_data(qint32 offset_i)
{
if (! wav_ok) {return;}
binStream1.device()->seek(0); // retour de la lecture au début du fichier
binStream1.skipRawData(44+offset_i); // saut bien plus loin dans le fichier
binStream1.readRawData (temp_data, 2000000); //lecture des données audio
//file_wav.close();
}
void MainWindow::calcul_compression()
{
double R;
double R_max=0;
for(uint n =0; n<nb_ech/4; n++)
{
R=ech[n].a;
if (R>R_max) {R_max=R;}
}
}
void MainWindow::remplir_tableau_echantillons_signal() // lecture du signal à analyser dans le 'temp_data' -> ech[n]
{
/*
Le fichier .wav doit avoir les caractéristiques suivantes :
- RIFF PCM WAVE fmt
- stéréo deux voies
- 96000 octets/s c'est à dire acqui avec 48000 octets/s x 2 voies
La FTT sera effectuée en 10 passes, sur un nb_ech = 2^10 = 1024 échantillons
*/
uint n, n_max, i, offset_x;
double R;
offset_x = 40; // ou plus, si lecture plus loin dans le temps, à voir
uint8_t octet_n; // octet_B;
/*
pour un codage 8 bits/sample = 2 * 1 octet/sample (stéréo)
[Octet du Sample1 Canal1 ] (octet_A, octet_B)
[Octet du Sample1 Canal2 ]
...
*/
i=0;
n=0;
// on ne prend pas en compte tous les échantillons dispo dans le .wav (inutile ici)
// mais 1 sur 40 (la fréquence de la note la plus aigue (midi94) est = 932Hz)
// et seul le fondamental est pertinant pour reconnaitre la note, les harmoniques (timbre) sont ignorées
// il faut donc échantilloner à 932*2 = 1864 Hz au minimum
// Le fichier .wav d'origine est échantilloné à 48kHz
// 48000 / 1864 = 25.75
// On va prendre 1 échantillon sur 24 , ce qui donne un échantillonage à 48000/24 = 2000Hz
// attention : tout changement de cette valeur (pas = 24) change l'échelle de représentation de la FFT
// et fout le bocson dans tout le programme !!!
// chaque échantillon représente donc une durée = 1/2000 seconde = 0.5ms = 500us
// voir la suite des explications vers la fin de la fonction 'void MainWindow::tracer_notes_FFT()'
n_max = nb_ech /2; // = 1024/2 = 512
// 512*0.5ms = 256 ms
while (n < n_max)
{
octet_n = temp_data[offset_x + i]; // canal A
i++; // pour sauter (et ignorer) le canal B (le signal dans le fichier .wav est stéréo)
i+= pas_echantillonage;
// x= 256 * octet_A + octet_B;
R = octet_n - 128; // pour oter la composante continue propre au codage par des octets non signés
R /= 10.0;
R *= doubleSpinBox_1->value();
if (hamming) { R = R * (1- cos (2* M_PI * n / nb_ech/2 ));}
if (bourrage_de_zeros)
{
if (n<=nb_ech/4) // /4 -> signal
{
ech[n].a = R; // partie reelle
ech[n].b = 0; // partie imaginaire
}
else // -> Bourage de zéros
{
ech[n].a = 0; // partie reelle
ech[n].b = 0; // partie imaginaire
}
}
else
{
ech[n].a = R; // partie reelle
ech[n].b = 0; // partie imaginaire
}
n++; // n atteindra la valeur max = 512 ech représentant une durée totale de 256 ms
}
//calcul_compression();
}
/*
void MainWindow::on_toolButton_2_clicked() // recup signal
{
RAZ_tableau_echantillons();
remplir_tableau_echantillons_signal();
tracer_signal_a_convertir();
}
*/
void MainWindow::calcul_ofsset()
{
if(!wav_ok) return;
offset_t = spinBox_1->value() + 10 * spinBox_2->value() + 100 * spinBox_3->value() + 1000 * spinBox_4->value()
+ 10000 * spinBox_5->value() + 100000 * spinBox_6->value();
QString s1;
s1 = separ_milliers(offset_t) ;
lineEdit_3->setText(s1);
effacer_calque(scene2, calque_encadrement2);
encadrement_signal(offset_t/1200, 25);
}
void MainWindow::traitement_signal()
{
if (! wav_ok) {return;}
garnis_temp_data(offset_t);
remplir_tableau_echantillons_signal();
calcul_tableau_W();
//RAZ_tableau_echantillons();
if(!rapide) { tracer_signal_a_convertir();}
bit_reverse_tableau_X();
calcul_FFT(); // 1 FFT s'effectue sur 1 cluster de 1024 échantillons.
//Le résultat représente un spectre élémentaire correspondant au cluster, c.a.d à une durée = 1024 x 0.5ms = 512 ms
// ce résultat est retourné dans le tableau 'Complexe tab_X[2048]'
//if (radioButton_notes->isChecked()) { tracer_notes_FFT(); }
//else { tracer_modules_FFT(); }
tracer_notes_FFT();
}
void MainWindow::analyse_tout()
{
progressBar_1->setVisible(true);
/*
if (wav_ok == false)
{
QMessageBox msgBox;
msgBox.setText("file 'wav' not open");
int ret = msgBox.exec();
return;
}
*/
/*
if (data_nts_ok == false)
{
QMessageBox msgBox;
msgBox.setText("file 'nts' not open");
int ret = msgBox.exec();
return;
}
*/
QString s1;
offset_t=0;
T_i=0;
uint16_t i_max = data_size / 1024; // = environ 6000
//i_max=7000; // < ----- bridé pour la phase de tests <---- ******************************
// data_size est affiché par le visualisateur d'entête
// et aussi dans le LineEdit en bas de l'onglet FFT
// data_size = de l'ordre de 7MB, ce qui nous donne un i_max d'environ 6900
effacer_calque(scene4, calque_TAB_FRQ);
liste_ENR_FFT.clear();
//clear_temp_data();
//garnis_temp_data(0);
// effacer_calque(scene4, calque_gradu_TAB_FRQ);
// effacer_calque(scene4, calque_notes);
// effacer_calque(scene4, calque_notes_jouee);
// effacer_calque(scene4, calque_grille_mesures);
// effacer_calque(scene4, calque_echelle_temporelle);
for(uint16_t i=0 ; i<i_max ; i++) // cette boucle sera donc parcourue environ 6000 fois...
{
qDebug() << "i=" << i;
if ((i % 200) == 0)
{
uint16_t pc = 100 * i / i_max;
pc+=2;
progressBar_1->setValue(pc);
// la ligne suivante permet l'affichage de la progression
QCoreApplication::processEvents(QEventLoop::AllEvents, 1);
}
//s1.setNum(i); lineEdit_compteur->setText(s1);
traitement_signal(); // traitement d'un cluster de 1024 échantillons
offset_t += 1024; // on passera au cluster de 1024 échantillons suivant c.a.d à un temps + 0.5ms
}
progressBar_1->setVisible(false);
tracer_echelle_temps_TAB_NOTES();
}
void MainWindow::on_spinBox_1_valueChanged(int arg1)
{
if (arg1==10) { spinBox_1->setValue(0); spinBox_2->stepUp();}
calcul_ofsset();
traitement_signal();
}
void MainWindow::on_spinBox_2_valueChanged(int arg1)
{
if (arg1==-1) {spinBox_2->setValue(9); spinBox_3->stepDown();}
if (arg1==10) { spinBox_2->setValue(0); spinBox_3->stepUp();}
calcul_ofsset();
traitement_signal();
}
void MainWindow::on_spinBox_3_valueChanged(int arg1)
{
if (arg1==-1) {spinBox_3->setValue(9); spinBox_4->stepDown();}
if (arg1==10) { spinBox_3->setValue(0); spinBox_4->stepUp();}
calcul_ofsset();
traitement_signal();
}
void MainWindow::on_spinBox_4_valueChanged(int arg1)
{
if (arg1==-1) {spinBox_4->setValue(9); spinBox_5->stepDown();}
if (arg1==10) { spinBox_4->setValue(0); spinBox_5->stepUp();}
calcul_ofsset();
traitement_signal();
}
void MainWindow::on_spinBox_5_valueChanged(int arg1)
{
if (arg1==-1) {spinBox_5->setValue(9); spinBox_6->stepDown();}
if (arg1==10) { spinBox_5->setValue(0); spinBox_6->stepUp();}
calcul_ofsset();
traitement_signal();
}
void MainWindow::on_spinBox_6_valueChanged()
{
//if (arg1==-1) {spinBox_6->setValue(9);} //spinBox_6->stepDown();}
calcul_ofsset();
traitement_signal();
}
void MainWindow::on_doubleSpinBox_1_valueChanged()
{
calcul_ofsset();
traitement_signal();
}
void MainWindow::on_pushButton_8_clicked()
{
QMessageBox msgBox;
QString s1;
s1 = "Le fichier audio à analyser doit être au format unsigned 8 bits PCM";
s1 += " (généré par exemple avec Audacity)";
s1 += "\n";
s1 += "Attention: ce n'est pas le format PCM par défaut, il faut aller dans les sous-menus :";
s1 += "\n";
s1 += "Fichier/Exporter/Exporter en WAV / encodage : Unsigned 8-bit PCM";
s1 += "\n";
s1 += "Ce n'est pas 'top WiFi', mais largement suffisant pour en faire la FFT et retrouver les notes.";
s1 += "\n";
s1 += "Ce qui est le but ici.";
s1 += "\n";
s1 += "D'autre part il est vivement conseillé de compresser la dynamique de l'audio (ce qui est simple avec Audacity)";
s1 += "\n";
s1 += "ce qui facilite grandement la détection des notes.";
msgBox.setText(s1);
msgBox.exec();
}
void MainWindow::on_Bt_RAZ_clicked()
{
spinBox_1->setValue(0);
spinBox_2->setValue(0);
spinBox_3->setValue(0);
spinBox_4->setValue(0);
spinBox_5->setValue(0);
spinBox_6->setValue(0);
offset_t=0;
}
void MainWindow::on_spinBox_7_valueChanged(int arg1)
{
// ligne verticale
effacer_calque(scene1,calque_curseur);
int x = arg1;
ligne1 = new QGraphicsLineItem(x, 10, x, 500);
ligne1->setPen(pen_curseur);
calque_curseur->addToGroup(ligne1);
}
void MainWindow::on_pushButton_2_clicked()
{
Timer1->stop();
nb_tics=0;
}
void MainWindow::on_Btn_52_clicked()
{
//spinBox_n_midi->setValue(52);
file_wav.setFileName(nom_fichier_in);
if (!file_wav.open(QIODevice::ReadOnly))
{
wav_ok = false;
return ;
}
wav_ok = true;
on_Bt_RAZ_clicked();
binStream1.setDevice(&file_wav);
calcul_ofsset();
traitement_signal();
}
void MainWindow::on_Btn_94_clicked()
{
file_wav.setFileName(nom_fichier_in);
if (!file_wav.open(QIODevice::ReadOnly))
{
wav_ok = false;
return ;
}
wav_ok = true;
on_Bt_RAZ_clicked();
binStream1.setDevice(&file_wav);
calcul_ofsset();
traitement_signal();
}
void MainWindow::on_Bt_efface_clicked()
{
QMessageBox msgBox;
msgBox.setText("Erase Frequences");
msgBox.setInformativeText("Effacer toutes les FFT ?");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Cancel);
int ret = msgBox.exec();
if (ret == QMessageBox::Ok )
{
effacer_calque(scene1,calque_trace_signal1 );
effacer_calque(scene1,calque_trace_FFT );
effacer_calque(scene1,calque_lignes_F_zero );
effacer_calque(scene1,calque_encadrement1 );
effacer_calque(scene2,calque_encadrement2 );
effacer_calque(scene4,calque_TAB_FRQ );
on_Bt_RAZ_clicked();
T_i=0;
}
}
void MainWindow::on_pushButton_3_clicked()
{
tableWidget_1->setVisible(true);
Bt_close_entete->setVisible(true);
}
void MainWindow::on_Bt_close_entete_clicked()
{
tableWidget_1->setVisible(false);
Bt_close_entete->setVisible(false);
}
void MainWindow::on_Bt_close_frame1_clicked()
{
frame_1->setVisible(false);
frame_3->setVisible(true);
}
void MainWindow::on_pushButton_4_clicked()
{
frame_3->setVisible(false);
frame_1->setVisible(true);
}
void MainWindow::on_pushButton_6_clicked()
{
frame_notes->setVisible(false);
}
void MainWindow::on_pushButton_5_clicked()
{
spinBox_offset->setValue(-2146); // 2154 ; ref -2210 ; +/- 22 pour un décalage de +/-1/4 de ton
spinBox_echx->setValue(498); // 498
}
void MainWindow::save_fichier_FRQ()
{
// enregisterment incrémentiel sur le disque (nom de fichier = 1..2...3...)
ENR_FFT enr_i;
QString s1;
long n_max = liste_ENR_FFT.length();
if (n_max == 0)
{
QMessageBox msgBox; msgBox.setText("liste vide !"); msgBox.exec();
return;
}
int numero=0;
QString s2;
Timer1->stop();
bool existe =1;
while(existe)
{
numero++;
s2.setNum(numero);
existe = QFile::exists(string_currentDir + "/" + "TAB_FREQ_" + s2 + ".tfrq");
}
s2.setNum(numero);
QFile file1(string_currentDir + "/" + "TAB_FREQ_" + s2 + ".tfrq");
if (file1.open(QIODevice::WriteOnly | QIODevice::Text))
{
QByteArray byteArray1;
QDataStream stream1(&byteArray1, QIODevice::WriteOnly);
stream1.setDevice(&file1);
stream1.setVersion(QDataStream::Qt_6_8);
// entête 64 octets------------------
uint8_t d_barre = spinBox_d_barres->value(); // 1 octet
double dx = doubleSpinBox_dt->value(); // 8 octets
double x0 = doubleSpinBox_x0->value(); // 8 octets
stream1 << d_barre << dx << x0; //1+8+8 = 17 octets au début du fichier
for (int i=0; i<(64-17); i++) {stream1 << 'x';} // complète à 64 octets
// ----------------------------------
for(int n=0; n<n_max; n++)
{
enr_i = liste_ENR_FFT[n];
stream1 << enr_i.amplitude << enr_i.midi << enr_i.num << enr_i.t; // sérialisation des data
}
}
file1.close();
QMessageBox msgBox;
s1="Fichier enregistré: " + string_currentDir + "/" + "TAB_FREQ_" + s2 + ".tfrq"; msgBox.setText(s1); msgBox.exec();
}
void MainWindow::choix_dossier_de_travail()
{
QString actuel = string_currentDir;
string_currentDir = QFileDialog::getExistingDirectory
(this, tr("Open Directory"), actuel, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
if (string_currentDir != "")
{
dossier_de_travail_ok = true;
save_fichier_ini();
lineEdit_current_dir->setText(string_currentDir);
}
else { dossier_de_travail_ok = false; } // -> si on clique sur le bouton 'cancel'
}
void MainWindow::on_Bt_choix_current_dir_clicked()
{
choix_dossier_de_travail();
}
void MainWindow::load_fichier_FRQ()
{
QString filename1;
QString s1;
if(dossier_de_travail_ok == false)
{
choix_dossier_de_travail();
if (dossier_de_travail_ok == false)
{
QMessageBox msgBox;
s1="Le dossier de travail n'est pas spécifié";
msgBox.setText(s1); msgBox.exec();
return ;
}
}
filename1 = QFileDialog::getOpenFileName(this,
tr("Ouvrir Fichier tfrq"), string_currentDir + "/", tr("Fichiers tfrq (*.tfrq)"));
lineEdit_fichier_FREQ->setText(filename1);
file_dat.setFileName(filename1);
if (!file_dat.open(QIODevice::ReadOnly))
{
data_nts_ok = false;
return ;
}
else
{
binStream1.setDevice(&file_dat); // accès à la totalité du fichier dat
binStream1.setVersion(QDataStream::Qt_6_8);
data_nts_ok = true;
liste_ENR_FFT.clear();
}
//int z = sizeof(ENR_FFT); // ATTENTION ! ne PAS utiliser cette valeur qui tient en compte l'alignement en RAM
long n_max = file_dat.bytesAvailable() / 14; // 14 bits par enr; voir la def de 'ENR_FFT' dans le .h
// lecture entête -----------------
uint8_t d_barre; // 1 octet
double dt;
double x0;
binStream1 >> d_barre >> dt >> x0; // 1+8+8 = 17 octets au début du fichiers
uint8_t xx;
for (int i=0; i<(64-17); i++) {binStream1 >> xx;} // lit en tout à 64 octets libre pour l'avenir
//---------------------------------
doubleSpinBox_dt->setValue(dt);
doubleSpinBox_x0->setValue(x0);
ENR_FFT enr_i;
for(int n=0; n<n_max; n++)
{
binStream1 >> enr_i.amplitude >> enr_i.midi >> enr_i.num >> enr_i.t;
liste_ENR_FFT << enr_i;
}
file_dat.close();
retracer_TAB_FRQ();
spinBox_d_barres->setValue(d_barre);
}
void MainWindow::save_fichier_NOTES()
{
// enregisterment incrémentiel sur le disque (nom de fichier = 1..2...3...)
NOTE note_i;
QString s1;
double x = 80;
double dx = doubleSpinBox_dt->value();
uint16_t n_max = liste_NOTES.length();
if (n_max == 0)
{
QMessageBox msgBox; msgBox.setText("liste vide !"); msgBox.exec();
return;
}
int numero=0;
QString s2;
Timer1->stop();
bool existe =1;
while(existe)
{
numero++;
s2.setNum(numero);
existe = QFile::exists(string_currentDir + "/" + "NOTES_" + s2 + ".nts");
}
s2.setNum(numero);
QFile file1(string_currentDir + "/" + "NOTES_" + s2 + ".nts");
if (file1.open(QIODevice::WriteOnly | QIODevice::Text))
{
QByteArray byteArray1;
QDataStream stream1(&byteArray1, QIODevice::WriteOnly);
stream1.setDevice(&file1);
stream1.setVersion(QDataStream::Qt_6_8);
// entête 64 octets --------------
double vts = doubleSpinBox_vitesse->value(); // 8 octets
stream1 << x << dx << vts; //8+8+8 = 24 octets au début du fichier
for (int i=0; i<(64-24); i++) {stream1 << 'x';} // complète à 64 octets
//--------------------------------
for(uint16_t n=0; n<n_max; n++)
{
note_i = liste_NOTES[n];
// sérialisation des data
stream1 << note_i.numero << note_i.midi << note_i.n_barre << note_i.n_temps << note_i.duree;
}
}
file1.close();
QMessageBox msgBox;
s1="Fichier enregistré: " + string_currentDir + "/" + "NOTES_" + s2 + ".nts"; msgBox.setText(s1); msgBox.exec();
}
void MainWindow::load_fichier_NOTES()
{
QString filename1;
QString s1;
if(dossier_de_travail_ok == false)
{
choix_dossier_de_travail();
if (dossier_de_travail_ok == false)
{
QMessageBox msgBox;
s1="Le dossier de travail n'est pas spécifié";
msgBox.setText(s1); msgBox.exec();
return ;
}
}
filename1 = QFileDialog::getOpenFileName(this,
tr("Ouvrir Fichier nts"), string_currentDir+"/", tr("Fichiers nts (*.nts)"));
lineEdit_fichier->setText(filename1);
file_dat.setFileName(filename1);
if (!file_dat.open(QIODevice::ReadOnly))
{
data_nts_ok = false;
return ;
}
else
{
binStream1.setDevice(&file_dat); // accès à la totalité du fichier dat
binStream1.setVersion(QDataStream::Qt_6_8);
data_nts_ok = true;
}
//int z = sizeof(NOTE); // ATTENTION ! ne PAS utiliser cette valeur qui tient en compte l'alignement en RAM
uint16_t n_max = file_dat.bytesAvailable() / 7; // 7 bits par enr; voir la def de 'NOTE' dans le .h
double x;
double dx;
double vts;
// lecture entête -----------------
binStream1 >> x >> dx >> vts; // 8+8+8=24 octets au début du fichiers
uint8_t xx;
for (int i=0; i<(64-24); i++) {binStream1 >> xx;} // lit en tout à 64 octets
//---------------------------------
doubleSpinBox_dt->setValue(dx);
doubleSpinBox_vitesse->setValue(vts);
NOTE note_i;
for(int n=0; n<n_max; n++)
{
binStream1 >> note_i.numero >> note_i.midi >> note_i.n_barre >> note_i.n_temps >> note_i.duree;
liste_NOTES << note_i;
}
file_dat.close();
Bt_jouer_tout->setStyleSheet("background-color: rgb(0, 255, 0);"); // vert
}
void decalle_notes(int d)
{
int nb;
uint16_t i_max = liste_NOTES.length();
for( int i =0; i<i_max; i++)
{
nb = liste_NOTES[i].n_barre;
nb += d;
liste_NOTES[i].n_barre = nb;
}
}
void MainWindow::on_Bt_save_clicked()
{
save_fichier_FRQ();
}
void MainWindow::on_Bt_load_FREQ_clicked()
{
load_fichier_FRQ();
tracer_echelle_temps_TAB_NOTES();
}
void MainWindow::on_checkBox_rapide_stateChanged(int arg1)
{
rapide=arg1;
init_TAB_FRQ();
}
void MainWindow::on_doubleSpinBox_seuil_valueChanged(double arg1)
{
seuil = arg1;
effacer_calque(scene4, calque_TAB_FRQ);
retracer_TAB_FRQ();
}
void MainWindow::on_checkBox_norm_clicked()
{
if (checkBox_norm->isChecked())
{
textEdit_ETAT->setText("Mode Normalisé : \n Les couleurs représentent l'amplitude du signal");
}
else
{
textEdit_ETAT->setText(
"Mode Fréquences : \n"
"Les couleurs représentent la valeur (fréquence) de la note (Do, RE, MI...) \n"
"La taille de chaque tiret représente (abusivement sur l'axe fréquenciel) l'amplitude du signal \n"
"cette taille ne signifie donc pas un étalement en fréquence du signal, c'est juste une astuce de représentation."
);
}
effacer_calque(scene4, calque_TAB_FRQ);
retracer_TAB_FRQ();
}
void MainWindow::surbrille_note(uint16_t n)
{
double x_note;
NOTE note_i;
uint16_t n_max = liste_NOTES.length();
if ( n>= n_max) {return;}
note_i = liste_NOTES[n];
x_note = calcul_x_barre(note_i.n_barre);
if (checkBox_scrolling->isEnabled())
{
double scroll_x = graphicsView4->horizontalScrollBar()->value();
double diff_x = (2.0*x_note - scroll_x);
if (diff_x > (memo_scroll_x + 1800))
{
graphicsView4->horizontalScrollBar()->setValue(diff_x - 2000);
memo_scroll_x = diff_x;
}
}
int y;
int midi_i;
QString blanc = "#FFFFFF";
midi_i = note_i.midi;
y = calcul_y_note(midi_i) ;
//if (effacer) {effacer_calque(scene4, calque_notes_jouee);}
QPen pen1;
pen1.setColor(blanc);
pen1.setWidth(2);
ellipse1 = new QGraphicsEllipseItem(x_note-15, y-15, 30, 30) ;
ellipse1->setPen(pen1);
//ellipse1->setBrush(blanc);
calque_notes_jouee->addToGroup(ellipse1);
}
void MainWindow::complete_case(int row, int column, QString s1, QString c1, QString c2, bool bold1, QTableWidget *QTI)
{
QTableWidgetItem *Item1 = new QTableWidgetItem();
Item1->QTableWidgetItem::setForeground(QColor(c1));
Item1->QTableWidgetItem::setBackground(QColor(c2));
QFont font1;
font1.setBold(bold1);
Item1->setFont(font1);
Item1->setTextAlignment(Qt::AlignCenter);
Item1->setText(s1);
QTI->setItem(row,column,Item1);
}
void MainWindow::affiche_liste_notes() // en TEXTE dans le petit tableau
{
NOTE note_i;
QString s1;
tableWidget_lst_notes->clear();
tableWidget_lst_notes->setRowCount(0);
init_TAB_lst_notes();
uint16_t n_max = liste_NOTES.length();
if (n_max == 0) {return;}
for(int n=0; n<n_max; n++) //n_max
{
note_i=liste_NOTES[n];
int numero = note_i.numero;
int midi = note_i.midi;
int n_barre =note_i.n_barre;
int n_temps =note_i.n_temps;
int duree = note_i.duree;
int num_ligne = tableWidget_lst_notes->rowCount();
tableWidget_lst_notes->setRowCount(num_ligne+1); // ajout une ligne au tableau
s1.setNum(numero);
complete_case(n, 0, s1, "#000000", "#FFFFFF", false, tableWidget_lst_notes);
s1.setNum(midi);
int h1 = calcul_hauteur_note(midi); h1-=3; if(h1<0){h1+=12;} if(h1>11){h1-=12;}
if ((h1<0) || (h1>(liste_couleurs.length()-1))) {h1 = 0;}
QString couleur1 = liste_couleurs[h1];
complete_case(n, 1, s1, "#000000", couleur1, true, tableWidget_lst_notes);
s1 = nom_note(midi);
complete_case(n, 2, s1, "#000000", "#FFFFFF", false, tableWidget_lst_notes);
s1.setNum(n_barre);
complete_case(n, 3, s1, "#000000", "#FFFFFF", false, tableWidget_lst_notes);
s1.setNum(n_temps); // ok
complete_case(n, 4, s1, "#000000", "#FFFFFF", true, tableWidget_lst_notes);
s1.setNum(duree); //ok
complete_case(n, 5, s1, "#000000", "#FFFFFF", true, tableWidget_lst_notes);
if (duree == 16) {s1 = "R";} // Ronde
if (duree == 12) {s1 = "B.";}
if (duree == 8) {s1 = "B";} // blanche
if (duree == 4) {s1 = "N";} // noire
if (duree == 2) {s1 = "c";} // croche
if (duree == 1) {s1 = "db_c";} // double croche
if (duree == 0) {s1 = "-";}
//if (midi == 100) {s1 = "--";}
complete_case(n, 6, s1, "#000000", "#FFFFFF", true, tableWidget_lst_notes);
}
}
void MainWindow::calcule_durees_notes()
{
int db;
uint16_t i_max = liste_NOTES.length();
if (i_max == 0) {return;}
int n_barre_1;
int n_barre_2;
for(int n=0; n<i_max-1; n++) // 'i_max-1' because plus bas on indexe liste_NOTES[n+1] !!!
{
n_barre_1 = liste_NOTES[n].n_barre;
n_barre_2 = liste_NOTES[n+1].n_barre;
db = n_barre_2 - n_barre_1;
if (db<0) {db=0;}
if (db>48){db=48;}
liste_NOTES[n].duree = db;
}
}
void MainWindow::trace_liste_notes() //dessine des cercles)colorés (couleur midi) sur la grille
{
QString s1;
int db, midi_i;
float x, dx, dbx, y; //, dy;
int numero;
uint16_t n_max = liste_NOTES.length();
int n_barre_1;
//int n_barre_2;
for(int n=0; n<n_max; n++)
{
n_barre_1 = liste_NOTES[n].n_barre;
db = liste_NOTES[n].duree;
dx = doubleSpinBox_dt->value();
dbx = (float)db * dx * zoom_x / 2.0 ;
//if (visu_duree_notes == false) {dbx = 20;} // dessinera 1 note toute ronde
// x = x0 + (float)n_barre_1 * doubleSpinBox_dt->value()/2.0 * zoom_x;
x = calcul_x_barre(n_barre_1);
midi_i = liste_NOTES[n].midi;
if ((midi_i >=52) && (midi_i <=92) )
{
y = calcul_y_note(midi_i);
int h1 = calcul_hauteur_note(midi_i); h1-=3; if(h1<0){h1+=12;} if(h1>11){h1-=12;}
QString couleur1 = liste_couleurs[h1];
QString blanc = "#FFFFFF";
// rectangle (queue de la note représentant sa durée)
rect1 = new QGraphicsRectItem(x, y-2, dbx, 4);
QBrush br1;
br1.setStyle(Qt::SolidPattern);
br1.setColor(couleur1);
rect1->setBrush(br1);
//rect1->setPen(pen_i);
calque_notes->addToGroup(rect1);
ellipse1 = new QGraphicsEllipseItem(x-10, y-10, 20, 20);
ellipse1->setPen(QColor(blanc));
ellipse1->setBrush(QColor(couleur1));
calque_notes->addToGroup(ellipse1);
// inscrit le numéro de la note au centre de la note
numero = liste_NOTES[n].numero;
s1.setNum(numero);
GraphicsTextItem = new QGraphicsTextItem(s1);
GraphicsTextItem->setFont(QFont("Arial", 8));
GraphicsTextItem->setDefaultTextColor("#000000");
int x2 = x-12; if(numero<10){x2+=4;}
GraphicsTextItem->setPos(x2, y-10);
calque_notes->addToGroup(GraphicsTextItem);
}
/*
if (midi_i == 100) // barre de mesure
{
ellipse1 = new QGraphicsEllipseItem(x-5, 20, 10, 10);
QColor vert = "#00FF00";
ellipse1->setBrush(vert);
calque_notes->addToGroup(ellipse1);
}
*/
}
}
void MainWindow::numerotation_liste_notes()
{
uint16_t i_max = liste_NOTES.length();
for(uint16_t n=0; n<i_max; n++)
{
liste_NOTES[n].numero = n;
}
}
void MainWindow::suppression_notes_invalides()
{
int midi;
int nb = 0;
uint16_t n_max = liste_NOTES.length();
if (n_max == 0) {return;}
bool boucler=true;
do
{
nb=0;
for(uint16_t n=0; n<n_max; n++)
{
midi = liste_NOTES[n].midi;
if ((midi <52) || (midi>92))
{
supprime_note(n);
if (n_max == 0) {return;}
n_max --;
nb++;
}
}
if (nb==0) {boucler=false;}
}
while (boucler == true);
}
void MainWindow::suppression_mesures()
{
//int midi;
uint16_t n_max = liste_NOTES.length();
int nb = 0;
bool boucler=true;
do
{
nb=0;
for(int n=0; n<n_max; n++)
{
//midi = liste_NOTES[n].midi;
/*
if (midi == 100) // midi=100 codera pour les barres de mesures
{
supprime_note(n);
n_max --;
nb++;
}
*/
}
if (nb==0) {boucler=false;}
}
while (boucler == true);
}
void MainWindow::tri_liste_notes_par_num_barre()
{
// tri par bulles
int ba1, ba2; // barres
NOTE note_i;
uint16_t i_max = liste_NOTES.length();
if (i_max==0){return;}
for(int p=0; p<i_max; p++)
{
for(int n=0; n<i_max-1; n++)
{
ba1=liste_NOTES[n].n_barre;
ba2=liste_NOTES[n+1].n_barre;
if(ba1 > ba2)
{
//permutter les notes
note_i = liste_NOTES[n];
liste_NOTES[n] = liste_NOTES[n+1];
liste_NOTES[n+1] = note_i;
}
}
}
}
void MainWindow::ajout_note(int midi, int B, int T)
{
NOTE note_i;
note_i.midi = midi;
note_i.n_barre = B; // valeur discrete, entière : numéro de la barre de la grille temporelle
note_i.n_temps = T; // numero au sein de la mesure
liste_NOTES << note_i;
}
void MainWindow::supprime_note(uint16_t n)
{
int L = liste_NOTES.length();
if (L==0) {return;}
if ((n>0) && (n<L))
{
liste_NOTES.remove(n);
effacer_calque(scene4,calque_notes);
tri_liste_notes_par_num_barre(); //les notes seront triées par temps croissant
numerotation_liste_notes(); //les notes seront numérotées par temps croissant
trace_liste_notes();
}
}
int MainWindow::test_if_note_in_liste(uint16_t n_barre, int midi)
{
// retourne la position dans la liste, sinon zéro;
uint16_t barre_i;
int midi_i;
int i_max = liste_NOTES.length();
for(int n=0; n<i_max; n++)
{
barre_i = liste_NOTES[n].n_barre;
midi_i = liste_NOTES[n].midi;
if ((barre_i == n_barre) && (midi_i == midi))
{
return n;
}
}
return -1;
}
void MainWindow::affi_txt_en_couleur(int midi_i, QLineEdit *lineEdit_i)
{
int h1 = calcul_hauteur_note(midi_i); h1-=3; if(h1<0){h1+=12;} if(h1>11){h1-=12;}
if ((h1<0) || (h1>(liste_couleurs.length()-1))) {h1 = 0;}
QString c1 = liste_couleurs[h1];
QString c2 = "#000000";
lineEdit_i->setStyleSheet("color: " + c1 + "; background-color: " + c2 + "; font: 700 14pt 'Ubuntu';");
QString s1;
lineEdit_7b->setText(nom_note(midi_i));
}
void MainWindow::encadre_midi(int n_midi)
{
effacer_calque(scene3, calque_gradu_TAB_FRQ);
tracer_graduation_TAB_NOTES();
double x, dx, y, dy;
int dm = n_midi -52;
y = 645 - (dm * 16);
dy=16;
x=0;
dx=85;
rectangle1 = new QGraphicsRectItem(x, y, dx, dy);
QPen pen1;
pen1.setColor("#FFFFFF");
pen1.setWidth(2);
rectangle1->setPen(pen1);
calque_gradu_TAB_FRQ->addToGroup(rectangle1);
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if (tabWidget_Global->currentIndex() != 1) {return;}
QString s1;
float x0, y0; // position cliquée, libre
int n_barre=0; // numero de la barre verticale de la grille temporelle (depuis le départ)
int n_temps=0; // numero de la barre verticale de la grille temporelle (au sein de la mesure)
int n2;
float x_barre=0;
float x_min, x_max;
float y_min, y_max;
int midi=0; // midi
float scroll_x = graphicsView4->horizontalScrollBar()->value();
float scroll_y = graphicsView4->verticalScrollBar()->value();
graphicsView4->verticalScrollBar()->setValue(0);
s1.setNum(scroll_x); lineEdit_8->setText(s1);
s1.setNum(scroll_y); lineEdit_9->setText(s1);
x_min = graphicsView4->x();
x_max = x_min + graphicsView4->width();
y_min = graphicsView4->y();
y_max = y_min + graphicsView4->height() + 16; // +16 sinon rate le mi grave (midi 52)
x_clic_ecran = event->position().x();
y_clic_ecran = event->position().y();
x0 = x_clic_ecran-102 + scroll_x; // position cliquée
y0 = y_clic_ecran-78 + scroll_y;
// pour test (petite croix)
if (0)
{
QPen pen1; pen1.setColor("#FFFF00");
ligne1 = new QGraphicsLineItem(x0-10, y0-10, x0+10, y0+10);
ligne1->setPen(pen1);
calque_notes->addToGroup(ligne1);
ligne1 = new QGraphicsLineItem(x0-10, y0+10, x0+10, y0-10);
ligne1->setPen(pen1);
calque_notes->addToGroup(ligne1);
}
// ----------------------
if (x_clic_ecran > x_min && x_clic_ecran < x_max && y_clic_ecran > y_min && y_clic_ecran < y_max)
{
s1.setNum(x0); lineEdit_4->setText(s1);
s1.setNum(y0); lineEdit_5->setText(s1); // =481 foire !
// recherche (verticale) de la note midi la plus proche du point cliqué
int dm =0;
for(int n=0; n<=40; n++)
{
int d1= (655-y0) - 16*n; // 681
d1=abs(d1);
if(d1<=8) // 8 sachant que l'espacement de niveaux est = 16
{
dm = n;
}
}
// calcul de la valeur midi de la note
midi = 52 + dm;
encadre_midi(midi);
// calcul de l'abscisse discret
// voir le tracé des barres de mesure 'tracer_grille_MESURES()'
float pas_grille = doubleSpinBox_dt->value()/2.0 * zoom_x; // pas de la grille
float offset_grille = 0; //75;
x_barre = offset_grille; //0
bool trouve = false; // à priori
int n =-1;
while ((n<800) && (trouve == false))
{
float dx2 = x0 - x_barre;
if (dx2<0) {dx2 = -dx2;}
if (dx2 < pas_grille/2.0)
{
trouve = true;
n_barre = n; // numero de la barre verticale de la grille temporelle
n_temps = n%32;
if (n_temps == 0) {n_temps = 32;}
}
x_barre = calcul_x_barre(n+1);
n++;
}
if (trouve == false) {return ;}
if (n==0) {return ;}
s1.setNum(midi);
lineEdit_7->setText(s1);
lineEdit_7b->setText(nom_note(midi));
float F = freq_mid(midi);
s1.setNum(F);
s1.replace('.',','); // ce qui permet un copier collé direct dans 'Adacity'
lineEdit_7d->setText(s1);
affi_txt_en_couleur(midi, lineEdit_7b);
n2 = test_if_note_in_liste(n_barre, midi); // si note in liste, -> n2 = num de la note
if (n2 != -1) { QString s1; s1.setNum(n2); lineEdit_7c->setText(s1); }
play_note(midi);
}
int h1 = calcul_hauteur_note(midi);
h1-=3;
if(h1<0){h1+=12;}
if(h1>11){h1-=12;}
//QColor couleur1 = liste_couleurs[h1];
/*
// détection de la couleur du point cliqué
QPixmap pixmap1 = graphicsView4->grab();
QRgb rgbValue = pixmap1.toImage().pixel(x0, y0);
QColor couleur1 = QColor::fromRgb(rgbValue);
*/
if (write_mode == 1)
{
n2 = test_if_note_in_liste(n_barre, midi);
if (n2 == -1) // si la note ne figure pas déjà dans la liste...
{
// enregistre la note dans la liste (à la fin)
ajout_note(midi, n_barre, n_temps);
}
else { supprime_note(n2); }
effacer_calque(scene4,calque_notes);
tri_liste_notes_par_num_barre(); // les notes seront triées par num_barre croissant
numerotation_liste_notes(); // les notes seront numérotées par num_barre croissant
calcule_durees_notes();
affiche_liste_notes();
trace_liste_notes();
}
}
int MainWindow::joue_1_note_de_la_liste()
{
NOTE note_i1, note_i2;
int midi_i;
int barre1, barre2; // barres de la grille temporelle
int d_barre;
QString s1;
QDateTime tpi;
tpi = QDateTime::currentDateTime();
qint64 tp_relatif = temps_0.msecsTo(tpi); // en ms
s1.setNum(tp_relatif);
uint16_t num_max = liste_NOTES.length();
if (num_max == 0) {return 0;}
if (num_max < 3) {return 0;}
if (num_note_jouee > (num_max -2)) {return 0;}
note_i1 = liste_NOTES[num_note_jouee];
note_i2 = liste_NOTES[num_note_jouee+1];
barre1 = note_i1.n_barre;
barre2 = note_i2.n_barre;
if (barre1 == barre2) // detection d'un accord (notes simultanées)
{
surbrille_note(num_note_jouee);
surbrille_note(num_note_jouee+1);
nb_acc++;
}
else
{
if (nb_acc == 0) {effacer_calque(scene4, calque_notes_jouee);}
surbrille_note(num_note_jouee);
nb_acc--; if (nb_acc<0) {nb_acc=0;}
}
d_barre = barre2 - barre1;
if (d_barre < 0){d_barre = -d_barre;}
midi_i=note_i1.midi;
s1.setNum(num_note_jouee); lineEdit_7c->setText(s1);
lineEdit_7->setText("");
lineEdit_7b->setText("");
s1.setNum(midi_i) ;
if ( ! (checkBox_basses->isChecked() && midi_i > spinBox_filtre_2->value() ))
{
play_note(midi_i);
encadre_midi(midi_i);
}
num_note_jouee++;
if(num_note_jouee >= num_note_max)
{
Timer2->stop();
num_note_jouee=0;
}
return d_barre; // permet de retrouver l'espacement temporel entre la note et celle qui suit.
}
void MainWindow::on_Bt_toggle_visu_notes_clicked() // toggle visu notes
{
if(visu_notes == 0)
{
visu_notes = 1;
Bt_toggle_visu_notes->setStyleSheet("background-color: rgb(0, 255, 0);"); // vert
tri_liste_notes_par_num_barre(); //les notes seront triées par temps croissant
numerotation_liste_notes(); //les notes seront numérotées par temps croissant
affiche_liste_notes();
trace_liste_notes();
}
else
{
Bt_toggle_visu_notes->setStyleSheet("background-color: rgb(200, 200, 200);");
visu_notes = 0;
effacer_calque(scene4,calque_notes);
}
}
void MainWindow::on_Bt_save_notes_clicked()
{
calcule_durees_notes();
save_fichier_NOTES();
}
void MainWindow::on_Bt_load_notes_clicked()
{
uint16_t n_max = liste_NOTES.length();
if (n_max != 0) // pour éviter d'enregistrer un fichier vide lors du 1er chargement des notes
{
QMessageBox msgBox;
msgBox.setText("Confirmation de chargement");
msgBox.setInformativeText("Un enregistrement des notes va être effectué au préalable. Si clic sur Cancel, opération annulée");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
int ret = msgBox.exec();
if (ret == QMessageBox::Cancel ) { return; }
on_Bt_save_notes_clicked();
}
liste_NOTES.clear();
effacer_calque(scene4,calque_notes);
load_fichier_NOTES();
suppression_notes_invalides();
affiche_liste_notes(); // en texte
trace_liste_notes(); // ellipses
}
void MainWindow::on_Bt_joue_passage_clicked()
{
if (liste_NOTES.length() == 0) {return;}
if (Timer2->isActive())
{
Timer2->stop();
return;
}
Timer2->start(500);
int note_de_depart = spinBox_10->value();
int nb_notes_a_jouer = spinBox_11->value()-1;
num_note_jouee = note_de_depart;
num_note_max = note_de_depart + nb_notes_a_jouer;
if (num_note_max > liste_NOTES.length()-1) { num_note_max = liste_NOTES.length()-1;}
NOTE note_i = liste_NOTES[note_de_depart];
double x_note = 80 + note_i.n_barre * doubleSpinBox_dt->value()/2.0 * zoom_x;
graphicsView4->horizontalScrollBar()->setValue(x_note - 100);
Timer2->start(500);
}
void MainWindow::on_Bt_joue_wav_clicked()
{
QStringList arguments;
arguments << string_currentFile;
QString program = "audacity"; //"audacious"
QProcess *myProcess = new QProcess();
myProcess->start(program, arguments);
}
void MainWindow::on_Bt_joue_wav_2_clicked()
{
on_Bt_joue_wav_clicked();
}
void MainWindow::on_Btn_RAZ_notes_clicked()
{
QMessageBox msgBox;
msgBox.setText("Erase notes");
msgBox.setInformativeText("Effacer toutes les notes ?");
msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Cancel);
int ret = msgBox.exec();
if (ret == QMessageBox::Ok )
{
liste_NOTES.clear();
effacer_calque(scene4,calque_notes);
Bt_jouer_tout->setStyleSheet("background-color: rgb(200, 200, 200);");
affiche_liste_notes();
}
}
void MainWindow::on_Bt_stop_clicked()
{
num_note_depart = 0;
num_note_jouee=0;
Timer2->stop();
Bt_jouer_tout->setText("Jouer tout");
}
void MainWindow::on_spinBox_8_textChanged()
{
effacer_calque(scene4, calque_echelle_temporelle);
tracer_echelle_temps_TAB_NOTES();
}
void MainWindow::on_doubleSpinBox_dt_valueChanged()
{
effacer_calque(scene4, calque_grille_mesures);
tracer_grille_MESURES();
effacer_calque(scene4, calque_notes);
affiche_liste_notes();
trace_liste_notes();
effacer_calque(scene4, calque_echelle_temporelle);
tracer_echelle_temps_TAB_NOTES();
}
void MainWindow::on_Bt_mode_R_clicked()
{
write_mode=0;
MainWindow::setCursor(Qt::ArrowCursor);
Bt_mode_R->setStyleSheet("background-color: rgb(0, 255, 0);"); // vert
Bt_mode_W->setStyleSheet("background-color: rgb(200, 200, 200);");
}
void MainWindow::on_Bt_mode_W_clicked()
{
write_mode=1;
//MainWindow::setCursor(Qt::SplitHCursor);
Bt_mode_W->setStyleSheet("background-color: rgb(255, 0, 0);"); // rouge
Bt_mode_R->setStyleSheet("background-color: rgb(200, 200, 200);");
}
void MainWindow::on_Bt_scan_auto_clicked()
{
if (! wav_ok) {return;}
if (rapide)
{
tabWidget_Global->setCurrentIndex(1);
on_Bt_RAZ_clicked();
MainWindow::setCursor(Qt::WaitCursor);
effacer_calque(scene4, calque_TAB_FRQ );
analyse_tout();
MainWindow::setCursor(Qt::ArrowCursor);
}
else {Timer1->start(10);}
}
void MainWindow::on_Bt_jouer_tout_clicked()
{
if (liste_NOTES.length() == 0) {return;}
if (lecture_en_cours == false)
{
Bt_jouer_tout->setText("[]");
lecture_en_cours = true;
graphicsView4->horizontalScrollBar()->setValue(0);
memo_scroll_x = 0;
//Timer2->stop();
//num_note_depart = 0;
// num_note_jouee=0;
num_note_max = liste_NOTES.length()-1;
if (num_note_max < 1) {return;}
Timer2->start(1);
temps_0 = QDateTime::currentDateTime();
}
else
{
lecture_en_cours = false;
Timer2->stop();
Bt_jouer_tout->setText(">");
}
}
void MainWindow::on_Btn_SUPP_notes_invalides_clicked()
{
suppression_notes_invalides();
}
void MainWindow::on_Bt_toggle_visu_freq_clicked()
{
if(visu_freq == 0)
{
visu_freq = 1;
Bt_toggle_visu_freq->setStyleSheet("background-color: rgb(0, 255, 0);"); // vert
calque_TAB_FRQ->setVisible(true);
}
else
{
visu_freq = 0;
Bt_toggle_visu_freq->setStyleSheet("background-color: rgb(200, 200, 200);");
calque_TAB_FRQ->setVisible(false);
}
}
void MainWindow::on_Bt_efface_2_clicked() // sur tab FREQ-NOTES
{
on_Bt_efface_clicked(); // (celui de l'autre tab FFT)
}
void MainWindow::on_Bt_toggle_visu_ech_tps_clicked()
{
if(visu_ech_tps == 0)
{
visu_ech_tps = 1;
Bt_toggle_visu_ech_tps->setStyleSheet("background-color: rgb(0, 255, 0);"); // vert
tracer_echelle_temps_TAB_NOTES();
}
else
{
visu_ech_tps = 0;
Bt_toggle_visu_ech_tps->setStyleSheet("background-color: rgb(200, 200, 200);");
//effacer_calque(scene4,calque_echelle_temporelle);
tracer_echelle_temps_TAB_NOTES();
}
}
void MainWindow::on_Bt_toggle_visu_mesures_clicked()
{
if(visu_mesures == 0)
{
visu_mesures = 1;
Bt_toggle_visu_mesures->setStyleSheet("background-color: rgb(0, 255, 0);"); // vert
tracer_grille_MESURES();
}
else
{
visu_mesures = 0;
Bt_toggle_visu_mesures->setStyleSheet("background-color: rgb(200, 200, 200);");
tracer_grille_MESURES();
}
}
void MainWindow::on_doubleSpinBox_t0_valueChanged()
{
effacer_calque(scene4, calque_grille_mesures);
tracer_grille_MESURES();
effacer_calque(scene4, calque_notes);
affiche_liste_notes();
trace_liste_notes();
effacer_calque(scene4, calque_echelle_temporelle);
tracer_echelle_temps_TAB_NOTES();
}
void MainWindow::maj_TAB_NOTES()
{
effacer_calque(scene4, calque_grille_mesures);
effacer_calque(scene4, calque_notes);
effacer_calque(scene4, calque_echelle_temporelle);
effacer_calque(scene4, calque_TAB_FRQ);
retracer_TAB_FRQ();
tracer_grille_MESURES();
trace_liste_notes();
tracer_echelle_temps_TAB_NOTES();
}
void MainWindow::on_spinBox_zoom_2_valueChanged(int arg1)
{
zoom_x = arg1;
MainWindow::setCursor(Qt::WaitCursor);
maj_TAB_NOTES();
MainWindow::setCursor(Qt::ArrowCursor);
}
void MainWindow::on_pushButton_9_clicked()
{
QMessageBox msgBox;
QString s1;
s1 = "n° barre (de fraction 1/16 de mesure) est comptée depuis le début.";
s1 += "\n";
s1 += "'temps'= numéro de barre au sein de chaque mesure.";
s1 += "\n";
s1 += "'durée' est exprimée en nb de barres.";
s1 += "\n";
s1 += "'type' = 'B' = blanche, 'N' = noire, 'C' = croche etc...";
s1 += "\n";
msgBox.setText(s1);
msgBox.exec();
}
void MainWindow::on_spinBox_offset_valueChanged()
{
//do_FFT_freq_midi();
}
void MainWindow::on_doubleSpinBox_x0_valueChanged(double arg1)
{
//double offset_x = 10 * doubleSpinBox_dx->value();
float pas_grille = doubleSpinBox_dt->value()/2.0 * zoom_x;
double offset_x = 10 * arg1 + spinBox_d_barres->value() * pas_grille;
calque_TAB_FRQ->setPos(offset_x, 0);
}
void MainWindow::on_spinBox_d_barres_valueChanged()
{
float pas_grille = doubleSpinBox_dt->value()/2.0 * zoom_x;
double offset_x = 10 * doubleSpinBox_x0->value() + spinBox_d_barres->value() * pas_grille;
calque_TAB_FRQ->setPos(offset_x, 0);
}
void MainWindow::on_Bt_decale_notes_L_clicked()
{
decalle_notes(-1);
effacer_calque(scene4,calque_notes);
affiche_liste_notes(); // en texte
trace_liste_notes(); // ellipses
}
void MainWindow::on_Bt_decale_notes_R_clicked()
{
decalle_notes(1);
effacer_calque(scene4,calque_notes);
affiche_liste_notes(); // en texte
trace_liste_notes(); // ellipses
}
void MainWindow::on_tableWidget_type_M_cellClicked(int row, int column)
{
switch (row)
{
case 0: {lineEdit_10->setText("2/4"); Ta=2; Tb=4;} break;
case 1: {lineEdit_10->setText("3/4"); Ta=3; Tb=4;} break;
case 2: {lineEdit_10->setText("4/4"); Ta=4; Tb=4;} break;
default: break;
}
tracer_grille_MESURES();
}
void MainWindow::on_Bt_stop_2_clicked()
{
on_Bt_stop_clicked();
}
void MainWindow::on_checkBox_flitrer_clicked()
{
if (checkBox_flitrer->isChecked())
{
textEdit_ETAT->setText("Mode Filtré : \n Ne sont représentés que les signaux dont l'amplitude est supérieure au seuil choisi");
}
else
{
textEdit_ETAT->setText("Mode non-filtré : \n Tous les signaux issus de la FFT sont représentés");
}
MainWindow::setCursor(Qt::WaitCursor);
maj_TAB_NOTES();
MainWindow::setCursor(Qt::ArrowCursor);
}
/*
void MainWindow::on_spinBox_filtre_valueChanged()
{
if (checkBox_flitrer->isChecked())
{
MainWindow::setCursor(Qt::WaitCursor);
maj_TAB_NOTES();
MainWindow::setCursor(Qt::ArrowCursor);
}
}
*/
/*
void MainWindow::on_doubleSpinBox_gain_valueChanged()
{
MainWindow::setCursor(Qt::WaitCursor);
effacer_calque(scene4, calque_TAB_FRQ);
retracer_TAB_FRQ();
MainWindow::setCursor(Qt::ArrowCursor);
}
*/
void MainWindow::on_Bt_open_DIR_clicked()
{
QString s1, path1;
path1 = string_currentDir;
QProcess process1;
process1.setProgram("caja");
process1.setArguments({path1});
process1.startDetached();
}
void MainWindow::on_Bt_goto_clicked()
{
QString s1;
int n = lineEdit_7c->text().toInt();
if ((n<0) || (n>=liste_NOTES.length())) {return;}
spinBox_10->setValue(n);
NOTE note_i = liste_NOTES[n];
double x_note = 80 + note_i.n_barre * doubleSpinBox_dt->value()/2.0 * zoom_x;
num_note_jouee = n;
surbrille_note(n);
int midi = note_i.midi;
s1.setNum(midi);
lineEdit_7->setText(s1);
affi_txt_en_couleur(midi, lineEdit_7b); // nom de la note
graphicsView4->horizontalScrollBar()->setValue(x_note - 100);
}
void MainWindow::on_doubleSpinBox_T0_valueChanged(double arg1)
{
tracer_echelle_temps_TAB_NOTES();
}
void MainWindow::on_Bt_test1_clicked()
{
QString s1;
float valeur;
for (int n = 0; n <= 255; n+=1)
{
valeur = (float )n / 255;
s1 = calcul_couleur(valeur);
qDebug() << "n" << n;
qDebug() << "valeur" << valeur;
qDebug() << "s1" << s1;
qDebug() << "______________";
rectangle1 = new QGraphicsRectItem(n, 200, 2, 5);
QPen pen1;
pen1.setColor(s1);
pen1.setWidth(2);
rectangle1->setPen(pen1);
calque_grille_mesures->addToGroup(rectangle1);
}
}
void MainWindow::on_Bt_div2_clicked()
{
gain /=2;
MainWindow::setCursor(Qt::WaitCursor);
effacer_calque(scene4, calque_TAB_FRQ);
retracer_TAB_FRQ();
MainWindow::setCursor(Qt::ArrowCursor);
}
void MainWindow::on_pushButton_clicked()
{
gain *=2;
MainWindow::setCursor(Qt::WaitCursor);
effacer_calque(scene4, calque_TAB_FRQ);
retracer_TAB_FRQ();
MainWindow::setCursor(Qt::ArrowCursor);
}
void MainWindow::on_Bt_next_clicked()
{
double scroll_x = graphicsView4->horizontalScrollBar()->value();
graphicsView4->horizontalScrollBar()->setValue(scroll_x + 1000);
}
void MainWindow::on_Bt_prev_clicked()
{
double scroll_x = graphicsView4->horizontalScrollBar()->value();
graphicsView4->horizontalScrollBar()->setValue(scroll_x - 1000);
}
void MainWindow::on_Bt_debut_clicked()
{
graphicsView4->horizontalScrollBar()->setValue(0);
}
void MainWindow::on_Bt_fin_clicked()
{
graphicsView4->horizontalScrollBar()->setValue(10000);
}