音频相关

Posted xianjian_x

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了音频相关相关的知识,希望对你有一定的参考价值。

音频帧概念详解

  正常人听觉的频率范围大约在20Hz~20kHz之间。

  采样频率是指将模拟声音波形进行数字化时,每秒钟抽取声波幅度样本的次数。
  根据奈奎斯特采样理论,为了保证声音不失真,采样频率应该在40kHz左右。
  常用的音频采样频率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等,如果采用更高的采样频率,还可以达到DVD的音质。

  对采样率为44.1kHz的AAC音频进行解码时,一帧的解码时间须控制在23.22毫秒内。
  通常是按1024个采样点一帧

分析:

AAC

  一个AAC原始帧包含某段时间内1024个采样点相关数据。用1024主要是因为AAC是用的1024点的mdct

  音频帧的播放时间 = 一个AAC帧对应的采样样本的个数 / 采样频率(单位为s)。

  采样率(samplerate)为 44100Hz,表示每秒 44100个采样点,
所以,根据公式,
  音频帧的播放时长 = 一个AAC帧对应的采样点个数 / 采样频率
则,当前一帧的播放时间 = 1024 * 1000000/44100= 22.32ms(单位为ms)

48kHz采样率:
则,当前一帧的播放时间 = 1024 * 1000000/48000= 21.32ms(单位为ms)

22.05kHz采样率,
则,当前一帧的播放时间 = 1024 * 1000000/22050= 46.43ms(单位为ms)

MP3

  mp3 每帧均为1152个字节,
则:
每帧播放时长 = 1152 * 1000000 / sample_rate
例如:sample_rate = 44100HZ时,
计算出的时长为26.122ms,
这就是经常听到的mp3每帧播放时间固定为26ms的由来。

  mp3 和 aac 格式音频文件都是压缩过的文件,如果要送入windows播放还需要解码,此时要有 一个转化,一般用ffmpeg 的 swr_convert :

swr_convert函数介绍

用ffmpeg将mp3转为wav

解码之后的数据帧一般都是 AV_SAMPLE_FMT_S16 格式的,这个就是pcm数据。

关于PCM格式如下:
音频编解码·格式篇(1)Wave PCM audio format(WAV)

示例代码,附上我写的一个mp3 播放器

WAVEPLAY.h:

#pragma once

#include "stdafx.h"
#include <Windows.h>
#include <mmsystem.h>
#include <stdio.h>

#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000
#define BLOCKCOUNT 10
#define BLOCKSIZE   (AVCODEC_MAX_AUDIO_FRAME_SIZE * 4)

class WAVEPLAY

public:
    BOOL            OpenDevice(void);
    void            StartDevice(void);
    void            StopDevice(void);   
    BOOL            IsPLayOver(void);

private:
    HWAVEOUT        m_hWaveout;
    WAVEFORMATEX    m_wfx;
    WAVEHDR         m_waveHdr[BLOCKCOUNT];
    void *          m_Block[BLOCKCOUNT];
    BOOL            AddWaveOutBuffer(UINT nIndex,int len);
    void            WriteBuffer(PWAVEHDR);
    BOOL            m_bStop;
    HANDLE          m_hStop;

    BOOL            m_bPlayOver;

    CRITICAL_SECTION    m_csWaveOut;
    void PlayProc(UINT, DWORD_PTR);
    static void CALLBACK waveOutProc(HWAVEOUT, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR);

    ////ffmpeg相关数据
    SwrContext *        m_pSwrCtx;
    AVFormatContext *   m_pFmtCtx;
    AVCodecContext *    m_pCodecCtx;
    AVCodec*            m_pCodec;
    AVFrame *           m_pAVFrame;
    AVPacket            m_AudioPkt;
    int                 m_StreamIndex;

    int OpenCodecContext(enum AVMediaType type);

public:
    WAVEPLAY(void);
    virtual ~WAVEPLAY(void);
;

WAVEPLAY.cpp:

#include "StdAfx.h"

#include "WAVEPLAY.h"

#pragma comment(lib, "./avformat.lib")
#pragma comment(lib, "./avutil.lib")
#pragma comment(lib, "./avcodec.lib")
#pragma comment(lib, "./avdevice.lib")
#pragma comment(lib, "./avfilter.lib")
#pragma comment(lib, "./swresample.lib")
#pragma comment(lib, "./swscale.lib")

#pragma comment(lib,"winmm.lib")

DECLARE_ALIGNED(16,uint8_t,audio_buf) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];


