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();