Qtを使ってオーディオを再生します

36747 ワード

記事の目次
  • .使用類Qudio Format、Qudio Output
  • .オーディオを再生する
  • 前の2つの記事では、FFMpegを使ってオーディオ情報を取得する方法と、FFMpegを使ってMP 3ファイルの情報と画像を取得する方法を紹介しました.FFMpegを使ってオーディオファイルを復号します.
    この記事では、Qtを使って復号後のPCMデータを再生する方法を紹介します.
    1.使用類Qudio Format、Qudio Output
    Qudio Formatを使用して、サンプルレート、サンプルビット数、チャネル数などのオーディオのフォーマット関連情報を設定することができます.コードは以下の通りです
    QAudioFormat nFormat;
    nFormat.setSampleRate(sampleRate);
    nFormat.setSampleSize(sampleSize);
    nFormat.setChannelCount(channelCount);
    nFormat.setCodec("audio/pcm");
    nFormat.setByteOrder(QAudioFormat::LittleEndian);
    nFormat.setSampleType(QAudioFormat::UnSignedInt);
    
    Qudio Outputクラスの構成にはFormatを設定する必要があります.Qudio Outputはオーディオの再生、一時停止、設定、音量の取得などの制御ができます.
    Qudio Outputを使ってオーディオを再生する方法は以下の通りです.
  • Qudio Output*m_OutPut=new Qudio Output(nFormat)Qudio Outputオブジェクトを作成します.
  • QIODevice*m_AudioIo=m_OutPut->start();QIODeviceのポインタオブジェクトを取得します.
  • m_AudioIo->write関数はオーディオデバイスにデータを書き込みます.
  • 2.オーディオを再生する
    ここでは、スレッドを使ってオーディオを再生し、スレッドの中で絶えずにオーディオデータを書き込み、オーディオが連続して非同期で再生されます.コードは以下の通りです
    void AudioPlayThread::run(void)
    {
        while (!this->isInterruptionRequested())
        {
            if (!m_IsPlaying)
            {
                continue;
                QThread::msleep(10);
            }
    
            QMutexLocker locker(&m_Mutex);
    
            if (m_PCMDataBuffer.count() <= 0 || m_CurrentPlayIndex >= m_PCMDataBuffer.count())
            {
                QThread::msleep(10);
                continue;
            }
    
            if (m_OutPut->bytesFree() >= m_OutPut->periodSize())
            {
                char *writeData = new char[m_OutPut->periodSize()];
    
                //          
                int size = m_OutPut->periodSize();
                size = qMin(size, m_PCMDataBuffer.size() - m_CurrentPlayIndex);
                memcpy(writeData, &m_PCMDataBuffer.data()[m_CurrentPlayIndex], size);
    
                //       
                m_AudioIo->write(writeData, size);
                m_CurrentPlayIndex += size;
    
                emit updatePlayStatus();
    			delete []writeData;
                QThread::msleep(10);
            }
        }
    }
    
    オーディオの再生データをQByteAray m_に入れました.PCMDA Bufferにあります.periodSize個のデータを書き込むたびに、デバイスのオーディオバッファに一旦解放されたら>=periodSizeのメモリを一度書き込みます.これにより、バッファではずっと再生されているデータがあり、元のデータも上書きされません.一時停止または再生終了まで.
    以下の時の完全なコード:AudioPlayThread.h
    #ifndef AUDIO_PLAY_THREAD_H
    #define AUDIO_PLAY_THREAD_H
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define g_AudioPlayThread AudioPlayThread::getInstance()
    class AudioPlayThread : public QThread
    {
        Q_OBJECT
    
    public:
        static AudioPlayThread *getInstance(void);
    
    public:
        AudioPlayThread(QObject *parent = nullptr);
        ~AudioPlayThread();
    
        // -----------        ----------------------------------------
        //      PCM Buffer
        void setCurrentBuffer(QByteArray buffer);
        //     
        void addAudioBuffer(char* pData, int len);
        //        
        void cleanAllAudioBuffer(void);
        // ------------- End ----------------------------------------------
    
        //         、    、    
        void setCurrentSampleInfo(int sampleRate, int sampleSize, int channelCount);
    
        virtual void run(void) override;
    
        //            
        int getCurrentBuffIndex(void);
        //        
        int getCurrentTime(void);
    
        //       
        void playMusic(bool status);
        //          
        bool getPlayMusicStatus(void);
        //     
        void setCurrentVolumn(qreal volumn);
        //       
        qreal getCurrentVolumn(void);
    
    private:
        QAudioOutput *m_OutPut = nullptr;
        QIODevice *m_AudioIo = nullptr;
    
        QByteArray m_PCMDataBuffer;
        int m_CurrentPlayIndex = 0;
    
        QMutex m_Mutex;
        //     
        bool m_IsPlaying = true;
    
    signals:
        void updatePlayStatus(void);
    };
    
    #endif
    
    
    AudioPlayThread.cpp
    #include "AudioPlayThread.h"
    #include 
    #include 
    
    AudioPlayThread::AudioPlayThread(QObject *parent)
        :QThread(parent)
    {
        m_PCMDataBuffer.clear();
    }
    
    AudioPlayThread::~AudioPlayThread()
    {
    
    }
    
    AudioPlayThread *AudioPlayThread::getInstance(void)
    {
        static AudioPlayThread instance;
        return &instance;
    }
    
    void AudioPlayThread::setCurrentBuffer(QByteArray buffer)
    {
        QMutexLocker locker(&m_Mutex);
    
        m_PCMDataBuffer.clear();
        m_PCMDataBuffer = buffer;
        m_IsPlaying = true;
    }
    
    void AudioPlayThread::setCurrentSampleInfo(int sampleRate, int sampleSize, int channelCount)
    {
        QMutexLocker locker(&m_Mutex);
        //this->requestInterruption();
    
        // Format
        QAudioFormat nFormat;
        nFormat.setSampleRate(sampleRate);
        nFormat.setSampleSize(sampleSize);
        nFormat.setChannelCount(channelCount);
        nFormat.setCodec("audio/pcm");
        nFormat.setByteOrder(QAudioFormat::LittleEndian);
        nFormat.setSampleType(QAudioFormat::UnSignedInt);
    
        if (m_OutPut != nullptr)
            delete m_OutPut;
        m_OutPut = new QAudioOutput(nFormat);
        m_AudioIo = m_OutPut->start();
        //this->start();
    }
    
    void AudioPlayThread::run(void)
    {
        while (!this->isInterruptionRequested())
        {
            if (!m_IsPlaying)
            {
                continue;
                QThread::msleep(10);
            }
    
            QMutexLocker locker(&m_Mutex);
    
            if (m_PCMDataBuffer.count() <= 0 || m_CurrentPlayIndex >= m_PCMDataBuffer.count())
            {
                QThread::msleep(10);
                continue;
            }
    
            if (m_OutPut->bytesFree() >= m_OutPut->periodSize())
            {
                char *writeData = new char[m_OutPut->periodSize()];
    
                //          
                int size = m_OutPut->periodSize();
                size = qMin(size, m_PCMDataBuffer.size() - m_CurrentPlayIndex);
                memcpy(writeData, &m_PCMDataBuffer.data()[m_CurrentPlayIndex], size);
    
                //       
                m_AudioIo->write(writeData, size);
                m_CurrentPlayIndex += size;
    
                emit updatePlayStatus();
    			delete []writeData;
                QThread::msleep(10);
            }
        }
    }
    
    //     
    void AudioPlayThread::addAudioBuffer(char* pData, int len)
    {
        QMutexLocker locker(&m_Mutex);
    
        m_PCMDataBuffer.append(pData, len);
        m_IsPlaying = true;
    }
    
    void AudioPlayThread::cleanAllAudioBuffer(void)
    {
        QMutexLocker locker(&m_Mutex);
        m_CurrentPlayIndex = 0;
        m_PCMDataBuffer.clear();
        m_IsPlaying = false;
    }
    
    void AudioPlayThread::playMusic(bool status)
    {
        m_IsPlaying = status;
    }
    
    bool AudioPlayThread::getPlayMusicStatus(void)
    {
        return m_IsPlaying;
    }
    
    void AudioPlayThread::setCurrentVolumn(qreal volumn)
    {
        if (m_OutPut)
            m_OutPut->setVolume(volumn);
    }
    
    qreal AudioPlayThread::getCurrentVolumn(void)
    {
        if (!m_OutPut)
            return 0;
    
        return m_OutPut->volume();
    }
    
    //            
    int AudioPlayThread::getCurrentBuffIndex(void)
    {
        return m_CurrentPlayIndex;
    }
    
    int AudioPlayThread::getCurrentTime(void)
    {
        QMutexLocker locker(&m_Mutex);
    
        qreal sec = m_CurrentPlayIndex * 1.0 / 4 * (1 * 1.0 / m_OutPut->format().sampleRate());
        return sec * 1000;
    }