天天看點

Qt 之 WAV檔案屬性計算(比特率、檔案大小、檔案時長)簡述代碼之路尾

簡述

在 Qt 之 WAV檔案解析 中給出了WAV檔案屬性的計算,具體包括檔案大小、音頻時長、比特率等屬性,這裡我們再次驗證一下這些屬性值的計算 。

在計算之前,我們要知道一下wav檔案中的三個參數 采樣頻率、音頻通道數、每次采樣得到的樣本位數 ,這三個參數用來表示聲音,同時決定了wav檔案的音質,大小。下面簡單介紹一下這三個參數。

采樣頻率

指每秒鐘取得聲音樣本的次數。采樣的過程就是抽取某點的頻率值,很顯然,在一秒中内抽取的點越多,擷取得頻率資訊更豐富,為了複原波形,采樣頻率越高,聲音的品質也就越好,聲音的還原也就越真實,但同時它占的資源比較多。由于人耳的分辨率很有限,太高的頻率并不能分辨出來。22050 的采樣頻率是常用的,44100已是CD音質,超過48000或96000的采樣對人耳已經沒有意義。

音頻通道數

聲音的通道的數目。常見的單聲道和立體聲(雙聲道),現在發展到了四聲環繞(四聲道)和5.1聲道。如果是雙聲道,采樣就是雙份的,檔案也差不多要大一倍。

每次采樣得到的樣本位數

采樣位數可以了解為聲霸卡處理聲音的解析度。這個數值越大,解析度就越高,錄制和回放的聲音就越真實。 采樣位數也叫采樣大小或量化位數。它是用來衡量聲音波動變化的一個參數,也就是聲霸卡的分辨率或可以了解為聲霸卡處理聲音的解析度。它的數值越大,分辨率也就越高,錄制和回放的聲音就越真實。

計算公式

波形資料傳輸速率(每秒平均位元組數) = 采樣頻率 × 音頻通道數 × 每次采樣得到的樣本位數 / 8

比特率(kbs) = 波形資料傳輸速率 × 8 / 1000

WAV檔案所占大小(位元組) = 波形資料傳輸速率 × 音頻檔案時長

音頻檔案時長(秒) = WAV檔案所占容量 / 波形資料傳輸速率

關于以上幾個屬性我們可以右擊wav檔案檢視檔案屬性看到這幾個值。見下圖。

Qt 之 WAV檔案屬性計算(比特率、檔案大小、檔案時長)簡述代碼之路尾
Qt 之 WAV檔案屬性計算(比特率、檔案大小、檔案時長)簡述代碼之路尾

從上述兩幅圖中我們可以知道這個wav檔案的總大小為6947位元組,比特率為88kbs,時間為0s,是不是很詫異,為什麼這裡時間為0呢?實際上windows這裡隻是按整數顯示了音頻時長,那麼真正的時間怎麼計算呢?

這裡我們已經知道了wav檔案的大小,看上述公式,我們還要知道波形資料傳輸速率,波形資料傳輸速率而又是由采樣頻率 、 音頻通道數 、 每次采樣得到的樣本位數 來決定,那麼這些參數怎麼擷取到呢?

看過Qt 之 解析wav檔案的頭資訊(詳細分析、對比不同wav檔案的資料)這篇文章就應該知道如何去解析一個wav檔案,并擷取所有的檔案頭資訊,如果不知道檔案頭資訊是什麼,請參考Qt 之 WAV檔案解析。

好了,既然對于一個wav檔案,我們能夠擷取到所有的頭資訊,那麼接下來就來驗證以上公式計算的結果。

Qt 之 WAV檔案屬性計算(比特率、檔案大小、檔案時長)簡述代碼之路尾

上圖為wav檔案的頭資訊資料,我們可以看到波形資料傳輸速率(nBytesPerSecond)的值為11025,檔案總大小為6947位元組,音頻資料大小為6903位元組,檔案頭資訊為44位元組。

音頻檔案時長(秒) = WAV檔案所占容量 / 波形資料傳輸速率 = 6903 / 11025 = 0.626122 s

比特率(kbs) = 波形資料傳輸速率 × 8 / 1000 = 11025 × 8 / 1000 = 88 kbs

