From cba90bc47f96e0c0b3808c40b475cc9c4522d298 Mon Sep 17 00:00:00 2001
From: SeeLook <945374+SeeLook@users.noreply.github.com>
Date: Fri, 1 Apr 2016 11:14:20 +0200
Subject: [PATCH] Fixed glitches of playing sound: emitting signal when decoded
 ogg data is ready and starting playing then instead of blocking main thread
 with sleep and waiting for decoding. Code cleaned.

---
 TODO                           |   4 +-
 changes                        |   9 +-
 src/libs/sound/toggscale.cpp   | 158 +++++++++++++++++---------------
 src/libs/sound/toggscale.h     | 163 +++++++++++++++++----------------
 src/libs/sound/tqtaudioout.cpp |  88 +++++++++---------
 src/libs/sound/tqtaudioout.h   |  37 ++++----
 6 files changed, 234 insertions(+), 225 deletions(-)

diff --git a/TODO b/TODO
index c239555f0..f853406cb 100644
--- a/TODO
+++ b/TODO
@@ -22,19 +22,17 @@ ANDROID:
         - hide 'clear score' one when undesired (single mode, exam with playing):
           innerWidget->flyActions()->append(bar->scoreDeleteAll()); // TODO: idea is brilliant but needs more efforts
     - resetting options closes Nootka but does not launch it again
-    - played sound is sometimes distorted (when note starts playing)
     - no level/exam icon in file manager, not all file managers support opening Nootka files
 
 - when no guitar preview - staff lines are too big
 - keep scroll bar of score always off - it appears when switching single/multi
-- Help QTextBrowser has QScroller but it works only with two-touch move, but TlevelPreview works good
 - HOME button would be switching available menus
 - handle BACK button to destroy visible menu and/or prevent to close exam
 - duration of animation of note correction was decreased to 200ms to work properly under Android,
   but it would be better to signal when it is finished and perform some routines in main score after that
 ============================================================================
 - TexamExecutor has many, many un-wrapped strings, many ""
-- Use int and enumerations between plugins and main window instead of text messages - especially updater
+- Use int and enumerations between plugins and main window instead of text messages (level plugin to do)
 
 To consideration:
 - additional settings options:
diff --git a/changes b/changes
index 6b2d14398..4503fb834 100644
--- a/changes
+++ b/changes
@@ -1,10 +1,13 @@
-1.3.0 alpha1
-     - removed roundness of widget/tips
-     - prepared code for further features
+1.2A.5 testing
+   ANDROID
+     - fixed fluency of playing, no more glitches
     Under the hood
      - reorganized layout of libraries, score is in separate lib
      - exam execution moved to plugin
 
+1.2.5
+     Clef menu visibility fixes for Qt 5.6 compatibility
+
 1.2A.4 beta2
     ANDROID
      - exam file can be sent directly through Android native services
diff --git a/src/libs/sound/toggscale.cpp b/src/libs/sound/toggscale.cpp
index 9ebf0cced..831bd0006 100644
--- a/src/libs/sound/toggscale.cpp
+++ b/src/libs/sound/toggscale.cpp
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2013-2015 by Tomasz Bojczuk                             *
+ *   Copyright (C) 2013-2016 by Tomasz Bojczuk                             *
  *   seelook@gmail.com                                                     *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -20,11 +20,11 @@
 #include "toggscale.h"
 #include <music/tinstrument.h>
 #include <tpath.h>
-#include <QFile>
-#include <QDataStream>
-#include <QDebug>
-#include <QThread>
-#include <QFileInfo>
+#include <QtCore/qfile.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qfileinfo.h>
 #include <stdlib.h>
 #include <unistd.h>
 