int WAVEPLAY::OpenCodecContext( enum AVMediaType type) 

    int                 ret;
    AVDictionary *      opts            = NULL;

    ret = av_find_best_stream(m_pFmtCtx, type, -1, -1, NULL, 0);

    if (ret >= 0) 
        m_StreamIndex                   = ret;
        m_pCodecCtx                     = m_pFmtCtx->streams[m_StreamIndex]->codec;

        /* find decoder for the stream */
        m_pCodec                        = avcodec_find_decoder(m_pCodecCtx->codec_id);
        if (!m_pCodec) 
            return AVERROR(EINVAL);
        

        if(avcodec_open2(m_pCodecCtx, m_pCodec ,NULL) < 0 )
            printf("error opencodec!!\\n");
        
     else 
        return ret;
    
    return 0;



BOOL WAVEPLAY::OpenDevice(void)

    char                filename[256];
    int                 err;
    WAVEFORMATEX        m_wfx = 0;

    avcodec_register_all();
    av_register_all();

    //strcpy(filename,"F:\\\\Ring08.wav");
    strcpy(filename,"F:\\\\work\\\\testfile\\\\zhushalei.mp3");

    err = avformat_open_input(&m_pFmtCtx, filename, NULL, NULL);
    if(err < 0) 
        printf("can not open file %s.\\n", filename);
        return -1;
    

    err = avformat_find_stream_info(m_pFmtCtx,NULL );
    if(err < 0) 
        printf("can not find stream info of file %s.\\n", filename);
        return -1;
    

    av_dump_format(m_pFmtCtx , 0, 0 , 0);

    OpenCodecContext( AVMEDIA_TYPE_AUDIO);

    m_wfx.nSamplesPerSec  = m_pCodecCtx->sample_rate;

    switch(m_pCodecCtx->sample_fmt)
    
    case AV_SAMPLE_FMT_U8:
        m_wfx.wBitsPerSample = 8;
        break;
    case AV_SAMPLE_FMT_S16:
        m_wfx.wBitsPerSample = 16;
        break;
    case AV_SAMPLE_FMT_S16P:
        m_wfx.wBitsPerSample = 16;
        break;
    case AV_SAMPLE_FMT_S32:
        m_wfx.wBitsPerSample = 32;
        break;
    case AV_SAMPLE_FMT_FLT:
        m_wfx.wBitsPerSample = sizeof(double) * 8;
        break;
    default:
        m_wfx.wBitsPerSample = 0;
        break;
    

    m_wfx.nChannels         = FFMIN(2, m_pCodecCtx->channels);
    m_wfx.cbSize            = 0;
    m_wfx.wFormatTag        = WAVE_FORMAT_PCM;
    m_wfx.nBlockAlign       = (m_wfx.wBitsPerSample * m_wfx.nChannels) >> 3;
    m_wfx.nAvgBytesPerSec   = m_wfx.nBlockAlign * m_wfx.nSamplesPerSec;

    printf("sample rate: %d\\n", m_wfx.nSamplesPerSec);
    printf("channel count: %d\\n", m_wfx.nChannels);
    printf("bit per sample: %d\\n", m_wfx.wBitsPerSample);

    m_pAVFrame              = av_frame_alloc();

    av_init_packet(&m_AudioPkt);
    m_AudioPkt.data         = NULL;
    m_AudioPkt.size         = 0;

    MMRESULT mRet = waveOutOpen(&m_hWaveout,
        WAVE_MAPPER,
        &m_wfx,
        (DWORD_PTR)waveOutProc,
        (DWORD)this,
        CALLBACK_FUNCTION | WAVE_ALLOWSYNC);

    if (mRet != MMSYSERR_NOERROR)
        return FALSE;

    int i = 0;
    for (i; i < BLOCKCOUNT; i++)
    
        m_Block[i] = malloc(BLOCKSIZE);
        if (m_Block[i] == NULL)
        
            return FALSE;
        
        else
            memset(m_Block[i], 0, BLOCKSIZE);
        
    

    m_hStop = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (m_hStop == INVALID_HANDLE_VALUE)
        return FALSE;

    InitializeCriticalSection(&m_csWaveOut);

    return TRUE;


void WAVEPLAY::StartDevice(void)

    int i = 0;      //
    for (i; i < BLOCKCOUNT; i++)
    
        AddWaveOutBuffer(i, 512);
    


BOOL WAVEPLAY::AddWaveOutBuffer(UINT index,int len)

    m_waveHdr[index].lpData             = (LPSTR)m_Block[index];
    m_waveHdr[index].dwBufferLength     = len;
    m_waveHdr[index].dwBytesRecorded    = 0;
    m_waveHdr[index].dwFlags            = 0;
    m_waveHdr[index].dwLoops            = 0;
    m_waveHdr[index].dwUser             = index;

    MMRESULT mRet = waveOutPrepareHeader(m_hWaveout, &m_waveHdr[index], sizeof(WAVEHDR));
    if (mRet != MMSYSERR_NOERROR)
        return FALSE;

    mRet = waveOutWrite(m_hWaveout, &m_waveHdr[index], sizeof(WAVEHDR));
    if (mRet != MMSYSERR_NOERROR)
        return FALSE;

    return TRUE;


