From 9f19b0a0af22f75e05e42deba5f1d91a62032b61 Mon Sep 17 00:00:00 2001 From: Samuel Nicholas Date: Wed, 25 Oct 2017 18:45:20 +1030 Subject: [PATCH 1/6] preliminary work for SoundTouch BPM detection removed dependency on aubio --- src/QUMainWindow.cpp | 112 ++++++++++++++++++++------------------ src/QUMainWindow.h | 9 ++- src/UltraStar-Creator.pro | 2 + 3 files changed, 68 insertions(+), 55 deletions(-) diff --git a/src/QUMainWindow.cpp b/src/QUMainWindow.cpp index fa5f483..afd4bbd 100644 --- a/src/QUMainWindow.cpp +++ b/src/QUMainWindow.cpp @@ -28,7 +28,10 @@ #include #include -#include +#include +#include +#include +#include QUMainWindow::QUMainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::QUMainWindow) { _player = new QMediaPlayer; @@ -979,61 +982,21 @@ void QUMainWindow::handleMP3() { ui->comboBox_Video->setCurrentIndex(1); } - // fixme/todo: determine BPM from MP3 with some open source library - uint_t samplerate = 0; - uint_t win_size = 2048; // window size - uint_t hop_size = win_size / 2; - uint_t n_frames = 0; - uint_t read = 0; - smpl_t max_confidence = 0.; - smpl_t current_confidence = 0.; - smpl_t current_bpm = 0; - smpl_t bpm_with_max_confidence = 0; - - aubio_source_t *source = new_aubio_source(fileInfo_MP3->absoluteFilePath().toStdString().c_str(), samplerate, hop_size); - if(source) { - samplerate = aubio_source_get_samplerate(source); - - // create some vectors - fvec_t *in = new_fvec(hop_size); // input audio buffer - fvec_t *out = new_fvec(1); // output position - // create tempo object - aubio_tempo_t *o = new_aubio_tempo("default", win_size, hop_size, samplerate); - do { - // put some fresh data in input vector - aubio_source_do(source, in, &read); - // execute tempo - aubio_tempo_do(o, in, out); - // do something with the beats - if (out->data[0] != 0) { - current_confidence = aubio_tempo_get_confidence(o); - current_bpm = aubio_tempo_get_bpm(o); - if(current_confidence > max_confidence) { - max_confidence = current_confidence; - bpm_with_max_confidence = current_bpm; - } - //qDebug() << QString("beat at %1 s, frame %2, %3 bpm with confidence %4\n").arg(QString::number(aubio_tempo_get_last_s(o), 'f', 3)).arg(QString::number(aubio_tempo_get_last(o))).arg(QString::number(current_bpm, 'f', 2)).arg(QString::number(current_confidence, 'f', 2)); - } - n_frames += read; - } while (read == hop_size); - //qDebug() << QString("read %1 s, %2 frames at %3 Hz (%4 blocks) from %5\n").arg(QString::number(n_frames * 1. / samplerate, 'f', 2)).arg(QString::number(n_frames)).arg(QString::number(samplerate)).arg(QString::number(n_frames / hop_size)).arg(QString(fileInfo_MP3->absoluteFilePath().toStdString().c_str())); - //qDebug() << QString("%1 bpm with confidence %2\n").arg(QString::number(bpm_with_max_confidence)).arg(QString::number(max_confidence)); - - // clean up memory - del_aubio_tempo(o); - del_fvec(in); - del_fvec(out); - del_aubio_source(source); - } else { - qDebug() << "Error opening audio file to determine BPM."; - aubio_cleanup(); - } + //FIXME:Enetheru determine BPM from MP3 with some open source library + desiredFormat.setChannelCount(2); + desiredFormat.setCodec("audio/x-raw"); + desiredFormat.setSampleType(QAudioFormat::Float); + desiredFormat.setSampleRate(48000); + desiredFormat.setSampleSize(16); - BPMFromMP3 = bpm_with_max_confidence; - //qDebug() << "BPMFromMP3 = " << BPMFromMP3; - BPM = BPMFromMP3*4; // quarter beats per minute - //qDebug() << "BPM = " << BPM; + decoder = new QAudioDecoder(this); + decoder->setAudioFormat(desiredFormat); + decoder->setSourceFilename( fileInfo_MP3->filePath() ); + connect(decoder, SIGNAL( bufferReady() ), this, SLOT( on_bufferReady() )); + decoder->start(); + + // Now wait for bufferReady() signal and call decoder->read() ui->doubleSpinBox_BPM->setValue(BPM); ui->label_BPMSet->setPixmap(QPixmap(":/icons/path_ok.png")); @@ -2455,3 +2418,44 @@ void QUMainWindow::montyPrev() { void QUMainWindow::montyNext() { monty->answer(ui->montyArea->montyLbl, ui->montyArea->helpLbl); } + +void +QUMainWindow::on_bufferReady() +{ + using namespace soundtouch; + + float bpmValue; + BPMDetect bpm( desiredFormat.channelCount(), desiredFormat.sampleRate() ); + + // Processing chunk size (size chosen to be divisible by 2, 4, 6, 8, 10, 12, 14, 16 channels ...) + //FIXME make sure this SAMPLETYPE and QAudioFormat match + + // detect bpm rate + qDebug() << "Detecting BPM rate..."; + + // Process the 'inFile' in small blocks, repeat until whole file has + // been processed + while( decoder->bufferAvailable() ) + { + // Read sample data from input file + auto buf = decoder->read(); + + // Enter the new samples to the bpm analyzer class + bpm.inputSamples( static_cast( buf.data() ), desiredFormat.sampleSize() ); + } + + // Now the whole song data has been analyzed. Read the resulting bpm. + bpmValue = bpm.getBpm(); + qDebug() << "Done!\n"; + + + if (bpmValue > 0) + { + qDebug( "Detected BPM rate %.1f\n\n", bpmValue ); + } + else + { + qDebug("Couldn't detect BPM rate.\n\n"); + return; + } +} diff --git a/src/QUMainWindow.h b/src/QUMainWindow.h index 9ef8039..99e65cb 100644 --- a/src/QUMainWindow.h +++ b/src/QUMainWindow.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include class QURibbonBar; @@ -22,7 +24,7 @@ class QUMainWindow: public QMainWindow, private Ui::QUMainWindow { Q_OBJECT public: - QUMainWindow(QWidget *parent = 0); + QUMainWindow(QWidget *parent = nullptr); protected: virtual void closeEvent(QCloseEvent *event); @@ -101,6 +103,9 @@ private slots: void on_comboBox_Background_currentIndexChanged(const QString &background); void on_comboBox_Video_currentIndexChanged(const QString &video); + //FIXME:Enetheru currently testing audio decoder things + void on_bufferReady(); + private: QURibbonBar *_menu; Ui::QUMainWindow *ui; @@ -151,6 +156,8 @@ private slots: QMap timeLineMap; void updateOutputLyrics(); QMediaPlayer* _player; + QAudioFormat desiredFormat; + QAudioDecoder *decoder; }; #endif // QCMAINWINDOW_H diff --git a/src/UltraStar-Creator.pro b/src/UltraStar-Creator.pro index cf42906..149877c 100644 --- a/src/UltraStar-Creator.pro +++ b/src/UltraStar-Creator.pro @@ -102,6 +102,8 @@ unix:!macx { CONFIG += link_pkgconfig PKGCONFIG += taglib + LIBS += -lSoundTouch + QMAKE_LFLAGS += '-Wl,-rpath,\'\$$ORIGIN/lib\'' } From b4492c0c443dbb3da79e1e2c1b08b8aa5592bf9c Mon Sep 17 00:00:00 2001 From: Samuel Nicholas Date: Thu, 26 Oct 2017 13:10:17 +1030 Subject: [PATCH 2/6] created BPMDetect derived from QAudioDecoder a very light shim over the top of QAudioDecoder interfacing it with SoundTouch::BPMDetect class --- src/QUMainWindow.cpp | 67 +++++++-------------------------------- src/QUMainWindow.h | 7 ++-- src/UltraStar-Creator.pro | 6 ++-- src/bpmdetect.cpp | 54 +++++++++++++++++++++++++++++++ src/bpmdetect.h | 26 +++++++++++++++ 5 files changed, 97 insertions(+), 63 deletions(-) create mode 100644 src/bpmdetect.cpp create mode 100644 src/bpmdetect.h diff --git a/src/QUMainWindow.cpp b/src/QUMainWindow.cpp index afd4bbd..c9ff069 100644 --- a/src/QUMainWindow.cpp +++ b/src/QUMainWindow.cpp @@ -29,9 +29,7 @@ #include #include -#include -#include -#include + QUMainWindow::QUMainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::QUMainWindow) { _player = new QMediaPlayer; @@ -983,20 +981,18 @@ void QUMainWindow::handleMP3() { } //FIXME:Enetheru determine BPM from MP3 with some open source library - desiredFormat.setChannelCount(2); - desiredFormat.setCodec("audio/x-raw"); - desiredFormat.setSampleType(QAudioFormat::Float); - desiredFormat.setSampleRate(48000); - desiredFormat.setSampleSize(16); - - decoder = new QAudioDecoder(this); - decoder->setAudioFormat(desiredFormat); - decoder->setSourceFilename( fileInfo_MP3->filePath() ); + QAudioFormat format; + format.setChannelCount( 2 ); + format.setCodec( "audio/x-raw" ); + format.setSampleType( QAudioFormat::Float ); + format.setSampleRate( 48000 ); + format.setSampleSize( 32 ); - connect(decoder, SIGNAL( bufferReady() ), this, SLOT( on_bufferReady() )); - decoder->start(); + bpm = new BPMDetect; + bpm->setAudioFormat( format ); + bpm->setSourceFilename( fileInfo_MP3->filePath() ); + bpm->start(); - // Now wait for bufferReady() signal and call decoder->read() ui->doubleSpinBox_BPM->setValue(BPM); ui->label_BPMSet->setPixmap(QPixmap(":/icons/path_ok.png")); @@ -2418,44 +2414,3 @@ void QUMainWindow::montyPrev() { void QUMainWindow::montyNext() { monty->answer(ui->montyArea->montyLbl, ui->montyArea->helpLbl); } - -void -QUMainWindow::on_bufferReady() -{ - using namespace soundtouch; - - float bpmValue; - BPMDetect bpm( desiredFormat.channelCount(), desiredFormat.sampleRate() ); - - // Processing chunk size (size chosen to be divisible by 2, 4, 6, 8, 10, 12, 14, 16 channels ...) - //FIXME make sure this SAMPLETYPE and QAudioFormat match - - // detect bpm rate - qDebug() << "Detecting BPM rate..."; - - // Process the 'inFile' in small blocks, repeat until whole file has - // been processed - while( decoder->bufferAvailable() ) - { - // Read sample data from input file - auto buf = decoder->read(); - - // Enter the new samples to the bpm analyzer class - bpm.inputSamples( static_cast( buf.data() ), desiredFormat.sampleSize() ); - } - - // Now the whole song data has been analyzed. Read the resulting bpm. - bpmValue = bpm.getBpm(); - qDebug() << "Done!\n"; - - - if (bpmValue > 0) - { - qDebug( "Detected BPM rate %.1f\n\n", bpmValue ); - } - else - { - qDebug("Couldn't detect BPM rate.\n\n"); - return; - } -} diff --git a/src/QUMainWindow.h b/src/QUMainWindow.h index 99e65cb..c37e995 100644 --- a/src/QUMainWindow.h +++ b/src/QUMainWindow.h @@ -5,6 +5,7 @@ #include "taglib.h" #include "fileref.h" +#include "bpmdetect.h" #include #include @@ -103,9 +104,6 @@ private slots: void on_comboBox_Background_currentIndexChanged(const QString &background); void on_comboBox_Video_currentIndexChanged(const QString &video); - //FIXME:Enetheru currently testing audio decoder things - void on_bufferReady(); - private: QURibbonBar *_menu; Ui::QUMainWindow *ui; @@ -156,8 +154,7 @@ private slots: QMap timeLineMap; void updateOutputLyrics(); QMediaPlayer* _player; - QAudioFormat desiredFormat; - QAudioDecoder *decoder; + BPMDetect *bpm; }; #endif // QCMAINWINDOW_H diff --git a/src/UltraStar-Creator.pro b/src/UltraStar-Creator.pro index 149877c..88c0036 100644 --- a/src/UltraStar-Creator.pro +++ b/src/UltraStar-Creator.pro @@ -37,7 +37,8 @@ HEADERS += main.h \ song/QUSongInterface.h \ song/QUSongFile.h \ song/QUSongDatabase.h \ - QUStringSupport.h + QUStringSupport.h \ + bpmdetect.h SOURCES += main.cpp \ QUMainWindow.cpp \ @@ -52,7 +53,8 @@ SOURCES += main.cpp \ song/QUSongLine.cpp \ song/QUSongFile.cpp \ song/QUSongDatabase.cpp \ - QUStringSupport.cpp + QUStringSupport.cpp \ + bpmdetect.cpp FORMS += QUMainWindow.ui \ QUAboutDialog.ui \ diff --git a/src/bpmdetect.cpp b/src/bpmdetect.cpp new file mode 100644 index 0000000..b313ee2 --- /dev/null +++ b/src/bpmdetect.cpp @@ -0,0 +1,54 @@ +#include "bpmdetect.h" + +#include +#include +#include + +BPMDetect::BPMDetect( QObject *parent ) : + QAudioDecoder( parent ) +{ + connect( this, SIGNAL( bufferReady() ), this, SLOT( on_bufferReady() ) ); + connect( this, SIGNAL( finished() ), this, SLOT( on_finished() ) ); +} + +BPMDetect::~BPMDetect() +{ + delete bpm; +} + +void +BPMDetect::on_bufferReady() +{ + while( bufferAvailable() ) + { + auto buf = read(); + if( buf.format().sampleType() != QAudioFormat::Float ){ + qDebug() << "invalid sampleType"; + return; + } + if( bpm == nullptr ){ + qDebug() << "creating new BPMDetect using:"; + qDebug() << buf.format(); + bpm = new soundtouch::BPMDetect( + buf.format().channelCount(), + buf.format().sampleRate() ); + } + bpm->inputSamples( + static_cast< float *>( buf.data() ), + buf.sampleCount() / buf.format().channelCount() + ); + } +} + +void +BPMDetect::on_finished() +{ + qDebug() << "on_finished"; + auto bpmValue = bpm->getBpm(); + //delete bpm; + + if (bpmValue > 0) qDebug() << "Detected BPM rate: " << bpmValue; + else qDebug("Couldn't detect BPM rate."); + + return; +} diff --git a/src/bpmdetect.h b/src/bpmdetect.h new file mode 100644 index 0000000..db93284 --- /dev/null +++ b/src/bpmdetect.h @@ -0,0 +1,26 @@ +#ifndef BPMDETECT_H +#define BPMDETECT_H + +#include + +#include + + +class BPMDetect : public QAudioDecoder +{ + Q_OBJECT + +public: + explicit BPMDetect( QObject *parent = nullptr); + ~BPMDetect(); + +public slots: + void on_bufferReady(); + void on_finished(); + +private://state + soundtouch::BPMDetect *bpm = nullptr; + +}; + +#endif // BPMDETECT_H From 7a672f049309a9cad8c9b3231136af80f196c90f Mon Sep 17 00:00:00 2001 From: Samuel Nicholas Date: Thu, 26 Oct 2017 18:13:52 +1030 Subject: [PATCH 3/6] updated travis.yml for CI testing --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 7ab4f1b..027cbc2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ matrix: - qtbase5-dev-tools - qt5-qmake - libtag1-dev + - libsoundtouch-dev - os: osx osx_image: xcode8.2 compiler: clang @@ -28,6 +29,7 @@ install: brew install qt5; brew link --force qt5; brew install taglib; + brew install sound-touch fi before_script: From 239fb5bc7c90afb7075b448f8ecedc17332aefef Mon Sep 17 00:00:00 2001 From: Samuel Nicholas Date: Thu, 26 Oct 2017 18:14:40 +1030 Subject: [PATCH 4/6] created bpmDetected signal and connected it to the ui --- src/QUMainWindow.cpp | 12 +++++++----- src/QUMainWindow.h | 2 +- src/bpmdetect.cpp | 9 ++++++--- src/bpmdetect.h | 4 +++- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/QUMainWindow.cpp b/src/QUMainWindow.cpp index c9ff069..267adaa 100644 --- a/src/QUMainWindow.cpp +++ b/src/QUMainWindow.cpp @@ -32,9 +32,12 @@ QUMainWindow::QUMainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::QUMainWindow) { - _player = new QMediaPlayer; + _player = new QMediaPlayer; ui->setupUi(this); + connect( &bpm, SIGNAL( bpmDetected( double ) ), + ui->doubleSpinBox_BPM , SLOT( setValue( double ) ) ); + initWindow(); initRibbonBar(); @@ -988,10 +991,9 @@ void QUMainWindow::handleMP3() { format.setSampleRate( 48000 ); format.setSampleSize( 32 ); - bpm = new BPMDetect; - bpm->setAudioFormat( format ); - bpm->setSourceFilename( fileInfo_MP3->filePath() ); - bpm->start(); + bpm.setAudioFormat( format ); + bpm.setSourceFilename( fileInfo_MP3->filePath() ); + bpm.start(); ui->doubleSpinBox_BPM->setValue(BPM); diff --git a/src/QUMainWindow.h b/src/QUMainWindow.h index c37e995..8daea95 100644 --- a/src/QUMainWindow.h +++ b/src/QUMainWindow.h @@ -154,7 +154,7 @@ private slots: QMap timeLineMap; void updateOutputLyrics(); QMediaPlayer* _player; - BPMDetect *bpm; + BPMDetect bpm; }; #endif // QCMAINWINDOW_H diff --git a/src/bpmdetect.cpp b/src/bpmdetect.cpp index b313ee2..6b1086f 100644 --- a/src/bpmdetect.cpp +++ b/src/bpmdetect.cpp @@ -13,7 +13,7 @@ BPMDetect::BPMDetect( QObject *parent ) : BPMDetect::~BPMDetect() { - delete bpm; + if( bpm )delete bpm; } void @@ -45,9 +45,12 @@ BPMDetect::on_finished() { qDebug() << "on_finished"; auto bpmValue = bpm->getBpm(); - //delete bpm; + delete bpm; bpm = nullptr; - if (bpmValue > 0) qDebug() << "Detected BPM rate: " << bpmValue; + if (bpmValue > 0){ + qDebug() << "Detected BPM rate: " << bpmValue; + emit bpmDetected( bpmValue ); + } else qDebug("Couldn't detect BPM rate."); return; diff --git a/src/bpmdetect.h b/src/bpmdetect.h index db93284..5ee3ecd 100644 --- a/src/bpmdetect.h +++ b/src/bpmdetect.h @@ -14,13 +14,15 @@ class BPMDetect : public QAudioDecoder explicit BPMDetect( QObject *parent = nullptr); ~BPMDetect(); +signals: + void bpmDetected( double value ); + public slots: void on_bufferReady(); void on_finished(); private://state soundtouch::BPMDetect *bpm = nullptr; - }; #endif // BPMDETECT_H From 90be571e5d30e09dd3b5d740574ceaa4e39d8a4e Mon Sep 17 00:00:00 2001 From: Samuel Nicholas Date: Thu, 26 Oct 2017 18:24:54 +1030 Subject: [PATCH 5/6] missed semicolon in travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 027cbc2..8dab169 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ install: brew install qt5; brew link --force qt5; brew install taglib; - brew install sound-touch + brew install sound-touch; fi before_script: From af30f6180b6a233ece89994bd2ae5a8500bd1014 Mon Sep 17 00:00:00 2001 From: Samuel Nicholas Date: Thu, 26 Oct 2017 18:55:37 +1030 Subject: [PATCH 6/6] trying to fix travis.yml includes of soundtouch dont work on OSX qtmultimedia5-dev needed --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8dab169..fd6eb45 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ matrix: packages: - qt5-default - qtbase5-dev-tools + - qtmultimedia5-dev - qt5-qmake - libtag1-dev - libsoundtouch-dev @@ -30,6 +31,7 @@ install: brew link --force qt5; brew install taglib; brew install sound-touch; + brew link sound-touch; fi before_script: