Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to properly close QThread
QtWS25 Last Chance

How to properly close QThread

Scheduled Pinned Locked Moved Unsolved General and Desktop
2 Posts 2 Posters 147 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    JuliusCaesar
    wrote on 29 Oct 2020, 13:34 last edited by JuliusCaesar
    #1

    Hey,
    I'm trying to play an audio file via QAudioOutput.
    The Idea is, that the User can start an audio playback and stop an audio playback at any time. This is the reason for wrapping QAudioOutput in a QThread.
    Now I'm not sure if I'm doing it the right way.
    I'm using QT 5.12.8.

     class IRCAM2020_MOD_AUDIO_DECL AudioOutputNewThread : public QThread, public AudioSettings
      {
        Q_OBJECT
      public:
    
        /**
         * @brief Constructor
         * @param strFileNameArg Filename to play
         * @param audioOutputDeviceArg Outputdevice where to play the Audio
         * @param parent Parent Element
        */
        explicit AudioOutputNewThread(const std::string& strFileNameArg, QAudioDeviceInfo audioOutputDeviceArg = QAudioDeviceInfo::defaultOutputDevice(), QObject* parent = nullptr);
    
        ///Destruktor
        ~AudioOutputNewThread();
    
      protected:
        /**
         * @brief Runs this thread
        */
        void run() override;
    
      private:
    
        ///Path to the source file to play
        const std::string strFileName;
    
        ///the Src File to play => set by constructor
        QFile sourceFile;
    
        ///Pointer to the AudioOutput
        std::unique_ptr<QAudioOutput> m_pAudio;
    
        ///the audio output device where to play the music
        const QAudioDeviceInfo audioOutputDevice;
    
        /**
         * @brief Starts playing the File
         * @return true if file exists and if audio output was started correctly
        */
        bool StartPlayFile();
    
      signals:
    
        /**
         * @brief This thread signals an error if an error occurs
        */
        void error(const std::string strErrorMsg);
    
      public slots:
    
        /**
         * @brief Called when audio device changed the state
        */
        void handleStateChanged(QAudio::State newState);
    
      };
    

    My Implementation looks like this

    AudioOutputNewThread::AudioOutputNewThread(const std::string& strFileNameArg, QAudioDeviceInfo audioOutputDeviceArg, QObject* parent) :
        QThread(parent),
        strFileName(strFileNameArg),
        audioOutputDevice(audioOutputDeviceArg)
      {
        setTerminationEnabled(true);
        sourceFile.setFileName(QString::fromStdString(strFileName));
        if (!sourceFile.exists())
        {
          //throw some exception...
        }
    
      }
    
    
    
      AudioOutputNewThread::~AudioOutputNewThread()
      {
        if (nullptr != m_pAudio)
        {
          m_pAudio->stop();
        }
        exit(0);
        wait();
      }
    
    
    void AudioOutputNewThread::run()
      {
        //Thread starts here    
        qDebug() << "Starting task";
        if (!StartPlayFile())
        {
          emit error("Failed to play file");
        }
    
        connect(m_pAudio.get(), SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));
        const int iExcecRes = exec();
        if (0 != iExcecRes)
        {
          emit error("Failed to excecute thread. Exec Result " + iExcecRes);
        }
      }
    
    bool AudioOutputNewThread::StartPlayFile()
      {
    
        if (!sourceFile.open(QIODevice::ReadOnly))
        {
          qDebug() << "Failed to open file " << QString::fromStdString(strFileName);
          return false;
        }
    
        auto format = GetDefaultAudioFormat();
        
    
        qDebug() << "Playing to output device" << audioOutputDevice.deviceName() << "with format " << format;
    
        QAudioDeviceInfo info(audioOutputDevice);
        if (!info.isFormatSupported(format)) {
          qWarning() << "Raw audio format not supported by backend, cannot play audio.";
          format = audioOutputDevice.preferredFormat();
          qDebug() << "new format " << format;
        }
    
        m_pAudio = std::make_unique<QAudioOutput>(format, nullptr);
    
    
        m_pAudio->start(&sourceFile);
        int res = exec();
        return (0 == res);
      }
    
    void AudioOutputNewThread::handleStateChanged(QAudio::State newState)
      {
        qDebug() << "Audio playback state changed: " << newState;
        switch (newState) {
        case QAudio::IdleState:
          qDebug() << "Finnished playing audio";
          exit(0);
          break;
    
        case QAudio::StoppedState:
          // Stopped for other reasons
          if (m_pAudio->error() != QAudio::NoError) {
            qWarning() << "Stopped device for other reasons...";
            exit(0);
          }
          break;
    
        default:
          qWarning() << "handle state changed default state...";
          break;
        }
      }
    
    

    now to make things easy this class is called from a GTest case. The actual test case is pretty straight forward

    
    class UnitTestAudio : public ::testing::Test
    {
    public:
    protected:
    
      const std::string strWavFileName = "test.wav";
    
      //this list contains all available outputs acquired via QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
      const QList<QAudioDeviceInfo> lsOutputDevice;
    
    
    };
    
    
    
    TEST_F(UnitTestAudio, PlayQtAudioOutputThreadNew)
    {
      for (auto it = lsOutputDevice.begin(); it != lsOutputDevice.end(); it++)
      {
        qDebug() << "UnitTest: Start playing audio on output device "<<it->deviceName();
        auto pThread = std::make_unique< AudioOutputNewThread>(strWavFileName, *it);
    
        pThread->start();
        qDebug() << "Sleeping for 5s....";
        const auto sleepTime = 5s;
        std::this_thread::sleep_for(sleepTime);
        qDebug() << "UnitTest: finnished Playing " + QString::fromStdString(strWavFileName);
      }
    }
    
    

    Now I facing 2 problems:

    • On Windows is stuck at the End of AudioOutputNewThread Destruktor. (Probably within wait())

    • Linux on the other hand tells me

    "UnitTest: Start playing audio on output device "pulse"
    Sleeping for 5s....
    Starting task
    Playing to output device "pulse" with format QAudioFormat(11025Hz, 8bit, channelCount=2, sampleType=UnSignedInt, byteOrder=LittleEndian, codec="audio/pcm")
    "UnitTest: finnished Playing test.wav"
    QObject::killTimer: Timers cannot be stopped from another thread
    "
    =>so AudioOutputNewThread Destructor seams to cause an exception on Linux

    From reading the manual i understood 2 requirements for using QThreads

    1. void run() override; is never supposed to terminate. =>that's why i called excec() at the end of run().

    2. Within the Destructor of AudioOutputNewThread exit(0) is supposed to tell exec() to terminate with result 0.
      The following wait() is simply to wait for thread termination

    Where are my assumptions wrong? Could you help me out here?
    Why is handleStateChanged() never executed? Did i mess up the signal connection?

    thx Greets Julian

    1 Reply Last reply
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 29 Oct 2020, 14:47 last edited by
      #2

      Hi and welcome to devnet,

      @JuliusCaesar said in How to properly close QThread:

      StartPlayFile

      You call exec in that method and you connect your signal after calling StartPlayFile.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      1

      2/2

      29 Oct 2020, 14:47

      • Login

      • Login or register to search.
      2 out of 2
      • First post
        2/2
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved