PCM音频数据解析与原理分析

在这里插入图片描述

😎 作者介绍:欢迎来到我的主页👈,我是程序员行者孙,一个热爱分享技术的制能工人计算机本硕,人工制能研究生。公众号:AI Sun(领取大厂面经等资料),欢迎加我的微信交流:sssun902
🎈 本文专栏:本文收录于《音视频》系列专栏,相信一份耕耘一份收获,我会分享音视频相关学习内容,不说废话,祝大家都offer拿到手软
🤓 欢迎大家关注其他专栏,我将分享Web前后端开发、人工智能、机器学习、深度学习从0到1系列文章。
🖥随时欢迎您跟我沟通,一起交流,一起成长、进步!

PCM音频数据解析与原理分析

引言

脉冲编码调制(Pulse Code Modulation,简称PCM)是一种模拟信号的数字化方法,广泛应用于音频领域。PCM通过将连续的模拟信号转换为一系列离散的数字值,使得音频信号可以被数字设备处理和存储。本文将深入分析PCM的过程和原理,帮助读者理解这一基础而关键的技术。
在这里插入图片描述

PCM的基本概念

PCM是一种将模拟信号转换为数字信号的技术。它通过以下三个步骤实现:

  1. 采样:在固定的时间间隔内测量模拟信号的幅度。
  2. 量化:将采样得到的连续幅度值映射到最接近的有限数量的离散值。
  3. 编码:将量化后的离散值转换为数字代码。

PCM的关键参数

  • 采样率(Sampling Rate):每秒采样的次数,单位是Hz(赫兹)。常见的采样率有44.1kHz(CD音质)、48kHz(专业音频)等。
  • 量化位数(Bit Depth):每个采样值的比特数,常见的有16位、24位等。量化位数越多,能表示的动态范围越大,音质越好。
  • 声道数(Channels):音频信号的通道数量,如单声道(Mono)、立体声(Stereo)等。

PCM的工作原理

采样

采样是PCM过程中的第一步。在这个过程中,模拟音频信号在特定的时间间隔(由采样率决定)被转换成离散的样本。采样定理(Nyquist定理)指出,为了避免混叠现象,采样率应至少是信号最高频率的两倍。

量化

量化是将采样得到的连续幅度值映射到离散值的过程。这个过程涉及到量化位数的选择,量化位数决定了可以表示的最小幅度变化。量化位数越高,动态范围越大,但同时也需要更多的存储空间和处理能力。

编码

编码是将量化后的离散值转换为二进制数字代码的过程。这些数字代码随后可以被数字设备存储和处理。

PCM的常见格式

  • 线性PCM(LPCM):未压缩的PCM数据,通常用于高质量音频存储。
  • DSD(Direct Stream Digital):一种特殊的PCM格式,使用1位量化,但采样率非常高。

PCM的优势与局限

优势

  • 高质量:高采样率和量化位数可以提供非常接近原始模拟信号的音质。
  • 兼容性:PCM是许多音频设备的通用格式,易于处理和转换。

局限

  • 存储需求:高采样率和量化位数意味着PCM数据需要较大的存储空间。
  • 带宽需求:在传输过程中,PCM数据需要较高的带宽。

代码示例

在C++中实现PCM采样通常需要使用一些外部库来处理音频硬件的输入。例如,可以使用PortAudio库来捕获音频输入,并使用libsamplerate(也称为Secret Rabbit Code,SRC)来改变采样率。以下是一个使用PortAudiolibsamplerate的C++代码示例,展示了如何进行PCM采样。

环境准备

  1. 确保你的开发环境中安装了PortAudiolibsamplerate库。
  2. 包含这些库的开发头文件和库文件到你的项目中。

C++代码示例

#include <iostream>
#include <vector>
#include <portaudio.h>
#include <samplerate.h>

#define SAMPLE_RATE 44100 // 原始采样率
#define NEW_SAMPLE_RATE 44100 // 新采样率
#define FRAMES_PER_BUFFER 1024
#define NUM_SECONDS 5
#define NUM_CHANNELS 2 // 立体声

