精华内容
下载资源
问答
  • 2018-10-04 14:30:50


    本篇文章将介绍使用FFMpeg解码音频文件为PCM的数据。
    使用FFMpeg获取想要的音频数据的步骤如下:
    解封装(MP3文件)->解码(MP3编码)->PCM数据重采样

    1. 解封装

    使用FFMpeg解封装的步骤如下:

    1. 使用函数 av_register_all() 注册所有的封装器和解封装器。
    2. 使用函数 avformat_open_input() 打开一个文件,可以为文件名也可以为一个URL。
    3. 使用函数 avformat_find_stream_info() 查找流信息,把它存入AVFormatContext中。
    4. 查找流信息,获取音频流的索引位置,获取解码器的codec_id
    5. 根据codec_id,使用函数 avcodec_find_decoder() 获取解码器AVCodec*
    6. 使用函数 avcodec_open2() 打开解码器。

    下面是关键部分代码:

    bool MusicDecodecThread::openAudioFile(QString fileName)
    {
    	av_register_all();
    	m_AvFrame = av_frame_alloc();
    	
        // 打开文件
        int result = avformat_open_input(&m_AVFormatContext, fileName.toLocal8Bit().data(), nullptr, nullptr);
        if (result != 0 || m_AVFormatContext == nullptr)
            return false;
    
        // 查找流信息,把它存入AVFormatContext中
        if (avformat_find_stream_info(m_AVFormatContext, nullptr) < 0)
            return false;
    
        int streamsCount = m_AVFormatContext->nb_streams;
    
        // 读取详细信息
        AVDictionaryEntry *tag = nullptr;
        while (tag = av_dict_get(m_AVFormatContext->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))
        {
            QString keyString = tag->key;
            QString valueString = QString::fromUtf8(tag->value);
            m_InfoMap.insert(keyString, valueString);
        }
    
        // 查找音频流索引
        for (int i=0; i<streamsCount; ++i)
        {
            if (m_AVFormatContext->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC)
            {
                AVPacket pkt = m_AVFormatContext->streams[i]->attached_pic;
                m_InfoImage = QImage::fromData((uchar*)pkt.data, pkt.size);
            }
            if (m_AVFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
            {
                m_AudioIndex = i;
                continue;
            }
        }
    
        if (m_AudioIndex == -1)
            return false;
    
        // 获取总时间
        m_TotalTime = m_AVFormatContext->duration / AV_TIME_BASE * 1000;
    
        // 查找解码器
        m_AudioCodec = m_AVFormatContext->streams[m_AudioIndex]->codec;
        AVCodec *codec = avcodec_find_decoder(m_AudioCodec->codec_id);
        if (codec == nullptr)
            return false;
    
        // 打开音频解码器
        if (avcodec_open2(m_AudioCodec, codec, nullptr) != 0)
            return false;
    
        int rate = m_AudioCodec->sample_rate;
        int channel = m_AudioCodec->channels;
        m_AudioCodec->channel_layout = av_get_default_channel_layout(m_AudioCodec->channels);
    
        return true;
    }
    

    其中:
    AVFormatContext *m_AVFormatContext = nullptr;
    AVCodecContext *m_AudioCodec = nullptr;

    AVFormatContext中存储文件封装的信息,比如我们可以使用这个方法获取音频的总时间长度:
    m_TotalTime = m_AVFormatContext->duration / AV_TIME_BASE * 1000;

    AVCodecContext 中存储了与解码器相关的信息,如
    sample_rate: 表示采样率
    channels: 表示通道数
    sample_fmt: 表示采样的格式。(如AV_SAMPLE_FMT_S16、AV_SAMPLE_FMT_S32等)

    2. 解码

    1. 使用函数 av_read_frame() 获取一包数据。
    2. 使用函数 avcodec_send_packet() 发送一包数据。
    3. 使用函数 avcodec_receive_frame() 获取一帧数据。

    下面是关键部分代码:

    while (!this->isInterruptionRequested())
    {
        QMutexLocker locker(&m_Mutex);
    
        AVPacket pkt;
        int result = av_read_frame(m_AVFormatContext, &pkt);
        if (result != 0)
        {
            QThread::msleep(10);
            continue;
        }
    
        if (pkt.stream_index != m_AudioIndex)
            continue;
    
        // 解码音频帧, 发送音频包
        if (avcodec_send_packet(m_AudioCodec, &pkt))
            continue;
    
        // 解码音频帧,接收音频解码帧
        if (avcodec_receive_frame(m_AudioCodec, m_AvFrame))
            continue;
    
    	// 释放包的内存
    	av_packet_unref(&pkt);
    }
    

    m_AvFrame->data 就存储着解码后的数据。

    3. 对解码后的数据重采样

    这里使用的FFMpeg提供工具(SwrContext)对音频做重采样。使用方法如下:

    1. 使用方法 swr_alloc(); 创建一个 SwrContext* 类型的指针,并分配内存。
    2. 使用方法 swr_alloc_set_opts() 设置输入和输出的参数。
    3. 使用方法 swr_init() 初始化这个 SwrContext* 指针变量。
    4. 使用方法 swr_convert() 转换。
    5. 使用方法 swr_free() 释放内存。

    下面是主要部分代码:

    SwrContext *m_SWRtx = swr_alloc();
    swr_alloc_set_opts(m_SWRtx, m_AudioCodec->channel_layout, AV_SAMPLE_FMT_S16, \
                               m_AudioCodec->sample_rate, m_AudioCodec->channels, m_AudioCodec->sample_fmt, \
                               m_AudioCodec->sample_rate, 0, 0);
    swr_init(m_SWRtx);
    
    uint8_t *array[1];
    uint8_t arrays[10000] = {0};
    array[0] = arrays;
    int len = swr_convert(m_SWRtx, array, 10000, (const uint8_t **)m_AvFrame->data, \
    												m_AvFrame->nb_samples);
    
    swr_free(&m_SWRtx);
    

    我这里使用线程解码,完整代码如下:
    MusicDecodecThread.h

    #ifndef MUSCI_DECODEC_THREAD_H
    #define MUSCI_DECODEC_THREAD_H
    #include <QThread>
    #include <QObject>
    #include <QMap>
    #include <QImage>
    #include <QMutex>
    #include <QMutexLocker>
    #include "AudioPlayThread.h"
    extern "C"{
        #include <stdio.h>
        #include <stdlib.h>
        #include <libavformat/avformat.h>
        #include <libavcodec/avcodec.h>
        #include <libavutil/frame.h>
        #include <libswscale/swscale.h>
        #include <libswresample/swresample.h>
        #include <libavfilter/avfilter.h>
        #include <libavfilter/buffersink.h>
        #include <libavfilter/buffersrc.h>
        #include <libavutil/opt.h>
        #include <libavutil/error.h>
    }
    
    class MusicDecodecThread : public QThread
    {
        Q_OBJECT
    
    public:
        MusicDecodecThread(QObject *parent = nullptr);
        ~MusicDecodecThread();
    
        // 打开文件
        bool openAudioFile(QString fileName);
    
        void run(void) override;
    
        // 获取信息列表中的内容
        QMap<QString, QString> getInfoMap(void);
        // 获取音乐的头像
        QImage getMusicIcon(void);
        // 获取音乐的总时长
        int getTotalTime(void);
    
    private:
        AVFormatContext *m_AVFormatContext = nullptr;
        AVCodecContext *m_AudioCodec = nullptr;
        AVFrame *m_AvFrame;
    
        int m_AudioIndex = -1;
        int m_TotalTime = 0;
    
        QMap<QString, QString> m_InfoMap;
        QImage m_InfoImage;
    
        QMutex m_Mutex;
    };
    
    #endif
    
    

    MusicDecodecThread.cpp

    #include "MusicDecodecThread.h"
    #include <QDebug>
    #include <QTime>
    
    MusicDecodecThread::MusicDecodecThread(QObject *parent)
        :QThread(parent)
    {
        av_register_all();
        avfilter_register_all();
        m_AvFrame = av_frame_alloc();
        g_AudioPlayThread->start();
    }
    
    MusicDecodecThread::~MusicDecodecThread()
    {
    
    }
    
    bool MusicDecodecThread::openAudioFile(QString fileName)
    {
        QMutexLocker locker(&m_Mutex);
        if (m_AVFormatContext)
            avformat_close_input(&m_AVFormatContext);
    
        // 打开文件
        int result = avformat_open_input(&m_AVFormatContext, fileName.toLocal8Bit().data(), nullptr, nullptr);
        if (result != 0 || m_AVFormatContext == nullptr)
            return false;
    
        // 查找流信息,把它存入AVFormatContext中
        if (avformat_find_stream_info(m_AVFormatContext, nullptr) < 0)
            return false;
    
        int streamsCount = m_AVFormatContext->nb_streams;
    
        // 读取详细信息
        AVDictionaryEntry *tag = nullptr;
        while (tag = av_dict_get(m_AVFormatContext->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))
        {
            QString keyString = tag->key;
            QString valueString = QString::fromUtf8(tag->value);
            m_InfoMap.insert(keyString, valueString);
        }
    
        // 查找音频流索引
        for (int i=0; i<streamsCount; ++i)
        {
            if (m_AVFormatContext->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC)
            {
                AVPacket pkt = m_AVFormatContext->streams[i]->attached_pic;
                m_InfoImage = QImage::fromData((uchar*)pkt.data, pkt.size);
            }
            if (m_AVFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
            {
                m_AudioIndex = i;
                continue;
            }
        }
    
        if (m_AudioIndex == -1)
            return false;
    
        // 获取总时间
        m_TotalTime = m_AVFormatContext->duration / AV_TIME_BASE * 1000;
    
        // 查找解码器
        m_AudioCodec = m_AVFormatContext->streams[m_AudioIndex]->codec;
        AVCodec *codec = avcodec_find_decoder(m_AudioCodec->codec_id);
        if (codec == nullptr)
            return false;
    
        // 打开音频解码器
        if (avcodec_open2(m_AudioCodec, codec, nullptr) != 0)
            return false;
    
        int rate = m_AudioCodec->sample_rate;
        int channel = m_AudioCodec->channels;
        m_AudioCodec->channel_layout = av_get_default_channel_layout(m_AudioCodec->channels);
        g_AudioPlayThread->cleanAllAudioBuffer();
        g_AudioPlayThread->setCurrentSampleInfo(rate, 16, channel);
    
        return true;
    }
    
    void MusicDecodecThread::run(void)
    {
        QTime time;
        int count = 0;
        while (!this->isInterruptionRequested())
        {
            QMutexLocker locker(&m_Mutex);
    
            AVPacket pkt;
            int result = av_read_frame(m_AVFormatContext, &pkt);
            if (result != 0)
            {
                QThread::msleep(10);
                continue;
            }
    
            if (pkt.stream_index != m_AudioIndex)
                continue;
    
            // 解码视频帧, 发送视频包
            if (avcodec_send_packet(m_AudioCodec, &pkt))
                continue;
    
            // 解码视频帧,接收视频解码帧
            if (avcodec_receive_frame(m_AudioCodec, m_AvFrame))
                continue;
    
            SwrContext *m_SWRtx = swr_alloc();
            swr_alloc_set_opts(m_SWRtx, m_AudioCodec->channel_layout, AV_SAMPLE_FMT_S16, \
                               m_AudioCodec->sample_rate, m_AudioCodec->channels, m_AudioCodec->sample_fmt, \
                               m_AudioCodec->sample_rate, 0, 0);
            swr_init(m_SWRtx);
    
            uint8_t *array[1];
            uint8_t arrays[10000] = {0};
            array[0] = arrays;
            int len = swr_convert(m_SWRtx, array, 10000, (const uint8_t **)m_AvFrame->data, m_AvFrame->nb_samples);
    
            g_AudioPlayThread->addAudioBuffer((char*)arrays, m_AvFrame->linesize[0]);
    
            swr_free(&m_SWRtx);
    
            av_packet_unref(&pkt);
        }
    }
    
    QMap<QString, QString> MusicDecodecThread::getInfoMap(void)
    {
        QMutexLocker locker(&m_Mutex);
        return m_InfoMap;
    }
    
    QImage MusicDecodecThread::getMusicIcon(void)
    {
        QMutexLocker locker(&m_Mutex);
        return m_InfoImage;
    }
    
    int MusicDecodecThread::getTotalTime(void)
    {
        return m_TotalTime;
    }
    
    
    更多相关内容
  • 包括 2 个用于写入和读取 OGG Vorbis 文件的函数。 它的工作方式类似于命令 WAVWRITE 和 WAVREAD。 1.- 解压到MATLAB目录下的toolbox文件夹即可。 2.- 设置 MATLAB 搜索路径以包含该文件夹。 此版本是在 MATLAB ...
  • 最小的音频文件编码器/解码器节点套接字服务器。 运行LAME和FLAC命令,通过websocket传入和传出音频文件,以最大程度地减少内存使用量。 相关性: , 和 。 一旦这些在命令行上可用,就可以使用。 跑步: node ...
  • aac音频文件,用于测试编解码
  • g711,g726音频解码程序,带测试文件,研究学习。 学习g711,g726挺好的工程,适合研究学习 g711 g726
  • 音频视频编码解码

    2013-08-16 08:38:26
    音频视频编码解码 mpeg
  • 媒体播放器的解码包,支持几乎所有的压缩和使用现代化的视频和音频文件的文件类型。 该软件包是安装简单,同时还提供了高级设置到高端用户: 对于简单的安装选择“简易安装”。 对于高级安装选项中选择“专家安装”...
  • 软件功能 支持的输入模块: libsndfile 音频文件解码器 MAD MPEG 音频文件解码器 Ogg Vorbis 解码器 FLAC 音频文件解码器 BASS 音频文件解码器 CD 音频抓取 支持的输出模块: LAME mp3 编码器 Ogg Vor
  • 内容索引:VB源码,多媒体技术,多媒体 这是个动态链接库DLL的源代码,本动态库的功能主要是在音频处理方面,比如进行音频播放、音频编码解码,它支持DSP、频谱分析以及可视化效果控制等,能实现WAV, MP3, OGG ...
  • 封装格式AVI、VOB、WMV、RM、RMVB、MOV、MKV、FLV、MP4、MP3、WebM、DAT、3gpp、asf、mpeg、ogg; ...音频编码格式: G.711、G722、G726、AAC、MP1/MP2/MP3、AC-3、WMA、amr_nb、amr_wb、RealAudio
  • 音频解码,分贝计算 适用 wav/mp3/aac 16/24/32bit的音频类型 音频可视化
  • 纯前端解码、播放、录音、编码 AMR 音频,无须服务器支持,基于amr.js 和 RecorderJs。 特性 方便的 API 实现解码、播放、录音、编码 AMR 文件。 支持 url 和 blob (即<input type="file">)方式获取 AMR。...
  • 该库集成了所有支持格式(mp3, mp2, mp1, ogg, flac, ac3, aac, oga, wav and pcm )的编码解码器。库本身是由WINAPI编写,你无须额外的库,也不需要MFC / .NET的支持,只能在Windows下运行。 库直接对声卡播放音乐...
  • FFmpeg提取视音频文件

    2018-11-07 16:27:19
    3、掌握和熟悉提取视音频文件的基本方法 二、实验要求 1、对ffmpeg的编译环境进行配置; 2、对一个视频文件,提取基本信息(例如,封装格式,码流,视频编码方式,音频编码方式,分辨率,帧率,时长等等),并输出...
  • 《精通Visual C++视频、音频解码技术》全面、详细地介绍了Visual C++视频/音频解码技术的基本原理和编程知识,并结合工程实际,给出了丰富的实例和大量的Visual C++源代码文件。全书共分为4篇16章,其中“基础篇...
  • G.711u-law编码格式音频文件,可以作为测试实验用,挺好用的。
  • 音频解码编码源代码

    2015-01-10 16:52:17
    功能:读入自己的一段录音文件wav格式等,利用mp3进行编码,保存成mp3 上传内容包含:源码一套、读入的自己的录音文件、保存的mp3文件
  • ac3音频文件解码

    2012-10-11 15:20:56
    ac3音频文件解码器,可播放ac3音频格式文件
  • 音频编码解码原理

    万次阅读 2016-09-13 19:49:16
    作者:yuyin86 视频流中的DTS/PTS到底是什么...mpeg文件中的每一个包都有一个SCR时间戳并且这个时间戳就是读取这个数据包时的系统时间。通常情况下,解码器会在它开始读取mpeg流时启动系统时钟(系统时钟的初始值

    作者:yuyin86

    视频流中的DTS/PTS到底是什么?

    DTS(解码时间戳)和PTS(显示时间戳)分别是解码器进行解码和显示帧时相对于SCR(系统参考)的时间戳。SCR可以理解为解码器应该开始从磁盘读取数据时的时间。
    mpeg文件中的每一个包都有一个SCR时间戳并且这个时间戳就是读取这个数据包时的系统时间。通常情况下,解码器会在它开始读取mpeg流时启动系统时钟(系统时钟的初始值是第一个数据包的SCR值,通常为0但也可以不从0开始)。
    DTS 时间戳决定了解码器在SCR时间等于DTS时间时进行解码,PTS时间戳也是类似的。通常,DTS/PTS时间戳指示的是晚于音视频包中的SCR的一个时 间。例如,如果一个视频数据包的SCR是100ms(意味着此包是播放100ms以后从磁盘中读取的),那么DTS/PTS值就差不多是200 /280ms,表明当SCR到200ms时这个视频数据应该被解码并在80ms以后被显示出来(视频数据在一个buffer中一直保存到开始解码)下 溢通常发生在设置的视频数据流相关mux率太高。
    如果mux率是1000000bits/sec(意味着解码器要以1000000bits/sec的速率 读取文件),可是视频速率是2000000bits/sec(意味着需要以2000000bits/sec的速率显示视频数据),从磁盘中读取视频数据时 速度不够快以至于1秒钟内不能够读取足够的视频数据。这种情况下DTS/PTS时间戳就会指示视频在从硬盘中读出来之前进行解码或显示(DTS/PTS时间戳就要比包含它们的数据包中的SCR时间要早了)。
    如今依靠解码器,这基本已经不是什么问题了(尽管MPEG文件因为应该没有下溢而并不完全符合MPEG标准)。一些解码器(很多著名的基于PC的播放器)尽可能快的读取文件以便显示视频,可以的话直接忽略SCR。
    注意在你提供的列表中,平均的视频流速率为~3Mbps(3000000bits/sec)但是它的峰值达到了14Mbps(相当大,DVD限制在 9.8Mbps内)。这意味着mux率需要调整足够大以处理14Mbps的部分, bbMPEG计算出来的mux率有时候太低而导致下溢。
    你计划让视频流速率这么高么?这已经超过了DVD的说明了,而且很可能在大多数独立播放其中都不能播放。如果你不是这么计划,我会从1增加mquant的值并且在视频设置中将最大码流设置为9Mbps以保持一个小一点的码流。
    如果你确实想让视频码率那么高,你需要增大mux率。从提供的列表可以得出bbMPEG使用14706800bits/sec或者1838350bytes /sec的mux率(总数据速率为:1838350bytes/sec(14706800bits/sec)行)。你在强制mux率字段设置的值应该是以 bytes/sec为单位并被50整除。所以我会从36767(1838350/50)开始,一直增加直到不会再出现下溢错误为止。

    音视频同步原理[ffmpeg]

    ffmpeg对视频文件进行解码的大致流程:
    1. 注册所有容器格式和CODEC: av_register_all()
    2. 打开文件: av_open_input_file()
    3. 从文件中提取流信息: av_find_stream_info()
    4. 穷举所有的流,查找其中种类为CODEC_TYPE_VIDEO
    5. 查找对应的解码器: avcodec_find_decoder()
    6. 打开编解码器: avcodec_open()
    7. 为解码帧分配内存: avcodec_alloc_frame()
    8. 不停地从码流中提取中帧数据: av_read_frame()
    9. 判断帧的类型,对于视频帧调用: avcodec_decode_video()
    10. 解码完后,释放解码器: avcodec_close()
    11. 关闭输入文件:av_close_input_file()
    output_example.c 中AV同步的代码如下(我的代码有些修改),这个实现相当简单,不过挺说明问题。

    音视频同步-时间戳

    媒体内容在播放时,最令人头痛的就是音视频不同步。从技术上来说,解决音视频同步问题的最佳方案就是时间戳:首先选择一个参考时钟(要求参考时钟上的时间是线性递增的);生成数据流时依据参考时钟上的时间给每个数据块都打上时间戳(一般包括开始时间和结束时间);在播放时,读取数据块上的时间戳,同时参考当前参考时钟上的时间来安排播放(如果数据块的开始时间大于当前参考时钟上的时间,则不急于播放该数据块,直到参考时钟达到数据块的开始时间;如果数据块的开始时间小于当前参考时钟上的时间,则“尽快”播放这块数据或者索性将这块数据“丢弃”,以使播放进度追上参考时钟)。
    可见,避免音视频不同步现象有两个关键——一是在生成数据流时要打上正确的时间戳。如果数据块上打的时间戳本身就有问题,那么播放时再怎么调整也于事无补。假如,视频流内容是从0s开始的,假设10s时有人开始说话,要求配上音频流,那么音频流的起始时间应该是10s,如果时间戳从0s或其它时间开始打,则这个混合的音视频流在时间同步上本身就出了问题。打时间戳时,视频流和音频流都是参考参考时钟的时间,而数据流之间不会发生参考关系;也就是说,视频流和音频流是通过一个中立的第三方(也就是参考时钟)来实现同步的。第二个关键的地方,就是在播放时基于时间戳对数据流的控制,也就是对数据块早到或晚到采取不同的处理方法。图2.8中,参考时钟时间在0-10s内播放视频流内容过程中,即使收到了音频流数据块也不能立即播放它,而必须等到参考时钟的时间达到10s之后才可以,否则就会引起音视频不同步问题。
    基于时间戳的播放过程中,仅仅对早到的或晚到的数据块进行等待或快速处理,有时候是不够的。如果想要更加主动并且有效地调节播放性能,需要引入一个反馈机制,也就是要将当前数据流速度太快或太慢的状态反馈给“源”,让源去放慢或加快数据流的速度。熟悉DirectShow的读者一定知道,DirectShow中的质量控制(Quality Control)就是这么一个反馈机制。DirectShow对于音视频同步的解决方案是相当出色的。但WMF SDK在播放时只负责将ASF数据流读出并解码,而并不负责音视频内容的最终呈现,所以它也缺少这样的一个反馈机制。
    音视频同步通讯SDK源码包分享:
    Android:http://down.51cto.com/data/711001
    Windows:http://down.51cto.com/data/715497
    Linux:http://download.csdn.net/detail/weixiaowenrou/5169796
    IOS:http://down.51cto.com/data/715486
    WEB:http://down.51cto.com/data/710983

    音频编码和解码原理

    音频编码和解码原理

    每张CD光盘重放双声道立体声信号可达74分钟。VCD视盘机要同时重放声音和图像,图像信号数据需要压缩,其伴音信号数据也要压缩,否则伴音信号难于存储到VCD光盘中。

    一、伴音压缩编码原理
    伴音信号的结构较图像信号简单一些。伴音信号的压缩方法与图像信号压缩技术有相似性,也要从伴音信号中剔除冗余信息。人耳朵对音频信号的听觉灵敏度有其其规律性,对于不同频段或不同声压级的伴音有其特殊的敏感特性。在伴音数据压缩过程中,主要应用了听觉阈值及掩蔽效应等听觉心理特性。

    1、阈值和掩蔽效应
    (1) 阈值特性
    人耳朵对不同频率的声音具有不同的听觉灵敏度,对低频段(例如100Hz以下)和超高频段(例如16KHZ以上)的听觉灵敏度较低,而在1K-5KHZ的中音频段时,听觉灵敏度明显提高。通常,将这种现象称为人耳的阈值特性。若将这种听觉特性用曲线表示出来,就称为人耳的阈值特性曲线,阈值特性曲线反映该特性的数值界限。将曲线界限以下的声音舍弃掉,对人耳的实际听音效果没有影响,这些声音属于冗余信息。
    在伴音压缩编码过程中,应当将阈值曲线以上的可听频段的声音信号保留住,它是可听频段的主要成分,而那些听觉不灵敏的频段信号不易被察觉。应当保留强大的信号,忽略舍弃弱小的信号。经过这样处理的声音,人耳在听觉上几乎察觉不到其失真。在实际伴音压缩编码过程中,也要对不同频段的声音数据进行量化处理。可对人耳不敏感频段采用较粗的量化步长进行量化,可舍弃一些次要信息;而对人耳敏感频段则采用较细小的量化步长,使用较多的码位来传送。
    (2)掩蔽效应
    掩蔽效应是人耳的另一个重要生理特征。如果在一段较窄的频段上存在两种声音信号,当一个强度大于另一个时,则人耳的听觉阈值将提高,人耳朵可以听到大音量的声音信号,而其附近频率小音量的声音信号却听不到,好像是小音量信号被大音量信号掩蔽掉了。由于其它声音信号存在而听不到本声音存在的现象,称为掩蔽效应。
    根据人耳的掩蔽特性,可将大音量附近的小音量信号舍弃掉,对实际听音效果不会发生影响。既使保留这些小音量信号,人耳也听不到它们的存在,它属于伴音信号中的冗余信息。舍弃掉这些信号,可以进一步压缩伴音数据总量。
    经仔细观察,掩蔽效应分为两大类,一类是同时掩蔽效应,另一类是短时掩蔽效应。其中,同时掩蔽效应是指同时存在一个弱信号和一个强信号,两者频率接近,强信号将提高弱信号的听阈值,将弱信号的听阈值提高到一定程度时,可使人耳听不到弱信号。例如,同时出现A、B两声,若A声的听觉阈值为50dB,由于存在另一个不同频率的B声,将使A声的阈值提高到64~68dB,例如取68dB,那么数值(68~50)dB=18dB,该值称为掩蔽量。将强大的B声称为掩蔽声,而较弱的A声称为被掩蔽声。上述掩蔽现象说明,若仅有A声时,其声压级50dB以上的声音可以传送出去,而50dB以下的声音将听不到;若同时出现B声,B声具有同时掩蔽效应,使得A声在声压级68dB以下的声音也听不到了,即50~68dB之间的A声人耳也听不到了,这些声音不必传送,即使传送也听不到,只须传送声压级68dB以上的声音。总之,为了提高一个声音的阈值,可以同时设置另一个声音,使用这种办法可以压缩掉一部分声音数据。在周围十分安静的环境下,人耳可以听到声压级很低的各种频率声音,但对低频声和高频声的掩蔽阈值较高,即听觉不灵敏。经研究还发现,掩蔽声越强,掩蔽作用越强;当掩蔽声与被掩蔽声的频率相差越小,掩蔽效果越明显,两者频率相等时,掩蔽效果最佳;低频声(设为B)可有效地掩蔽高频声(设为A),而高频声(设为B)几乎不能掩蔽低频声(设为A)。因而输入信号时,在受掩蔽的频带内加入更大的噪声时,人耳也感觉不到与原始信号有所区别。上述的同时掩蔽效应,又称为频域掩蔽效应,它主要反映在频域方面对掩蔽作用的影响。在声音压缩编码中,更多地使用单频声音的掩蔽效应。
    如果A声和B声不同时出现,也可发生掩蔽作用,称它为短时掩蔽效应。短时掩蔽又可分为两种类型,作用仍可持续一段时间,即后向掩蔽和前向掩蔽。后向掩蔽是指掩蔽声B消失后,其掩蔽作用仍可持续一段时间,一般可达0.5~2秒。掩蔽机理是人耳的存储效应所致。而前向掩蔽是指被掩蔽声A出现一段时间后出现掩蔽声B,只要A、B声音隔不太大(一般在0.05~0.2秒以内),B也可对A起掩蔽作用。掩蔽机理是A声尚未被人耳感知接受时,强大的B声已来临所致。在实践中,后向掩蔽有较高的应用价值。短时掩蔽效应具有很强的时域结构特性,故又称为时域掩蔽效应。在声音压缩编码中,应兼顾好人耳的频域和时域两种掩蔽效应。

    2、子带编码原理
    (1)子带编码和解码过程
    所谓子带编码技术,是将原始信号由时间域转变为频率域,然后将其分割为若干个子频带,并对其分别进行数字编码的技术。它是利用带通滤波器(BPF)组把原始信号分割为若干(例如m个)子频带(简称子带)。

    图2.3.1子带编码示意图 http://www.elecfans.com

    在接收端实现发送端的逆过程。输入子带编码数据流,将各子带信号分别送到相应的数字解码电路(共m个)进行数字解调,经过诸路低通滤波器(m路),并重新解调,可把各子带频域恢复为当初原始信号的分布状态。最后,将各路子带输出信号送到同步相加器,经过相加恢复为原始信号,该恢复的信号与原始信号十分相似。
    (2)子带编码的应用
    子带编码技术具有突出的优点。首先,声音频谱各频率分量的幅度值各不相同,若对不同子带分配以合适的比例系数,可以更合理地分别控制各子带的量化电平数目和相应的重建误差,使码率更精确地与各子带的信号源特性相匹配。通常,在低频基音附近,采用较大的比特数目来表示取样值,而在高频段则可分配以较小的编码比特。其次,通过合理分配不同子带的比特数,可控制总的重建误差频谱形状,通过与声学心理模型相结合,可将噪声频谱按人耳主观噪声感知特性来形成。于是,利用人耳听觉掩蔽效应可节省大量比特数。
    在采用子带编码时,利用了听觉的掩蔽效应进行处理。它对一些子带信号予以删除或大量减少比特数目,可明显压缩传输数据总量。比如,不存在信号频率分量的子带,被噪声掩蔽的信号频率的子带,被邻近强信号掩蔽的信号频率分量子带等,都可进行删除处理。另外,全系统的传输信息量与信号的频带范围、动态范围等均有关系,而动态范围则决定于量化比特数,若对信号引入合理的比特数,可使不同子带内按需要给以不同的比特数,也可压缩其信息量。

    二、MPEG-1音频编码方框图

    1、MPEG-1音频编码的依据
    MPEG-1音频压缩编码标准采用了心理学算法。利用感知模型删去那些听觉不灵敏的声音数据,而使重建的声音质量无明显下降。它采用子带编码技术,根据心理声学模型取得不同子带的听觉掩蔽阈值;对各子带的取样值进行动态量化。它根据不同频段上大音量信号所引起的小音量信号掩蔽阈值的变化规律,对不同频段给以不同的量化步长,以便保留主要信号,而舍弃对听觉效果影响很小的成分,经过数据压缩,可取得合理的比特流,将原来大约1.5Mbit/s的声音传输码率减少到0.3Mbit/s,即压缩率可达到1/5。

    2、编码流程
    图2.3.2是基于MUSICAM(掩蔽模式通用子带编码和多路复用)的MPEG-1音频压缩编码方框图。输入信号是经过取样的二进制PCM数字音频信号,取样频率可以取44.1KHz、48KHz或32KHz,该音频数码信号的码值与原来采样信号的幅度、频率成正比。
    图2.3.2 MPEG-1音频编码方框图 http://www.elecfans.com

    数字音频信号首先进入数字滤波器组,它被分成等带宽的32个子频带,可由数字滤波器输出32个子带数据信号。这种处理方法与图像编码信号进行DCT变换的作用相似,但不是像图像信号那样分为64种余弦频率信息,这里仅分成32个子带,即将音频数据流改为32种频率的组合。声音的分解力低于图像,这种处理方法是可行的。然后,对32个子带的伴音数据进行再量化,以便再压缩数据量。对于各个子频带的量化步长不相同,量化步长是根据人耳的听觉阈值和掩蔽效应而确定的。经过量化处理的已压缩数据,保留了伴音信息的主体部分,而舍弃了听觉效果影响较小的伴音信息。
    进入编码系统的输入信号,分流部分信号送到并列的1024点快速傅利叶变换器(FFT)进行变换,它检测输入信号每一个瞬间取样点在主频谱分量频域的分布的强度,经变换的信号送到心理声学模型控制单元。根据听觉心理声学测量统计结果,可以归纳出一个心理声学控制对照表格,并按照此表格制成控制单元,而单元电路可以集中地反映出人耳的阈值特性和掩蔽特性。
    经过量化的32个子频带数据已经被压缩,还要加上比例因子、位分配信息等辅助信息,共同加到1位流格式化单元,编码成为两个层次的伴音编码信号。它既含有32个子频带的伴音数码,又带有这些数码所对应的位分配数据和不同频带数据的强弱比例因子。待将来数据解码时,可根据各子频带的数据恢复声音信号,以及压缩时码位分配和强弱比例情况,在进行反量化时,参照压缩时的程序进行还原。
    可见,伴音的压缩编码和图像处理一样,也要经过变换、量化、码位压缩等处理过程,它运用了许多数学模型和心理听觉测量的统计数据,对32个子频带和各个层次信号的处理也各有不相同的取样速率。实际的心理听觉模型和适时处理控制过程十分复杂。这些算法细节都已按硬件方式被固化在解码芯片中,这些内容不能再改变。

    3、伴音与图像的同步
    图像和声音信号的压缩方法有许多不同,图像数据量又远远大于声音数据量,两者传送的数据码率大不相同。每传送14~15个视频数据包才传送1个音频数据包,而播放声音和图像的内容又必须作到良好同步,否则将无法保证视听统一的效果。
    为了作到声图同步,MPEG-1采用了独立的系统时钟(简称为STC)作为编码的参照基准,并将图像和声音的数据分为许多播放单元。例如,将图像分为若干帧,将声音分为若干段落。在数据编码时,在每个播放单元前面加置一个展示时标(PTS),或者加置一个解码时标(DTS)。当这些时标出现时,表示前一个播放单元已经结束,一个新的图像和声音播放单元立即开始。在播放相互对应的同一图像单元和声音单元时,可实现互相同步。
    为了使整个系统在时钟在编码和重放时,声图有共同的时钟基准,又引入系统参考时钟SCR的概念。系统参考时钟是一个实时时钟,其数值代表声图的实际播放时间,用它作为参照基准,以保证声图信号的传输时间保持一致。实时时钟SCR必须与生活中的真实时间一致,要求它的准确度很高,否则可能发生声音和图像都播快或播慢的现象。为了使SCR时间基准稳定、准确,MPEG-1采用了系统时钟频率SCF,以它作为定时信息的参照基础。SCF系统时钟的频率是90KHz,频率误差为90KHz±4.5KHz。声图信号以SCF为统一的基准,其它定时信号SCR、PTS、DTS也是以它为基础。

    三、其它MPEG标准的音频编码器

    1、MPEG-2音频编码方框图
    MPEG-1是处理双声道立体声信号,而MPEG-2是处理5声道(或7声道)环绕立体声信号,它的重放效果更加逼真。
    图2.3.3是MPEG-2音频编码方框图。它输入互相独立的5声道音频信号,有前置左、右主声道(L、R),前置中央声道(C),还有后置左、右环绕声道(LS、RS)。各声源经过模-数转化后,首先进入子带滤波器,每一声道都要分割为32个子频带,各子带的带宽均为750Hz。为了兼容MPEG-1、普通双声道立体声和环绕模拟立体声等编码方式,原来按MPEG-1编码的立体声道能够扩展为多声道,应当包括所有5声道的信息,为此设置了矩阵变换电路。该电路可生成兼容的传统立体声信号LO、RO,还有经过“加重”的左、中、右、左环绕、右环绕声音信号(共5路)。对5路环绕立体声信号进行“加重”处理的原因:当计算兼容的立体声信号(LO、RO)时,为了防止过载,已在编码前对所有信号进行了衰减,经加重处理可以去失真;另外,矩阵转变中也包含了衰减因子和类似相移的处理。
    编码器原始信号是5路,输入通道是5个,经过矩阵转化处理后产生了7种声音信号。应当设置通道选择电路,它能够根据需要,对7路信号进行合理的选择处理。该处理过程决定于解矩阵的过程,以及传输通道的分配信息;合理的通道选择,有利于减弱人为噪声加工而引起的噪声干扰。此外,还设置了多声道预测计算电路,用于减少各通道间冗余度。在进行多声道预测时,在传输通道内的兼容信号LO、RO,可由MPEG-1数据计算出来。根据人耳生理声学基

    图2.3.3  MPEG-2音频编码方框图

    础,后级设置了动态串话电路,可在给定比特的情况下提高声音质量,或在要求声音质量的前提下降低比特率。但设置该电路增加了MPEG-2解码器的复杂程度。
    经过编码器产生了多种信息,主要有编码取样值,比例因子,比特分配数据,动态串话模式,多声道预测信息,通道预测选择信号等,诸信息传递给复接成帧模块电路,最后以MPEG-2比特流形式输出压缩编码信号。
    MPEG-2解码器基本上是编码器的逆过程,其电路结构简单一些,运算量小一些。解码器的解码转换矩阵可输出5路信号,再经过32分频子带滤波器处理,可输出LS、L、C、R、RS信号;另外,经过量化、SCF和子带滤波器处理后,还可以取得前置立体声LO、RO,共计可输出7路音频信号。

    2、MPEG-4音频解码
    MPEG-4音频编码和MPEG-4视频编码一样,具有许多特点和功能,例如可分级性,有限时间音频流,音频变化/时间尺度变化,可编辑性,延迟性等。它具优越的交互性能和高压缩比。它不仅利用分级方法可对语言和音乐进行编辑,也能解决合成语言和音乐问题,它将成为多媒体世界的一个主要格式,将成为“全能”的系统。
    通过MPEG-4音频编码,可以存储、传送多种音频内容。它具有高质量的音频信号(单声道、立体声和多通道)。它采用低码率编码,而声音重放质量很高。它可以传送宽带语言信号(例如7KHz宽的语音),也可传送窄带宽语言信号(例如长途电话)。可以传输、制作可理解的各种语音信号。可以合成语言,例如进行音素或其它记号为基础的文本转换;也可以合成音频,例如支持音乐描述语言。

    四、杜比AC-3技术
    1、什么是杜比AC-3
    在杜比定向逻辑环绕声技术的基础上,于1990年杜比公司与日本先锋公司合作,采用先进的数位压缩技术,推出新颖的全数字化杜比数码环绕声系统。它可使多声道信号有更多的信息被压缩到双声道中去,并将这种系统称为AC-3。AC是英语“音频感觉编码系统”的缩写词。AC-3技术首先应用到电影院,后来又进入普通家庭。
    杜比AC-3系统设置完全独立的6个声道,即全频带的左、中、右、左环绕和右环绕声道,再加上一个超重低音声道。由于这样声道的结构,AC-3系统又称为5.1声道。

    2、杜比AC-3的基本原理
    (1)应用听觉掩蔽效应开发出自适应编码系统
    AC-3技术的理论基础,也是利用心理声学中的听觉阈值和掩蔽效应,但具体技术上与MPEG标准又有所不同。
    对音频信号进行数据处理时,都要进行数据压缩,将没有用途或用途不大的数据信息忽略掉。为此,可以应用听觉阈值和掩蔽规律,省略掉那些多余的数据信息。杜比公司除运用上述声学原理外,还运用了它拥有的杜比降噪技术,开发出数码化的“自适应编码”系统。这是一种极具选择性和抑制噪声能力的自适应编码体系。杜比公司依据音响心理学的基本原理,在未输入音乐信号时,保持宁静状态;当输入音乐信号时,对复杂的音频信号进行分析和分解,用较强信号掩蔽噪声,删除听觉界限以外,或由于频率相近而音量小的信号,经过这种处理方法,可以大大减少需要处理的数据信息。人耳的听觉范围是20Hz-20KHz,在如此宽阔的频带范围内,人耳对不同频率的听觉灵敏度具有极大的差异。杜比AC-3根据这个特性,将各声道的音响频道划分为许多大小不等的狭窄频带,各个子频带与人耳临界频带的宽度相接近,保留有效的音频,将不同的噪声频率紧跟每个声道信号进行编码,即编码噪声只能存在于编码音频信号的频带内。这样能够更陡峭地滤除掉编码噪声,将频带内多余信号和无音频信号的编码噪声降低或除掉,而将有用的音频信号保留下来。AC-3系统精确地运用了掩蔽效应和“公用位元群”的设计方法,使数据压缩效率大大提高,且具有很高水平的音质。该系统的比特率是根据个别频谱的需要,或者音源的动态状况,再分配到每个窄频段,它设计了内置的听觉掩盖程序,可让编码器改变其频率灵敏度和时间分解力,以确保有充足的比特被采用,掩盖掉噪声,而良好地记录音乐信号。
    为了高效地利用有限的信息传输介质(光盘、胶片等),它在压缩音频信号时与其它压缩系统一样,利用人耳的听觉特性,根据当时的具体情况,将某些声道的系数合并(这些声道系数反映了那个频带的能量大小),以便提高压缩率。并不是所有声道都能进行这种合并。编码器可根据各声道的信息特征自动决定和调整,只有相似的声道才能混合在一起,若压缩比不要求很高时也不必合并。一般情况下,合并的起始频率越高,音质就越好,但要求数据传输速率也越高。当取样频率为48KHz时,合并的起始频率应为3.42MHz;若取样频率为44.1KHz时,起始频率应为3.14MHz。若硬件和软件搭配适当,AC-3的音质可达到或接近CD唱片的水平。
    (2)杜比AC-3解码器简易方框图
    AC-3解码器输入信号是一组频谱信号,它是由时域信号PCM数据经过时-频变换而得到。该频谱数据流分为指数部和尾数部两部分,指数部分采用差分方式进行编码,编码后的指数代表了整个信号的频谱,可作为频谱包络的参数。其尾数部分按照比特分配的结果进行量化。于是,量化尾数和频谱包络形成了AC-3码流的主要信息,连同其它辅助信号(例如比特分配等)构成了AC-3比特流。
    图2.3.4是AC-3系统的解码方框图,它是AC-3编码的逆过程。AC-3比特流首先进入缓冲级,然后以帧为处理单元进行误码纠错,经纠错处理后对比特流中的固定数据(指数数据、匹配系数、模式符号等)解码,使数据比特流恢复为原来的比特分配。
    图2.3.4  AC-3解码器方框图

    然后,数据信号分为两路。其中一路,将比特流恢复为原来的比特分配之后,确定尾数部量化的大小,再对比特流中的可变数据解码;再接着恢复高频成分,为反频率变换做好准备。最后,将指数部数据和尾数部数据汇合,变换为固定小数点数据,再对它进行频率变换,以获得时间轴数据。已经恢复为时域的数据信号需进行窗处理,进行重叠加算,即可得到5.1环绕声道的输出信号。

    3、杜比AC-3的特点
    (1)配置5.1声道
    将输入的音频信号解码后,可以输出5.1声道信号,其中有3个前置声道(L、C、R),还有2个后置环绕声道(LS、RS),它们互相独立,频响宽度都是全声频域,即20Hz-20KHz(±0.5dB)及3Hz-20.3KHz(-3dB),各频道的频响十分宽阔。目前,广泛应用于音响系统的杜比定向逻辑环绕声系统,无法和杜比AC-3频带宽度相比。还有,杜比定向逻辑环绕声系统实为4声道系统,即前置左、中、右和后置环绕声,它的环绕声实为单声道环绕声,两个后置环绕声道重放共同的声音信号,两声道采取并联甚至串联方式;其环绕声的频响被限制在100Hz-7KHz范围内;另外,它没有设置独立的超低音声道,它是由前置左、右声道分离出20Hz-120Hz的超重低音,来重放具有震撼效果的超重低音。AC-3系统配置了独立的超低声道,其频响为20Hz-120HZ(±0.5dB)及3Hz-121Hz(-3dB),要求超低音箱的音量比其它各声道大10dB,具有更加震撼的低效果。
    (2)各声道全数字化且互相独立
    AC-3各声道互相独立地携带不同信号,是全数字化音频信号。取样频率是32、44.1或48KHz,数据传输量每声道为32kb/s-640kb/s,在5.1声道模式下取典型值384kb/s,在双声道模式下典型值为192kb/s。经过数字处理后,5个主声道的频率压缩在20Hz-20KHz范围内。
    (3)可将5.1声道压缩输出
    由于AC-3的“比特流”内对每种节目方式(单声道、立体声、环绕声等)都有一个“指导信号”,能使AC-3自动地为使用者指出节目方式。它可把5.1声道信号压缩为双声道,以供给录制常规VHS录像带,或作为杜比环绕声的输入节目源,以便与它兼容,它甚至可将5.1声道信号压缩为单声道输出。总之,AC-3可输出5.1声道杜比环绕声、混合4声道杜比环绕声、双声道立体声及单声道。将5.1声道数据压缩后所占频带较窄,例如可在LD影碟机的FM调制的右声道所占用的频带宽度内,编入AC-3数据编码,输出AC-3的RF信号,它的中心频率取在2.88MHz,可由LD原先的模拟输出右声道取出频率为2.88MHz的AC-3编码信号。于是,在原有一个模拟声道内就能够容纳5.1声道的全部内容。
    (4)经过声音时间校准使音效极为理想
    杜比AC-3将所有声道通过“时间校准”技术,使每个扬声器的声音好像与聆听者的距离相同,以产生更好的音响效果,其环绕声效果不仅是前、后、左、右的声源定位鲜明,上下的音场也清晰可辨。

    展开全文
  • Wav2MP3 向导是日常音频编码/解码的简单解决方案。 它支持 WAV、MP3、OGG 和 FLAC 文件,并具有直观的界面。 新人易于学习(带有预设配置文件),并且具有足够的高级功能。
  • 本产品主要采用的核心技术为MP3音频编码算法,其技术指标分述如下: 标准:与ISO 11172-3 Audio Encoding Layer Ⅲ 完全兼容,编码输出文件可由主流媒体播放软件(RealPlayer、Windows Media Player)直接...
  • 音频文件格式解析与编解码

    千次阅读 2019-05-22 18:45:33
    说到语音技术就不得不说起音频数据,从硬件设备采集语音信号,语音信号的处理,到语音信号A/D转换得到原始数据(raw data),再到对原始数据进行编码得到音频文件,对音频文件解码进行播放。那么为什么会出现如此多的...

    一、音频格式总览

            说到语音技术就不得不说起音频数据,从硬件设备采集语音信号,语音信号的处理,到语音信号A/D转换得到原始数据(raw data),再到对原始数据进行编码得到音频文件,对音频文件解码进行播放。那么为什么会出现如此多的音频格式?使用最多的几种音频格式有MP3、WMA、WAV、AAC、FLAC、APE、WV、ASF、VQF、MID、OGG、M4A、eAAC+。目前只用到了其中三种,故详细分析WAV、MP3、OGG。

    二、原始数据(raw data,以PCM编码为例)

          模拟音频信号经模数转换(A/D变换)直接形成的二进制序列,该文件没有附加的文件头和文件结束标志。而PCM(Pulse-code Modulation,脉冲编码调制)是一种模拟信号的数字化方法,常被用于数字电信系统中,非常频繁地,PCM编码以一种串行通信的形式,使数字传讯由一点至下一点变得更容易——不论在已给定的系统内,或物理位置。

           PCM过程放在ALSA的实践中作为理论部分呈现,这里给出多通道音频数据的比特位特征。

                        

    图1 多通道音频数据的比特位格式

                                                                               图1 多通道音频数据的比特流格式

           如图1所示,单通道音频数据以采样位数(bit)串行记录在比特流中:

             1) 8 bit 采样位数: 意味着每个采样值能占据1个字节大小;

    +------+------+------+------+------+------+------+------+------+
    |  500 |  300 | -100 | -20  | -300 |  900 | -200 |  -50 |  250 |      
    +------+------+------+------+------+------+------+------+------+

              2)16 bit 采样位数:分为两个字节以小端(little-endian)方式存储在比特流中;

           双通道及多通道数据音频数据以采样位数组织如下(n 为采样位数):

     +-- n bit---+---n bit---+-----+---n bit---+---n bit--+------+--n bit---+---n bit---+----------+
     |  channel1 |  channel2 | ... | channel1  | channel2 |  ... | channel1 |  channel2 |  ... |      
     +----------+----------+---------+----------+---------+----------+---------+----------+----------+

         PCM的每个样本值包含在一个整数i中,i的长度为容纳指定样本长度所需的最小字节数。首先存储低有效字节,表示样本幅度的位放在i的高有效位上,剩下的位置为0。

            计算机读入原始音频数据的方式与打开二进制文件相同。

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        char *fn;
        char *data;
        FILE *fp;
        int len;
    
        fn = "./test.pcm";
        fp = fopen(fn, "rb");
        if(!fp) {
            printf("file open failed.\n");
            exit(1);
        }
        fseek(fp, 0, SEEK_END);
        len = ftell(fp);
        if(!len) {
            printf("file is null.\n");
            fclose(fp);
            exit(1);
        }
        printf("file len = %d\n",len);
        data = (char *)malloc(len);
        fseek(fp, 0, SEEK_SET);
        fread(data, sizeof(__int16_t), len/sizeof(__int16_t), fp);
        fclose(fp);
        printf("%.*s\n",1,data);
        free(data);
        return 0;
    }

    三、wav文件

            wav(Waveform Audio FIle Format),即波形声音文件,由微软公司开发,是最早的数字音频格式,音质与CD相差无几,但缺点是wav文件非常庞大,不利于数据传输。

    struct waitor_wav_format
    {
    #pragma pack(1)
        char riff_id[4];                     //  "RIFF",big-endian
        __uint32_t file_len;                 //  file length,little-endian
        char wave_id[4];                     //  "WAVE“,big-endian
        char fmt_id[4];                      //  "fmt",big-endian,beginning of fmt chunk
        char transition[4];                  //  size of fmt chunk
        __uint16_t fmt_type;                 //  1-PCM
        __uint16_t channel;                  //  通道数
        __uint16_t sample_rate;              //  采样率
        __uint32_t avg_bytes_per_sec;        //  sample_rate * block_align
        __uint16_t block_align;              //  每次采样大小
        __uint16_t bit_per_second;           //  采样精度
        //__uint16_t cbsize                    //  附加数据大小
        char data_id[4];                     //  "data"
        __uint32_t audio_len;                //  音频数据的长度
    #pragma pack()
    };

           wav文件分为3个(或4个)chunk,每个chunk基本格式为 chunk_id + chunk_size + chunk_data。总体结构为 文件头 wav_hdr + 音频数据 PCM格式。在解析wav文件时,可能会由于不同系统不同调制方式方式的不同增加几个额外的字节,这时应在数据结构中加入#pragma pack(1),以设置对齐的方式空出额外长度。

    int main()
    {
        waitor_wav_t *w;
        char *fn, *audio;
        // char buffer[4096];
        int ret;
    
        FILE *fp = fopen("./enc.ogg", "wb");
    
        fn = "../data/test.wav";
        w = waitor_wav_read(audio, fn);                    // 解析wav文件格式,转为二进制流voice
        if(!w) {
            printf("wavfile read failed.\n");
            fclose(fp);
            free(w);
            exit(1);
        }
        printf(" chunk_id1 : %.*s\n", 4, w->riff_id);
        printf(" file len : %d\n", w->file_len);
        printf(" fomat : %.*s\n", 4, w->wave_id);
        printf(" sub chunk id : %.*s\n", 4, w->fmt_id);
        printf(" fmt type : %d\n", w->fmt_type);
        printf(" channel : %d.\n", w->channel);
        printf(" sample_rate : %d \n", w->sample_rate);
    
        fclose(fp);
        free(w);
        return 0;
    }

    四、MP3文件

            MP3(Moving Picture Experts Group Audio Layer III),是利用同名压缩技术,将音乐以1:10甚至1:12的压缩率压缩成容量较小的文件,并且能够保证损失音质极少,也因此成为网络音乐传输的主要格式。特点:兼具少损音质和小体积的特点,缺点是最高比特率是320K,高频部分一刀切,音质不高。

            MPEG压缩有

            MP3音频压缩包含编码和解码两个部分。编码是将WAV文件中的数据转换成高压缩率的位流形式,解码是接受位流并将其重建到WAV文件中。

            MP3采用了感知音频编码(Perceptual Audio Coding)这一失真算法。人耳感受声音的频率范围是20Hz-220kHz,MP3截掉了大量的冗余信号和无关的信号,编码器通过混合滤波器组将原始声音变换到频率域,利用心理声学模型,估算刚好能被察觉到的噪声水平,再经过量化,转换成Huffman编码,形成MP3位流。解码器要简单得多,它的任务是从编码后的谱线成分中,经过反量化和逆变换,提取出声音信号。

            在压缩音频数据时,先将原始声音数据分成固定的分块,然后作顺向MDCT变换,MDCT本身并不进行数据压缩,只是将一组时域数据转换成频域数据,以得知时域变化情况,顺向MDCT将每块的值转换为512个MDCT系数。量化使数据得到压缩,在对量化后的变换样值进行比特分配时要考虑使整个量化块最小,这就成为有损压缩了。解压时,经反向MDCT将512个系数还原成原始声音数据,前后的原始声音数据是不一致的,因为在压缩过程中,去掉了冗余和不相关数据。

           MP3文件的详细格式参见以下两个博客:

           MP3文件格式全解

           MP3文件结构解析(超详细)

             libmad库常被用来作为mp3解码器,不需要自己造轮子,基于libmad的mp3解码播放器参见:

           基于libmad的解码播放器

     接下来总结MP3文件的格式与libmad解码应用过程。

       1、MPEG压缩格式

            MP3文件格式分为三个部分:

     标签头和标签帧数据结构,每个ID3V2都包含一个标签头和若干标签帧,而标签帧由帧头和至少一个字节的内容组成。

    ID3V2有四个版本,但流行的只有ID3V2.3:

    /* ID3V2 的标签头结构 */
    struct ID3V2Header
    {
    #pragma pack(1)
        unsigned char Header[3];   /* 保存的值比如为"ID3"表示是ID3V2 */
        unsigned char Version;     /* 如果是ID3V2.3则保存3,如果是ID3V2.4则保存4 */
        unsigned char Revision;    /* 副版本号 */
        unsigned char Flag;        /* 标志,使用高三位,其它位为0 */
        unsigned char Size[4];     /* 整个标签帧大小,除去本结构体的 10 个字节 */
    #pragma pack()
    };
    
    /* 标签帧帧头的结构 */
    struct ID3V2Frame
    {
    #pragma pack(1)
        unsigned char id[4];       /* 标志帧,说明其内容,例如作者/标题等*/
        char size[4];              /* 标志帧大小 */
        char flags[2];             /* 标志帧,只定义了6位 */
    #pragma pack()
    }

    ID3V1:

    /* ID3V1信息结构 */
    struct ID3V1
    {
        char Header[3];   /* 标签头"TAG",标识ID3V1 */
        char Title[30];   /* 歌名 */
        char Artist[30];  /* 作者 */
        char Album[30];   /* 专辑 */
        char Year[4];     /* 年份 */
        char Comment[30]; /* 备注 */
        char Genre;       /* 类型 */
    }

     音频数据帧由帧头和音频数据组成:

    struct DataFrameHeader
    {
        unsigned int bzFrameSyncFlag1:8;   /* 全为 1 */
        unsigned int bzProtectBit:1;       /* CRC */
        unsigned int bzVersionInfo:4;      /* 包括 mpeg 版本,layer 版本 */
        unsigned int bzFrameSyncFlag2:3;   /* 全为 1 */
        unsigned int bzPrivateBit:1;       /* 私有 */
        unsigned int bzPaddingBit:1;      /* 是否填充,1 填充,0 不填充
        layer1 是 4 字节,其余的都是 1 字节 */
        unsigned int bzSampleIndex:2;     /* 采样率索引 */
        unsigned int bzBitRateIndex:4;    /* bit 率索引 */
        unsigned int bzExternBits:6;      /* 版权等,不关心 */ 
        unsigned int bzCahnnelMod:2;      /* 通道
        * 00 - Stereo 01 - Joint Stereo
        * 10 - Dual   11 - Single
        */
    };

       2、MPEG解码(以libmad-0.15.1b源代码为例)

           libmad运行demo为minimad.c,从其中的用法可以看出需要用户设置几个回调函数已控制程序运行。分别为input, output,header,filter 和error 。其中input实现mp3文件输入并转换为mad_stream,output实现根据用户需求对数据的再处理。

            使用libmad几个封装好的基本结构如下:

    /* 存储未解码的比特流 */
    struct mad_stream {
      unsigned char const *buffer;		/* input bitstream buffer */
      unsigned char const *bufend;		/* end of buffer */
      unsigned long skiplen;		/* bytes to skip before next frame */
    
      int sync;				/* stream sync found */
      unsigned long freerate;		/* free bitrate (fixed) */
    
      unsigned char const *this_frame;	/* start of current frame */
      unsigned char const *next_frame;	/* start of next frame */
      struct mad_bitptr ptr;		/* current processing bit pointer */
    
      struct mad_bitptr anc_ptr;		/* ancillary bits pointer */
      unsigned int anc_bitlen;		/* number of ancillary bits */
    
      unsigned char (*main_data)[MAD_BUFFER_MDLEN];
    					/* Layer III main_data() */
      unsigned int md_len;			/* bytes in main_data */
    
      int options;				/* decoding options (see below) */
      enum mad_error error;			/* error code (see above) */
    };
    
    /* 数据帧帧头  */
    struct mad_header {
      enum mad_layer layer;			/* audio layer (1, 2, or 3) */
      enum mad_mode mode;			/* channel mode (see above) */
      int mode_extension;			/* additional mode info */
      enum mad_emphasis emphasis;		/* de-emphasis to use (see above) */
    
      unsigned long bitrate;		/* stream bitrate (bps) */
      unsigned int samplerate;		/* sampling frequency (Hz) */
    
      unsigned short crc_check;		/* frame CRC accumulator */
      unsigned short crc_target;		/* final target CRC checksum */
    
      int flags;				/* flags (see below) */
      int private_bits;			/* private bits (see below) */
    
      mad_timer_t duration;			/* audio playing time of frame */
    };
    
    /* 有效数据帧 */
    struct mad_frame {
      struct mad_header header;		/* MPEG audio header */
    
      int options;				/* decoding options (from stream) */
    
      mad_fixed_t sbsample[2][36][32];	/* synthesis subband filter samples */
      mad_fixed_t (*overlap)[2][32][18];	/* Layer III block overlap data */
    };
    
    /* 采样后的PCM音频数据 */
    struct mad_pcm {
      unsigned int samplerate;		/* sampling frequency (Hz) */
      unsigned short channels;		/* number of channels */
      unsigned short length;		/* number of samples per channel */
      mad_fixed_t samples[2][1152];		/* PCM output samples [ch][sample] */
    };
    
    /* 解码后的音频数据 */
    struct mad_synth {
      mad_fixed_t filter[2][2][2][16][8];	/* polyphase filterbank outputs */
      					/* [ch][eo][peo][s][v] */
    
      unsigned int phase;			/* current processing phase */
    
      struct mad_pcm pcm;			/* PCM output */
    };
    
    /* 解码过程控制 */
    enum mad_flow {
      MAD_FLOW_CONTINUE = 0x0000,	/* continue normally */
      MAD_FLOW_STOP     = 0x0010,	/* stop decoding normally */
      MAD_FLOW_BREAK    = 0x0011,	/* stop decoding and signal an error */
      MAD_FLOW_IGNORE   = 0x0020	/* ignore the current frame */
    };
    

      libmad的示例程序流程如下,用户可以直接使用回调机制使用libmad,也可以使用decoder.c的接口按照需求获取中间数据。 

    libmad应用流程

     

    五、OGG 文件 (参考:RFC 3533)

           OGG(OGG Vobis)是一种新的音频压缩格式,类似于MP3等现有的音乐格式。完全免费,没有专利限制。Vorbis 是这种音频压缩机制的名字,OGG是一个计划的名字,该计划意图设计一个完全开放的多媒体系统,OGG Vorbis是该计划的一部分。

           特点:支持多声道;现在创建的OGG文件可以在未来的任何播放器上播放,因此,这种文件格式可以不断地进行大小和音质的改良,而不影响既有的编码器和播放器;目前最好的有损格式之一,最高比特率500K。  

    1、相关概念

         stream : 分为逻辑流与物理流,Ogg封装的结果称作物理流,一个物理流包含一个或多个逻辑流,这些逻辑流由ogg编码器创建,每个物理流中的逻辑流以一个特殊的页开始 -- bos,以一个特殊的页结束 -- eos;

    physical bitstream with pages of
              different logical bitstreams grouped and chained
          -------------------------------------------------------------
          |*A*|*B*|*C*|A|A|C|B|A|B|#A#|C|...|B|C|#B#|#C#|*D*|D|...|#D#|
          -------------------------------------------------------------
           bos bos bos             eos           eos eos bos       eos                  
    bos : beginning of stream; eos : end of stream
    图中有两个物理流,第一个时序上有三个逻辑流ABC,分别以bos和eos为头尾;第二个只有一个逻辑流D

       packet : 一个解码单元,或是一帧数据;
       segment : 由 packet 分割而成,一个segment 最多包含255格式bytes,segment没有header ;
       page : 是 ogg 文件格式的基本组成单元,是对 segment 的封装,为几个连续的 segment 添加 header 构成 page;

    2、音频格式剖析:

            OGG文件基本构成单位是页(Page),编码过程是由物理流向页转换的过程,OGG编码的主要过程如下:

              逻辑流由若干个packet组成
     -----------------------------------------------------------------
     > |       packet_1             | packet_2         | packet_3 |  <
     -----------------------------------------------------------------
    
                         |每个packet可以分成若干个segment
                         v
    
          packet_1 (5 segments)          packet_2 (4 segs)    p_3 (2 segs)
         ------------------------------ -------------------- ------------
     ..  |seg_1|seg_2|seg_3|seg_4|s_5 | |seg_1|seg_2|seg_3|| |seg_1|s_2 | ..
         ------------------------------ -------------------- ------------
    
                         | 给连续的几个segment加上page header就组成了page
                         v
    
     page_1 (packet_1 data)   page_2 (pket_1 data)   page_3 (packet_2 data)
    ------------------------  ----------------  ------------------------
    |H|------------------- |  |H|----------- |  |H|------------------- |
    |D||seg_1|seg_2|seg_3| |  |D|seg_4|s_5 | |  |D||seg_1|seg_2|seg_3| | ...
    |R|------------------- |  |R|----------- |  |R|------------------- |
    ------------------------  ----------------  ------------------------
    
                        |
    pages of            |
    other    --------|  |
    logical         -------
    bitstreams      | MUX |
                    -------
                       |
                       v
    
                  page_1  page_2          page_3
          ------  ------  -------  -----  -------
     ...  ||   |  ||   |  ||    |  ||  |  ||    |  ...
          ------  ------  -------  -----  -------
                  physical Ogg bitstream

       1)从编码器获得逻辑流的各个packet;
       2)将packet分割成Segment,分片;
       3)将Segment 打包成Page,页封装;
       4)将多个已经封装 page 完毕的逻辑流按应用要求的时序关系合成物理流,从而获得 OGG文件,对于只包含一个 stream 的文件,这个过程可以没有; 

    3、OGG页结构

     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1| Byte
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | capture_pattern: Magic number for page start "OggS"           | 0-3
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | version       | header_type   | granule_position              | 4-7
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                                                               | 8-11
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                               | bitstream_serial_number       | 12-15
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                               | page_sequence_number          | 16-19
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                               | CRC_checksum                  | 20-23
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                               |page_segments  | segment_table | 24-27
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | ...                                                           | 28-
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    

        1) 0~3字节:magic numbers,标识page开始;

        2) version: 1字节;

        3) page头类型:1字节:

             位 0x01 : 1-该页包含前一页的连续的packet ;0- 该页和前后页无联系;

             位 0x02 :  1- 该页为bos ; 0- 该页不是 bos;

             位 0x04 :  1-该页为eos ; 0- 该页不是 eos ;

        4) 位置信息: 8字节,对于音频

        5) serial number: 4字节,唯一标识stream;

        6) page sequence number: 4 字节,page序列号,为解码器提供页丢失标识,每个逻辑流中递增;

        7) CRC校验和: 4字节;

        8) number_page_segment : 1字节,segment table 中segment的个数;

        9) segment table: 长度为 number_page_segment字节,每个字节对应一个segment的长度。

    六、总结

         总的来说,我了解了三种主流的音频文件格式,在以后的开发中打好了基础,但依然有一些不足,细节上不够深入,后续在接触后更新。libogg编解码ogg文件源码阅读进行中...

    参考资料

    1、  维基百科-WAV

    2、  音频格式比较

    3、   https://blog.csdn.net/ljxt523/article/details/52068241

    4、   MP3:https://wenku.baidu.com/view/a071bf4e852458fb770b56a0.html

    5、   OGG:https://blog.csdn.net/yu_yuan_1314/article/details/16884313

    展开全文
  • QAudioCoder是一个Qt库,用于解码编码原始音频样本。 该库还提供直接解码编码音频文件以及在不同格式之间转换的功能。 当前仅支持WAVE,MP3和FLAC格式。
  • Wav2MP3向导是日常音频编码/解码的简单解决方案。 它支持WAV,MP3,OGG和FLAC文件,并具有直观的界面。 新手易于学习(带有预置配置文件),并且具有足够的高级功能。
  • 下面演示我读取 file1 文件,进行编码,然后再解码,保存为另一个 file2 文件。最后的 file1 和 file2 是一样的。 图片、音频文件都是二进制的文件,所以读取和写入要用 rb 和 wb,都多个 b。

    最近在研究项目,需要调用百度语音的api,传入参数需要本地语音文件 base64 位编码后内容。下面来演示一下。
    其实很简单,base64 是系统自带的库。
    base64.b64encode() 进行编码。
    base64.b64decode() 进行解码。
    下面演示我读取 file1 文件,进行编码,然后再解码,保存为另一个 file2 文件。最后的 file1file2 是一样的。
    图片、音频等文件都是二进制的文件,所以读取和写入要用 rbwb,都多个 b

    import base64
    
    file1 = open("16k.pcm","rb").read()   # 读取二进制文件
    text = base64.b64encode(file1)   # 进行编码
    
    file2 = open("17k.pcm","wb")   # 写入二进制文件
    text = base64.b64decode(text)   # 进行解码
    file2.write(text)
    file2.close()   # 写入文件完成后需要关闭文件才能成功写入
    

    base64 编码使用实例演示
    Python 技术篇-百度语音识别API接口调用演示
    音频文件 base64 位编码后的样子:
    在这里插入图片描述
    喜欢的点个赞❤吧!

    展开全文
  • 本文介绍了MPEG音频解码器及其应用,并展现现代音频编码方案成功的创建者Fraunhofer IIS.  MPEG L3: mp3  mp3彻底改变了音乐产业,也改变了消费者购买和享受音乐的方式。mp3目前仍然是音乐发行的主要格式,...
  • 音频编码解码简介

    2014-05-15 14:46:00
    音频的编码解码 1、处理音频数据的过程分为编码解码两个过程,这两个过程的具体说明如下: (1)编码:把存放在波形文件里的数字音频数据转换为压缩的形式,...2、音频文件根据支持的编码格式,分为两类: (1)无
  • 音频编码流程详解

    千次阅读 2021-07-28 09:56:20
    1、音频编码整体流程 2、FFmpeg音频编码详细流程 3、关键函数说明 (1)avcodec_find_encoder:根据指定的AVCodecID查找注册的编码器。 (2)avcodec_alloc_context3:为AVCodecContext分配内存。 (3)...
  • https://www.cnblogs.com/dcb3688/p/4610642.html

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 38,462
精华内容 15,384
关键字:

音频文件 编码解码

友情链接: XListView.rar