void WAVEPLAY::StopDevice(void)

    int i = 0;
    for (i; i < BLOCKCOUNT; i++)
    
        if (m_waveHdr[i].dwFlags == WHDR_PREPARED)
            waveOutUnprepareHeader(m_hWaveout, &m_waveHdr[i], sizeof(WAVEHDR));
    

    MMRESULT mRet;
    while (!m_bStop)
        mRet = waveOutClose(m_hWaveout);

    WaitForSingleObject(m_hStop, INFINITE); 

    for (i=0; i < BLOCKCOUNT; i++)
    
        free(m_Block[i]);
        m_Block[i] = NULL;
    

    if(m_pSwrCtx != NULL)
        swr_free(&m_pSwrCtx);

    av_free(m_pAVFrame);
    avcodec_close(m_pCodecCtx);



/*******************************************************************************************/
/*******************************************************************************************/

typedef  class WAVEPLAY * PWAVEPALY;

void CALLBACK WAVEPLAY::waveOutProc(HWAVEOUT hWaveOut, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2, DWORD_PTR dwParam3)

    PWAVEPALY pWavePlay = (PWAVEPALY)dwParam1;
    pWavePlay->PlayProc(uMsg, dwParam2);


void WAVEPLAY::PlayProc(UINT uMsg, DWORD_PTR hdr)

    PWAVEHDR waveOutHdr = (PWAVEHDR)hdr;
    switch (uMsg)
    
    case WOM_OPEN:
        m_bStop = FALSE;
        break;
    case WOM_CLOSE:
        m_bStop = TRUE;
        SetEvent(m_hStop);
        break;
    case WOM_DONE:
        if (!m_bPlayOver)
        
            EnterCriticalSection(&m_csWaveOut);
            WriteBuffer(waveOutHdr);
            LeaveCriticalSection(&m_csWaveOut);
               
        break;
    



void WAVEPLAY::WriteBuffer(PWAVEHDR pWaveHdr)

    int             cnt,len2;
    int             decodesize;

    DWORD           index   = pWaveHdr->dwUser;
    uint8_t *       out[]   =  audio_buf ;

    waveOutUnprepareHeader(m_hWaveout, pWaveHdr, sizeof(WAVEHDR));

    memset(m_Block[index], 0, BLOCKSIZE);

    av_read_frame(m_pFmtCtx, &m_AudioPkt);

    int     got_frame;
    int     packet_size = m_AudioPkt.size;

    cnt     = 0;

    do 
        if (m_AudioPkt.stream_index == m_StreamIndex ) 
            decodesize                  = avcodec_decode_audio4(m_pCodecCtx ,m_pAVFrame , &got_frame, &m_AudioPkt);
            if(decodesize > 0 ) 
                if(m_pSwrCtx == NULL)
                    m_pSwrCtx = swr_alloc_set_opts(NULL, AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, m_pCodecCtx->sample_rate, 
                        av_get_default_channel_layout(m_pAVFrame->channels), (AVSampleFormat)m_pAVFrame->format ,m_pAVFrame->sample_rate, 
                        0, NULL);
                    swr_init(m_pSwrCtx);
                
                len2 = swr_convert(m_pSwrCtx, out, AVCODEC_MAX_AUDIO_FRAME_SIZE * 4,
                                (const uint8_t **)m_pAVFrame->extended_data, m_pAVFrame->nb_samples);

                memcpy(m_Block[index],audio_buf,len2 * 4);
                cnt     += len2 * 4 ;
            
            packet_size -= decodesize;
         else 
            packet_size = 0;
        
     while (packet_size > 0);

    m_bPlayOver = FALSE;

    AddWaveOutBuffer(index,cnt);


BOOL WAVEPLAY::IsPLayOver(void)

    return m_bPlayOver;



WAVEPLAY::WAVEPLAY(void)

    m_pFmtCtx       = NULL;
    m_pCodecCtx     = NULL;
    m_pCodec        = NULL;;
    m_pAVFrame      = NULL;
    m_pSwrCtx       = NULL;

    m_bPlayOver = FALSE;



WAVEPLAY::~WAVEPLAY(void)

    CloseHandle(m_hStop);
    DeleteCriticalSection(&m_csWaveOut);

play1.cpp :

#include "stdafx.h"
#include "WAVEPLAY.h"

int _tmain(int argc, _TCHAR* argv[])

    WAVEPLAY wpPlayPCM;

    wpPlayPCM.OpenDevice();
    wpPlayPCM.StartDevice();

    while (!wpPlayPCM.IsPLayOver())
        Sleep(10);

    wpPlayPCM.StopDevice();

    return 0;

计算音频帧的播放时间

以上是关于音频相关的主要内容,如果未能解决你的问题,请参考以下文章

音频相关

Android 自定义View - 柱状波形图 wave view

数字音频技术

在iOS中绘制录音音频波形图

音频相关的基本概念

转:音频与采样的计算