diff --git a/tests/nootini/CMakeLists-libsndfile.txt b/tests/nootini/CMakeLists-libsndfile.txt new file mode 100755 index 0000000000000000000000000000000000000000..9c01324b95bef93197b5a75ea980840f12f0d294 --- /dev/null +++ b/tests/nootini/CMakeLists-libsndfile.txt @@ -0,0 +1,93 @@ +######################################################################## +# nootini - Nootka research tool for pitch detection +######################################################################## + +cmake_minimum_required(VERSION 2.8.12) + +project(nootini) + +if (CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") +endif (CMAKE_BUILD_TYPE STREQUAL "Release") + +find_package(Qt5Widgets REQUIRED) +find_package(Qt5Svg REQUIRED) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) + +if (SNDFILE_LIBRARIES AND SNDFILE_INCLUDE_DIRS) + set(LIBSNDFILE_FOUND TRUE) + else(SNDFILE_LIBRARIES AND SNDFILE_INCLUDE_DIRS) + find_path(LIBSNDFILE_INCLUDE_DIR + NAMES + sndfile.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_BINARY_DIR}/../libsndfile + ) + + find_library(LIBSNDFILE_LIBRARY + NAMES + sndfile + PATHS + /usr/lib + /usr/lib64 + /usr/local/lib + /opt/local/lib + /sw/lib + ${CMAKE_BINARY_DIR}/../libsndfile + ${CMAKE_CURRENT_SOURCE_DIR} + ) + + set(SNDFILE_INCLUDE_DIRS ${LIBSNDFILE_INCLUDE_DIR}) + set(SNDFILE_LIBRARIES ${LIBSNDFILE_LIBRARY}) + + if (SNDFILE_INCLUDE_DIRS AND SNDFILE_LIBRARIES) + set(LIBSNDFILE_FOUND TRUE) + endif (SNDFILE_INCLUDE_DIRS AND SNDFILE_LIBRARIES) + + if (LIBSNDFILE_FOUND) + message(STATUS "libsndfile was Found in:\n${SNDFILE_LIBRARIES}\nheaders are in: ${SNDFILE_INCLUDE_DIRS}") + ELSE(LIBSNDFILE_FOUND) + message(FATAL_ERROR "Could not find libsndfile\nTry call cmake with args:\n-DSNDFILE_LIBRARIES=/path_to/libSoundTouch.la(dll) -DSNDFILE_INCLUDE_DIRS=/path_to_dir_with/sndfile.h\nor put those files in build dir or source dir") + ENDIF(LIBSNDFILE_FOUND) + + ENDIF(SNDFILE_LIBRARIES AND SNDFILE_INCLUDE_DIRS) + +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../../src/) + +include_directories( ../../src/libs/core ../../src/libs/sound ../../src/plugins/charts ${SNDFILE_INCLUDE_DIRS} ) + +if (${WIN32}) + include_directories(${FFTW3_INCLUDE_DIRS}) +endif(${WIN32}) + +set(NOOTINI_SRC + main.cpp + nootiniwindow.cpp + nchart.cpp + naudioloader.cpp + nootinisettings.cpp + + ../../src/plugins/charts/tchart.cpp + ../../src/plugins/charts/tabstractaxis.cpp + ../../src/plugins/charts/txaxis.cpp + ../../src/plugins/charts/tyaxis.cpp + ../../src/plugins/charts/ttiphandler.cpp + ../../src//plugins/charts/tgroupedqaunit.cpp + ../../src/plugins/settings/tvolumeslider.cpp + +) + +add_executable(nootini WIN32 ${NOOTINI_SRC}) +target_link_libraries(nootini + NootkaCore + NootkaSound + Qt5::Widgets + Qt5::Svg + ${SNDFILE_LIBRARIES} +) + diff --git a/tests/nootini/CMakeLists.txt b/tests/nootini/CMakeLists.txt index eb997bc798d333bc4c235f34b355727aeec7012c..ac76eda7c8ad192cf2d2284df84678a27f6f04c3 100755 --- a/tests/nootini/CMakeLists.txt +++ b/tests/nootini/CMakeLists.txt @@ -6,6 +6,10 @@ cmake_minimum_required(VERSION 2.8.12) project(nootini) +if (CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") +endif (CMAKE_BUILD_TYPE STREQUAL "Release") + find_package(Qt5Widgets REQUIRED) find_package(Qt5Svg REQUIRED) set(CMAKE_INCLUDE_CURRENT_DIR ON) diff --git a/tests/nootini/naudioloader-libsndfile.cpp b/tests/nootini/naudioloader-libsndfile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2aa997635eb04001f72c56398c33269013159158 --- /dev/null +++ b/tests/nootini/naudioloader-libsndfile.cpp @@ -0,0 +1,147 @@ +/*************************************************************************** + * Copyright (C) 2015-2016 by Tomasz Bojczuk * + * tomaszbojczuk@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#include "naudioloader.h" +#include <tpitchfinder.h> +#include <tinitcorelib.h> +#include <taudioparams.h> +#include <QtCore/qendian.h> +#include <QtCore/qdebug.h> +#include <QtWidgets/qmessagebox.h> + + +/*static*/ +int NaudioLoader::m_range = 1; + +NaudioLoader::NaudioLoader(QObject* parent) : + QObject(parent), + m_pf(0), + m_sndFile(0) +{ +} + + +NaudioLoader::~NaudioLoader() { + delete m_pf; +} + + +bool NaudioLoader::setAudioFile(const QString& fileName) { + if (m_pf) { + delete m_pf; + m_pf = 0; + } + m_pf = new TpitchFinder; + m_pf->setOffLine(true); + bool ok = true; + if (!fileName.isEmpty()) { + m_fileName = fileName; + SF_INFO sndInfo; + m_sndFile = sf_open(fileName.toStdString().data(), SFM_READ, &sndInfo); + if (sndInfo.format & SF_FORMAT_WAV && sndInfo.format & SF_FORMAT_PCM_16) { + qDebug() << "supported WAV format detected" << "frames" << sndInfo.frames << "channels" << sndInfo.channels + << "sample rate" << sndInfo.samplerate << "sections" << sndInfo.sections << "seekable" << sndInfo.seekable; + ok = true; + m_channelsNr = sndInfo.channels; + m_samplesCount = sndInfo.frames; + m_pf->setSampleRate(sndInfo.samplerate, m_range); + m_totalChunks = m_samplesCount / m_pf->aGl()->framesPerChunk + 1; + qDebug() << "chunks to go:" << m_totalChunks; + } else + QMessageBox::warning(0, QLatin1String("Nootini"), tr("Only WAV with 16 bit per sample are supported.")); + } + return ok; +} + + +void NaudioLoader::startLoading() { + connect(m_pf, SIGNAL(noteFinished(TnoteStruct*)), this, SLOT(forwardNoteFinished(TnoteStruct*)), Qt::UniqueConnection); + connect(m_pf, SIGNAL(volume(float)), this, SLOT(chunkProcessed()), Qt::UniqueConnection); + sf_seek(m_sndFile, 0, SEEK_SET); + performThread(); +} + + +void NaudioLoader::fillTartiniParams(TartiniParams* tp) { + if (m_pf) { + m_pf->aGl()->threshold = tp->threshold; + m_pf->aGl()->doingHarmonicAnalysis = tp->doingHarmonicAnalysis; + m_pf->aGl()->equalLoudness = Tcore::gl()->A->equalLoudness; + m_pf->aGl()->dBFloor = tp->dBFloor; + m_pf->aGl()->doingAutoNoiseFloor = tp->doingAutoNoiseFloor; + m_pf->setSkipStillerVal(Tcore::gl()->A->skipStillerVal / 100.0); + m_pf->setMinimalDuration(Tcore::gl()->A->minDuration); + m_pf->setSplitByVolChange(Tcore::gl()->A->minSplitVol > 0.0); + m_pf->setSplitVolume(Tcore::gl()->A->minSplitVol / 100.0); + m_pf->stop(true); + m_totalChunks = m_samplesCount / m_pf->aGl()->framesPerChunk + 1; + } +} + +//################################################################################################# +//################### PROTECTED ############################################ +//################################################################################################# + +void NaudioLoader::chunkProcessed() { + if (m_pf->currentChunk() > m_totalChunks - 1) { + emit processingFinished(); + sf_close(m_sndFile); + } else { + emit chunkReady(); + performThread(); + } +} + + +void NaudioLoader::threadFinished() { + +} + + +void NaudioLoader::performThread() { + qint16 chL; + qint16 frame[2]; + qint16* outBuffer = new qint16[m_pf->aGl()->framesPerChunk]; + for (int i = 0; i < m_pf->aGl()->framesPerChunk; ++i) { + if (m_samplesCount > m_pf->currentChunk() * m_pf->aGl()->framesPerChunk + i) { + if (m_channelsNr == 2) { + sf_readf_short(m_sndFile, frame, 1); + chL = frame[1]; +// chR = frame[0]; +// chL = ((qint32)chL + (qint32)chR) / 2; // mix channels to mono, FIXME: but it doesn't work always + } else { + sf_read_short(m_sndFile, &chL, 1); + } + outBuffer[i] = chL; + } else + outBuffer[i] = 0; + } + m_pf->copyToBufferOffline(outBuffer); + delete[] outBuffer; +} + + +void NaudioLoader::forwardNoteFinished(TnoteStruct* note) { + emit noteFinished(note); +} + + + + + + diff --git a/tests/nootini/naudioloader-libsndfile.h b/tests/nootini/naudioloader-libsndfile.h new file mode 100644 index 0000000000000000000000000000000000000000..2e8b69a40edf01c0d826ca255f44cdf80f6c2ac8 --- /dev/null +++ b/tests/nootini/naudioloader-libsndfile.h @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (C) 2015-2016 by Tomasz Bojczuk * + * tomaszbojczuk@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifndef NAUDIOLOADER_H +#define NAUDIOLOADER_H + +#include <QtCore/qobject.h> +#include <sndfile.h> + + +class TnoteStruct; +class TartiniParams; +class TpitchFinder; + + +/** + * Manages loading audio data from file in separate thread. + * Grabbed data are push to @class TpitchFinder (in offline mode) + * After all, @p processingFinished() signal is emitted. + */ +class NaudioLoader : public QObject +{ + Q_OBJECT +public: + explicit NaudioLoader(QObject* parent = 0); + virtual ~NaudioLoader(); + + /** Sets audio file to be loaded with @p startLoading(). + * Only when file is set a valid @p TpitchFinder is returned by @p finder() */ + bool setAudioFile(const QString& fileName); + void startLoading(); /** Starts loading audio data and push it into TpitchFinder. */ + + TpitchFinder* finder() { return m_pf; } /** Pointer to @class TpitchFinder processing data of current file or 0 if no file. */ + + int totalChunks() { return m_totalChunks; } + + void fillTartiniParams(TartiniParams* tp); /** Applies Tartini parameters to existing @class TpitchFinder instance */ + + float volume() { return m_volume; } /** The strength value of amplitude in current chunk. */ + quint32 sampleRate() { return m_sampleRate; } + + static int pitchRange() {return m_range;} + static void setPitchRange(int range) { m_range = qBound<int>(0, range, 2); } + + QString fileName() { return m_fileName; } + +signals: + void processingFinished(); /** Emitted when all data were processed */ + void noteFinished(TnoteStruct*); /** Forwarded from @class TpitchFinder */ + void chunkReady(); + +protected: + void performThread(); + +protected slots: + void forwardNoteFinished(TnoteStruct *note); + void chunkProcessed(); + void threadFinished(); + + +private: + TpitchFinder *m_pf; + int m_totalChunks, m_samplesCount; + quint16 m_channelsNr; + quint32 m_sampleRate; + SNDFILE *m_sndFile; + float m_volume; + static int m_range; + QString m_fileName; +}; + +#endif // NAUDIOLOADER_H diff --git a/tests/nootini/naudioloader.cpp b/tests/nootini/naudioloader.cpp index e520a21b5d12bd807f542fdedd53e626bac92883..00993b17070f15cf1410e79370722ccf47ff79c9 100644 --- a/tests/nootini/naudioloader.cpp +++ b/tests/nootini/naudioloader.cpp @@ -31,15 +31,10 @@ NaudioLoader::NaudioLoader(QObject* parent) : QObject(parent), m_pf(0) { -// moveToThread(&m_thread); -// connect(&m_thread, &QThread::started, this, &NaudioLoader::performThread, Qt::DirectConnection); -// connect(&m_thread, &QThread::finished, this, &NaudioLoader::threadFinished, Qt::DirectConnection); } NaudioLoader::~NaudioLoader() { - m_thread.quit(); - m_thread.wait(); delete m_pf; } @@ -131,12 +126,11 @@ bool NaudioLoader::setAudioFile(const QString& fileName) { void NaudioLoader::startLoading() { if (m_audioFile.isOpen()) { - connect(m_pf, SIGNAL(noteFinished(TnoteStruct*)), this, SLOT(forwardNoteFinished(TnoteStruct*)), Qt::DirectConnection); - connect(m_pf, SIGNAL(volume(float)), this, SLOT(chunkProcessed()), Qt::DirectConnection); + connect(m_pf, SIGNAL(noteFinished(TnoteStruct*)), this, SLOT(forwardNoteFinished(TnoteStruct*)), Qt::UniqueConnection); + connect(m_pf, SIGNAL(volume(float)), this, SLOT(chunkProcessed()), Qt::UniqueConnection); performThread(); } else qDebug() << "Wrong file" << m_audioFile.fileName(); -// m_thread.start(QThread::HighestPriority); } @@ -167,7 +161,6 @@ void NaudioLoader::chunkProcessed() { } else { emit chunkReady(); performThread(); -// m_thread.start(); } } @@ -186,8 +179,8 @@ void NaudioLoader::performThread() { chL = qFromBigEndian<qint16>(chL); if (m_channelsNr == 2) { m_in >> chR; - chR = qFromBigEndian<qint16>(chR); - chL = ((qint32)chL + (qint32)chR) / 2; // mix channels to mono +// chR = qFromBigEndian<qint16>(chR); +// chL = ((qint32)chL + (qint32)chR) / 2; // mix channels to mono FIXME: mixing doesn't work in all cases } buffer[i] = chL; } else diff --git a/tests/nootini/nchart.cpp b/tests/nootini/nchart.cpp index acf6c4a9ef8e0f1c984f17d72d1c47be91c4692e..074c90c2d944d385efc455831177c78d96d34fac 100644 --- a/tests/nootini/nchart.cpp +++ b/tests/nootini/nchart.cpp @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2015 by Tomasz Bojczuk * + * Copyright (C) 2015-2016 by Tomasz Bojczuk * * tomaszbojczuk@gmail.com * * * * This program is free software; you can redistribute it and/or modify * @@ -115,10 +115,10 @@ void Nchart::setAudioLoader(NaudioLoader* loader) { m_loader = loader; m_pitchF = loader->finder(); m_chunkNr = -1; - connect(m_loader, &NaudioLoader::chunkReady, this, &Nchart::drawChunk, Qt::DirectConnection); - connect(m_loader, &NaudioLoader::processingFinished, this, &Nchart::allDataLoaded, Qt::DirectConnection); - connect(m_pitchF, &TpitchFinder::noteStarted, this, &Nchart::noteStartedSlot, Qt::DirectConnection); - connect(m_loader, &NaudioLoader::noteFinished, this, &Nchart::copyChunk, Qt::DirectConnection); + connect(m_loader, &NaudioLoader::chunkReady, this, &Nchart::drawChunk, Qt::UniqueConnection); + connect(m_loader, &NaudioLoader::processingFinished, this, &Nchart::allDataLoaded, Qt::UniqueConnection); + connect(m_pitchF, &TpitchFinder::noteStarted, this, &Nchart::noteStartedSlot, Qt::UniqueConnection); + connect(m_loader, &NaudioLoader::noteFinished, this, &Nchart::copyChunk, Qt::UniqueConnection); m_progresItem->show(); m_prevNoteIndex = 0; m_prevChunk = 0; @@ -433,4 +433,12 @@ void Nchart::adjustHeight() { } +void Nchart::scrollContentsBy(int dx, int dy) { + QGraphicsView::scrollContentsBy(dx, dy); + if (m_progresItem->isVisible()) { + m_progresItem->setPos(mapToScene(0, 0)); + } +} + + diff --git a/tests/nootini/nchart.h b/tests/nootini/nchart.h index 4fda5980d9b989504afd6414705ba6c9255ab283..c757f5aa1bb2e8348ac2f65540fb1171cc304834 100644 --- a/tests/nootini/nchart.h +++ b/tests/nootini/nchart.h @@ -75,6 +75,7 @@ protected: int xMap(int xx) { return m_xLine->line().x1() + (xx + 1) * xSc; } qreal yMap(qreal yy) { return yAxis->y() + yAxis->mapValue(yy); } + virtual void scrollContentsBy(int dx, int dy) Q_DECL_OVERRIDE; protected slots: void adjustHeight();