@@ -98,12 +98,12 @@ ToggScale::ToggScale() :
   m_oggConnected(false), m_touchConnected(false),
   m_instrument(-1)
 {
-	m_touch = new soundtouch::SoundTouch();
-	m_touch->setChannels(1);
+  m_touch = new soundtouch::SoundTouch();
+  m_touch->setChannels(1);
 #if defined (Q_OS_UNIX) // increase minimal audio data must to be processed when system works with PulseAudio
-	QFileInfo pulseBin("/usr/bin/pulseaudio");
-	if (pulseBin.exists()) // it is necessary both for Nootka with native PA and PA in ALSA bridge mode
-		minDataAmount = 15000;
+  QFileInfo pulseBin("/usr/bin/pulseaudio");
+  if (pulseBin.exists()) // it is necessary both for Nootka with native PA and PA in ALSA bridge mode
+    minDataAmount = 15000;
 #endif
   moveToThread(m_thread);
   connect(m_thread, SIGNAL(started()), this, SLOT(decodeOgg()));
@@ -121,7 +121,7 @@ void ToggScale::deleteData() {
   stopDecoding();
   if (m_thread->isRunning()) {
     m_thread->terminate();
-    m_thread->quit();  
+    m_thread->quit();
   }
   if (m_oggInMemory) {
     delete m_oggInMemory;
@@ -135,33 +135,36 @@ void ToggScale::deleteData() {
 
 
 qint16 ToggScale::getSample(int offset) {
-    return m_pcmBuffer[offset];
+  return m_pcmBuffer[offset];
 }
 
 
 
 void ToggScale::setNote(int noteNr) {
-  if (noteNr == m_prevNote)
+  if (noteNr == m_prevNote) {
+    emit oggReady();
     return;
-	int baseNote = noteNr;
+  }
+
+  int baseNote = noteNr;
   m_isReady = false;
-	if (noteNr < m_firstNote || noteNr > m_lastNote) { // prepare SoundTouch
-		if (noteNr < m_firstNote) {
-				baseNote = m_firstNote;
-				m_innerOffset = noteNr - m_firstNote;
-		} else if (noteNr > m_lastNote) {
-				baseNote = m_lastNote;
-				m_innerOffset = noteNr - m_lastNote;
-		}		
-	} else
-			m_innerOffset = 0.0;
-	adjustSoundTouch();
+  if (noteNr < m_firstNote || noteNr > m_lastNote) { // prepare SoundTouch
+    if (noteNr < m_firstNote) {
+        baseNote = m_firstNote;
+        m_innerOffset = noteNr - m_firstNote;
+    } else if (noteNr > m_lastNote) {
+        baseNote = m_lastNote;
+        m_innerOffset = noteNr - m_lastNote;
+    }
+  } else
+      m_innerOffset = 0.0;
+  adjustSoundTouch();
   int fasterOffset = 1000;
-	if (baseNote - m_firstNote == 0)
-			fasterOffset = 0;
+  if (baseNote - m_firstNote == 0)
+      fasterOffset = 0;
   stopDecoding();
   m_prevNote = noteNr;
-	int ret = ov_pcm_seek(&m_ogg, (baseNote - m_firstNote) * 44100 * 2 - fasterOffset);
+  int ret = ov_pcm_seek(&m_ogg, (baseNote - m_firstNote) * 44100 * 2 - fasterOffset);
   m_thread->start();
 }
 
@@ -183,60 +186,60 @@ void ToggScale::setPitchOffset(float pitchOff) {
 
 
 bool ToggScale::loadAudioData(int instrument) {
-	QString fileName;
-	if (instrument != m_instrument) {
-		switch ((Einstrument)instrument) {
-			case e_classicalGuitar:
-				fileName = Tpath::sound("classical-guitar");
-				m_firstNote = -11; m_lastNote = 41;
-				break;
-			case e_electricGuitar:
-				fileName = Tpath::sound("electric-guitar");
-				m_firstNote = -11; m_lastNote = 41;
-				break;
-			case e_bassGuitar:
-				fileName = Tpath::sound("bass-guitar");
-				m_firstNote = -24; m_lastNote = 21;
-				break;
-			default:
-				return false;
-		}
-	} else
-			return false;
+  QString fileName;
+  if (instrument != m_instrument) {
+    switch ((Einstrument)instrument) {
+      case e_classicalGuitar:
+        fileName = Tpath::sound("classical-guitar");
+        m_firstNote = -11; m_lastNote = 41;
+        break;
+      case e_electricGuitar:
+        fileName = Tpath::sound("electric-guitar");
+        m_firstNote = -11; m_lastNote = 41;
+        break;
+      case e_bassGuitar:
+        fileName = Tpath::sound("bass-guitar");
+        m_firstNote = -24; m_lastNote = 21;
+        break;
+      default:
+        return false;
+    }
+  } else
+      return false;
 
   QFile oggFile(fileName);
   if (!oggFile.exists())
       return false;
-  
+
   oggFile.open(QIODevice::ReadOnly);
   QDataStream oggStream(&oggFile);
-	if (m_oggInMemory)
-			delete m_oggInMemory;
+  if (m_oggInMemory)
+      delete m_oggInMemory;
   m_oggInMemory = new qint8[oggFile.size()];
   oggStream.readRawData((char*)m_oggInMemory, oggFile.size());
-  
+
   ov_callbacks myCallBacks;
   m_oggWrap.curPtr = m_oggInMemory;
   m_oggWrap.filePtr = m_oggInMemory;
   m_oggWrap.fileSize = oggFile.size();
-  
+
   oggFile.close();
-	
-	if (m_pcmBuffer)
-			delete m_pcmBuffer;
+
+  if (m_pcmBuffer)
+      delete m_pcmBuffer;
   m_pcmBuffer = new qint16[2 * m_sampleRate];
   myCallBacks.read_func = readOggStatic;
   myCallBacks.seek_func = seekOggStatic;
   myCallBacks.close_func = closeOggStatic;
   myCallBacks.tell_func = tellOggStatic;
-  
+
   int ret = ov_open_callbacks((void*)&m_oggWrap, &m_ogg, NULL, 0, myCallBacks);
-  
+
   if (ret < 0) {
     qDebug() << "cant open ogg stream";
     return false;
   }
-  
+
 //   vorbis_info *oggInfo = ov_info(&m_ogg, -1);
 //   qDebug() << oggInfo->rate << oggInfo->channels << (bool)ov_seekable(&m_ogg);
 //   char **ptr=ov_comment(&m_ogg, -1)->user_comments;
@@ -244,9 +247,9 @@ bool ToggScale::loadAudioData(int instrument) {
 //     while(*ptr){
 //       fprintf(stderr,"%s\n",*ptr);
 //       ++ptr;
-//     }  
-  
-  return true;  
+//     }
+
+  return true;
 }
 
 
@@ -276,8 +279,10 @@ void ToggScale::decodeOgg() {
   while (m_doDecode && loops < 500 && pos < maxSize) {
     read = ov_read(&m_ogg, (char*)m_pcmBuffer + pos, maxSize - pos, 0, 2, 1, &bitStream);
     pos += read;
-    if (pos > minDataAmount) // amount of data needed by single loop of rtAudio outCallBack
+    if (pos > minDataAmount && !m_isReady) { // amount of data needed by single loop of rtAudio outCallBack
       m_isReady = true;
+      emit oggReady();
+    }
     loops++;
   }
   m_isDecoding = false;
@@ -287,17 +292,16 @@ void ToggScale::decodeOgg() {
 
 
 void ToggScale::decodeAndResample() {
-  int bitStream;
   m_isDecoding = true;
   int maxSize = 44100 * 2 - 8192; // two sec. of audio minus some silence on the end
   long int tmpPos = 0, tmpRead = 0;
   uint pos = 0, read = 0;
   int samplesReady = 0;
-  
+
   float **oggChannels;
   float *left ;
   float *tmpTouch = new float[8192];
-  
+
   while (m_doDecode && pos < maxSize) {
     /// 1. Grab audio data from ogg
     if (tmpPos < 172000) { // almost 2 sec. of a note
@@ -310,13 +314,15 @@ void ToggScale::decodeAndResample() {
     }
     samplesReady = m_touch->numSamples();
     if (samplesReady > 0) { /// 3. Get resampled/offsetted data from SoundTouch
-      read = m_touch->receiveSamples((SAMPLETYPE*)tmpTouch, samplesReady);      
+      read = m_touch->receiveSamples((SAMPLETYPE*)tmpTouch, samplesReady);
       for (int i = 0; i < read; i++) /// 4. Convert samples to 16bit integer
           *(m_pcmBuffer + pos + i) = qint16(*(tmpTouch + i) * 32768);
       pos += read;
     }
-    if (pos > minDataAmount) // below this value SoundTouch is not able to prepare data
+    if (pos > minDataAmount && !m_isReady) { // below this value SoundTouch is not able to prepare data
       m_isReady = true;
+      emit oggReady();
+    }
   }
   m_isDecoding = false;
 //   qDebug() << "decodeAndResample finished" << pos;
@@ -342,15 +348,15 @@ void ToggScale::adjustSoundTouch() {
         connect(m_thread, SIGNAL(started()), this, SLOT(decodeAndResample()));
     m_touchConnected = true;
     if (m_oggConnected)
-				disconnect(m_thread, SIGNAL(started()), this, SLOT(decodeOgg()));
+        disconnect(m_thread, SIGNAL(started()), this, SLOT(decodeOgg()));
     m_oggConnected = false;
   } else {
-			if (!m_oggConnected)
-					connect(m_thread, SIGNAL(started()), this, SLOT(decodeOgg()));
-			m_oggConnected = true;
-			if (m_touchConnected)
-					disconnect(m_thread, SIGNAL(started()), this, SLOT(decodeAndResample()));
-			m_touchConnected = false;
+      if (!m_oggConnected)
+          connect(m_thread, SIGNAL(started()), this, SLOT(decodeOgg()));
+      m_oggConnected = true;
+      if (m_touchConnected)
+          disconnect(m_thread, SIGNAL(started()), this, SLOT(decodeAndResample()));
+      m_touchConnected = false;
   }
 }
 
diff --git a/src/libs/sound/toggscale.h b/src/libs/sound/toggscale.h
index f8d70b613..c19aea4df 100644
--- a/src/libs/sound/toggscale.h
+++ b/src/libs/sound/toggscale.h
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2013-2015 by Tomasz Bojczuk                             *
+ *   Copyright (C) 2013-2016 by Tomasz Bojczuk                             *
  *   seelook@gmail.com                                                     *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -37,99 +37,102 @@
 using namespace soundtouch;
 
 
-/** 
+/**
 * @class ToggScale manages audio data (musical scale) taken from ogg file.
 * It keeps it in m_oggInMemory array and decode it when setNote is called.
-* Decompressed data are available through getSample() method. 
-* Data is decompressed in separate thread and some SLEEP calls are performed 
+* Decompressed data are available through getSample() method.
+* Data is decompressed in separate thread and some SLEEP calls are performed
 * if data is not ready.
-* 
-* @p setSampleRate() and @p setPitchOffset() control appropirate parameters of note.
-* 
+*
+* @p setSampleRate() and @p setPitchOffset() control appropriate parameters of note.
+*
 * To get sample @p setNote has to be called first.
 * It starts decoding thread which prepares first portion of data.
 * Data is ready only when @p isReady() returns true.
-* Preparing process takes around 1-10 ms (depends on CPU) 
+* Preparing process takes around 1-10 ms (depends on CPU)
 */
 class NOOTKASOUND_EXPORT ToggScale : public QObject
 {
   Q_OBJECT
-  
+
 public:
-  
-    ToggScale(); 
-    virtual ~ToggScale();
-    
-        /** Loads ogg file with scale of given instrument to RAM.
-				 * If everything is OK, returns true.
-				 * Also it is setting low and hi notes of a scale.
-				 * Notes beyond scale are generated with SoundTouch. */
-    bool loadAudioData(int instrument);
-        /** Unloads audio data from buffer. */
-    void deleteData();
-    
-    
-        /** To read ogg data from RAM */
-    struct SoggFile {
-        qint8* curPtr;
-        qint8* filePtr;
-        size_t fileSize;
-    };
-    
-        /** Prepares m_pcmBuffer:
-				 * - determines is pitch offset necessary
-         * - seek ogg 
-         * - starts decoding (re-sampling). 
-         * - stops previous decoding if performed. */
-    void setNote(int noteNr);
-    qint16 getSample(int offset);
-    unsigned int sampleRate() { return m_sampleRate; }
-    
-        /** TRUE when appropirate data amount in a buffer is ready. */
-    bool isReady() { return m_isReady; }
-    
-    void setSampleRate(unsigned int rate);
-		
-        /** Sets decimal offset of a pitch -0.99 to +0.99 */
-    void setPitchOffset(float pitchOff);
-    
-    
+
+  ToggScale();
+  virtual ~ToggScale();
+
+      /** Loads ogg file with scale of given instrument to RAM.
+        * If everything is OK, returns true.
+        * Also it is setting low and hi notes of a scale.
+        * Notes beyond scale are generated with SoundTouch. */
+  bool loadAudioData(int instrument);
+      /** Unloads audio data from buffer. */
+  void deleteData();
+
+
+      /** To read ogg data from RAM */
+  struct SoggFile {
+      qint8* curPtr;
+      qint8* filePtr;
+      size_t fileSize;
+  };
+
+      /** Prepares m_pcmBuffer:
+        * - determines is pitch offset necessary
+        * - seek ogg
+        * - starts decoding (re-sampling).
+        * - stops previous decoding if performed. */
+  void setNote(int noteNr);
+  qint16 getSample(int offset);
+  unsigned int sampleRate() { return m_sampleRate; }
+
+      /** TRUE when appropriate data amount in a buffer is ready. */
+  bool isReady() { return m_isReady; }
+
+  void setSampleRate(unsigned int rate);
+
+      /** Sets decimal offset of a pitch -0.99 to +0.99 */
+  void setPitchOffset(float pitchOff);
+
+signals:
+  void oggReady(); /**< Emitted when appropriate amount of decoded data is ready */
+
+
 protected slots:
-        /** Preforms decoding. Usually is invoked by m_thread.start() 
-         * called from setNote() method. */
-    void decodeOgg();
-        /** Decoding and re-sampling or/and pitch shifting */
-    void decodeAndResample();
-        /** Checks is decoding performed and stops it
-         * by setting m_doDecode=false and waiting for stop. */
-    void stopDecoding();
-    
+      /** Preforms decoding. Usually is invoked by m_thread.start()
+        * called from setNote() method. */
+  void decodeOgg();
+      /** Decoding and re-sampling or/and pitch shifting */
+  void decodeAndResample();
+      /** Checks is decoding performed and stops it
+        * by setting m_doDecode=false and waiting for stop. */
+  void stopDecoding();
+
 private:
-      /** Methods needed by vorbisfile library. */
-    static size_t readOggStatic(void* dst, size_t size1, size_t size2, void* fh);
-    static int    seekOggStatic(void *fh, ogg_int64_t to, int type );
-    static int    closeOggStatic(void* fh);
-    static long   tellOggStatic(void *fh );
-    
-    void adjustSoundTouch(); // sets SoundTouch parameters
-    
+    /** Methods needed by vorbisfile library. */
+  static size_t readOggStatic(void* dst, size_t size1, size_t size2, void* fh);
+  static int    seekOggStatic(void *fh, ogg_int64_t to, int type );
+  static int    closeOggStatic(void* fh);
+  static long   tellOggStatic(void *fh );
+
+  void adjustSoundTouch(); // sets SoundTouch parameters
+
 private:
-    qint8             *m_oggInMemory;
-    OggVorbis_File    m_ogg; /** ogg vorbis handler */
-    qint16            *m_pcmBuffer; /** buffer with decompressed data of selected note. */
-    SoggFile          m_oggWrap; /** Structure wrapped m_oggInMemory used by ogg vorbis. */
-    QThread           *m_thread;
-    unsigned int      m_sampleRate;
-    int               m_prevNote;
-    bool              m_doDecode; /** If new note is going to be decoded it goes to FALSE - then stops decoding loop */
-    bool              m_isDecoding; /** TRUE during decoding/resampling process. */
-    bool              m_isReady;
-    SoundTouch        *m_touch;
-    float             m_pitchOffset; /** Offset set from outside (by user) */
-    float							m_innerOffset; /** Offset calculated in setNote when SoundTouch has to generate note. */
-    bool              m_oggConnected, m_touchConnected;
-		int								m_firstNote, m_lastNote; /** Numbers of first and last notes in file with scale. */
-		int								m_instrument; /** current instrument which samples are loaded */
+  qint8             *m_oggInMemory;
+  OggVorbis_File     m_ogg; /**< ogg vorbis handler */
+  qint16            *m_pcmBuffer; /**< buffer with decompressed data of selected note. */
+  SoggFile           m_oggWrap; /**< Structure wrapped m_oggInMemory used by ogg vorbis. */
+  QThread           *m_thread;
+  unsigned int       m_sampleRate;
+  int                m_prevNote;
+  bool               m_doDecode; /**< If new note is going to be decoded it goes to FALSE - then stops decoding loop */
+  bool               m_isDecoding; /**< TRUE during decoding/resampling process. */
+  bool               m_isReady;
+  SoundTouch        *m_touch;
+  float              m_pitchOffset; /**< Offset set from outside (by user) */
+  float              m_innerOffset; /**< Offset calculated in setNote when SoundTouch has to generate note. */
+  bool               m_oggConnected, m_touchConnected;
+  int                m_firstNote, m_lastNote; /**< Numbers of first and last notes in file with scale. */
+  int                m_instrument; /**< current instrument which samples are loaded */
 
 };
 
diff --git a/src/libs/sound/tqtaudioout.cpp b/src/libs/sound/tqtaudioout.cpp
index 49a0dc12b..137654afe 100755
--- a/src/libs/sound/tqtaudioout.cpp
+++ b/src/libs/sound/tqtaudioout.cpp
@@ -47,8 +47,8 @@ QStringList TaudioOUT::getAudioDevicesList() {
   return devNamesList;
 }
 
-QString          			TaudioOUT::m_devName = QStringLiteral("anything");
-TaudioOUT*            TaudioOUT::m_instance = 0;
+QString                TaudioOUT::m_devName = QStringLiteral("anything");
+TaudioOUT*             TaudioOUT::m_instance = 0;
 /*end static*/
 
 
@@ -73,12 +73,13 @@ TaudioOUT::TaudioOUT(TaudioParams *_params, QObject *parent) :
   }
   m_instance = this;
   setType(e_audio);
-	m_crossBuffer = new qint16[1000];
+  m_crossBuffer = new qint16[1000];
 
   setAudioOutParams();
 
   connect(this, &TaudioOUT::finishSignal, this, &TaudioOUT::playingFinishedSlot);
-  
+  connect(oggScale, &ToggScale::oggReady, this, &TaudioOUT::startPlayingSlot);
+
 }
 
 
@@ -98,23 +99,23 @@ TaudioOUT::~TaudioOUT()
 
 
 void TaudioOUT::setAudioOutParams() {
-	playable = oggScale->loadAudioData(audioParams()->audioInstrNr);
+  playable = oggScale->loadAudioData(audioParams()->audioInstrNr);
   if (m_audioParams->OUTdevName != m_devName)
     createOutputDevice();
-	if (playable) {
-			ratioOfRate = m_sampleRate / 44100;
-			quint32 oggSR = m_sampleRate;
-			if (ratioOfRate > 1) { //
-				if (m_sampleRate == 88200 || m_sampleRate == 176400)
-					oggSR = 44100;
-				else if (m_sampleRate == 96000 || m_sampleRate == 192000)
-					oggSR = 48000;
-			}
-			oggScale->setSampleRate(oggSR);
-			// Shifts only float part of a440diff - integer part is shifted by play() method
-			oggScale->setPitchOffset(audioParams()->a440diff - (float)int(audioParams()->a440diff));
-
-	} else
+  if (playable) {
+      ratioOfRate = m_sampleRate / 44100;
+      quint32 oggSR = m_sampleRate;
+      if (ratioOfRate > 1) { //
+        if (m_sampleRate == 88200 || m_sampleRate == 176400)
+          oggSR = 44100;
+        else if (m_sampleRate == 96000 || m_sampleRate == 192000)
+          oggSR = 48000;
+      }
+      oggScale->setSampleRate(oggSR);
+      // Shifts only float part of a440diff - integer part is shifted by play() method
+      oggScale->setPitchOffset(audioParams()->a440diff - (float)int(audioParams()->a440diff));
+
+  } else
       playable = false;
 }
 
@@ -159,44 +160,41 @@ void TaudioOUT::createOutputDevice() {
   qDebug() << "OUT:" << m_deviceInfo.deviceName() << m_audioOUT->format().sampleRate() << m_maxSamples;
 
   connect(m_buffer, &TaudioBuffer::feedAudio, this, &TaudioOUT::outCallBack, Qt::DirectConnection);
-  connect(m_audioOUT, &QAudioOutput::stateChanged, this, &TaudioOUT::stateChangedSlot);  
+  connect(m_audioOUT, &QAudioOutput::stateChanged, this, &TaudioOUT::stateChangedSlot);
 }
 
 
 bool TaudioOUT::play(int noteNr) {
   if (!playable)
       return false;
-  
-	while (m_callBackIsBussy) {
-		  SLEEP(1);
-// 			qDebug() << "Oops! Call back method is in progress when a new note wants to be played!";
-	}
-	
+
+  while (m_callBackIsBussy) {
+      SLEEP(1); // TODO: seems like it never occurs in Qt Audio - remove it then
+       qDebug() << "Oops! Call back method is in progress when a new note wants to be played!";
+  }
+
   if (m_samplesCnt < m_maxSamples) {
-			int off = m_samplesCnt + 1; // next chunk of playing sound
-			for (int i = 0; i < 1000; i++) // copy data of current sound to perform crrosfading
-				m_crossBuffer[i] = oggScale->getSample(off + i);
-			m_doCrossFade = true;
-	} else
-			m_doCrossFade = false;
-  
+      int off = m_samplesCnt + 1; // next chunk of playing sound
+      for (int i = 0; i < 1000; i++) // copy data of current sound to perform crrosfading
+        m_crossBuffer[i] = oggScale->getSample(off + i);
+      m_doCrossFade = true;
+  } else
+      m_doCrossFade = false;
+
   noteNr = noteNr + int(audioParams()->a440diff);
-	
+
   doEmit = true;
-  oggScale->setNote(noteNr);
-  int loops = 0;
-  while (!oggScale->isReady() && loops < 40) { // 40ms - max latency
-      SLEEP(1);
-      loops++;
-  }
+  oggScale->setNote(noteNr); // oggScale will emit oggReady() and invoke startPlayingSlot()
+
+  return true;
+}
+
+
+void TaudioOUT::startPlayingSlot() {
   m_samplesCnt = -1;
-//   if (loops)
-//     qDebug() << "latency:" << loops << "ms";
-    
+
   if (m_audioOUT->state() != QAudio::ActiveState)
     m_audioOUT->start(m_buffer);
-    
-  return true;
 }
 
 
diff --git a/src/libs/sound/tqtaudioout.h b/src/libs/sound/tqtaudioout.h
index 7ea7a8760..f36c5531d 100755
--- a/src/libs/sound/tqtaudioout.h
+++ b/src/libs/sound/tqtaudioout.h
@@ -1,5 +1,5 @@
 /***************************************************************************
- *   Copyright (C) 2015 by Tomasz Bojczuk                                  *
+ *   Copyright (C) 2015-2016 by Tomasz Bojczuk                             *
  *   seelook@gmail.com                                                     *
  *                                                                         *
  *   This program is free software; you can redistribute it and/or modify  *
@@ -33,13 +33,13 @@ class TaudioParams;
 class QAudioOutput;
 class TaudioBuffer;
 
-/** 
+/**
  * Implementation of guitar sound player using Qt Multimedia
  */
 class NOOTKASOUND_EXPORT TaudioOUT : public TabstractPlayer
 {
   Q_OBJECT
-   
+
 public:
   TaudioOUT(TaudioParams* _params, QObject* parent = 0);
   virtual ~TaudioOUT();
@@ -51,39 +51,40 @@ public:
         /** Starts playing given note and then returns true, otherwise gets false. */
   bool play(int noteNr);
   void setAudioOutParams();
-  void stop(); /** Immediately stops playing. */
+  void stop(); /**< Immediately stops playing. */
 
   TaudioParams* audioParams() { return m_audioParams; }
 
 protected:
-  int crossCount() { return m_crossCount; } /** counts samples of crossing buffer */
+  int crossCount() { return m_crossCount; } /**< counts samples of crossing buffer */
   void createOutputDevice();
-  
+
 signals:
   void finishSignal();
 
 protected:
-  ToggScale 										*oggScale;
-  int     				 							 ratioOfRate; // ratio of current sample rate to 44100
+  ToggScale                      *oggScale;
+  int                             ratioOfRate; // ratio of current sample rate to 44100
 
 private slots:
   void outCallBack(char* data, qint64 maxLen, qint64& wasRead);
-// 	void updateSlot() { setAudioOutParams(); }
-	void playingFinishedSlot();
-	void stateChangedSlot(QAudio::State state);
+//   void updateSlot() { setAudioOutParams(); }
+  void playingFinishedSlot();
+  void stateChangedSlot(QAudio::State state);
+  void startPlayingSlot();
 
 
 private:
   static QString      m_devName;
   static TaudioOUT   *m_instance;
-  int                 m_samplesCnt; /** Number of performed samples. */
-  int                 m_maxSamples; /** Duration of a sound counted in samples */
+  int                 m_samplesCnt; /**< Number of performed samples. */
+  int                 m_maxSamples; /**< Duration of a sound counted in samples */
   int                 m_bufferFrames, m_sampleRate;
-	qint16             *m_crossBuffer; /** buffer with data of part of previous note to fade out */
-	bool                m_doCrossFade;
-	float               m_cross; /** current volume factor of fading effect */
-	int                 m_crossCount;
-	bool                m_callBackIsBussy;
+  qint16             *m_crossBuffer; /**< buffer with data of part of previous note to fade out */
+  bool                m_doCrossFade;
+  float               m_cross; /**< current volume factor of fading effect */
+  int                 m_crossCount;
+  bool                m_callBackIsBussy;
   TaudioParams       *m_audioParams;
   QAudioOutput       *m_audioOUT;
   TaudioBuffer       *m_buffer;
-- 
GitLab