// 回调函数,PortAudio调用此函数获取音频数据
int paCallback(const void* inputBuffer, void* outputBuffer,
              unsigned long framesPerBuffer,
              const PaStreamCallbackTimeInfo* timeInfo,
              PaStreamCallbackFlags statusFlags,
              void* userData) {
    std::vector<float>* recordedSamples = static_cast<std::vector<float>*>(userData);
    const float* in = static_cast<const float*>(inputBuffer);

    for (unsigned int i = 0; i < framesPerBuffer; i++) {
        // 将采样数据存储在向量中
        recordedSamples->push_back(in[i*NUM_CHANNELS]); // 假设我们只记录第一个通道的数据
    }

    return paContinue;
}

int main() {
    PaStream* stream;
    PaError err;
    std::vector<float> recordedSamples;

    // 初始化PortAudio
    err = Pa_Initialize();
    if (err != paNoError) {
        std::cerr << "PortAudio 初始化失败!" << std::endl;
        return -1;
    }

    // 打开流
    Pa_OpenStreamParameters inputParameters;
    inputParameters.device = Pa_GetDefaultInputDevice(); // 默认输入设备
    inputParameters.channelCount = NUM_CHANNELS;
    inputParameters.sampleFormat = paFloat32; // 32位浮点
    inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
    inputParameters.hostApiSpecificStreamInfo = nullptr;

    err = Pa_OpenStream(
        &stream,
        &inputParameters,
        nullptr, // 无输出
        SAMPLE_RATE,
        FRAMES_PER_BUFFER,
        paClipOff, // 我们不处理峰值
        paCallback,
        &recordedSamples);

    if (err != paNoError) {
        std::cerr << "打开流失败!" << std::endl;
        return -1;
    }

    err = Pa_StartStream(stream);
    if (err != paNoError) {
        std::cerr << "开始流失败!" << std::endl;
        return -1;
    }

    std::cout << "录制音频..." << std::endl;

    // 录制指定时间的音频
    Pa_Sleep((NUM_SECONDS * 1000) / (FRAMES_PER_BUFFER / NUM_CHANNELS));

    err = Pa_StopStream(stream);
    if (err != paNoError) {
        std::cerr << "停止流失败!" << std::endl;
        return -1;
    }

    err = Pa_CloseStream(stream);
    if (err != paNoError) {
        std::cerr << "关闭流失败!" << std::endl;
        return -1;
    }

    Pa_Terminate();

    // 此处可以添加代码来处理recordedSamples,例如保存到文件或进行重采样

    std::cout << "录制完成!" << std::endl;

    return 0;
}

编译说明

  • 使用g++编译时,需要链接PortAudiolibsamplerate库,例如:g++ -o pcm_record pcm_record.cpp -lportaudio -lsamplerate
  • 根据你的环境和库的安装方式,可能需要指定库文件的路径。

注意事项

  • 这个示例程序只是展示了如何使用PortAudio来录制音频,并存储采样数据到一个std::vector<float>中。
  • 为了简化示例,我们只存储了第一个通道的数据。在实际应用中,你可能需要根据需要处理所有通道的数据。
  • 这个示例没有展示如何使用libsamplerate进行重采样。如果需要改变采样率,可以在录制完成后使用SRC转换采样率。

示例提供了一个基本的框架,展示了如何在C++中使用PortAudio库进行PCM采样。

结论

PCM是音频数字化的基础技术,它通过采样、量化和编码三个步骤将模拟信号转换为数字信号。虽然PCM数据提供了高质量的音频体验,但同时也带来了存储和带宽的挑战。随着技术的发展,出现了许多基于PCM的压缩技术,如AAC、MP3等,它们在保持音质的同时,降低了存储和传输的需求。

PCM的原理和过程是理解现代音频技术的关键,无论是在音频编辑、音乐制作还是多媒体应用中,PCM都扮演着重要的角色。

祝大家学习顺利~
如有任何错误,恳请批评指正~~
以上是我通过各种方式得出的经验和方法,欢迎大家评论区留言讨论呀,如果文章对你们产生了帮助,也欢迎点赞收藏,我会继续努力分享更多干货~


🎈关注我的公众号AI Sun可以获取Chatgpt最新发展报告以及腾讯字节等众多大厂面经
😎也欢迎大家和我交流,相互学习,提升技术,风里雨里,我在等你~


  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员行者孙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值