這裡為什麼精确到小數點後六位,其實也是為了與程式記錄的時間做對比,這裡也要特别注意:實際上 WAV檔案所占容量 為 WAV檔案中 音頻資料大小 ,而并非WAV檔案總大小 , 但是 檔案頭資訊所占位元組非常小,是以就算是将這塊大小加上進行計算,對最後的計算結果影響也非常小。下面我們就用QAudioOutput 來播放這個wav檔案,同時記錄播放時間 。

代碼之路

// 播放wav檔案
void MyAudioInput::onPlay()
{
    sourceFile.setFileName(WAV_RECORD_FILENAME);
    sourceFile.open(QIODevice::ReadOnly);

    // 設定播放音頻格式;
    QAudioFormat format;
    format.setSampleRate();
    format.setChannelCount();
    format.setSampleSize();
    format.setCodec("audio/pcm");
    // wav檔案即按照這個位元組存儲順序儲存資料;
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::UnSignedInt);

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
    //qDebug() << info.supportedCodecs();
    if (!info.isFormatSupported(format))
    {
        qWarning() << "Raw audio format not supported by backend, cannot play audio.";
        return;
    }

    m_audioOutput = new QAudioOutput(format, this);
    connect(m_audioOutput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));
    m_audioOutput->start(&sourceFile);
    m_time.start();
}

// 播放狀态更新;
void MyAudioInput::handleStateChanged(QAudio::State state)
{
    switch (state) {
    case QAudio::IdleState:
        // Finished playing (no more data)
        qDebug() << "elapsedUSecs:" << m_audioOutput->elapsedUSecs();
        qDebug() << "time : " << m_time.elapsed();
        onStopPlay();
        break;

    case QAudio::StoppedState:
        // Stopped for other reasons
        if (m_audioOutput->error() != QAudio::NoError) {
            // Error handling
        }
        break;

    default:
        break;
    }
}
// 關閉播放;
void MyAudioInput::onStopPlay()
{
    if (m_audioOutput != NULL)
    {
        m_audioOutput->stop();
        sourceFile.close();
        delete m_audioOutput;
        m_audioOutput = NULL;
    }
}
           

代碼中我分别用了QAudioOutput類的elapsedUSecs方法和QTime類的elapsed方法來記錄wav檔案音頻時長。以下是兩個方法的介紹。

Qt 之 WAV檔案屬性計算(比特率、檔案大小、檔案時長)簡述代碼之路尾
Qt 之 WAV檔案屬性計算(比特率、檔案大小、檔案時長)簡述代碼之路尾

elapsedUSecs() 輸出為微妙

elapsed() 輸出為毫秒

通過記錄得到以下資料:

m_audioOutput->elapsedUSecs() : 636000

m_time.elapsed() : 635

m_audioOutput->elapsedUSecs() : 639000

m_time.elapsed() : 638

m_audioOutput->elapsedUSecs() : 642000

m_time.elapsed() : 641

m_audioOutput->elapsedUSecs() : 639000

m_time.elapsed() : 639

而我們的計算結果為: 0.626122 s = 626.122 ms = 626122 us , 顯然程式中擷取的時間大于計算的時間,這也很好了解,因為程式的運作需要消耗一定的時間,是以記錄的時間存在很小的誤差(誤差範圍大緻在0.009s ~ 0.016s),如果電腦性能更好這個誤差就越小。

特别注意

這裡我們用QAudioOutput類來計算wav檔案時長,這裡我們要給QAudioOutput類對象設定播放格式QAudioFormat ,設定的格式必須與解析出來的檔案頭資訊中的 采樣頻率、音頻通道數、每次采樣得到的樣本位數、編碼格式等嚴格保持一緻,否則不僅播放出來的聲音不清楚,記錄的音頻時長也有問題。

通過以上發現,我們的計算公式是成立的。基本上我們可以在wav檔案頭資訊中擷取wav檔案的全部資訊,唯一就是wav**檔案時長**需要通過檔案頭中的資訊進行計算得到。是以如果我們想要做一個播放器,在播放器上顯示一個wav檔案的時長,我們就需要先解析wav檔案的頭資訊,通過計算得到檔案時長。

更多參考

Qt 之 WAV檔案解析

Qt之實作錄音播放及raw(pcm)轉wav格式

Qt 之 解析wav檔案的頭資訊(詳細分析、對比不同wav檔案的資料)

繼續閱讀