La Transformée de Fourier et la Musique

Programme en C++ Qt6 basé directement sur la transformée de Fourier rapide. Retrouve les notes de musique (et leur code midi) à partir d'un fichier audio (fichier.wav)
Ce fichier source doit être stéréo échantillonné à 48kb/s, ce qui peut être facilement obtenu avec le soft avidemux (par exemple), éventuellement après extraction de la piste audio d'une vidéo .mp4.

1 Vue d'ensemble



Les notes obtenues peuvent être rejouées, les durées et le rythme sont pris en compte

2 Scan en cours (Transformée de fourier d'un échantillon de signal)

3 Aperçu en vidéo


A voir directement sur Youtube en 1080p pour voir les détails !!!

4 Le code source 'wav to midi' en C++ Qt6: fichier mainwindow.cpp



CODE SOURCE en C++
  1. /* top
  2.   Calcul de la transformée de Fourier rapide d'un signal audio au format PCM enregistré dans un fichier .wav
  3.   Format WAVE, RIFF, fmt échantilloné à 48000Hz
  4.  
  5.   Format : Wave
  6.   File size : 2.39 MiB
  7.   Duration : 13 s 54 ms
  8.   Overall bit rate mode : Constant
  9.   Overall bit rate : 1 536 kb/s
  10.  
  11.   Audio
  12.   Format : PCM
  13.   Format settings : Little / Signed
  14.   Codec ID : 1
  15.   Duration : 13 s 54 ms
  16.   Bit rate mode : Constant
  17.   Bit rate : 1 536 kb/s
  18.   Channel(s) : 2 channels
  19.   Sampling rate : 48.0 kHz
  20.   Bit depth : 16 bits
  21.   Stream size : 2.39 MiB (100%)
  22.  
  23.   Programme écrit par Silicium628
  24.   ce logiciel est libre et open source
  25. */
  26. /*
  27. ***** RAPPEL**** (source: https://helpx.adobe.com/fr/audition/using/digitizing-audio.html) **
  28. Taux d’échantillonnage Niveau de qualité Plage de fréquences
  29.  
  30. 11,025 Hz Faible qualité radio AM (multimédia bas de gamme) 0- 5 512 Hz
  31. 22,050 Hz Comparable à la qualité radio FM (multimédia haut de gamme) 0-11 025 Hz
  32. 32 000 Hz Meilleur que la qualité radio FM (taux de diffusion standard) 0-16 000 Hz
  33. 44 100 Hz CD 0-22 050 Hz
  34. 48 000 Hz DVD standard 0-24 000 Hz
  35. 96 000 Hz DVD Blu-ray 0-48 000 Hz
  36. *********************************************************************************************/
  37. /*
  38. Le présent logiciel est paramétré pour traiter les fichiers audio ECHANTILLONES à 48000Hz (qualité DVD)
  39. ET PAS 44.1 !!
  40. Je le rendrai compatible avec les autres taux, mais pour l'instant il faut enregistrer
  41. au préalable l'audio à traiter avec un taux de 48kHz
  42. (par exemple avec Audacity, c'est facile voir en bas à gauche de sa fenêtre).
  43. */
  44.  
  45.  
  46. #include "mainwindow.h"
  47. #include "ui_mainwindow.h"
  48. #include "math.h"
  49. #include "complexe.cpp"
  50.  
  51. #include <QMessageBox>
  52. #include <QFileDialog>
  53. #include <QFile>
  54. //#include <QImage>
  55.  
  56. #include <QtMultimedia/QMediaPlayer>
  57. #include <QAudioOutput>
  58.  
  59. #include <QMouseEvent>
  60. #include <QDateTime>
  61. #include <QScrollBar>
  62.  
  63.  
  64. /* FFT_wav */
  65.  
  66. //QString version = "38.0";
  67. // nouvelle numérotation par (date de compilation)
  68. QString version = "2025-02-11 c";
  69.  
  70. int write_mode =0;
  71.  
  72. QString couleur_ecran = "#141414";
  73. QString couleur_ligne = "#878787";
  74. QString couleur_trace1 = "#0EA004";
  75. QString couleur_trace2 = "#00FFFF";
  76. QString couleur_curseur = "#FFFF00";
  77. QString couleur_texte = "#FFFF00"; // JAUNE
  78. QString couleur_encadrement = "#FF0000"; // ROUGE
  79.  
  80. QPen pen_ligne(QColor(couleur_ligne), 1, Qt::SolidLine);
  81. QPen pen_trace1(QColor(couleur_trace1), 1, Qt::SolidLine);
  82. QPen pen_trace2(QColor(couleur_trace2), 1, Qt::SolidLine);
  83. QPen pen_curseur(QColor(couleur_curseur), 1, Qt::SolidLine);
  84. QPen pen_reticule(QColor(couleur_ligne), 1, Qt::SolidLine);
  85. QPen pen_encadrement(QColor(couleur_encadrement), 1, Qt::SolidLine);
  86.  
  87. char temp_entete[100]; // signal entete lu dans le fichier .wav
  88. char temp_data[2000000]; // signal audio lu dans le fichier .wav (max 2MB)
  89. int pas_echantillonage = 24;
  90.  
  91. Complexe ech[2048]; // nb_ech echantillons
  92. Complexe tab_X[2048]; // nb_ech valeurs traitées
  93. Complexe tab_W[2048];
  94. bool tableau_w_plein = false;
  95.  
  96. uint8_t table_FREQ_midi[2000][1]; // [t][num_raie]
  97. double table_integration_amplitudes[83];// [code midi]
  98.  
  99. uint16_t table_histogramme_durees[100]; // [valeur_duree][nb d'occurences]
  100.  
  101. double facteur_compression=1.0;
  102.  
  103. double frequence1=400; // Hz
  104. double frequence2=400; // Hz
  105. double frequence3=100; // Hz
  106.  
  107. uint nb_etapes=10; // 10
  108. uint nb_ech = pow (2, nb_etapes); // nombre d'échantillons = 2 puissance(nb_etapes) ->2^10 = 1024
  109.  
  110. double table_modules_FFT[1024]; // = 2^10
  111.  
  112. double x_clic_ecran, y_clic_ecran;
  113.  
  114. int nb_tics;
  115. long T_i=0; // variable GLOBALE
  116. int n_player=1;
  117. double seuil;
  118. double gain =1.0;
  119. int num_note_depart;
  120. int num_note_jouee=0;
  121. int nb_acc=0;
  122. int num_note_max=0;
  123. double memo_scroll_x=0;
  124. QDateTime temps_0; // départ du jeu des notes
  125. bool hamming = true;
  126. bool bourrage_de_zeros = true;
  127. bool second_Frq = false;
  128. bool bz = true;
  129. bool modul = false;
  130.  
  131. // type de mesure noté Ta/Tb (4/4, 3/2 ...)
  132. int8_t Ta = 4;
  133. int8_t Tb = 4;
  134. int8_t tempo_libre = 0;
  135.  
  136. //QList<int> index_midi; // position x des notes midi sur le graphe FFT (à partir de midi=40)
  137. QList<QString> liste_couleurs;
  138. QList<ENR_FFT> liste_ENR_FFT; // liste de "notes" détectées (maximums de FFT)
  139. QList<NOTE> liste_NOTES;
  140. //QList<QString> noms_notes;
  141. QList<QString> gamme_chromatique;
  142. QList<QString> gamme_chromatique_GB;
  143.  
  144.  
  145. QFile file_wav; // fichier à analyser
  146. QFile file_dat; // datas du TAB_FRQ
  147.  
  148. QDir currentDir;
  149. QString base_Dir;
  150. QString default_Dir = "/home/";
  151. QString string_currentDir = default_Dir; // à priori; sera mis à jour lors de la lecture du fichier 'params.ini'
  152. QString string_currentFile;
  153. QString nom_fichier_in="";
  154.  
  155. QDataStream binStream1;
  156.  
  157. double duree_totale; // calculée dans la fonction 'decode_entete'
  158. double data_size; // calculée dans la fonction 'decode_entete'
  159.  
  160. bool wav_ok = false;
  161. bool dossier_de_travail_ok = false;
  162. bool data_nts_ok = false;
  163. bool rapide = false;
  164. bool etendre = true;
  165. bool visu_notes = true;
  166. bool visu_freq = true;
  167. bool visu_ech_tps;
  168. bool visu_M; // echelle temporelle sous forme de mesures (temps forts, temps faibles)
  169. bool visu_T; // echelle temporelle simple en 1/20s
  170. bool visu_duree_notes = true;
  171. bool visu_notes_auto = true;
  172. bool lecture_en_cours = false;
  173.  
  174. uint32_t offset_t;
  175. double zoom_x =1.0;
  176.  
  177.  
  178. MainWindow::MainWindow(QWidget *parent) :
  179. QMainWindow(parent)
  180. {
  181. setupUi(this);
  182. setWindowTitle("Transformée de Fourier Rapide FFT - version " + version);
  183.  
  184. //this->setGeometry(0,0, 1900, 1000);
  185. setWindowState(Qt::WindowMaximized);
  186. tabWidget_Global->setGeometry(0,0, 1920, 1280);
  187. tabWidget_Global->setCurrentIndex(0);
  188.  
  189. //-----------------------------------------------------
  190. // SCENE 1 - celle du haut ('Transformée de Fourier' ET 'partie échantillonée du signal')
  191.  
  192. scene1 = new QGraphicsScene(this);
  193. scene1->setBackgroundBrush(QColor(couleur_ecran));
  194. graphicsView1->setScene(scene1);
  195. graphicsView1->setGeometry(0, 0, 1900, 655); //( 0, 0, 1900, 700)
  196. graphicsView1->verticalScrollBar()->setValue(0);
  197.  
  198. calque_lignes_F_zero = new QGraphicsItemGroup();
  199. scene1->addItem(calque_lignes_F_zero);
  200.  
  201. calque_reticule1 = new QGraphicsItemGroup();
  202. scene1->addItem(calque_reticule1);
  203. afficher_titre_calque("Transformée de Fourier", 0, 0, calque_reticule1);
  204.  
  205. calque_trace_FFT = new QGraphicsItemGroup();
  206. scene1->addItem(calque_trace_FFT);
  207.  
  208. calque_trace_signal1 = new QGraphicsItemGroup();
  209. scene1->addItem(calque_trace_signal1);
  210.  
  211. calque_curseur = new QGraphicsItemGroup();
  212. scene1->addItem(calque_curseur);
  213.  
  214.  
  215. //-----------------------------------------------------
  216. // SCENE 2 - celle du bas ('Fichier Audio .wav')
  217.  
  218. scene2 = new QGraphicsScene(this);
  219. QBrush QB1("#222222");
  220. scene2->setBackgroundBrush(QB1); // couleur_ecran
  221.  
  222. graphicsView2->setScene(scene2);
  223. graphicsView2->setGeometry(0, 660, 1900, 200); // (0, 660, 1900, 200)
  224.  
  225. calque_trace_signal2 = new QGraphicsItemGroup();
  226. scene2->addItem(calque_trace_signal2);
  227.  
  228. calque_reticule2 = new QGraphicsItemGroup();
  229. scene2->addItem(calque_reticule2);
  230. afficher_titre_calque("Fichier Audio .wav", 0, -80, calque_reticule2);
  231.  
  232. calque_encadrement1 = new QGraphicsItemGroup();
  233. scene2->addItem(calque_encadrement1);
  234.  
  235. calque_encadrement2 = new QGraphicsItemGroup();
  236. scene2->addItem(calque_encadrement2);
  237.  
  238. //-----------------------------------------------------
  239. // SCENE 3 - LISTE verticale NOTES sur l'onglet NOTES
  240. scene3 = new QGraphicsScene(this);
  241. scene3->setBackgroundBrush(QColor(couleur_ecran));
  242. graphicsView3->setScene(scene3);
  243.  
  244. calque_gradu_TAB_FRQ = new QGraphicsItemGroup();
  245. scene3->addItem(calque_gradu_TAB_FRQ);
  246.  
  247. //-----------------------------------------------------
  248. // SCENE 4 - ANALYSE sur l'onglet NOTES
  249.  
  250. scene4 = new QGraphicsScene(this);
  251. scene4->setBackgroundBrush(QColor(couleur_ecran));
  252. graphicsView4->setScene(scene4);
  253.  
  254. calque_grille_M = new QGraphicsItemGroup();
  255. scene4->addItem(calque_grille_M);
  256.  
  257. calque_grille_T = new QGraphicsItemGroup();
  258. scene4->addItem(calque_grille_T);
  259.  
  260. calque_TAB_FRQ = new QGraphicsItemGroup();
  261. scene4->addItem(calque_TAB_FRQ);
  262. afficher_titre_calque("Table Fréquences & notes", 200, 0, calque_TAB_FRQ);
  263.  
  264. calque_notes_manuelles = new QGraphicsItemGroup();
  265. scene4->addItem(calque_notes_manuelles);
  266.  
  267. calque_notes_auto = new QGraphicsItemGroup();
  268. scene4->addItem(calque_notes_auto);
  269.  
  270. calque_notes_jouee = new QGraphicsItemGroup();
  271. scene4->addItem(calque_notes_jouee);
  272.  
  273. calque_echelle_temporelle = new QGraphicsItemGroup();
  274. scene4->addItem(calque_echelle_temporelle);
  275.  
  276. //-----------------------------------------------------
  277. // SCENE 5 - Histogramme sur l'onglet NOTES
  278.  
  279. scene5 = new QGraphicsScene(this);
  280. scene5->setBackgroundBrush(QColor(couleur_ecran));
  281. graphicsView5->setScene(scene5);
  282. calque_histogramme = new QGraphicsItemGroup();
  283. scene5->addItem(calque_histogramme);
  284. //-----------------------------------------------------
  285.  
  286. Timer1 = new QTimer(this);
  287. connect(Timer1, SIGNAL(timeout()), this, SLOT(Tic1()));
  288.  
  289. Timer2 = new QTimer(this);
  290. connect(Timer2, SIGNAL(timeout()), this, SLOT(Tic2()));
  291.  
  292.  
  293. //noms_notes<<"La"<<"Si"<<"Do"<<"Ré"<<"Mi" <<"Fa"<<"Sol"<<"--"<<"--"; // <- A B C D E F G
  294. gamme_chromatique<<"Do"<<"Do#"<<"Ré"<<"Mib"<<"Mi"<<"Fa"<<"Fa#"<<"Sol"<<"Sol#"<<"La"<<"Sib"<<"Si";
  295.  
  296. //la gamme GB commence en principe par la lettre A (=La) mais pour simplifier ce programme
  297. //on la fait partir de C (=Do) comme la gamme FR...
  298. gamme_chromatique_GB<<"C"<<"C#"<<"D"<<"Eb"<<"E"<<"F"<<"F#"<<"G"<<"G#"<<"A"<<"A#"<<"B";
  299.  
  300. // dans la liste suivante, la première couleur (#0)->"#EF2929" est le rouge pour la note DO
  301. liste_couleurs <<"#EF2929"<<"#FF5C00"<<"#FCAF3E"<<"#FFE300"<<"#BFFF00"<<"#07F64F"
  302. <<"#16D298"<<"#16D2C4"<<"#00AEFF"<<"#1667D2"<<"#7C00FF"<<"#FF67EF"<<"#EEEEEC";
  303.  
  304. player1 = new QMediaPlayer;
  305. audioOutput1 = new QAudioOutput(this);
  306. player1->setAudioOutput(audioOutput1);
  307.  
  308. player2 = new QMediaPlayer;
  309. audioOutput2 = new QAudioOutput(this);
  310. player2->setAudioOutput(audioOutput2);
  311.  
  312. player3 = new QMediaPlayer;
  313. audioOutput3 = new QAudioOutput(this);
  314. player3->setAudioOutput(audioOutput3);
  315.  
  316. player4 = new QMediaPlayer;
  317. audioOutput4 = new QAudioOutput(this);
  318. player4->setAudioOutput(audioOutput4);
  319.  
  320. parametrages();
  321. }
  322.  
  323.  
  324. void MainWindow::parametrages()
  325. {
  326. load_fichier_ini();
  327.  
  328. calcul_tableau_W();
  329. tracer_graduations_signal();
  330. tracer_graduations_FFT();
  331.  
  332. init_TW_1();
  333. init_TAB_FRQ();
  334. init_TAB_lst_notes();
  335. init_TW_type_M();
  336. init_Autres();
  337.  
  338. seuil = doubleSpinBox_seuil->value();
  339.  
  340. spinBox_offset->setValue(-2124); // 2124; +/- 22 pour un décalage de +/-1/4 de ton
  341. spinBox_echx->setValue(499);
  342. write_mode=0;
  343. on_Bt_mode_R_clicked();
  344.  
  345. visu_ech_tps = true;
  346. on_Bt_toggle_visu_ech_tps_clicked();
  347.  
  348. visu_notes = false;
  349. on_Bt_toggle_visu_notes_clicked();
  350.  
  351. visu_notes_auto = false;
  352. on_Bt_toggle_visu_notes_auto_clicked();
  353.  
  354. on_tableWidget_type_M_cellClicked(2, 0);
  355.  
  356. spinBox_zoom_2->setValue(2);
  357. // Bt_jouer_tout->setStyleSheet("background-color: rgb(200, 200, 200);");
  358.  
  359. progressBar_1->setValue(0);
  360.  
  361. effacer_calque(scene1,calque_trace_FFT );
  362.  
  363. effacer_calque(scene1,calque_lignes_F_zero );
  364. effacer_calque(scene1,calque_encadrement1 );
  365. effacer_calque(scene1,calque_trace_signal1 );
  366. effacer_calque(scene1,calque_trace_FFT );
  367. effacer_calque(scene1,calque_curseur );
  368.  
  369. effacer_calque(scene2,calque_encadrement2 );
  370. effacer_calque(scene2, calque_trace_signal2 );
  371.  
  372. effacer_calque(scene4,calque_TAB_FRQ );
  373. effacer_calque(scene4, calque_notes_manuelles );
  374. effacer_calque(scene4, calque_notes_auto );
  375. effacer_calque(scene4, calque_notes_jouee );
  376.  
  377. effacer_calque(scene5, calque_histogramme );
  378.  
  379. on_Bt_RAZ_clicked();
  380.  
  381. T_i=0; // T_i : variable GLOBALE
  382.  
  383. on_Bt_grille_T_clicked();
  384. //doubleSpinBox_dt->setValue(9.375); // 1 barre toutes les 1/20 seconde
  385. spinBox_nb_barres->setValue(20);
  386. effacer_calque(scene4, calque_grille_T);
  387. tracer_grille_T();
  388.  
  389.  
  390. }
  391.  
  392.  
  393. void MainWindow::init_TAB_FRQ()
  394. {
  395. frame_notes->setGeometry(0,0,1920,720);
  396.  
  397. graphicsView3->setGeometry(0, 30, 100, 730);
  398.  
  399. graphicsView4->setGeometry(100, 30, 2000, 730);
  400. graphicsView4->setEnabled(false); // empêche le recadrage auto par le contenu, mais disable le scroll manuel !
  401.  
  402. rectangle1 = new QGraphicsRectItem(0, 0, 2000, 670);
  403.  
  404. rectangle1->setPen(QColor(couleur_ligne));
  405. calque_TAB_FRQ->addToGroup(rectangle1);
  406. tracer_graduation_TAB_NOTES();
  407. tracer_grille_M();
  408. }
  409.  
  410.  
  411. void MainWindow::init_TAB_lst_notes()
  412. {
  413. tableWidget_lst_notes->setGeometry(1000, 30, 450, 201);
  414. QStringList liste_entetes1;
  415. liste_entetes1<<"numéro"<<"midi"<<"note"<<"n° barre"<< "temps"<< "durée" << " ";
  416. tableWidget_lst_notes->setHorizontalHeaderLabels (liste_entetes1);
  417. tableWidget_lst_notes->setEditTriggers(QAbstractItemView::NoEditTriggers);
  418. }
  419.  
  420.  
  421.  
  422. void MainWindow::init_Autres()
  423. {
  424. frame_1->setGeometry(1250,865,410,131);
  425. frame_1->setVisible(false);
  426. frame_2->setGeometry(10,865,970,75);
  427. frame_3->setGeometry(1000,865,665,121);
  428. frame_5->setGeometry(0, 760, 1920, 260);
  429. frame_6->setGeometry(10, 950, 901, 40);
  430.  
  431. Bt_efface->setGeometry(915,950,60,21);
  432.  
  433. lineEdit_fichier_FREQ->setGeometry(1250, 0, 700, 22);
  434. Bt_open_DIR_2->setGeometry(912, 40, 70, 26);
  435. Bt_efface_2->setGeometry(1165, 0, 81, 26);
  436. progressBar_1->setGeometry(98, 4, 101, 18); //(1085, 3, 78, 18);
  437. lineEdit_fichier->setGeometry(380, 40, 530, 26);
  438. groupBox_2->setGeometry(380, 70, 241, 161);
  439. groupBox_3->setGeometry(630, 70, 351, 161);
  440.  
  441. // groupBox_4->setGeometry(5, 5, 5, 5); // (215, 40, 155, 191)
  442.  
  443. textEdit_ETAT->setGeometry(1480, 30, 400, 100);
  444.  
  445. graphicsView5->setGeometry(1480, 135, 400, 95); // histogramme des durées de notes
  446. graphicsView5->setScene(scene5);
  447.  
  448. int x0 = 1500;
  449. Bt_debut->setGeometry(x0+0, 760, 80, 22);
  450. Bt_prev->setGeometry(x0+90, 760, 80, 22);
  451. Bt_next->setGeometry(x0+180, 760, 80, 22);
  452. Bt_fin->setGeometry(x0+270, 760, 80, 22);
  453. Bt_RAZ_general->setGeometry(1700, 900, 150, 50);
  454.  
  455. tracer_gradu_temporelle_signal_entree();
  456. tracer_graduations_signal();
  457. }
  458.  
  459.  
  460. void MainWindow::init_TW_type_M()
  461. {
  462. frame_4->setGeometry(210, 5, 140, 90);
  463. frame_7->setGeometry(210, 100, 140, 50);
  464.  
  465. tableWidget_type_M->setGeometry(5, 20, 40, 62);
  466. lineEdit_10->setGeometry(60,30,51,26);
  467. label_8->setGeometry(2,2,111,20);
  468.  
  469. //spinBox_barre_zero->setGeometry(5, 110, 61, 21);
  470. //spinBox_nb_barres->setGeometry(5, 130, 61, 21);
  471.  
  472.  
  473. QStringList liste_labels; // pour mettre directement dans la 1ere colonne (et pas en entêtes)
  474. liste_labels << "2/4" << "3/4" << "4/4";
  475. for(int n=0; n<3; n++) { tableWidget_type_M->setItem(0, n, new QTableWidgetItem (liste_labels[n])); }
  476. }
  477.  
  478.  
  479. void MainWindow::init_TW_1() // affi entete du fichier wav
  480. {
  481. tableWidget_1->setGeometry(700, 200, 600, 300);
  482. Bt_close_entete->setGeometry(1280, 200, 20, 20);
  483. tableWidget_1->setColumnCount(6);
  484. tableWidget_1->setColumnWidth(4, 120);
  485. tableWidget_1->setColumnWidth(5, 130);
  486.  
  487. tableWidget_1->setVisible(false);
  488. Bt_close_entete->setVisible(false);
  489.  
  490. QStringList liste_entetes;
  491. liste_entetes <<"FileTypeBlocID"
  492. <<"FileSize"
  493. <<"FileFormatID"
  494. <<"FormatBlocID"
  495. <<"BlocSize"
  496. <<"AudioFormat"
  497. <<"NbrCanaux"
  498. <<"Frequence"
  499. <<"ECH (BytePerSec)"
  500. <<"BytePerBloc"
  501. <<"BitsPerSample"
  502. <<"DataBlocID"
  503. <<"DataSize"
  504. <<"durée calculée";
  505.  
  506. tableWidget_1->setVerticalHeaderLabels(liste_entetes);
  507. }
  508.  
  509.  
  510. void MainWindow::load_fichier_ini()
  511. {
  512. QString line;
  513. int p1, p2, p3;
  514.  
  515. dossier_de_travail_ok = false; // à priori
  516. QFile file1(QDir::currentPath() + "/" + "params_FFT.ini"); // dans le dossier de l'exécutable (= QDir::currentPath() )
  517. if (file1.open(QIODevice::ReadOnly | QIODevice::Text))
  518. {
  519. QTextStream in(&file1);
  520. in.reset();
  521. while ( !in.atEnd() )
  522. {
  523. line = in.readLine();
  524. if (line.at(0) !='#')
  525. {
  526. if ((p1 = line.indexOf("<currentDir>")) != -1)
  527. {
  528. p2 = line.indexOf('>',p1);
  529. p3 = line.indexOf("</",p2);
  530. QString s1 = line.mid(p2+1, p3-p2-1);
  531. string_currentDir = s1;
  532. dossier_de_travail_ok = true;
  533. }
  534.  
  535. if ((p1 = line.indexOf("<currentFile>")) != -1)
  536. {
  537. p2 = line.indexOf('>',p1);
  538. p3 = line.indexOf("</",p2);
  539. QString s1 = line.mid(p2+1, p3-p2-1);
  540. string_currentFile = s1;
  541. }
  542. }
  543. }
  544. file1.close();
  545. }
  546. else
  547. {
  548. QString s1 = "fichier .ini non trouvé, je le crée (dans le dossier de l'executable)";
  549. QMessageBox msgBox; msgBox.setText(s1); msgBox.exec();
  550. base_Dir=QDir::currentPath() + "/";
  551. save_fichier_ini();
  552. dossier_de_travail_ok = false;
  553. }
  554. lineEdit_current_dir->setText(string_currentDir);
  555. lineEdit_fichier->setText(string_currentDir);
  556. }
  557.  
  558.  
  559.  
  560. QString calcul_couleur(double x) // x = 0.0 à 1.0 ; return: rouge->jaune->vert
  561. {
  562. QString coul_str ="#000000";
  563. QString coul_R_str="#000000";
  564. QString coul_V_str="#000000";
  565. int R, V; // rouge, vert
  566.  
  567. V= x * 255;
  568. R= 300 - (1.2 * V);
  569.  
  570. double coxh;
  571. // la formule suivante met le jaune en valeur (avec un cosinus hyperbolique)
  572. 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) )
  573.  
  574. R *= coxh/1.2;
  575. V *= coxh/1.2;
  576.  
  577. coul_R_str.setNum(R, 16); coul_R_str = "0" + coul_R_str; coul_R_str = coul_R_str.right(2);
  578. coul_V_str.setNum(V, 16); coul_V_str = "0" + coul_V_str; coul_V_str = coul_V_str.right(2);
  579. coul_str = coul_R_str + coul_V_str;
  580. coul_str = "#" + coul_str + "00";
  581. return coul_str; // de la forme "#AABB00" (rouge & vert en exa, (bleu à zéro, à traiter))
  582. }
  583.  
  584.  
  585.  
  586. int MainWindow::calcul_y_note(int midi) // en pixels sur la grille
  587. {
  588. int y =700 - ((midi-40) * 16); // voir 'tracer_graduation_TAB_NOTES()' pour l'espacement vertical
  589. return y;
  590. }
  591.  
  592.  
  593.  
  594. int MainWindow::calcul_hauteur_note(int mid) //'hauteur' au sens musical, gamme chromatique de 0 à 11
  595. {
  596. int h1 = mid;
  597.  
  598. while(h1>=12) {h1-=12;}
  599. while(h1<0) {h1+=12;}
  600. if (h1==12) {h1=0;}
  601. if ((h1<0) || (h1>(liste_couleurs.length()-1))) {h1 = 0;}
  602.  
  603. return h1; // h1 = 0..11
  604. }
  605.  
  606.  
  607.  
  608. QString MainWindow::nom_note(int mid)
  609. {
  610. QString s1;
  611.  
  612. int h1 = calcul_hauteur_note(mid);
  613. s1 = gamme_chromatique[h1];
  614. return s1;
  615. }
  616.  
  617.  
  618. QString MainWindow::nom_note_GB(int mid)
  619. {
  620. QString s1;
  621.  
  622. int h1 = calcul_hauteur_note(mid);
  623. s1 = gamme_chromatique_GB[h1];
  624. return s1;
  625. }
  626.  
  627.  
  628. QString MainWindow::nom_octave_GB(int mid)
  629. {
  630. QString octave;
  631. if((mid>=40)&&(mid < 48)) {octave = "2";}
  632. if((mid>=48)&&(mid < 60)) {octave = "3";}
  633. if((mid>=60)&&(mid < 72)) {octave = "4";}
  634. if((mid>=72)&&(mid < 90)) {octave = "5";}
  635. return octave;
  636. }
  637.  
  638.  
  639.  
  640. double freq_mid(int midi_i) // calcule la fréquence (en Hertz) d'une note à partir de son numéro midi
  641. {
  642. // https://fr.wikipedia.org/wiki/MIDI_Tuning_Standard
  643. double F, A;
  644. A=((double)midi_i-69.0)/12.0;
  645. F= 440 * powf(2,A);
  646. return F;
  647. }
  648.  
  649.  
  650. double calcul_id_midi(double freq_i) // complément de la fonction précédente ( double freq_mid(int midi_i) )
  651. {
  652. double id;
  653. id = 69.0 + 12.0 * log2(freq_i / 440.0);
  654. return id;
  655. }
  656.  
  657.  
  658. int MainWindow::calcul_num_midi(double x) // en fonction de la position horizontale dans le tableau d'affichage de la FFT
  659. {
  660. // on va détecter la note la plus proche de la position donnée
  661. // rappel : x=92 +(m-40) * 40.9 (voir la fonction : 'tracer_graduations_FFT()')
  662.  
  663. double dx;
  664. double xm;
  665.  
  666. for (int m=40; m<83; m++)
  667. {
  668. xm= 92.0 +(m-40) * 40.9;
  669. dx = xm-x;
  670. if (dx<0){dx=-dx;}
  671. if (dx < (40.9/2) )
  672. {
  673. encadrement_note(m);
  674. return m;
  675. }
  676. }
  677. return 0;
  678. }
  679.  
  680.  
  681. void MainWindow::play_note(int midi) // sortie audio
  682. {
  683. QString s1, freq_txt;
  684.  
  685. calcul_hauteur_note(midi);
  686. s1.setNum(midi);
  687.  
  688. s1="notes/"+s1+".wav"; // fichiers audio comprenant une seule note chacun, voir dans le dossier "notes" sur le HDD
  689.  
  690. if (n_player == 1) {player1->setSource(QUrl::fromLocalFile(s1)); player1->play();}
  691. if (n_player == 2) {player2->setSource(QUrl::fromLocalFile(s1)); player2->play();}
  692. if (n_player == 3) {player3->setSource(QUrl::fromLocalFile(s1)); player3->play();}
  693. if (n_player == 4) {player4->setSource(QUrl::fromLocalFile(s1)); player4->play();}
  694. if (n_player == 5) {player5->setSource(QUrl::fromLocalFile(s1)); player5->play();}
  695. n_player++;
  696. if (n_player >= 5) {n_player = 1;}
  697. }
  698.  
  699.  
  700.  
  701.  
  702. void MainWindow::effacer_calque(QGraphicsScene *scene_i, QGraphicsItemGroup *calque_i)
  703. {
  704. foreach( QGraphicsItem *item, scene_i->items( calque_i->boundingRect() ) )
  705. {if( item->group() == calque_i ) { delete item; }}
  706. }
  707.  
  708.  
  709. MainWindow::~MainWindow()
  710. {
  711.  
  712. }
  713.  
  714.  
  715.  
  716. void MainWindow::Etiquette(int x, int y, QString s, QString coul_txt, QString coul_fond, QString coul_bord,QGraphicsItemGroup *calque_i )
  717. {
  718. // fond noir sous le texte
  719. rect1 = new QGraphicsRectItem(x, y+4, 40, 18);
  720. rect1->setPen(QColor(coul_bord)); // bordure
  721. rect1->setBrush(QColor(coul_fond)); // fond
  722. calque_i->addToGroup(rect1);
  723.  
  724. //texte
  725. GraphicsTextItem = new QGraphicsTextItem(s);
  726. GraphicsTextItem->setPos(x, y);
  727. GraphicsTextItem->setDefaultTextColor(coul_txt);
  728. calque_i->addToGroup(GraphicsTextItem);
  729. }
  730.  
  731.  
  732.  
  733. void MainWindow::tracer_graduations_FFT() // sur onglet 'Source wav'
  734. {
  735. double x=0;
  736. QString s1;
  737. int y_bas =450; // 350
  738.  
  739. // cadre
  740. rectangle1 = new QGraphicsRectItem(0, 0, 1900, y_bas);
  741. QPen pen1("#12FF00");
  742. rectangle1->setPen(pen1);
  743.  
  744. calque_reticule1->addToGroup(rectangle1);
  745.  
  746. s1 = "midi :"; // numéro midi
  747. GraphicsTextItem = new QGraphicsTextItem(s1);
  748. GraphicsTextItem->setDefaultTextColor("#FFFFFF");
  749. GraphicsTextItem->setPos(30, 20);
  750. // if(x<1900)
  751. {
  752. calque_reticule1->addToGroup(GraphicsTextItem);
  753. }
  754.  
  755. // positions des notes midi
  756. x= 96;
  757. for(int m=40; m<=83; m++)
  758. {
  759. ligne1 = new QGraphicsLineItem(x, 70, x, y_bas);
  760. ligne1->setPen(pen_reticule);
  761. calque_reticule1->addToGroup(ligne1);
  762.  
  763. s1.setNum(m); // numéro midi
  764. GraphicsTextItem = new QGraphicsTextItem(s1);
  765. GraphicsTextItem->setDefaultTextColor("#D3D7CF");
  766. GraphicsTextItem->setPos(x-15, 20);
  767. if(x<1900) {calque_reticule1->addToGroup(GraphicsTextItem); }
  768.  
  769. int h1 = calcul_hauteur_note(m);
  770.  
  771. s1 = " " + gamme_chromatique[h1]; // nom de la note
  772. GraphicsTextItem = new QGraphicsTextItem(s1);
  773. GraphicsTextItem->setDefaultTextColor(liste_couleurs[h1]);
  774. GraphicsTextItem->setPos(x-18, 40);
  775. if(x<1900) {calque_reticule1->addToGroup(GraphicsTextItem); }
  776.  
  777. x+=40.9; // espacement des graduation
  778. }
  779. }
  780.  
  781.  
  782. double MainWindow::calcul_x_barre_T(int n_barre)
  783. {
  784. // calcul de l'abscisse (en pixel) d'une barre de mesure
  785.  
  786. double x0 = 80.0; // 100 à voir !
  787. double pas_grille = 9.375/2.0 * zoom_x; // 1 barre toutes les 1/40 seconde
  788. double x = x0 + double(n_barre) * pas_grille;
  789. return x;
  790. }
  791.  
  792.  
  793. double MainWindow::calcul_x_barre_M(int n_barre)
  794. {
  795. // calcul de l'abscisse (en pixel) d'une barre de mesure
  796. double x0 = 80.0 + 10.0 * doubleSpinBox_x0->value();
  797. double pas_grille = doubleSpinBox_dt->value()/2.0 * zoom_x;
  798.  
  799. double x = x0 + double(n_barre) * pas_grille;
  800. return x;
  801. }
  802.  
  803.  
  804.  
  805. void MainWindow::tracer_grille_M()
  806. {
  807. //grille mesures sur le TAB NOTES (barres verticales)
  808. QString s1;
  809. // QString bleu_clair = "#00AAFF";
  810. // QString jaune = "#FFFF00";
  811. QString gris_clair = "#CCCCCC";
  812. QString gris_fonce = "#777777";
  813. QString gris_tres_fonce = "#444444";
  814. QString couleur_i;
  815. QPen pen_i;
  816. double x_i1;
  817. int num_mesure;
  818. int nb = spinBox_nb_barres->value();
  819. int n_zero = spinBox_barre_zero->value();
  820.  
  821. for(int n=0; n<=120*16; n++)
  822. {
  823. if (Ta<2) {Ta=2;}
  824. x_i1 = calcul_x_barre_M(n);
  825.  
  826. couleur_i = gris_tres_fonce; // à priori
  827.  
  828. if ( n%(Ta/2) == 0) { couleur_i = gris_fonce; }
  829. if ( n%(4) == 0) { couleur_i = gris_clair; }
  830.  
  831. if ( n%(4*Ta) == 0) // 1er temps fort de la mmesure
  832. {
  833. // numérotation des mesures, en bas, chiffres jaunes
  834. num_mesure = n /Ta/4;
  835. s1.setNum(num_mesure);
  836. Etiquette(x_i1-0, 650, s1, "#FFFF00", "#000000", "#0000FF", calque_grille_M);
  837. }
  838. //if ( n%(4*m)== 0) {couleur_i = jaune;}
  839.  
  840. pen_i.setColor(couleur_i);
  841. ligne1 = new QGraphicsLineItem(x_i1, 0.0, x_i1, 700.0);//650
  842. ligne1->setPen(pen_i);
  843. calque_grille_M->addToGroup(ligne1);
  844. }
  845. }
  846.  
  847.  
  848. void MainWindow::tracer_grille_T()
  849. {
  850. //grille T sur l'onglet NOTES (barres verticales)
  851. QString s1;
  852. // QString bleu_clair = "#00AAFF";
  853. // QString jaune = "#FFFF00";
  854. QString gris_clair = "#CCCCCC";
  855. QString gris_fonce = "#777777";
  856. QString gris_tres_fonce = "#444444";
  857. QString couleur_i;
  858. QPen pen_i;
  859. double x_i1;
  860. int num_mesure;
  861. int nb = spinBox_nb_barres->value();
  862. int n_zero = spinBox_barre_zero->value();
  863.  
  864. for(int n=0; n<=120*16; n++)
  865. {
  866. if (Ta<2) {Ta=2;}
  867. x_i1 = calcul_x_barre_T(n);
  868.  
  869. couleur_i = gris_tres_fonce; // à priori
  870.  
  871. if (((n-n_zero)%nb) == 0) { couleur_i ="#00AAAA"; } //couleur_i = gris_fonce;
  872.  
  873. if ( n%(4*Ta) == 0) // 1er temps fort de la mmesure
  874. {
  875. // numérotation des mesures, en bas, chiffres jaunes
  876. num_mesure = n /Ta/4;
  877. s1.setNum(num_mesure);
  878. Etiquette(x_i1-0, 650, s1, "#FFFF00", "#000000", "#0000FF", calque_grille_T);
  879. }
  880. //if ( n%(4*m)== 0) {couleur_i = jaune;}
  881.  
  882. pen_i.setColor(couleur_i);
  883. ligne1 = new QGraphicsLineItem(x_i1, 0.0, x_i1, 700.0);//650
  884. ligne1->setPen(pen_i);
  885. calque_grille_T->addToGroup(ligne1);
  886. }
  887. }
  888.  
  889.  
  890. void MainWindow::afficher_titre_calque(QString titre, int x, int y, QGraphicsItemGroup *calque_i)
  891. {
  892. GraphicsTextItem = new QGraphicsTextItem(titre);
  893. GraphicsTextItem->setDefaultTextColor("#FFFFFF");
  894. GraphicsTextItem->setPos(x, y);
  895. calque_i->addToGroup(GraphicsTextItem);
  896. }
  897.  
  898.  
  899.  
  900. void MainWindow::tracer_graduation_TAB_NOTES() // midi ; en colonne à gauche + noms des notes
  901. {
  902. // positions des notes midi
  903. QString s1;
  904. int y= 640; //640
  905. for(int m=40; m<=82; m++)
  906. {
  907. //ligne1 = new QGraphicsLineItem(100, 0, 100, 350);
  908. //ligne1->setPen(pen_reticule);
  909. //calque_reticule1->addToGroup(ligne1);
  910.  
  911. s1.setNum(m); // numéro midi
  912. GraphicsTextItem = new QGraphicsTextItem(s1);
  913. GraphicsTextItem->setDefaultTextColor("#D3D7CF");
  914. GraphicsTextItem->setPos(0, y);
  915. if(y> -50) {calque_gradu_TAB_FRQ->addToGroup(GraphicsTextItem); }
  916.  
  917. int h1 = calcul_hauteur_note(m);
  918. s1 = " " + gamme_chromatique[h1]; // nom de la note
  919. GraphicsTextItem = new QGraphicsTextItem(s1);
  920. GraphicsTextItem->setDefaultTextColor(liste_couleurs[h1]);
  921. // GraphicsTextItem->setHtml("<div style='background-color:#666666;'>" + s1 + "</div>");
  922.  
  923. if (m==69) // La 440
  924. {
  925. rect3 = new QGraphicsRectItem(30, y+6, 40, 15);
  926. QBrush br2;
  927. QPen pen_i;
  928. pen_i.setColor("#AAAAAA");
  929. rect3->setPen(pen_i);
  930. calque_gradu_TAB_FRQ->addToGroup(rect3);
  931.  
  932. }
  933.  
  934. GraphicsTextItem->setPos(30, y);
  935. if(y > -50) {calque_gradu_TAB_FRQ->addToGroup(GraphicsTextItem); }
  936.  
  937. y-=16; // espacement des graduations
  938. }
  939. }
  940.  
  941.  
  942.  
  943. void MainWindow::tracer_graduations_signal() // sur le 1er onglet (Source.wav)
  944. {
  945. /*
  946.   midi57 = 110Hz
  947.   1/110Hz = 9ms -> delta x = 142-46 = 96px
  948.   echelle x = 96/9 = 10.6 px/ms
  949.   soit:
  950.   10ms -> 106px
  951. */
  952. double x;
  953. double nb_grad_max;
  954. double intervalle; // séparant les graduations
  955. QPen pen1;
  956. // int num_x;
  957. //QString sti;
  958. // uint decal = 3; // leger decallage vertical pour ne pas masquer la trace
  959.  
  960. /*
  961.   rectangle1 = new QGraphicsRectItem(5, 452, 1900, 200);
  962.   pen1.setColor("#0073FF");
  963.   rectangle1->setPen(pen1);
  964.   calque_reticule1->addToGroup(rectangle1);
  965.  
  966.   ligne1 = new QGraphicsLineItem(10, 475, 1900, 475); // ligne horizontale
  967.   pen1.setColor("#0073FF");
  968.   ligne1->setPen(pen1);
  969.   calque_reticule1->addToGroup(ligne1);
  970. */
  971. afficher_titre_calque("Partie échantillonée du signal", 0, 452, calque_reticule1);
  972.  
  973. // lignes verticales
  974.  
  975. intervalle = 106;
  976. nb_grad_max = 18;
  977.  
  978. for (int i=0; i<=nb_grad_max; i++)
  979. {
  980. x = intervalle * i;
  981.  
  982. ligne1 = new QGraphicsLineItem(x, 452, x, 780);
  983. ligne1->setPen(pen_reticule);
  984. }
  985.  
  986. /*
  987. // lignes horizontales
  988.   intervalle = 55;
  989.   nb_grad_max = 3;
  990.   for (i=0; i<=nb_grad_max; i++)
  991.   {
  992.   y = 300 - intervalle * i;
  993.  
  994.   ligne1 = new QGraphicsLineItem(0, y, 1350, y);
  995.   ligne1->setPen(pen_reticule);
  996.   calque_reticule1->addToGroup(ligne1);
  997.   }
  998.   texte_mid = new QGraphicsTextItem("Silicium628");
  999.   texte_mid->setDefaultTextColor(couleur_signature);
  1000.   texte_mid->setPos(5,5);
  1001.   calque_reticule1->addToGroup(texte_mid);
  1002.  */
  1003.  
  1004. scene1->addItem(calque_reticule1);
  1005. }
  1006.  
  1007.  
  1008.  
  1009. void MainWindow::tracer_gradu_temporelle_signal_entree()
  1010. {
  1011. // GRADUATION TEMPORELLE sur le signal d'entrée, en secondes
  1012.  
  1013. QString s1, s2;
  1014. int x=0;
  1015. int dy = 75;
  1016. int nb_s, nb_mn;
  1017. double pas =8;
  1018.  
  1019. for(int n=0; n<2400; n++) // 1200
  1020. {
  1021.  
  1022. if (n%10 == 0) { dy = 75; }
  1023. else if (n%5 == 0) {dy = 20; }
  1024. else {dy = 10;}
  1025.  
  1026. x = n *pas;
  1027. ligne1 = new QGraphicsLineItem(x, -dy, x, dy);
  1028. QPen P1("#AAAAAA");
  1029. ligne1->setPen(P1);
  1030. calque_reticule2->addToGroup(ligne1);
  1031.  
  1032. if (n%10 == 0)
  1033. {
  1034. nb_s = n/10;
  1035. s1.setNum(nb_s);
  1036. s1+="s";
  1037.  
  1038. if (nb_s>60)
  1039. {
  1040. nb_mn = nb_s/60;
  1041. nb_s -= nb_mn * 60;
  1042. s1.setNum(nb_s);
  1043. if (nb_s<10) {s1 = "0" +s1;}
  1044. s2.setNum(nb_mn);
  1045. s1 = s2 + ":" + s1;
  1046. }
  1047. GraphicsTextItem = new QGraphicsTextItem(s1);
  1048. GraphicsTextItem->setDefaultTextColor(couleur_texte);
  1049. GraphicsTextItem->setPos(x,55);
  1050. if(x<20000) {calque_reticule2->addToGroup(GraphicsTextItem); }//6000
  1051. }
  1052. }
  1053. }
  1054.  
  1055.  
  1056.  
  1057. void MainWindow::tracer_signal_complet() // totalité du fichier wav (dans le cadre du bas du 1er onglet)
  1058. {
  1059. uint8_t octet_n;
  1060. double R;
  1061.  
  1062. double offset_y = 0.0;
  1063. double echelle_y =doubleSpinBox_gain->value();
  1064. double echelle_x =0.2;
  1065.  
  1066. int x, y, memo_x, memo_y;
  1067. double min= 1000000.0;
  1068. double max=-1000000.0;
  1069.  
  1070. int pas = 10 * pas_echantillonage; // 240;
  1071.  
  1072. //rappel : duree_totale = data_size / 96000;
  1073.  
  1074. // le signal wav est échantillonné à l'origine à 96000 Bps (séréo 2*fois 48000 Bps)
  1075. // en ne prenant qu'un octet sur 24 on obtient 4000 Bps.
  1076. QString s1;
  1077.  
  1078. effacer_calque(scene2, calque_trace_signal2 );
  1079. segment_trace = new QGraphicsLineItem(0, offset_y, 512, offset_y);
  1080. QPen P1("#0000FF");
  1081. segment_trace->setPen(P1); //couleur_ligne
  1082. calque_trace_signal2->addToGroup(segment_trace);
  1083.  
  1084. x=0;
  1085. y=offset_y;
  1086.  
  1087. qDebug() << "data_size" << data_size;
  1088. qDebug() << "pas" << pas;
  1089.  
  1090. double n_max = data_size / pas;
  1091.  
  1092. long n;
  1093. long i=0;
  1094. // int j=0;
  1095.  
  1096. for(n=0; n<n_max; n+=10)
  1097. {
  1098. memo_x = x;
  1099. memo_y = y;
  1100. x = echelle_x * n;
  1101.  
  1102. octet_n = temp_data[i]; // canal 1
  1103. R = octet_n - 128; // pour ôter la composante continue propre au codage par des octets non signés
  1104. R *= echelle_y;
  1105. R *= doubleSpinBox_gain->value();
  1106. y=offset_y + R;
  1107.  
  1108. // recherche du mini-maxi:
  1109. if (y < min) {min = y;}
  1110. if (y > max) {max = y;}
  1111.  
  1112. //if (x<3000) //3000
  1113. {
  1114. segment_trace = new QGraphicsLineItem(memo_x ,memo_y, x, y);
  1115. QPen P1("#0055FF");
  1116. segment_trace->setPen(P1);
  1117. calque_trace_signal2->addToGroup(segment_trace);
  1118. }
  1119.  
  1120. i+= pas; // le nb de pas étant pair, on retombe toujours sur le même canal (droite ou gauche)
  1121. }
  1122. //scene1->addItem(calque_trace_signal);
  1123. s1.setNum(min);
  1124. //lineEdit_4->setText(s1);
  1125. s1.setNum(max);
  1126. // lineEdit_5->setText(s1);
  1127. }
  1128.  
  1129.  
  1130.  
  1131. void MainWindow::encadrement_signal(int x, int dx)
  1132. {
  1133. ligne1 = new QGraphicsLineItem(x, -80, x, 80);
  1134. ligne1->setPen(pen_encadrement);
  1135. calque_encadrement2->addToGroup(ligne1);
  1136.  
  1137. ligne1 = new QGraphicsLineItem(x+dx, -80, x+dx, 80);
  1138. ligne1->setPen(pen_encadrement);
  1139. calque_encadrement2->addToGroup(ligne1);
  1140.  
  1141. ligne1 = new QGraphicsLineItem(x, -80, x+dx, -80);
  1142. ligne1->setPen(pen_encadrement);
  1143. calque_encadrement2->addToGroup(ligne1);
  1144.  
  1145. ligne1 = new QGraphicsLineItem(x, 80, x+dx, 80);
  1146. ligne1->setPen(pen_encadrement);
  1147. calque_encadrement2->addToGroup(ligne1);
  1148. }
  1149.  
  1150.  
  1151.  
  1152. void MainWindow::encadrement_note(int n_midi) // par un rectangle
  1153. {
  1154. double x, dx, y, dy;
  1155.  
  1156. x=92 - 13 + ((double)n_midi-40.0) * 40.9;
  1157. dx=35;
  1158. y=16;
  1159. dy=50;
  1160.  
  1161. rectangle1 = new QGraphicsRectItem(x, y, dx, dy);
  1162. rectangle1->setPen(pen_encadrement);
  1163. calque_trace_FFT->addToGroup(rectangle1);
  1164. }
  1165.  
  1166.  
  1167.  
  1168. void MainWindow::tracer_signal_a_convertir() //partie à analyser s=f(t) (signal incident, pas le FFT); dans le cadre du haut
  1169. {
  1170. double offset_y = 575.0;
  1171. double echelle =5.0;
  1172. uint8_t octet_n;
  1173. double R;
  1174. double x, y, memo_x, memo_y;
  1175. double min= 1000000.0;
  1176. double max=-1000000.0;
  1177. QString s1;
  1178.  
  1179. effacer_calque(scene1,calque_trace_signal1 );
  1180. segment_trace = new QGraphicsLineItem(0, offset_y, 512, offset_y);
  1181. segment_trace->setPen(QColor(couleur_ligne));
  1182. calque_trace_signal1->addToGroup(segment_trace);
  1183.  
  1184. long n;
  1185. long i=0;
  1186. x=0;
  1187. y=offset_y;
  1188. int pas = 64;
  1189. for(n=0; n<nb_ech/2; n++)
  1190. {
  1191. memo_x = x;
  1192. memo_y = y;
  1193. x = echelle * n;
  1194.  
  1195. octet_n = temp_data[i]; // canal 1
  1196. R = octet_n - 128; // pour oter la composante continue propre au codage par des octets non signés
  1197. // R /= 10.0;
  1198. y=offset_y + R;
  1199.  
  1200. if (y < min) {min = y;}
  1201. if (y > max) {max = y;}
  1202.  
  1203. if (x<1800)
  1204. {
  1205. segment_trace = new QGraphicsLineItem(memo_x ,memo_y, x, y);
  1206. segment_trace->setPen(pen_trace1);
  1207. calque_trace_signal1->addToGroup(segment_trace);
  1208. }
  1209.  
  1210. i++; // pour sauter le canal B (le signal dans le fichier .wav est stéréo)
  1211. i+= pas;
  1212. }
  1213. //scene1->addItem(calque_trace_signal);
  1214. s1.setNum(min);
  1215. //lineEdit_4->setText(s1);
  1216. s1.setNum(max);
  1217. // lineEdit_5->setText(s1);
  1218. }
  1219.  
  1220.  
  1221.  
  1222.  
  1223. void MainWindow::tracer_segments_FFT() // sur le 2eme onglet (NOTES)
  1224. {
  1225. double offset_x= spinBox_offset->value(); // ref -2146 ; +/- 22 pour un décalage de +/-1/4 de ton
  1226. double echelle_x =spinBox_echx->value(); // 498
  1227. double offset_y = 350.0;
  1228.  
  1229. uint n;
  1230. double module_FFT;
  1231. double x, y;
  1232. double memo_x[5]={0,0,0,0,0};
  1233. double memo_y[5]={0,0,0,0,0};
  1234. double x0, y0; // fréquence centrale trouvée comme résultat (points d'inflexions de la courbe)
  1235.  
  1236. double z=1.6;
  1237. //int x1=0;
  1238. //int x2=0;
  1239. int num_raie=0;
  1240. //bool note_validee = false;
  1241.  
  1242. QPen pen1("#12FF00");
  1243.  
  1244. if(!rapide)
  1245. {
  1246. effacer_calque(scene1,calque_trace_FFT );
  1247. effacer_calque(scene1,calque_lignes_F_zero );
  1248. }
  1249.  
  1250. x=0;
  1251. y = offset_y;
  1252. ENR_FFT enr_i;
  1253. for(n=0; n<nb_ech; n++)
  1254. {
  1255. //qDebug() << "n" << n;
  1256. //note_validee = false;
  1257. memo_x[0] = x;
  1258. memo_y[0] = y;
  1259. // x = offset_x + echelle_x * n; // echelle linéaire -> pas top
  1260. x = offset_x + echelle_x * log2((double)(n+1)); // échelle logarithmique afin que les tons soient équi-espacés
  1261.  
  1262. // ci-dessous 'a' est la partie réelle du complexe, et 'b' la partie imaginaire
  1263. // on calcule le module :
  1264. module_FFT = z * sqrt( tab_X[n].a * tab_X[n].a + tab_X[n].b * tab_X[n].b ); // z * racine(a²+b²)
  1265.  
  1266. y = module_FFT*10.0; // amplitude
  1267. y = offset_y - y; // offset et inversion du sens d'affichage because à l'écran les y croissent de haut en bas
  1268.  
  1269.  
  1270. // décale la pile memo_x
  1271. // puis mise en mémoire (empile) le dernier point
  1272. memo_x[4] = memo_x[3]; memo_y[4] = memo_y[3];
  1273. memo_x[3] = memo_x[2]; memo_y[3] = memo_y[2];
  1274. memo_x[2] = memo_x[1]; memo_y[2] = memo_y[1];
  1275. memo_x[1] = memo_x[0]; memo_y[1] = memo_y[0];
  1276. memo_x[0] = x;
  1277. memo_y[0] = y;
  1278.  
  1279. if((x>20) && (x<1900))
  1280. {
  1281. //y *=1.0;
  1282. if (x > -100)
  1283. {
  1284. if (!rapide)
  1285. {
  1286. segment_trace = new QGraphicsLineItem(memo_x[1] ,memo_y[1], x, y);
  1287. segment_trace->setPen(pen_trace2);
  1288. calque_trace_FFT->addToGroup(segment_trace);
  1289. }
  1290. }
  1291.  
  1292. // DETECTION DES NOTES
  1293.  
  1294. //----------------------------------------------------------------------------------
  1295. // détection direct des points hauts (points d'inflexion de la courbe)
  1296. // on pourrait calculer la dérivée de la courbe, et détecter les valeurs pour lesquelles elle s'annule
  1297. // problème : peut ne pas détecter si le sommet est un pic (discontinuité)
  1298. //----------------------------------------------------------------------------------
  1299.  
  1300. // Autre méthode :
  1301. // détection de 2 doublets de points de part et d'autre, montants puis descendant
  1302. // le sommet se situe donc entre les 2 doublets
  1303.  
  1304. // la DETECTION s'effectue ICI
  1305.  
  1306. // ATTENTION : la ligne qui suit détermine la stratégie utilisée et son paramétrage influe grandement
  1307. // sur la qualité du résultat.
  1308. // on doit pouvoir l'améliorer mais attention : toujours expérimenter sur des données réelles (air de musique)
  1309. // avant de valider la moindre "améloration".
  1310.  
  1311. //if ( (memo_y[3] - memo_y[2]) >0.5 && (memo_y[1] - memo_y[2]) >0.5 )
  1312. if ( (memo_y[3] - memo_y[2]) > 1.1 && (memo_y[1] - memo_y[2]) > 1.1 )
  1313. {
  1314. x0 = memo_x[2]; // point d'inflexion probable
  1315. y0 = memo_y[2];
  1316.  
  1317. if( !rapide) // donc pas d'affichage en mode rapide
  1318. {
  1319. ligne1 = new QGraphicsLineItem(x0, 60, x0, 350);
  1320. ligne1->setPen(pen1); // BLEU
  1321. calque_lignes_F_zero->addToGroup(ligne1);
  1322. ligne1 = new QGraphicsLineItem(x0, T_i-1, x0, T_i+1); // T_i : variable GLOBALE
  1323. ligne1->setPen(pen1);
  1324. }
  1325.  
  1326. int m0 = 0;
  1327. m0 = calcul_num_midi(x0);
  1328.  
  1329. enr_i.t=T_i; // T_i : variable GLOBALE
  1330. enr_i.num=num_raie;
  1331. enr_i.midi=m0;
  1332. enr_i.amplitude=y0;
  1333.  
  1334. // tracer sur tableau fréquences
  1335. if(num_raie<10) // 6
  1336. {
  1337. liste_ENR_FFT << enr_i; // memorisation ; donc 5 notes max pour un temps (T_i) donné
  1338. // les notes ayant même T_i seront considérées comme simultanées (accord)
  1339. tracer_1enr(enr_i); // sur le 2eme onglet (NOTES)
  1340. }
  1341.  
  1342. num_raie++;
  1343. }
  1344. }
  1345. }
  1346. T_i++; // variable GLOBALE
  1347.  
  1348. // T_i est ici incrémenté à chaque appel de la fonction
  1349. // sachant que chaque appel de cette fonction analyse 1024 échantillons du signal.
  1350. // et que chaque échantillon représente une durée = 0.5ms du signal d'origine
  1351. // donc T_i est (virtuellement) incrémenté toutes les 1024*0.5ms = 512ms;
  1352. // pourquoi 'virtuellement' ? parce que chaque échantillon "REPRESENTE" une durée de 0.5 ms
  1353. // mais ne "DURE PAS" 0.5ms; L'analyse s'effectue à très haute vitesse sur un signal pré-échantilloné.
  1354. // de quel chapeau sort ce 0.5ms? Voir la fontion 'void MainWindow::remplir_tableau_echantillons_signal()'
  1355.  
  1356. num_raie=0;
  1357. //affi_notes();
  1358.  
  1359. }
  1360.  
  1361.  
  1362. void MainWindow::tracer_echelle_temps_TAB_NOTES() // en secondes sur l'onglet NOTES
  1363. {
  1364. QString s1;
  1365. double x, x0, dx;
  1366. int offset_x = 80;
  1367. int y0 = 620;
  1368. double nbs;
  1369.  
  1370. effacer_calque(scene4, calque_echelle_temporelle);
  1371.  
  1372. if (visu_ech_tps == true) {y0 = 0;} // grandes lignes
  1373. for(int n=0; n<=120; n++)
  1374. {
  1375. x0 = 10.0 * doubleSpinBox_T0->value();
  1376. dx = 1000.0 * pas_echantillonage/256.0; // =93.75
  1377.  
  1378. x = x0 + dx * zoom_x * (double)n;
  1379.  
  1380. ligne1 = new QGraphicsLineItem(offset_x + x, y0-5, offset_x + x, 650);
  1381. QPen pen1;
  1382. pen1.setColor("#00FF00"); //
  1383. ligne1->setPen(pen1);
  1384. calque_echelle_temporelle->addToGroup(ligne1);
  1385.  
  1386. nbs = (double) n;
  1387. s1.setNum(nbs, 10, 0); // base 10, 0 décimale
  1388. s1 += "s";
  1389.  
  1390. Etiquette(offset_x + x, 630, s1, "#FFFFFF", "#000000", "#00FF00", calque_echelle_temporelle);
  1391. }
  1392. }
  1393.  
  1394.  
  1395.  
  1396. void MainWindow::tracer_1enr(ENR_FFT enr_i) // c.a.d. ayant la durée élémentaire T_i = 256 ms
  1397. {
  1398. // calque calque_TAB_FRQ sur le 2eme onglet (NOTES)
  1399.  
  1400. int T2;
  1401. //uint8_t num_raie;
  1402. uint8_t m0;
  1403. double yH, yL;
  1404. double y0, y2, dy, dy2;
  1405. uint16_t v; // v comme 'vertical'
  1406. double offset_y = 350.0;
  1407. QString couleur1;
  1408.  
  1409. T2 = zoom_x * enr_i.t;
  1410.  
  1411. m0 = enr_i.midi;
  1412. y0 = enr_i.amplitude;
  1413.  
  1414. if (checkBox_flitrer->isChecked() && (m0 != spinBox_filtre->value())) {return;}
  1415.  
  1416. v= 700 - (16 * (m0-40) );
  1417. y2 = -y0 + offset_y;
  1418. dy = y2/30.0; // 40
  1419. //dy = y2/10;
  1420. //dy=dy*dy; // pour un meilleur affichage ; A VOIR !!!!
  1421. //dy /=5.0;
  1422. dy *= gain;
  1423.  
  1424. if (dy > 200) {dy = 200;} // bridage amplitude max
  1425.  
  1426. if(v > 730) {return;}
  1427.  
  1428. if (dy > seuil)
  1429. {
  1430. dy2 = dy;
  1431. if (checkBox_norm->isChecked()) {dy2=5; } // bride la hauteur des tirets
  1432. double x = 80.0 + T2;
  1433. yH = v+dy2/2;
  1434. yL = v-dy2/2;
  1435. // rabotage pour éviter le scrolling de l'affichage:
  1436. if(yH > 730) {yH = 730;}
  1437. if(yL <0) {yH = 0;}
  1438.  
  1439. //ligne1 = new QGraphicsLineItem(x, yL, x, yH);
  1440.  
  1441. rectangle1 = new QGraphicsRectItem(x, v-dy2/2, zoom_x-2, dy2);
  1442. int h1 = calcul_hauteur_note(m0);
  1443.  
  1444. if (checkBox_norm->isChecked()) // utilise une couleur en fonction de l'amplitude du signal
  1445. {
  1446. double dy3 = dy/ 20;
  1447. if (dy3 > 0.8) {dy3 = 0.8;}
  1448. couleur1 = calcul_couleur(dy3);
  1449. }
  1450. else
  1451. {
  1452. // utilise une couleur en fonction de la fréquence ('hauteur') de la note
  1453. couleur1 = liste_couleurs[h1];
  1454. }
  1455.  
  1456. QPen pen1;
  1457. pen1.setColor(couleur1);
  1458.  
  1459. rectangle1->setPen(pen1);
  1460. rectangle1->setBrush(QColor(couleur1));
  1461.  
  1462. QBrush br1;
  1463. br1.setStyle(Qt::SolidPattern);
  1464. br1.setColor(couleur1);
  1465. rectangle1->setBrush(br1);
  1466. calque_TAB_FRQ->addToGroup(rectangle1); // lent !
  1467.  
  1468. graphicsView1->verticalScrollBar()->setValue(0);
  1469. }
  1470. }
  1471.  
  1472.  
  1473. void MainWindow::detection_notes_auto()
  1474. {
  1475. ENR_FFT enr_i;
  1476.  
  1477. int T2;
  1478. uint8_t m0;
  1479. double A1;
  1480. double x, y, y0, y2;
  1481. double offset_y = 350.0;
  1482. double decalage;
  1483. QString couleur1;
  1484. QPen pen1;
  1485. pen1.setWidth(1);
  1486.  
  1487.  
  1488. // doubleSpinBox_dt->setValue(9.375); // 1 barre toutes les 1/20 seconde
  1489. // spinBox_nb_barres->setValue(20); // barres en surbrillances
  1490.  
  1491. double pas_grille = doubleSpinBox_dt->value()/2.0 * zoom_x;
  1492. double offset_x = 10 * doubleSpinBox_x0->value() + spinBox_d_barres->value() * pas_grille;
  1493.  
  1494. uint16_t n_max = liste_ENR_FFT.length();
  1495. if (n_max == 0) return;
  1496.  
  1497. tableWidget_type_M->setCurrentCell(2, 0);
  1498. on_tableWidget_type_M_cellClicked(2, 0);
  1499.  
  1500. liste_NOTES.clear();
  1501.  
  1502. for(int n=0; n<83; n++)
  1503. {
  1504. table_integration_amplitudes[n] =0; // RAZ
  1505. }
  1506. calque_notes_auto->setPos(0, 0);
  1507.  
  1508. for(uint16_t n=0; n<n_max-1; n++)
  1509. {
  1510. enr_i = liste_ENR_FFT[n];
  1511. T2 = zoom_x * enr_i.t;
  1512. m0 = enr_i.midi;
  1513.  
  1514. y0 = enr_i.amplitude;
  1515. y2 = -y0 + offset_y;
  1516.  
  1517. y = 700 - (16 * (m0-40) );
  1518. A1 = y2/10.0; // 40
  1519. A1 *= gain;
  1520.  
  1521. // table_integration_amplitudes[m0] /= 2.0; // usure volontaire
  1522. if(A1<20)
  1523. {
  1524. table_integration_amplitudes[m0] = 0; // ce qui est débloquant
  1525. }
  1526.  
  1527. if (A1 > 20) // 30
  1528. {
  1529. x = 80.0 + T2 -8; // le -8 pour décaler dans le temps (de la durée de l'intégration)
  1530.  
  1531. //intégration temporelle des amplitudes en vue de valider en tant que note
  1532. if(table_integration_amplitudes[m0] >=0) // si pas de blocage en cours
  1533. {
  1534. table_integration_amplitudes[m0] += A1;
  1535. }
  1536.  
  1537. if (table_integration_amplitudes[m0] > 200.0)
  1538. {
  1539. // la NOTE est comfirmée, on va l'ajouter à la fin de la liste des notes
  1540. // Mais il faut aussi empêcher des re-détections multiples lorsque l'amplitute du signal ne décroit
  1541. // que progressivement. Pour cela on attribue la valeur -1 dans la table d'intégration
  1542. // pour le n° midi de la note, ce qui sera bloquant.
  1543. // Le déblocage se produira lorsque l'amplitude du signal concernant cette note
  1544. // sera descendu en dessous d'un certain niveau.
  1545.  
  1546. table_integration_amplitudes[m0] = -1; //ce qui est bloquant (voir 10 lignes +haut)
  1547.  
  1548. int h1 = calcul_hauteur_note(m0); //'hauteur' au sens musical, gamme chromatique
  1549. couleur1 = liste_couleurs[h1];
  1550. pen1.setColor(couleur1);
  1551.  
  1552. ellipse1 = new QGraphicsEllipseItem(x-A1/2, y-A1/2, A1, A1);
  1553. ellipse1->setPen(pen1);
  1554. calque_notes_auto->addToGroup(ellipse1); // cercle coloré
  1555. //on va ajouter la note à la liste des notes
  1556. int n_barre=numero_barre_cliquee(x);
  1557. if (n_barre != -1) {ajout_note(m0, n_barre, 0);}
  1558.  
  1559. // ellipse1 = new QGraphicsEllipseItem(x, v-1, 2, 2);
  1560. // pen1.setColor("#FFFFFF");
  1561. // ellipse1->setPen(pen1);
  1562. // calque_notes_auto->addToGroup(ellipse1); // point blanc
  1563. }
  1564. }
  1565. // voir la fonction -> 'tracer_1enr(ENR_FFT enr_i)' pour le cadrage
  1566.  
  1567. }
  1568. calque_notes_auto->setPos(offset_x, 0);
  1569. visu_notes = 0; on_Bt_toggle_visu_notes_clicked(); // basculera à 1;
  1570. doubleSpinBox_vitesse->setValue(30);
  1571. }
  1572.  
  1573.  
  1574. void MainWindow::on_Bt_valider_clicked()
  1575. {
  1576.  
  1577. }
  1578.  
  1579.  
  1580. void MainWindow::retracer_TAB_FRQ()
  1581. {
  1582. ENR_FFT enr_i;
  1583. uint16_t n_max = liste_ENR_FFT.length();
  1584. if (n_max == 0) return;
  1585. //effacer_calque(scene1,calque_trace_FFT );
  1586. tracer_graduation_TAB_NOTES();
  1587.  
  1588. calque_TAB_FRQ->setPos(0, 0);
  1589. for(uint16_t n=0; n<n_max-1; n++)
  1590. {
  1591. enr_i = liste_ENR_FFT[n];
  1592. tracer_1enr(enr_i);
  1593. }
  1594.  
  1595. double pas_grille = doubleSpinBox_dt->value()/2.0 * zoom_x;
  1596. double offset_x = 10 * doubleSpinBox_x0->value() + spinBox_d_barres->value() * pas_grille;
  1597. calque_TAB_FRQ->setPos(offset_x, 0);
  1598. }
  1599.  
  1600.  
  1601.  
  1602. void RAZ_tableau_echantillons()
  1603. {
  1604. uint n;
  1605. for(n=0; n < nb_ech; n++)
  1606. {
  1607. ech[n].a = 0; // partie reelle
  1608. ech[n].b = 0; // partie imaginaire
  1609. }
  1610. }
  1611.  
  1612.  
  1613.  
  1614.  
  1615. void MainWindow::remplir_tableau_echantillons_sinus()
  1616. {
  1617. // création du signal à analyser
  1618. uint n, n_max;
  1619. double R;
  1620. double frq_echantillonnage=48000; // Hz
  1621. double Te; // période d'échantillonage
  1622.  
  1623. Te = 1.0 / frq_echantillonnage;
  1624. Te *=15.0;
  1625.  
  1626. if (bz==true) {n_max = nb_ech /4; } else {n_max = nb_ech; }
  1627.  
  1628. for(n=0; n < n_max; n++) // nb d'échantillons
  1629. {
  1630. R= cos(2.0 * M_PI * frequence1 * n * Te );
  1631.  
  1632. if (modul) {R *= 1 + 0.8 * cos(2 * M_PI * frequence3 * n * Te);} // modulation d'amplitude
  1633.  
  1634. if (second_Frq) {R+=cos(2 * M_PI * frequence2 * n * Te); }
  1635. if (hamming) { R = R * (1- cos (2* M_PI * n / n_max ));}
  1636.  
  1637. R/=10;
  1638. ech[n].a = R; // partie reelle
  1639. ech[n].b = 0; // partie imaginaire
  1640. }
  1641. }
  1642.  
  1643.  
  1644.  
  1645. uint bit_reversal(uint num, uint nb_bits)
  1646. {
  1647. uint r = 0, i, s;
  1648. if ( num > (1<< nb_bits)) { return 0; }
  1649.  
  1650. for (i=0; i<nb_bits; i++)
  1651. {
  1652. s = (num & (1 << i));
  1653. if(s) { r |= (1 << ((nb_bits - 1) - i)); }
  1654. }
  1655. return r;
  1656. }
  1657.  
  1658.  
  1659.  
  1660. void MainWindow::bit_reverse_tableau_X()
  1661. {
  1662. // recopie les échantillons en les classant dans l'ordre 'bit reverse'
  1663. uint n,r;
  1664.  
  1665. for(n=0; n < nb_ech; n++) // nb d'échantillons
  1666. {
  1667. r=bit_reversal(n,nb_etapes);
  1668. tab_X[n] = ech[r];
  1669. }
  1670. }
  1671.  
  1672. void MainWindow::calcul_tableau_W()
  1673. {
  1674. if (tableau_w_plein == true) {return;}
  1675. // calcul et memorisation dans un tableau des twiddle factors (facteurs de rotation)
  1676. uint n;
  1677. double x;
  1678. for(n=0; n<(nb_ech/2-1); n++)
  1679. {
  1680. x=2.0*M_PI * n / nb_ech;
  1681.  
  1682. tab_W[n].a = cos(x); // partie reelle
  1683. tab_W[n].b = -sin(x); // partie imaginaire
  1684. }
  1685. tableau_w_plein = true;
  1686. }
  1687.  
  1688.  
  1689. void MainWindow::calcul_FFT()
  1690. {
  1691. Complexe produit; // voir la classe "Complexe" : complexe.h et complexe.ccp
  1692.  
  1693. uint etape, e1, e2, li, w, ci;
  1694. uint li_max=nb_ech;
  1695. e2=1;
  1696.  
  1697. for (etape=1; etape<=nb_etapes; etape++)
  1698. {
  1699. e1=e2; //(e1 évite d'effectuer des divisions e2/2 plus bas)
  1700. e2=e2+e2; // plus rapide que *=2
  1701.  
  1702. for (li=0; li<li_max; li+=1)
  1703. {
  1704. ci=li & (e2-1); // ET bit à bit
  1705. if (ci>(e1-1))
  1706. {
  1707. w=li_max/e2*(li & (e1 -1)); // ET bit à bit calcul du numéro du facteur de rotation W
  1708. 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"
  1709. tab_X[li]=tab_X[li-e1] - produit; // concerne la ligne basse du croisillon; soustraction complexe; Voir "complexe.cpp"
  1710. tab_X[li-e1]=tab_X[li-e1] + produit; // concerne la ligne haute du croisillon; addition complexe; Voir "complexe.cpp"
  1711. }
  1712. }
  1713. }
  1714. }
  1715.  
  1716.  
  1717.  
  1718. void MainWindow::on_spinBox_frq_valueChanged(int arg1) // SELECTEUR DE FREQUENCE 1
  1719. {
  1720. effacer_calque(scene1,calque_trace_FFT );
  1721. frequence1 = arg1;
  1722. RAZ_tableau_echantillons();
  1723. remplir_tableau_echantillons_sinus();
  1724. tracer_signal_a_convertir();
  1725. calcul_tableau_W();
  1726. bit_reverse_tableau_X();
  1727. calcul_FFT(); // pour 1 cluster
  1728. // tracer_graduations();
  1729. tracer_segments_FFT();
  1730. }
  1731.  
  1732.  
  1733. void MainWindow::on_spinBox_frq_2_valueChanged(int arg1) // SELECTEUR DE FREQUENCE 2
  1734. {
  1735. effacer_calque(scene1,calque_trace_FFT );
  1736. frequence2 = arg1;
  1737. RAZ_tableau_echantillons();
  1738. remplir_tableau_echantillons_sinus();
  1739. tracer_signal_a_convertir();
  1740. calcul_tableau_W();
  1741. bit_reverse_tableau_X();
  1742. calcul_FFT();
  1743. // tracer_graduations();
  1744. tracer_segments_FFT();
  1745. }
  1746.  
  1747.  
  1748. void MainWindow::on_spinBox_frq_3_valueChanged(int arg1)
  1749. {
  1750. effacer_calque(scene1,calque_trace_FFT );
  1751. frequence3 = arg1;
  1752. RAZ_tableau_echantillons();
  1753. remplir_tableau_echantillons_sinus();
  1754. tracer_signal_a_convertir();
  1755. calcul_tableau_W();
  1756. bit_reverse_tableau_X();
  1757. calcul_FFT();
  1758. // tracer_graduations();
  1759. tracer_segments_FFT();
  1760. }
  1761.  
  1762.  
  1763. void MainWindow::on_checkBox1_toggled(bool checked) // HAMMING
  1764. {
  1765. effacer_calque(scene1,calque_trace_FFT );
  1766. if (checked) {hamming = true;} else {hamming = false;}
  1767. RAZ_tableau_echantillons();
  1768. remplir_tableau_echantillons_sinus();
  1769. tracer_signal_a_convertir();
  1770. calcul_tableau_W();
  1771. bit_reverse_tableau_X();
  1772. calcul_FFT();
  1773. // tracer_graduations();
  1774. tracer_segments_FFT();
  1775.  
  1776. }
  1777.  
  1778.  
  1779. void MainWindow::on_checkBox2_toggled(bool checked) // SECONDE FREQUENCE
  1780. {
  1781. effacer_calque(scene1,calque_trace_FFT );
  1782. if (checked) {second_Frq = true;} else {second_Frq = false;}
  1783. RAZ_tableau_echantillons();
  1784. remplir_tableau_echantillons_sinus();
  1785. tracer_signal_a_convertir();
  1786. calcul_tableau_W();
  1787. bit_reverse_tableau_X();
  1788. calcul_FFT();
  1789. // tracer_graduations();
  1790. tracer_segments_FFT();
  1791. }
  1792.  
  1793.  
  1794. void MainWindow::on_checkBox3_toggled(bool checked)
  1795. {
  1796. effacer_calque(scene1,calque_trace_FFT );
  1797. if (checked) {bz = true;} else {bz = false;}
  1798. RAZ_tableau_echantillons();
  1799. remplir_tableau_echantillons_sinus();
  1800. tracer_signal_a_convertir();
  1801. calcul_tableau_W();
  1802. bit_reverse_tableau_X();
  1803. calcul_FFT();
  1804. // tracer_graduations();
  1805. tracer_segments_FFT();
  1806. }
  1807.  
  1808.  
  1809. void MainWindow::on_checkBox4_clicked(bool checked)
  1810. {
  1811. effacer_calque(scene1,calque_trace_FFT );
  1812. if (checked) {modul = true;} else {modul = false;}
  1813. RAZ_tableau_echantillons();
  1814. remplir_tableau_echantillons_sinus();
  1815. tracer_signal_a_convertir();
  1816. calcul_tableau_W();
  1817. bit_reverse_tableau_X();
  1818. calcul_FFT();
  1819. // tracer_graduations();
  1820. tracer_segments_FFT();
  1821. }
  1822.  
  1823.  
  1824. void MainWindow::Tic1()
  1825. {
  1826.  
  1827. nb_tics++;
  1828. if(nb_tics > 4000)
  1829. {
  1830. Timer1->stop();
  1831. nb_tics=0;
  1832. }
  1833. else
  1834. {
  1835. spinBox_4->setValue( spinBox_4->value() +1);
  1836. Timer1->setInterval(2); // à voir
  1837. }
  1838. }
  1839.  
  1840.  
  1841. void MainWindow::Tic2()
  1842. {
  1843. int d_barre;
  1844. double dt, vts;
  1845. int itrv;
  1846.  
  1847. vts = doubleSpinBox_vitesse->value();
  1848. d_barre = joue_1_note_de_la_liste(); // nb de barres temporelles séparant la note avec celle qui suit
  1849.  
  1850. dt = 20.0 * (double)d_barre;
  1851. dt *= (100.0 / vts);
  1852. itrv = (int) dt;
  1853.  
  1854. Timer2->setInterval(itrv);
  1855. }
  1856.  
  1857.  
  1858. void clear_temp_data()
  1859. {
  1860. for(int i=0; i<2000000; i++)
  1861. {
  1862. temp_data[i]=0;
  1863. }
  1864.  
  1865. }
  1866.  
  1867.  
  1868. void MainWindow::on_Bt_load_wav_clicked()
  1869. {
  1870. open_fichier_wav(); // associe le binStream1 au fichier wav
  1871. decode_entete();
  1872.  
  1873. MainWindow::setCursor(Qt::WaitCursor);
  1874. liste_ENR_FFT.clear();
  1875. effacer_calque(scene1,calque_trace_signal1 );
  1876.  
  1877. effacer_calque(scene1, calque_trace_FFT);
  1878. clear_temp_data();
  1879. garnis_temp_data(0);
  1880.  
  1881. ajustement_gain();
  1882. // donne accès à la totalité (2MB) du fichier wav (dans le 'temp_data')
  1883. tracer_signal_complet();
  1884. MainWindow::setCursor(Qt::ArrowCursor);
  1885. tracer_gradu_temporelle_signal_entree();
  1886.  
  1887. frame_3->setVisible(true);
  1888. Bt_scan_auto->setVisible(true);
  1889. checkBox_rapide->setVisible(true);
  1890. }
  1891.  
  1892.  
  1893.  
  1894. /*
  1895.   char RIFF[4]; (4 bytes)
  1896.   unsigned long ChunkSize; (4 bytes)
  1897.   char WAVE[4]; (4 bytes)
  1898.   char fmt[4]; (4 bytes)
  1899.   unsigned long Subchunk1Size; (4 bytes)
  1900.   unsigned short AudioFormat; (2 octets)
  1901.   unsigned short NumOfChan; (2 octets)
  1902.   unsigned long SamplesPerSec; (4 bytes)
  1903.   unsigned long bytesPerSec; (4 bytes)
  1904.   unsigned short blockAlign; (2 octets)
  1905.   unsigned short bitsPerSample; (2 octets)
  1906.   char Subchunk2ID[4]; (4 bytes)
  1907.   unsigned long Subchunk2Size; (4 bytes)
  1908.  
  1909.   TOTAL 44 octets
  1910. */
  1911.  
  1912.  
  1913. QString separ_milliers(uint n) // séparation des miliers par des espaces
  1914. {
  1915. QString s1, s1a, s1b, s1c;
  1916. s1.setNum(n);
  1917. int L= s1.length(); s1a = s1.right(3); s1b = s1.mid(L-6, 3); s1c = s1.mid(L-9, 3);
  1918. return (s1c+" "+s1b+" "+s1a);
  1919. }
  1920.  
  1921.  
  1922.  
  1923. void MainWindow::save_fichier_ini()
  1924. {
  1925. QFile file1(QDir::currentPath() + "/" + "params_FFT.ini"); // dans le dossier de l'exécutable
  1926. if (file1.open(QIODevice::WriteOnly | QIODevice::Text))
  1927. {
  1928. //int p1= string_currentFile.lastIndexOf('/');
  1929. //string_currentDir = string_currentFile.left(p1);
  1930.  
  1931. QTextStream out(&file1);
  1932.  
  1933. out << "# Ce fichier est crée automatiquement par le programme";
  1934. out << '\n';
  1935. out << "#";
  1936. out << '\n';
  1937. out << "# chemins:";
  1938. out << '\n';
  1939. out << "<currentDir>" << string_currentDir << "</currentDir>";
  1940. out << '\n';
  1941. out << "<currentFile>" << string_currentFile << "</currentFile>";
  1942. }
  1943. file1.close();
  1944. }
  1945.  
  1946.  
  1947.  
  1948. void MainWindow::open_fichier_wav()
  1949. {
  1950. // Le fichier .wav comprend un signal audio stéréo échantilloné à 48 000 octets/seconde pour chaque canal
  1951. // ce qui fait 96000 octets/s pour l'ensemble du signal stéréo
  1952.  
  1953. string_currentFile = QFileDialog::getOpenFileName(this, tr("Ouvrir Fichier wav..."), string_currentDir,
  1954. tr("Fichiers wav (*.wav);;All Files (*)"));
  1955. if (string_currentFile != "")
  1956. {
  1957. save_fichier_ini();
  1958. }
  1959. lineEdit_1->setText(string_currentFile);
  1960.  
  1961. int p1= string_currentFile.lastIndexOf('/');
  1962. string_currentDir = string_currentFile.left(p1);
  1963. lineEdit_current_dir->setText(string_currentDir);
  1964.  
  1965. save_fichier_ini();
  1966.  
  1967. file_wav.setFileName(string_currentFile);
  1968. if (!file_wav.open(QIODevice::ReadOnly))
  1969. {
  1970. wav_ok = false;
  1971. Bt_scan_auto->setStyleSheet("background-color: rgb(200, 200, 200);");
  1972. return ;
  1973. }
  1974. else
  1975. {
  1976. binStream1.setDevice(&file_wav); // accès à la totalité du fichier wav
  1977. wav_ok = true;
  1978. Bt_scan_auto->setStyleSheet("background-color: rgb(0, 255, 0);"); // vert
  1979.  
  1980. }
  1981. // le fichier reste ouvert pour toute la session
  1982. }
  1983.  
  1984.  
  1985.  
  1986.  
  1987. void MainWindow::decode_entete()
  1988. {
  1989. // ============ [Bloc de déclaration d'un fichier au format WAVE] ============
  1990.  
  1991. if (! wav_ok) {return;}
  1992. QString s3, s4, s5, fileSize1;
  1993. uint8_t x, nb_canaux;
  1994. double tot1, tot2, tot3, tot4;
  1995.  
  1996.  
  1997. binStream1.device()->seek(0); // retour de la lecture au début du fichier
  1998. binStream1.readRawData (temp_entete, 100); // lecture entete du fichier
  1999. //FileTypeBlocID
  2000. x=(uint8_t)temp_entete[0]; if ((x>64) && (x<127)) { s3 = char(x); }
  2001. x=(uint8_t)temp_entete[1]; if ((x>64) && (x<127)) { s3 += char(x); }
  2002. x=(uint8_t)temp_entete[2]; if ((x>64) && (x<127)) { s3 += char(x); }
  2003. x=(uint8_t)temp_entete[3]; if ((x>64) && (x<127)) { s3 += char(x); }
  2004. tableWidget_1->setItem(0, 0, new QTableWidgetItem (s3) ); //FileTypeBlocID
  2005.  
  2006. // FileSize
  2007. x=(uint8_t)temp_entete[4]; s3.setNum(x); tableWidget_1->setItem(1, 0, new QTableWidgetItem (s3) );
  2008. x=(uint8_t)temp_entete[5]; s3.setNum(x); tableWidget_1->setItem(1, 1, new QTableWidgetItem (s3) );
  2009. x=(uint8_t)temp_entete[6]; s3.setNum(x); tableWidget_1->setItem(1, 2, new QTableWidgetItem (s3) );
  2010. x=(uint8_t)temp_entete[7]; s3.setNum(x); tableWidget_1->setItem(1, 3, new QTableWidgetItem (s3) );
  2011.  
  2012. tot1 = temp_entete[4] + (2<<7) * temp_entete[5] + (2<<15) * temp_entete[6] + (2<23)* temp_entete[7];
  2013. fileSize1 = separ_milliers(tot1);
  2014. tableWidget_1->setItem(1, 4, new QTableWidgetItem ("= " + fileSize1) );
  2015.  
  2016. // FileFormatID
  2017. x=(uint8_t)temp_entete[8]; if ((x>64) && (x<127)) { s3 = char(x); }
  2018. x=(uint8_t)temp_entete[9]; if ((x>64) && (x<127)) { s3 += char(x); }
  2019. x=(uint8_t)temp_entete[10]; if ((x>64) && (x<127)) { s3 += char(x); }
  2020. x=(uint8_t)temp_entete[11]; if ((x>64) && (x<127)) { s3 += char(x); }
  2021. tableWidget_1->setItem(2, 0, new QTableWidgetItem (s3) );
  2022.  
  2023. // ============ [Bloc décrivant le format audio] ==============
  2024.  
  2025. // FormatBlocID
  2026. x=(uint8_t)temp_entete[12]; if ((x>64) && (x<127)) { s3 = char(x); }
  2027. x=(uint8_t)temp_entete[13]; if ((x>64) && (x<127)) { s3 += char(x); }
  2028. x=(uint8_t)temp_entete[14]; if ((x>64) && (x<127)) { s3 += char(x); }
  2029. x=(uint8_t)temp_entete[15]; if ((x>64) && (x<127)) { s3 += char(x); }
  2030. tableWidget_1->setItem(3, 0, new QTableWidgetItem (s3) );
  2031.  
  2032. //BlocSize
  2033. x=(uint8_t)temp_entete[16]; s3.setNum(x); tableWidget_1->setItem(4, 0, new QTableWidgetItem (s3) );
  2034. x=(uint8_t)temp_entete[17]; s3.setNum(x); tableWidget_1->setItem(4, 1, new QTableWidgetItem (s3) );
  2035. x=(uint8_t)temp_entete[18]; s3.setNum(x); tableWidget_1->setItem(4, 2, new QTableWidgetItem (s3) );
  2036. x=(uint8_t)temp_entete[19]; s3.setNum(x); tableWidget_1->setItem(4, 3, new QTableWidgetItem (s3) );
  2037.  
  2038. 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];
  2039. tableWidget_1->setItem(4, 4, new QTableWidgetItem ("= " + separ_milliers(tot2)) );
  2040.  
  2041. //AudioFormat
  2042. x=(uint8_t)temp_entete[20]; s3.setNum(x); tableWidget_1->setItem(5, 0, new QTableWidgetItem (s3) );
  2043. x=(uint8_t)temp_entete[21]; s3.setNum(x); tableWidget_1->setItem(5, 1, new QTableWidgetItem (s3) );
  2044.  
  2045. if ((uint8_t)temp_entete[20]==1){tableWidget_1->setItem(5, 4, new QTableWidgetItem ("PCM") );}
  2046.  
  2047. //NbrCanaux
  2048. nb_canaux=(uint8_t)temp_entete[22]; s3.setNum(nb_canaux); tableWidget_1->setItem(6, 0, new QTableWidgetItem (s3) );
  2049. x=(uint8_t)temp_entete[23]; s3.setNum(x); tableWidget_1->setItem(6, 1, new QTableWidgetItem (s3) );
  2050.  
  2051. //Fréquence d'échantillonage en Hz
  2052. x=(uint8_t)temp_entete[24]; s3.setNum(x); tableWidget_1->setItem(7, 0, new QTableWidgetItem (s3) );
  2053. x=(uint8_t)temp_entete[25]; s3.setNum(x); tableWidget_1->setItem(7, 1, new QTableWidgetItem (s3) );
  2054. x=(uint8_t)temp_entete[26]; s3.setNum(x); tableWidget_1->setItem(7, 2, new QTableWidgetItem (s3) );
  2055. x=(uint8_t)temp_entete[27]; s3.setNum(x); tableWidget_1->setItem(7, 3, new QTableWidgetItem (s3) );
  2056.  
  2057. 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];
  2058. tableWidget_1->setItem(7, 4, new QTableWidgetItem ("= " + separ_milliers(tot3)) );
  2059.  
  2060. //BytePerSec
  2061. x=temp_entete[28]; s3.setNum(x); tableWidget_1->setItem(8, 0, new QTableWidgetItem (s3) );
  2062. x=temp_entete[29]; s3.setNum(x); tableWidget_1->setItem(8, 1, new QTableWidgetItem (s3) );
  2063. x=temp_entete[30]; s3.setNum(x); tableWidget_1->setItem(8, 2, new QTableWidgetItem (s3) );
  2064. x=temp_entete[31]; s3.setNum(x); tableWidget_1->setItem(8, 3, new QTableWidgetItem (s3) );
  2065.  
  2066. 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];
  2067. s3.setNum(tot4); // 96000
  2068. //qDebug() << "tot4" << tot4;
  2069.  
  2070. tableWidget_1->setItem(8, 4, new QTableWidgetItem (s3));
  2071. //tableWidget_1->setItem(8, 4, new QTableWidgetItem ("= " + separ_milliers(tot4)) );
  2072.  
  2073. //BytePerBloc
  2074. x=(uint8_t)temp_entete[32]; s3.setNum(x); tableWidget_1->setItem(9, 0, new QTableWidgetItem (s3) );
  2075. x=(uint8_t)temp_entete[33]; s3.setNum(x); tableWidget_1->setItem(9, 1, new QTableWidgetItem (s3) );
  2076.  
  2077. //BitsPerSample
  2078. x=(uint8_t)temp_entete[34]; s3.setNum(x); tableWidget_1->setItem(10, 0, new QTableWidgetItem (s3) );
  2079. x=(uint8_t)temp_entete[35]; s3.setNum(x); tableWidget_1->setItem(10, 1, new QTableWidgetItem (s3) );
  2080.  
  2081. x=(uint8_t)temp_entete[36]; if ((x>64) && (x<127)) { s3 = char(x); }
  2082. x=(uint8_t)temp_entete[37]; if ((x>64) && (x<127)) { s3 += char(x); }
  2083. x=(uint8_t)temp_entete[38]; if ((x>64) && (x<127)) { s3 += char(x); }
  2084. x=(uint8_t)temp_entete[39]; if ((x>64) && (x<127)) { s3 += char(x); }
  2085. tableWidget_1->setItem(11, 0, new QTableWidgetItem (s3) );
  2086.  
  2087. // DataSize
  2088. x=(uint8_t)temp_entete[40]; s3.setNum(x); tableWidget_1->setItem(12, 0, new QTableWidgetItem (s3) );
  2089. x=(uint8_t)temp_entete[41]; s3.setNum(x); tableWidget_1->setItem(12, 1, new QTableWidgetItem (s3) );
  2090. x=(uint8_t)temp_entete[42]; s3.setNum(x); tableWidget_1->setItem(12, 2, new QTableWidgetItem (s3) );
  2091. x=(uint8_t)temp_entete[43]; s3.setNum(x); tableWidget_1->setItem(12, 3, new QTableWidgetItem (s3) );
  2092.  
  2093. int tot5a = 1 * (uint8_t) temp_entete[40]; //because le temp_entête de type 'char' code avec entiers signés
  2094. int tot5b = (1<<8) * (uint8_t)temp_entete[41]; // remarque: (1<<8) = 2^8 = 256 // ne pas écrire (2<<8) !!
  2095. int tot5c = (1<<16) * (uint8_t)temp_entete[42];
  2096. int tot5d = (1<<24) * (uint8_t)temp_entete[43];
  2097.  
  2098. data_size = tot5a + tot5b + tot5c + tot5d;
  2099.  
  2100. // 21248 + 458752 = 480000
  2101. // pour le .wav LA 440 (48000, durée 10s) ->14x(2^16) + 166 * (2^8) = 960000
  2102. // 960 000 kb (stéréo) / 96 000 kb/s (stéréo)= 10s
  2103.  
  2104. tableWidget_1->setItem(12, 4, new QTableWidgetItem ("= " + separ_milliers(data_size)) ); // DataSize
  2105.  
  2106. // durée calculée
  2107. duree_totale = data_size / tot4;
  2108. s3.setNum(duree_totale, 'f', 3);// double n, char format = 'g', int precision = 6
  2109. tableWidget_1->setItem(13, 4, new QTableWidgetItem (s3 + " s") );
  2110.  
  2111. s4.setNum(tot4);
  2112. s4 = "ech : " + s3;
  2113. s4 += " Bps / 2 voies (stéréo) = ";
  2114.  
  2115. s5.setNum(tot4/2000);
  2116. s5 +=" kBps";
  2117.  
  2118. tableWidget_1->setItem(8, 5, new QTableWidgetItem (s5) );
  2119. lineEdit_6->setText("File size = " + fileSize1 + " bytes ; " + s4 + s5 + " ; durée calculée = " +s3 + "s");
  2120.  
  2121. if ((nb_canaux == 2) && (tot4 != 96000))
  2122. {
  2123. QMessageBox msgBox;
  2124. QString s1;
  2125. s1 = "Attention: Le taux d'échentillonage n'est pas 48 kb/s";
  2126. msgBox.setText(s1);
  2127. msgBox.exec();
  2128. }
  2129. if (nb_canaux != 2)
  2130. {
  2131. QMessageBox msgBox;
  2132. QString s1;
  2133. s1 = "Attention: Signal mono (doit être STEREO !)";
  2134. msgBox.setText(s1);
  2135. msgBox.exec();
  2136. }
  2137. }
  2138.  
  2139.  
  2140.  
  2141.  
  2142.  
  2143. // voir la page : https://fr.wikipedia.org/wiki/Waveform_Audio_File_Format
  2144. void MainWindow::garnis_temp_data(qint32 offset_i)
  2145. {
  2146. if (! wav_ok) {return;}
  2147.  
  2148. binStream1.device()->seek(0); // retour de la lecture au début du fichier
  2149. binStream1.skipRawData(44+offset_i); // saut bien plus loin dans le fichier
  2150. binStream1.readRawData (temp_data, 2000000); //lecture des données audio
  2151.  
  2152. //file_wav.close();
  2153. }
  2154.  
  2155.  
  2156. void MainWindow::calcul_compression()
  2157. {
  2158. double R;
  2159. double R_max=0;
  2160.  
  2161. for(uint n =0; n<nb_ech/4; n++)
  2162. {
  2163. R=ech[n].a;
  2164. if (R>R_max) {R_max=R;}
  2165. }
  2166. }
  2167.  
  2168.  
  2169. void MainWindow::remplir_tableau_echantillons_signal() // lecture du signal à analyser dans le 'temp_data' -> ech[n]
  2170. {
  2171.  
  2172. /*
  2173. Le fichier .wav doit avoir les caractéristiques suivantes :
  2174. - RIFF PCM WAVE fmt
  2175. - stéréo deux voies
  2176. - 96000 octets/s c'est à dire acqui avec 48000 octets/s x 2 voies
  2177. La FTT sera effectuée en 10 passes, sur un nb_ech = 2^10 = 1024 échantillons
  2178. */
  2179.  
  2180. uint n, n_max, i, offset_x;
  2181. double R;
  2182.  
  2183. offset_x = 40; // ou plus, si lecture plus loin dans le temps, à voir
  2184.  
  2185. uint8_t octet_n; // octet_B;
  2186. /*
  2187.   pour un codage 8 bits/sample = 2 * 1 octet/sample (stéréo)
  2188.   [Octet du Sample1 Canal1 ] (octet_A, octet_B)
  2189.   [Octet du Sample1 Canal2 ]
  2190.   ...
  2191. */
  2192. i=0;
  2193. n=0;
  2194. // on ne prend pas en compte tous les échantillons dispo dans le .wav (inutile ici)
  2195. // mais 1 sur 40 (la fréquence de la note la plus aigue (midi94) est = 932Hz)
  2196. // et seul le fondamental est pertinant pour reconnaitre la note, les harmoniques (timbre) sont ignorées
  2197. // il faut donc échantilloner à 932*2 = 1864 Hz au minimum
  2198. // Le fichier .wav d'origine est échantilloné à 48kHz
  2199. // 48000 / 1864 = 25.75
  2200. // On va prendre 1 échantillon sur 24 , ce qui donne un échantillonage à 48000/24 = 2000Hz
  2201. // attention : tout changement de cette valeur (pas = 24) change l'échelle de représentation de la FFT
  2202. // et fout le bocson dans tout le programme !!!
  2203. // chaque échantillon représente donc une durée = 1/2000 seconde = 0.5ms = 500us
  2204. // voir la suite des explications vers la fin de la fonction 'void MainWindow::tracer_segments_FFT()'
  2205.  
  2206. n_max = nb_ech /2; // = 1024/2 = 512
  2207. // 512*0.5ms = 256 ms
  2208.  
  2209. while (n < n_max)
  2210. {
  2211. octet_n = temp_data[offset_x + i]; // canal A
  2212. // i++; // pour sauter (et ignorer) le canal B (le signal dans le fichier .wav est stéréo)
  2213. i+= pas_echantillonage;
  2214.  
  2215. // x= 256 * octet_A + octet_B;
  2216. R = octet_n - 128; // pour oter la composante continue propre au codage par des octets non signés
  2217. R /= 10.0;
  2218. R *= doubleSpinBox_gain->value();
  2219.  
  2220. if (hamming) { R = R * (1- cos (2* M_PI * n / nb_ech/2 ));}
  2221.  
  2222. if (bourrage_de_zeros)
  2223. {
  2224. if (n<=nb_ech/4) // /4 -> signal
  2225. {
  2226. ech[n].a = R; // partie reelle
  2227. ech[n].b = 0; // partie imaginaire
  2228. }
  2229. else // -> Bourage de zéros
  2230. {
  2231. ech[n].a = 0; // partie reelle
  2232. ech[n].b = 0; // partie imaginaire
  2233. }
  2234. }
  2235. else
  2236. {
  2237. ech[n].a = R; // partie reelle
  2238. ech[n].b = 0; // partie imaginaire
  2239. }
  2240.  
  2241. n++; // n atteindra la valeur max = 512 ech représentant une durée totale de 256 ms
  2242. }
  2243.  
  2244. //calcul_compression();
  2245. }
  2246.  
  2247. /*
  2248. void MainWindow::on_toolButton_2_clicked() // recup signal
  2249. {
  2250.   RAZ_tableau_echantillons();
  2251.   remplir_tableau_echantillons_signal();
  2252.   tracer_signal_a_convertir();
  2253. }
  2254. */
  2255.  
  2256. void MainWindow::calcul_ofsset()
  2257. {
  2258. if(!wav_ok) return;
  2259. offset_t = spinBox_1->value() + 10 * spinBox_2->value() + 100 * spinBox_3->value() + 1000 * spinBox_4->value()
  2260. + 10000 * spinBox_5->value() + 100000 * spinBox_6->value();
  2261. QString s1;
  2262. s1 = separ_milliers(offset_t) ;
  2263. lineEdit_3->setText(s1);
  2264. effacer_calque(scene2, calque_encadrement2);
  2265. encadrement_signal(offset_t/1200, 25);
  2266. }
  2267.  
  2268.  
  2269. void MainWindow::traitement_signal() // lance les Transformées de Fourier du signal
  2270. {
  2271. if (! wav_ok) {return;}
  2272.  
  2273. garnis_temp_data(offset_t);
  2274. remplir_tableau_echantillons_signal();
  2275.  
  2276. calcul_tableau_W();
  2277. //RAZ_tableau_echantillons();
  2278.  
  2279. if(!rapide) { tracer_signal_a_convertir();}
  2280. bit_reverse_tableau_X();
  2281.  
  2282. calcul_FFT(); // 1 FFT s'effectue sur 1 cluster de 1024 échantillons.
  2283. //Le résultat représente un spectre élémentaire correspondant au cluster, c.a.d à une durée = 1024 x 0.5ms = 512 ms
  2284. // ce résultat est retourné dans le tableau 'Complexe tab_X[2048]'
  2285.  
  2286. //if (radioButton_notes->isChecked()) { tracer_segments_FFT(); }
  2287. //else { tracer_modules_FFT(); }
  2288.  
  2289. tracer_segments_FFT();
  2290. }
  2291.  
  2292.  
  2293.  
  2294. void MainWindow::analyse_tout() // FFT de tous les clusters, c.a.d. sur la totalité du signal
  2295. {
  2296.  
  2297. progressBar_1->setVisible(true);
  2298. /*
  2299.   if (wav_ok == false)
  2300.   {
  2301.   QMessageBox msgBox;
  2302.   msgBox.setText("file 'wav' not open");
  2303.   int ret = msgBox.exec();
  2304.   return;
  2305.   }
  2306. */
  2307. /*
  2308.   if (data_nts_ok == false)
  2309.   {
  2310.   QMessageBox msgBox;
  2311.   msgBox.setText("file 'nts' not open");
  2312.   int ret = msgBox.exec();
  2313.   return;
  2314.   }
  2315. */
  2316.  
  2317. QString s1;
  2318. offset_t=0;
  2319. T_i=0; // variable GLOBALE
  2320.  
  2321. uint16_t i_max = data_size / 1024; // = environ 6000
  2322.  
  2323. if (checkBox_debut_seul->isChecked()) {i_max=2000;} // pour gagner du temps lors de la phase de test
  2324.  
  2325.  
  2326. // data_size est affiché par le visualisateur d'entête
  2327. // et aussi dans le LineEdit en bas de l'onglet FFT
  2328. // data_size = de l'ordre de 7MB, ce qui nous donne un i_max d'environ 6900
  2329.  
  2330. effacer_calque(scene4, calque_TAB_FRQ);
  2331. liste_ENR_FFT.clear();
  2332. //clear_temp_data();
  2333. //garnis_temp_data(0);
  2334.  
  2335. // effacer_calque(scene4, calque_gradu_TAB_FRQ);
  2336. // effacer_calque(scene4, calque_notes_manuelles);
  2337. // effacer_calque(scene4, calque_notes_jouee);
  2338. // effacer_calque(scene4, calque_grille_mesures);
  2339. // effacer_calque(scene4, calque_echelle_temporelle);
  2340.  
  2341. for(uint16_t i=0 ; i<i_max ; i++) // cette boucle sera donc parcourue environ 6000 fois...
  2342. {
  2343. //qDebug() << "i=" << i;
  2344.  
  2345. if ((i % 200) == 0)
  2346. {
  2347. uint16_t pc = 100 * i / i_max;
  2348. pc+=2;
  2349. progressBar_1->setValue(pc);
  2350. // la ligne suivante permet l'affichage de la progression
  2351. QCoreApplication::processEvents(QEventLoop::AllEvents, 1);
  2352. }
  2353. //s1.setNum(i); lineEdit_compteur->setText(s1);
  2354.  
  2355. traitement_signal(); // traitement d'un cluster de 1024 échantillons
  2356. offset_t += 1024; // on passera au cluster de 1024 échantillons suivant c.a.d à un temps + 0.5ms
  2357.  
  2358. }
  2359. progressBar_1->setVisible(false);
  2360.  
  2361. tracer_echelle_temps_TAB_NOTES();
  2362. }
  2363.  
  2364.  
  2365.  
  2366.  
  2367. void MainWindow::on_spinBox_1_valueChanged(int arg1)
  2368. {
  2369. if (arg1==10) { spinBox_1->setValue(0); spinBox_2->stepUp();}
  2370. calcul_ofsset();
  2371. traitement_signal();
  2372. }
  2373.  
  2374.  
  2375. void MainWindow::on_spinBox_2_valueChanged(int arg1)
  2376. {
  2377. if (arg1==-1) {spinBox_2->setValue(9); spinBox_3->stepDown();}
  2378. if (arg1==10) { spinBox_2->setValue(0); spinBox_3->stepUp();}
  2379. calcul_ofsset();
  2380. traitement_signal();
  2381. }
  2382.  
  2383.  
  2384. void MainWindow::on_spinBox_3_valueChanged(int arg1)
  2385. {
  2386. if (arg1==-1) {spinBox_3->setValue(9); spinBox_4->stepDown();}
  2387. if (arg1==10) { spinBox_3->setValue(0); spinBox_4->stepUp();}
  2388. calcul_ofsset();
  2389. traitement_signal();
  2390. }
  2391.  
  2392.  
  2393. void MainWindow::on_spinBox_4_valueChanged(int arg1)
  2394. {
  2395. if (arg1==-1) {spinBox_4->setValue(9); spinBox_5->stepDown();}
  2396. if (arg1==10) { spinBox_4->setValue(0); spinBox_5->stepUp();}
  2397. calcul_ofsset();
  2398. traitement_signal();
  2399. }
  2400.  
  2401.  
  2402. void MainWindow::on_spinBox_5_valueChanged(int arg1)
  2403. {
  2404. if (arg1==-1) {spinBox_5->setValue(9); spinBox_6->stepDown();}
  2405. if (arg1==10) { spinBox_5->setValue(0); spinBox_6->stepUp();}
  2406. calcul_ofsset();
  2407. traitement_signal();
  2408. }
  2409.  
  2410.  
  2411. void MainWindow::on_spinBox_6_valueChanged()
  2412. {
  2413. //if (arg1==-1) {spinBox_6->setValue(9);} //spinBox_6->stepDown();}
  2414. calcul_ofsset();
  2415. traitement_signal();
  2416. }
  2417.  
  2418.  
  2419. void MainWindow::on_doubleSpinBox_1_valueChanged()
  2420. {
  2421. calcul_ofsset();
  2422. traitement_signal();
  2423. }
  2424.  
  2425.  
  2426. void MainWindow::on_pushButton_8_clicked()
  2427. {
  2428. QMessageBox msgBox;
  2429. QString s1;
  2430. s1 = "Le fichier audio à analyser doit être au format unsigned 8 bits PCM";
  2431. s1 += " (généré par exemple avec Audacity)";
  2432. s1 += "\n";
  2433. s1 += "Attention: ce n'est pas le format PCM par défaut, il faut aller dans les sous-menus :";
  2434. s1 += "\n";
  2435. s1 += "Fichier/Exporter/Exporter en WAV / encodage : Unsigned 8-bit PCM";
  2436. s1 += "\n";
  2437. s1 += "Ce n'est pas 'top WiFi', mais largement suffisant pour en faire la FFT et retrouver les notes.";
  2438. s1 += "\n";
  2439. s1 += "Ce qui est le but ici.";
  2440. s1 += "\n";
  2441. s1 += "D'autre part il est vivement conseillé de compresser la dynamique de l'audio (ce qui est simple avec Audacity)";
  2442. s1 += "\n";
  2443. s1 += "ce qui facilite grandement la détection des notes.";
  2444.  
  2445. msgBox.setText(s1);
  2446. msgBox.exec();
  2447. }
  2448.  
  2449.  
  2450. void MainWindow::on_Bt_RAZ_clicked()
  2451. {
  2452. spinBox_1->setValue(0);
  2453. spinBox_2->setValue(0);
  2454. spinBox_3->setValue(0);
  2455. spinBox_4->setValue(0);
  2456. spinBox_5->setValue(0);
  2457. spinBox_6->setValue(0);
  2458. offset_t=0;
  2459. }
  2460.  
  2461.  
  2462. void MainWindow::on_spinBox_7_valueChanged(int arg1)
  2463. {
  2464. // ligne verticale
  2465. effacer_calque(scene1,calque_curseur);
  2466. int x = arg1;
  2467. ligne1 = new QGraphicsLineItem(x, 10, x, 500);
  2468. ligne1->setPen(pen_curseur);
  2469. calque_curseur->addToGroup(ligne1);
  2470.  
  2471. }
  2472.  
  2473.  
  2474.  
  2475.  
  2476. void MainWindow::on_pushButton_2_clicked()
  2477. {
  2478. Timer1->stop();
  2479. nb_tics=0;
  2480. }
  2481.  
  2482.  
  2483. void MainWindow::on_Btn_52_clicked()
  2484. {
  2485. //spinBox_n_midi->setValue(52);
  2486.  
  2487. file_wav.setFileName(nom_fichier_in);
  2488. if (!file_wav.open(QIODevice::ReadOnly))
  2489. {
  2490. wav_ok = false;
  2491. return ;
  2492. }
  2493.  
  2494. wav_ok = true;
  2495.  
  2496. on_Bt_RAZ_clicked();
  2497.  
  2498. binStream1.setDevice(&file_wav);
  2499. calcul_ofsset();
  2500. traitement_signal();
  2501. }
  2502.  
  2503.  
  2504. void MainWindow::on_Btn_94_clicked()
  2505. {
  2506.  
  2507. file_wav.setFileName(nom_fichier_in);
  2508. if (!file_wav.open(QIODevice::ReadOnly))
  2509. {
  2510. wav_ok = false;
  2511. return ;
  2512. }
  2513.  
  2514. wav_ok = true;
  2515.  
  2516. on_Bt_RAZ_clicked();
  2517.  
  2518. binStream1.setDevice(&file_wav);
  2519. calcul_ofsset();
  2520. traitement_signal();
  2521. }
  2522.  
  2523.  
  2524. void MainWindow::on_Bt_efface_clicked()
  2525. {
  2526. QMessageBox msgBox;
  2527. msgBox.setText("Erase Frequences");
  2528. msgBox.setInformativeText("Effacer toutes les FFT ?");
  2529. msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
  2530. msgBox.setDefaultButton(QMessageBox::Cancel);
  2531. int ret = msgBox.exec();
  2532.  
  2533. if (ret == QMessageBox::Ok )
  2534. {
  2535. effacer_calque(scene1,calque_trace_signal1 );
  2536. effacer_calque(scene1,calque_trace_FFT );
  2537. effacer_calque(scene1,calque_lignes_F_zero );
  2538. effacer_calque(scene1,calque_encadrement1 );
  2539. effacer_calque(scene2,calque_encadrement2 );
  2540.  
  2541. effacer_calque(scene4,calque_TAB_FRQ );
  2542. on_Bt_RAZ_clicked();
  2543. T_i=0; // T_i : variable GLOBALE
  2544. }
  2545.  
  2546. }
  2547.  
  2548.  
  2549.  
  2550. void MainWindow::on_pushButton_3_clicked()
  2551. {
  2552.  
  2553. tableWidget_1->setVisible(true);
  2554. Bt_close_entete->setVisible(true);
  2555. }
  2556.  
  2557.  
  2558. void MainWindow::on_Bt_close_entete_clicked()
  2559. {
  2560. tableWidget_1->setVisible(false);
  2561. Bt_close_entete->setVisible(false);
  2562. }
  2563.  
  2564.  
  2565. void MainWindow::on_Bt_close_frame1_clicked()
  2566. {
  2567. frame_1->setVisible(false);
  2568. frame_3->setVisible(true);
  2569. }
  2570.  
  2571.  
  2572. void MainWindow::on_pushButton_4_clicked()
  2573. {
  2574. frame_3->setVisible(false);
  2575. frame_1->setVisible(true);
  2576. }
  2577.  
  2578.  
  2579.  
  2580. void MainWindow::on_pushButton_6_clicked()
  2581. {
  2582. frame_notes->setVisible(false);
  2583. }
  2584.  
  2585.  
  2586.  
  2587. void MainWindow::on_pushButton_5_clicked()
  2588. {
  2589. spinBox_offset->setValue(-2124); // 2154 ; ref -2210 ; +/- 22 pour un décalage de +/-1/4 de ton
  2590. spinBox_echx->setValue(499); // 498
  2591. }
  2592.  
  2593.  
  2594.  
  2595. void MainWindow::save_fichier_FRQ()
  2596. {
  2597. // enregisterment incrémentiel sur le disque (nom de fichier = 1..2...3...)
  2598. ENR_FFT enr_i;
  2599. QString s1;
  2600.  
  2601. long n_max = liste_ENR_FFT.length();
  2602. if (n_max == 0)
  2603. {
  2604. QMessageBox msgBox; msgBox.setText("liste vide !"); msgBox.exec();
  2605. return;
  2606. }
  2607.  
  2608. int numero=0;
  2609. QString s2;
  2610.  
  2611. Timer1->stop();
  2612.  
  2613. bool existe =1;
  2614. while(existe)
  2615. {
  2616. numero++;
  2617. s2.setNum(numero);
  2618. existe = QFile::exists(string_currentDir + "/" + "TAB_FREQ_" + s2 + ".tfrq");
  2619. }
  2620. s2.setNum(numero);
  2621.  
  2622. QFile file1(string_currentDir + "/" + "TAB_FREQ_" + s2 + ".tfrq");
  2623. if (file1.open(QIODevice::WriteOnly | QIODevice::Text))
  2624. {
  2625. QByteArray byteArray1;
  2626. QDataStream stream1(&byteArray1, QIODevice::WriteOnly);
  2627. stream1.setDevice(&file1);
  2628. stream1.setVersion(QDataStream::Qt_6_8);
  2629.  
  2630. // entête 64 octets------------------
  2631. uint8_t d_barre = spinBox_d_barres->value(); // 1 octet
  2632. double dx = doubleSpinBox_dt->value(); // 8 octets
  2633. double x0 = doubleSpinBox_x0->value(); // 8 octets
  2634. stream1 << d_barre << dx << x0 << Ta << Tb << tempo_libre; //1+8+8+1+1 = 19 octets au début du fichier
  2635. for (int i=0; i<(64-20); i++) {stream1 << 'x';} // complète à 64 octets (en prévision d'ajout futurs)
  2636. // ----------------------------------
  2637.  
  2638. for(int n=0; n<n_max; n++)
  2639. {
  2640. enr_i = liste_ENR_FFT[n];
  2641. stream1 << enr_i.amplitude << enr_i.midi << enr_i.num << enr_i.t; // sérialisation des data
  2642. }
  2643. }
  2644. file1.close();
  2645.  
  2646. QMessageBox msgBox;
  2647. s1="Fichier enregistré: " + string_currentDir + "/" + "TAB_FREQ_" + s2 + ".tfrq"; msgBox.setText(s1); msgBox.exec();
  2648. }
  2649.  
  2650.  
  2651. void MainWindow::choix_dossier_de_travail()
  2652. {
  2653.  
  2654. QString actuel = string_currentDir;
  2655. string_currentDir = QFileDialog::getExistingDirectory
  2656. (this, tr("Open Directory"), actuel, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
  2657.  
  2658. if (string_currentDir != "")
  2659. {
  2660. dossier_de_travail_ok = true;
  2661. save_fichier_ini();
  2662. lineEdit_current_dir->setText(string_currentDir);
  2663. }
  2664. else { dossier_de_travail_ok = false; } // -> si on clique sur le bouton 'cancel'
  2665.  
  2666. }
  2667.  
  2668.  
  2669. void MainWindow::on_Bt_choix_current_dir_clicked()
  2670. {
  2671. choix_dossier_de_travail();
  2672. }
  2673.  
  2674.  
  2675. void MainWindow::ajustement_gain()
  2676. {
  2677. double somme;
  2678. double moyenne;
  2679. double R;
  2680. double gain;
  2681. uint8_t octet_n;
  2682. long i, j;
  2683.  
  2684. double pas = 10.0 * pas_echantillonage; // 240
  2685. double n_max = 2000000 / pas;
  2686.  
  2687. i=0;
  2688. j=0;
  2689. somme =0;
  2690. for(long n=0; n<n_max; n++)
  2691. {
  2692. octet_n = temp_data[i]; // canal 1
  2693. if (octet_n !=0)
  2694. {
  2695. R = octet_n - 128;// pour ôter la composante continue (octets non signés)
  2696. if (R<0) {R= -R;}
  2697. somme += R;
  2698. j++; // nb de notes effectivement prises en compte
  2699. }
  2700. i+= pas;
  2701. }
  2702. moyenne = somme / j;
  2703.  
  2704. gain = 20.0/moyenne;
  2705. doubleSpinBox_gain->setValue(gain);
  2706. }
  2707.  
  2708.  
  2709. void MainWindow::load_fichier_FRQ()
  2710. {
  2711. QString filename1;
  2712. QString s1;
  2713.  
  2714. if(dossier_de_travail_ok == false)
  2715. {
  2716. choix_dossier_de_travail();
  2717. if (dossier_de_travail_ok == false)
  2718. {
  2719. QMessageBox msgBox;
  2720. s1="Le dossier de travail n'est pas spécifié";
  2721. msgBox.setText(s1); msgBox.exec();
  2722. return ;
  2723. }
  2724. }
  2725.  
  2726. filename1 = QFileDialog::getOpenFileName(this,
  2727. tr("Ouvrir Fichier tfrq"), string_currentDir + "/", tr("Fichiers tfrq (*.tfrq)"));
  2728.  
  2729.  
  2730. lineEdit_fichier_FREQ->setText(filename1);
  2731.  
  2732. file_dat.setFileName(filename1);
  2733. if (!file_dat.open(QIODevice::ReadOnly))
  2734. {
  2735. data_nts_ok = false;
  2736. return ;
  2737. }
  2738. else
  2739. {
  2740. binStream1.setDevice(&file_dat); // accès à la totalité du fichier dat
  2741. binStream1.setVersion(QDataStream::Qt_6_8);
  2742. data_nts_ok = true;
  2743. liste_ENR_FFT.clear();
  2744. }
  2745.  
  2746. //int z = sizeof(ENR_FFT); // ATTENTION ! ne PAS utiliser cette valeur qui tient en compte l'alignement en RAM
  2747. long n_max = file_dat.bytesAvailable() / 14; // 14 bits par enr; voir la def de 'ENR_FFT' dans le .h
  2748.  
  2749. // lecture entête -----------------
  2750.  
  2751. uint8_t d_barre; // 1 octet
  2752. double dt;
  2753. double x0;
  2754. binStream1 >> d_barre >> dt >> x0 >> Ta >> Tb >> tempo_libre; // 1+8+8+1+1+1 = 20 octets au début du fichiers
  2755. uint8_t xx;
  2756. for (int i=0; i<(64-20); i++) {binStream1 >> xx;} // lit en tout à 64 octets libre pour l'avenir
  2757. //---------------------------------
  2758.  
  2759. doubleSpinBox_dt->setValue(dt);
  2760. doubleSpinBox_x0->setValue(x0);
  2761.  
  2762. ENR_FFT enr_i;
  2763. for(int n=0; n<n_max; n++)
  2764. {
  2765. binStream1 >> enr_i.amplitude >> enr_i.midi >> enr_i.num >> enr_i.t;
  2766. liste_ENR_FFT << enr_i;
  2767. }
  2768. file_dat.close();
  2769.  
  2770. retracer_TAB_FRQ();
  2771. spinBox_d_barres->setValue(d_barre);
  2772. }
  2773.  
  2774.  
  2775. void MainWindow::save_fichier_NOTES()
  2776. {
  2777. // enregisterment incrémentiel sur le disque (nom de fichier = 1..2...3...)
  2778. NOTE note_i;
  2779. QString s1;
  2780.  
  2781. double x0 = doubleSpinBox_x0->value();
  2782. double dx = doubleSpinBox_dt->value();
  2783.  
  2784. uint16_t n_max = liste_NOTES.length();
  2785. if (n_max == 0)
  2786. {
  2787. QMessageBox msgBox; msgBox.setText("liste vide !"); msgBox.exec();
  2788. return;
  2789. }
  2790.  
  2791. int numero=0;
  2792. QString s2;
  2793.  
  2794. Timer1->stop();
  2795.  
  2796. bool existe =1;
  2797. while(existe)
  2798. {
  2799. numero++;
  2800. s2.setNum(numero);
  2801. existe = QFile::exists(string_currentDir + "/" + "NOTES_" + s2 + ".nts");
  2802. }
  2803. s2.setNum(numero);
  2804.  
  2805. QFile file1(string_currentDir + "/" + "NOTES_" + s2 + ".nts");
  2806. if (file1.open(QIODevice::WriteOnly | QIODevice::Text))
  2807. {
  2808. QByteArray byteArray1;
  2809. QDataStream stream1(&byteArray1, QIODevice::WriteOnly);
  2810. stream1.setDevice(&file1);
  2811. stream1.setVersion(QDataStream::Qt_6_8);
  2812.  
  2813. // entête 64 octets --------------
  2814. double vts = doubleSpinBox_vitesse->value(); // 8 octets
  2815. stream1 << x0 << dx << vts << Ta << Tb << tempo_libre; //8+8+8+1+1+1 =27 octets au début du fichier
  2816.  
  2817. for (int i=0; i<(64-27); i++) {stream1 << 'x';} // complète à 64 octets
  2818. //--------------------------------
  2819. for(uint16_t n=0; n<n_max; n++)
  2820. {
  2821. note_i = liste_NOTES[n];
  2822. // sérialisation des data
  2823. stream1 << note_i.numero << note_i.midi << note_i.n_barre << note_i.n_temps << note_i.duree;
  2824. }
  2825. }
  2826. file1.close();
  2827.  
  2828. QMessageBox msgBox;
  2829. s1="Fichier enregistré: " + string_currentDir + "/" + "NOTES_" + s2 + ".nts"; msgBox.setText(s1); msgBox.exec();
  2830. }
  2831.  
  2832.  
  2833.  
  2834. void MainWindow::load_fichier_NOTES()
  2835. {
  2836. QString filename1;
  2837. QString s1;
  2838.  
  2839. if(dossier_de_travail_ok == false)
  2840. {
  2841. choix_dossier_de_travail();
  2842. if (dossier_de_travail_ok == false)
  2843. {
  2844. QMessageBox msgBox;
  2845. s1="Le dossier de travail n'est pas spécifié";
  2846. msgBox.setText(s1); msgBox.exec();
  2847. return ;
  2848. }
  2849. }
  2850.  
  2851. filename1 = QFileDialog::getOpenFileName(this,
  2852. tr("Ouvrir Fichier nts"), string_currentDir+"/", tr("Fichiers nts (*.nts)"));
  2853.  
  2854.  
  2855. lineEdit_fichier->setText(filename1);
  2856.  
  2857. file_dat.setFileName(filename1);
  2858. if (!file_dat.open(QIODevice::ReadOnly))
  2859. {
  2860. data_nts_ok = false;
  2861. return ;
  2862. }
  2863. else
  2864. {
  2865. binStream1.setDevice(&file_dat); // accès à la totalité du fichier dat
  2866. binStream1.setVersion(QDataStream::Qt_6_8);
  2867. data_nts_ok = true;
  2868. }
  2869.  
  2870. //int z = sizeof(NOTE); // ATTENTION ! ne PAS utiliser cette valeur qui tient en compte l'alignement en RAM
  2871. uint16_t n_max = file_dat.bytesAvailable() / 7; // 7 bits par enr; voir la def de 'NOTE' dans le .h
  2872.  
  2873. double x0;
  2874. double dx;
  2875. double vts;
  2876.  
  2877. // lecture entête -----------------
  2878. binStream1 >> x0 >> dx >> vts >> Ta >> Tb >> tempo_libre; // 8+8+8+1+1+1=27 octets au début du fichiers
  2879. uint8_t xx;
  2880. for (int i=0; i<(64-27); i++) {binStream1 >> xx;} // lit en tout à 64 octets
  2881. //---------------------------------
  2882.  
  2883. doubleSpinBox_x0->setValue(x0);
  2884. doubleSpinBox_dt->setValue(dx);
  2885. doubleSpinBox_vitesse->setValue(vts);
  2886.  
  2887. NOTE note_i;
  2888. for(int n=0; n<n_max; n++)
  2889. {
  2890. binStream1 >> note_i.numero >> note_i.midi >> note_i.n_barre >> note_i.n_temps >> note_i.duree;
  2891. note_i.midi += spinBox_transposition->value();
  2892. liste_NOTES << note_i;
  2893. }
  2894.  
  2895. file_dat.close();
  2896.  
  2897. on_Bt_grille_T_clicked();
  2898.  
  2899. Bt_jouer_tout->setStyleSheet("background-color: rgb(0, 255, 0);"); // vert
  2900.  
  2901. }
  2902.  
  2903.  
  2904. void decalle_notes(int d)
  2905. {
  2906. int nb;
  2907. uint16_t i_max = liste_NOTES.length();
  2908. for( int i =0; i<i_max; i++)
  2909. {
  2910. nb = liste_NOTES[i].n_barre;
  2911. nb += d;
  2912. liste_NOTES[i].n_barre = nb;
  2913. }
  2914.  
  2915. }
  2916.  
  2917.  
  2918. void MainWindow::on_Bt_save_clicked()
  2919. {
  2920. save_fichier_FRQ();
  2921. }
  2922.  
  2923.  
  2924. void MainWindow::on_Bt_load_FREQ_clicked()
  2925. {
  2926. load_fichier_FRQ();
  2927. on_Bt_grille_T_clicked();
  2928. tracer_echelle_temps_TAB_NOTES();
  2929. }
  2930.  
  2931. void MainWindow::on_checkBox_rapide_stateChanged(int arg1)
  2932. {
  2933. rapide=arg1;
  2934. init_TAB_FRQ();
  2935. }
  2936.  
  2937.  
  2938.  
  2939. void MainWindow::on_doubleSpinBox_seuil_valueChanged(double arg1)
  2940. {
  2941. seuil = arg1;
  2942. effacer_calque(scene4, calque_TAB_FRQ);
  2943. retracer_TAB_FRQ();
  2944. }
  2945.  
  2946.  
  2947. void MainWindow::on_checkBox_norm_clicked()
  2948. {
  2949. if (checkBox_norm->isChecked())
  2950. {
  2951. textEdit_ETAT->setText("Mode Normalisé : \n Les couleurs représentent l'amplitude du signal");
  2952. }
  2953. else
  2954. {
  2955. textEdit_ETAT->setText(
  2956. "Mode Fréquences : \n"
  2957. "Les couleurs représentent la valeur (fréquence) de la note (Do, RE, MI...) \n"
  2958. "La taille de chaque tiret représente (abusivement sur l'axe fréquenciel) l'amplitude du signal \n"
  2959. "cette taille ne signifie donc pas un étalement en fréquence du signal, c'est juste une astuce de représentation."
  2960. );
  2961. }
  2962. effacer_calque(scene4, calque_TAB_FRQ);
  2963. retracer_TAB_FRQ();
  2964. }
  2965.  
  2966.  
  2967.  
  2968. void MainWindow::surbrille_note(uint16_t n)
  2969. {
  2970. double x_note;
  2971.  
  2972. NOTE note_i;
  2973. uint16_t n_max = liste_NOTES.length();
  2974. if ( n>= n_max) {return;}
  2975.  
  2976. note_i = liste_NOTES[n];
  2977. x_note = calcul_x_barre_T(note_i.n_barre);
  2978.  
  2979. if (checkBox_scrolling->isEnabled())
  2980. {
  2981. double scroll_x = graphicsView4->horizontalScrollBar()->value();
  2982. double diff_x = (2.0*x_note - scroll_x);
  2983. if (diff_x > (memo_scroll_x + 1800))
  2984. {
  2985. graphicsView4->horizontalScrollBar()->setValue(diff_x - 2000);
  2986. memo_scroll_x = diff_x;
  2987. }
  2988. }
  2989.  
  2990. int y;
  2991.  
  2992. int midi_i;
  2993. QString blanc = "#FFFFFF";
  2994.  
  2995. midi_i = note_i.midi;
  2996. y = calcul_y_note(midi_i) ;
  2997.  
  2998. //if (effacer) {effacer_calque(scene4, calque_notes_jouee);}
  2999.  
  3000. QPen pen1;
  3001. pen1.setColor(blanc);
  3002. pen1.setWidth(2);
  3003. ellipse1 = new QGraphicsEllipseItem(x_note-15, y-15, 30, 30) ;
  3004. ellipse1->setPen(pen1);
  3005. //ellipse1->setBrush(blanc);
  3006.  
  3007. calque_notes_jouee->addToGroup(ellipse1);
  3008.  
  3009.  
  3010. }
  3011.  
  3012.  
  3013. void MainWindow::complete_case(int row, int column, QString s1, QString c1, QString c2, bool bold1, QTableWidget *QTI)
  3014. {
  3015. QTableWidgetItem *Item1 = new QTableWidgetItem();
  3016. Item1->QTableWidgetItem::setForeground(QColor(c1));
  3017. Item1->QTableWidgetItem::setBackground(QColor(c2));
  3018.  
  3019. QFont font1;
  3020. font1.setBold(bold1);
  3021. Item1->setFont(font1);
  3022. Item1->setTextAlignment(Qt::AlignCenter);
  3023.  
  3024. Item1->setText(s1);
  3025. QTI->setItem(row,column,Item1);
  3026. }
  3027.  
  3028.  
  3029. void MainWindow::affiche_liste_notes() // en TEXTE dans le petit tableau
  3030. {
  3031. NOTE note_i;
  3032. QString s1;
  3033.  
  3034. tableWidget_lst_notes->clear();
  3035. tableWidget_lst_notes->setRowCount(0);
  3036. init_TAB_lst_notes();
  3037.  
  3038. uint16_t n_max = liste_NOTES.length();
  3039. if (n_max == 0) {return;}
  3040.  
  3041. for(int n=0; n<n_max; n++) //n_max
  3042. {
  3043. note_i=liste_NOTES[n];
  3044.  
  3045. int numero = note_i.numero;
  3046. int midi = note_i.midi;
  3047. int n_barre =note_i.n_barre;
  3048. int n_temps =note_i.n_temps;
  3049. int duree = note_i.duree;
  3050.  
  3051. int num_ligne = tableWidget_lst_notes->rowCount();
  3052. tableWidget_lst_notes->setRowCount(num_ligne+1); // ajout une ligne au tableau
  3053.  
  3054. s1.setNum(numero);
  3055. complete_case(n, 0, s1, "#000000", "#FFFFFF", false, tableWidget_lst_notes);
  3056.  
  3057. s1.setNum(midi);
  3058. int h1 = calcul_hauteur_note(midi);
  3059.  
  3060.  
  3061. QString couleur1 = liste_couleurs[h1];
  3062. complete_case(n, 1, s1, "#000000", couleur1, true, tableWidget_lst_notes);
  3063.  
  3064. s1 = nom_note(midi);
  3065. complete_case(n, 2, s1, "#000000", "#FFFFFF", false, tableWidget_lst_notes);
  3066.  
  3067. s1.setNum(n_barre);
  3068. complete_case(n, 3, s1, "#000000", "#FFFFFF", false, tableWidget_lst_notes);
  3069.  
  3070. s1.setNum(n_temps); // ok
  3071. complete_case(n, 4, s1, "#000000", "#FFFFFF", true, tableWidget_lst_notes);
  3072.  
  3073. s1.setNum(duree); //ok
  3074. complete_case(n, 5, s1, "#000000", "#FFFFFF", true, tableWidget_lst_notes);
  3075.  
  3076. calcul_histogramme_durees();
  3077. }
  3078. }
  3079.  
  3080.  
  3081. void MainWindow::affiche_histogramme_durees()
  3082. {
  3083.  
  3084. effacer_calque(scene5, calque_histogramme);
  3085. GraphicsTextItem = new QGraphicsTextItem("Histogramme des durées");
  3086. GraphicsTextItem->setFont(QFont("Arial", 8));
  3087. GraphicsTextItem->setDefaultTextColor("#FFFFFF");
  3088. GraphicsTextItem->setPos(220, -60);
  3089. calque_histogramme->addToGroup(GraphicsTextItem);
  3090.  
  3091. int x, y, dx, dy;
  3092.  
  3093. x=10;
  3094. y=0;
  3095. dx=4;
  3096. dy;
  3097. QString s1;
  3098.  
  3099. for(int n=0; n<=20; n++)
  3100. {
  3101. s1.setNum(n);
  3102. GraphicsTextItem = new QGraphicsTextItem(s1);
  3103. GraphicsTextItem->setFont(QFont("Arial", 8));
  3104. GraphicsTextItem->setDefaultTextColor("#FFFFFF");
  3105. GraphicsTextItem->setPos(15*n, 0);
  3106. calque_histogramme->addToGroup(GraphicsTextItem);
  3107. }
  3108.  
  3109. for(int i=0; i<100; i++)
  3110. {
  3111. x = 5+ 15*i;
  3112. dy = -5 * table_histogramme_durees[i];
  3113. rect1 = new QGraphicsRectItem(x, y, dx, dy);
  3114. QBrush br1;
  3115. br1.setStyle(Qt::SolidPattern);
  3116. br1.setColor("#FFFF00");
  3117. rect1->setBrush(br1);
  3118.  
  3119. calque_histogramme->addToGroup(rect1);
  3120. }
  3121.  
  3122. }
  3123.  
  3124.  
  3125.  
  3126. void MainWindow::calcul_histogramme_durees()
  3127. {
  3128. uint16_t n_max = liste_NOTES.length();
  3129. if (n_max == 0) {return;}
  3130.  
  3131. for(int i=0; i<100; i++)
  3132. {
  3133. table_histogramme_durees[i]=0; // RAZ
  3134. }
  3135.  
  3136. for(int n=0; n<n_max; n++) //n_max
  3137. {
  3138. int duree = liste_NOTES[n].duree;
  3139. table_histogramme_durees[duree] = table_histogramme_durees[duree] +1;
  3140. }
  3141. }
  3142.  
  3143.  
  3144. void MainWindow::calcul_durees_notes()
  3145. {
  3146. int db;
  3147. uint16_t i_max = liste_NOTES.length();
  3148. if (i_max == 0) {return;}
  3149.  
  3150. int n_barre_1;
  3151. int n_barre_2;
  3152.  
  3153. for(int n=0; n<i_max-1; n++) // 'i_max-1' because plus bas on indexe liste_NOTES[n+1] !!!
  3154. {
  3155. n_barre_1 = liste_NOTES[n].n_barre;
  3156. n_barre_2 = liste_NOTES[n+1].n_barre;
  3157. db = n_barre_2 - n_barre_1;
  3158.  
  3159. if (db<0) {db=0;}
  3160. if (db>48){db=48;}
  3161. liste_NOTES[n].duree = db;
  3162. }
  3163. }
  3164.  
  3165.  
  3166.  
  3167. void MainWindow::trace_liste_notes() //dessine des cercles colorés (couleur midi) sur la grille
  3168. {
  3169. QString s1;
  3170. int db, midi_i;
  3171. double x, dx, dbx, y; //, dy;
  3172. int numero;
  3173. uint16_t n_max = liste_NOTES.length();
  3174. int n_barre_1;
  3175. //int n_barre_2;
  3176. for(int n=0; n<n_max; n++)
  3177. {
  3178. n_barre_1 = liste_NOTES[n].n_barre;
  3179.  
  3180. db = liste_NOTES[n].duree;
  3181. dx = doubleSpinBox_dt->value();
  3182. dbx = (double)db * dx * zoom_x / 2.0 ;
  3183.  
  3184. //if (visu_duree_notes == false) {dbx = 20;} // dessinera 1 note toute ronde
  3185.  
  3186. // x = x0 + (double)n_barre_1 * doubleSpinBox_dt->value()/2.0 * zoom_x;
  3187.  
  3188. x = calcul_x_barre_T(n_barre_1);
  3189.  
  3190. midi_i = liste_NOTES[n].midi;
  3191.  
  3192. if ((midi_i >=40) && (midi_i <=83) )
  3193. {
  3194. y = calcul_y_note(midi_i);
  3195.  
  3196. int h1 = calcul_hauteur_note(midi_i);
  3197.  
  3198. QString couleur1 = liste_couleurs[h1];
  3199. QString blanc = "#FFFFFF";
  3200. /*
  3201. // rectangle (queue de la note représentant sa durée)
  3202.   rect1 = new QGraphicsRectItem(x, y-2, dbx, 4);
  3203.   QBrush br1;
  3204.   br1.setStyle(Qt::SolidPattern); // pour des cercle plein (= disques colorés)
  3205.   br1.setColor(couleur1);
  3206.   rect1->setBrush(br1);
  3207.   //rect1->setPen(pen_i);
  3208.   calque_notes_manuelles->addToGroup(rect1);
  3209. */
  3210. ellipse1 = new QGraphicsEllipseItem(x-10, y-10, 20, 20);
  3211. ellipse1->setPen(QColor(blanc));
  3212. ellipse1->setBrush(QColor(couleur1));
  3213. calque_notes_manuelles->addToGroup(ellipse1);
  3214.  
  3215.  
  3216. // inscrit le numéro de la note au centre de la note
  3217. numero = liste_NOTES[n].numero;
  3218. s1.setNum(numero);