处理 Audio PCM 数据24位偏移问题

news2025/1/24 17:32:14

在音频处理过程中,我们有时会遇到特殊的问题,例如某些WAV文件的PCM数据发生了位移,导致声音播放异常。最近,我遇到了一个具体的问题,48000,32bit,8ch的PCM数据每32位(4字节)数据整体向后偏移了24位(3字节),即每个采样点并没有从正确的起始位置开始,而是从数据块的第24位开始存储。这篇文章将描述该问题的原因及其解决方法,并给出一个完整的代码示例。

问题描述

WAV文件是一种常见的音频文件格式,其PCM数据部分是按采样点顺序存储的。在标准的32位PCM格式中,每个采样点由4字节组成,数据应当按如下顺序排列:

| Byte 1 | Byte 2 | Byte 3 | Byte 4 |

然而,问题文件中的数据从第24位开始偏移,使得数据变为:

| (空) | (空) | (空) | Byte 1 | Byte 2 | Byte 3 | Byte 4 |

这样的偏移会导致音频数据解析错误,播放时声音会发生异常。因此,我们需要对文件中的PCM数据重新排列,修正24位偏移。

解决思路

理解偏移:

数据偏移了24位,等同于偏移了3字节。这意味着我们需要跳过这3字节,从正确的位置读取数据。

数据重组:

将数据以32位(4字节)为一个采样点,修正每个采样点的起始位置。

处理对齐问题:

如果平台不支持未对齐内存访问,需要使用memcpy来安全地移动数据。

文件保存:

解析WAV文件头,读取并修正PCM数据,最后将结果保存为新的WAV文件。

修复前
修复前
修复后
在这里插入图片描述

解决方案实现

以下是完整的C代码实现,包含WAV文件的解析、数据偏移修正以及文件保存功能:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

// WAV file header structure
typedef struct {
    char riff[4];            // "RIFF" marker
    uint32_t file_size;      // Total file size minus 8 bytes
    char wave[4];            // "WAVE" marker
    char fmt_chunk_marker[4];// "fmt " subchunk marker
    uint32_t fmt_chunk_size; // Size of the fmt chunk
    uint16_t audio_format;   // Audio format (1 = PCM)
    uint16_t num_channels;   // Number of channels
    uint32_t sample_rate;    // Sample rate
    uint32_t byte_rate;      // Byte rate
    uint16_t block_align;    // Block alignment
    uint16_t bits_per_sample;// Bits per sample
    char data_chunk_header[4];// "data" subchunk marker
    uint32_t data_size;      // Size of the data chunk
} WavHeader;

// Function to fix 24-bit offset in PCM data
void fix_bit_offset(const uint8_t *input, uint8_t *output, size_t total_bytes) {
    size_t sample_count = total_bytes / sizeof(int32_t);

    // Adjust the input buffer with a 24-bit offset (3 bytes)
    for (size_t i = 0; i < sample_count; i++) {
        //((int32_t *)output)[i] = ((const int32_t *)(input + 3))[i];
        ((int32_t *)(output + 1))[i] = ((const int32_t *)(input + 0))[i];
    }
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <input WAV file> <output WAV file>\n", argv[0]);
        return -1;
    }

    const char *input_file = argv[1];
    const char *output_file = argv[2];

    // Open input WAV file
    FILE *input_fp = fopen(input_file, "rb");
    if (!input_fp) {
        fprintf(stderr, "Error: Could not open input file %s!\n", input_file);
        return -1;
    }

    // Read and parse the WAV header
    WavHeader header;
    if (fread(&header, sizeof(WavHeader), 1, input_fp) != 1) {
        fprintf(stderr, "Error: Failed to read WAV header!\n");
        fclose(input_fp);
        return -1;
    }

    // Validate the WAV file format
    if (strncmp(header.riff, "RIFF", 4) != 0 || strncmp(header.wave, "WAVE", 4) != 0 || strncmp(header.data_chunk_header, "data", 4) != 0) {
        fprintf(stderr, "Error: Invalid WAV file format!\n");
        fclose(input_fp);
        return -1;
    }

    // Check for PCM format
    if (header.audio_format != 1 || header.bits_per_sample != 32) {
        fprintf(stderr, "Error: Only 32-bit PCM WAV files are supported!\n");
        fclose(input_fp);
        return -1;
    }

    // Allocate memory for input and output buffers
    size_t data_size = header.data_size;
    uint8_t *input_data = (uint8_t *)malloc(data_size);
    uint8_t *output_data = (uint8_t *)malloc(data_size + 4);

    if (!input_data || !output_data) {
        fprintf(stderr, "Error: Memory allocation failed!\n");
        fclose(input_fp);
        free(input_data);
        free(output_data);
        return -1;
    }

    // Read PCM data from the input file
    size_t read_bytes = fread(input_data, 1, data_size, input_fp);
    fclose(input_fp);

    if (read_bytes != data_size) {
        fprintf(stderr, "Error: Failed to read PCM data, read bytes: %zu (expected: %zu)\n", read_bytes, data_size);
        free(input_data);
        free(output_data);
        return -1;
    }

    // Fix the 24-bit offset in the PCM data
    fix_bit_offset(input_data, output_data, data_size);

    // Update the WAV header for the output file
    header.file_size = data_size + sizeof(WavHeader) - 8;
    header.data_size = data_size;

    // Save the corrected PCM data to the output WAV file
    FILE *output_fp = fopen(output_file, "wb");
    if (!output_fp) {
        fprintf(stderr, "Error: Could not open output file %s!\n", output_file);
        free(input_data);
        free(output_data);
        return -1;
    }

    // Write the WAV header to the output file
    if (fwrite(&header, sizeof(WavHeader), 1, output_fp) != 1) {
        fprintf(stderr, "Error: Failed to write WAV header!\n");
        fclose(output_fp);
        free(input_data);
        free(output_data);
        return -1;
    }

    // Write the corrected PCM data to the output file
    size_t written_bytes = fwrite(output_data, 1, data_size, output_fp);
    fclose(output_fp);

    if (written_bytes != data_size) {
        fprintf(stderr, "Error: Failed to write PCM data, written bytes: %zu (expected: %zu)\n", written_bytes, data_size);
        free(input_data);
        free(output_data);
        return -1;
    }

    printf("Correction completed! The fixed WAV file has been saved to %s\n", output_file);

    // Free allocated memory
    free(input_data);
    free(output_data);

    return 0;
}

总结

通过上述代码,我们可以高效地修正WAV文件中因24位偏移导致的音频播放问题。这种解决方案不仅适用于当前问题,还可以作为处理其他PCM数据偏移问题的模板。

希望这篇文章能为有类似需求的朋友提供帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2263186.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【大模型】GraphRAG技术原理

核心概念 GraphRAG 的核心在于用大模型构建知识图谱知识图谱聚类社区化RAG RAG就是输入&#xff08;问题知识&#xff09;到大模型 1-大模型自动从海量数据中构建知识图谱&#xff08;提取合并实体关系&#xff09; 2-聚类算法从知识图谱中聚类社区并生成社区摘要 3-输入问题…

Vue与React:前端框架的巅峰对决

文章目录 一、引言&#xff08;一&#xff09;前端框架发展现状简述 二、Vue 与 React 框架概述&#xff08;一&#xff09;Vue.js 简介&#xff08;二&#xff09;React.js 简介 三、开发效率对比&#xff08;一&#xff09;Vue 开发效率分析&#xff08;二&#xff09;React …

Ubuntu下C语言操作kafka示例

目录 安装kafka&#xff1a; 安装librdkafka consumer Producer 测试运行 安装kafka&#xff1a; Ubuntu下Kafka安装及使用_ubuntu安装kafka-CSDN博客 安装librdkafka github地址&#xff1a;GitHub - confluentinc/librdkafka: The Apache Kafka C/C library $ apt in…

线程池ForkJoinPool详解

由一道算法题引发的思考 算法题&#xff1a;如何充分利用多核CPU的性能&#xff0c;快速对一个2千万大小的数组进行排序&#xff1f; 这道算法题可以拆解来看&#xff1a; 1&#xff09;首先这是一道排序的算法题&#xff0c;而且是需要使用高效的排序算法对2千万大小的数组…

基于多尺度动态卷积的图像分类

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

[Linux] 信号保存与处理

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;青果大战linux 总有光环在陨落&#xff0c;总有新星在闪烁 信号的保存 下面的概…

计算机网络-GRE Over IPSec实验

一、概述 前情回顾&#xff1a;上次基于IPsec VPN的主模式进行了基础实验&#xff0c;但是很多高级特性没有涉及&#xff0c;如ike v2、不同传输模式、DPD检测、路由方式引入路由、野蛮模式等等&#xff0c;以后继续学习吧。 前面我们已经学习了GRE可以基于隧道口实现分支互联&…

进网许可认证、交换路由设备检测项目更新25年1月起

实施时间 2025年1月1日起实施 涉及设备范围 核心路由器、边缘路由器、以太网交换机、三层交换机、宽带网络接入服务器&#xff08;BNAS&#xff09; 新增检测依据 GBT41266-2022网络关键设备安全检测方法交换机设备 GBT41267-2022网络关键设备安全技术要求交换机设备 GB/…

用C#(.NET8)开发一个NTP(SNTP)服务

完整源码&#xff0c;附工程下载&#xff0c;工程其实也就下面两个代码。 想在不能上网的服务器局域网中部署一个时间服务NTP&#xff0c;当然系统自带该服务&#xff0c;可以开启&#xff0c;本文只是分享一下该协议报文和能跑的源码。网上作为服务的源码不太常见&#xff0c;…

Connection lease request time out 问题分析

Connection lease request time out 问题分析 问题背景 使用apache的HttpClient&#xff0c;我们知道可以通过setConnectionRequestTimeout()配置从连接池获取链接的超时时间&#xff0c;而Connection lease request time out正是从连接池获取链接超时的报错&#xff0c;这通常…

【课程论文系列实战】:随机对照实验驱动的电商落地页优化

数据与代码见文末 摘要 随机对照试验&#xff08;Randomized Controlled Trial&#xff0c;RCT&#xff09;被认为是因果推断的“金标准”方法。通过随机分配实验参与者至不同组别&#xff0c;确保了组间可比性&#xff0c;RCT能够有效地消除样本选择偏差和混杂变量问题。本文…

UML 建模实验

文章目录 实验一 用例图一、安装并熟悉软件EnterpriseArchitect16二、用例图建模 实验二 类图、包图、对象图类图第一题第二题 包图对象图第一题第二题 实验三 顺序图、通信图顺序图银行系统学生指纹考勤系统饮料自动销售系统“买到饮料”“饮料已售完”“无法找零”完整版 通信…

高质量翻译如何影响软件用户体验 (UX)

在软件开发领域&#xff0c;用户体验 (UX) 是决定产品成败的关键因素之一。一个流畅、吸引人且直观的用户体验可以决定一款软件的成功与否。在影响优秀用户体验的众多因素中&#xff0c;高质量翻译尤为重要&#xff0c;尤其是在当今全球化的市场环境中。确保软件为不同语言和文…

ArcGIS Pro 3.4新功能2:Spatial Analyst新特性,密度、距离、水文、太阳能、表面、区域分析

Spatial Analyst 扩展模块在 ArcGIS Pro 3.4 中引入了新功能和增强功能。此版本为您提供了用于表面和区域分析的新工具以及改进的密度和距离分析功能&#xff0c;多种用于水文分析的工具性能的提高&#xff0c;一些新的太阳能分析功能。 目录 1.密度分析 2.距离分析 3.水文…

Linux C 程序 【05】异步写文件

1.开发背景 Linux 系统提供了各种外设的控制方式&#xff0c;其中包括文件的读写&#xff0c;存储文件的介质可以是 SSD 固态硬盘或者是 EMMC 等。 其中常用的写文件方式是同步写操作&#xff0c;但是如果是写大文件会对 CPU 造成比较大的负荷&#xff0c;采用异步写的方式比较…

凯酷全科技抖音电商服务的卓越践行者

在数字经济蓬勃发展的今天&#xff0c;电子商务已成为企业增长的新引擎。随着短视频平台的崛起&#xff0c;抖音作为全球领先的短视频社交平台&#xff0c;不仅改变了人们的娱乐方式&#xff0c;也为品牌和商家提供了全新的营销渠道。厦门凯酷全科技有限公司&#xff08;以下简…

精准提升:从94.5%到99.4%——目标检测调优全纪录

&#x1f680; 目标检测模型调优过程记录 在进行目标检测模型的训练过程中&#xff0c;我们面对了许多挑战与迭代。从初始模型的训练结果到最终的调优优化&#xff0c;每一步的实验和调整都有其独特的思路和收获。本文记录了我在优化目标检测模型的过程中进行的几次尝试&#…

STM8单片机学习笔记·GPIO的片上外设寄存器

目录 前言 IC基本定义 三极管基础知识 单片机引脚电路作用 STM8GPIO工作模式 GPIO外设寄存器 寄存器含义用法 CR1&#xff1a;Control Register 1 CR2&#xff1a;Control Register 2 ODR&#xff1a;Output Data Register IDR&#xff1a;Input Data Register 赋值…

国标GB28181平台EasyGBS在安防视频监控中的信号传输(电源/视频/音频)特性及差异

在现代安防视频监控系统中&#xff0c;国标GB28181协议作为公共安全视频监控联网系统的国家标准&#xff0c;该协议不仅规范了视频监控系统的信息传输、交换和控制技术要求&#xff0c;还为不同厂商设备之间的互联互通提供了统一的框架。EasyGBS平台基于GB28181协议&#xff0c…

如何使用checkBox组件实现复选框

文章目录 概念介绍使用方法示例代码我们在上一章回中介绍了DatePickerDialog Widget相关的内容,本章回中将介绍Checkbox Widget.闲话休提,让我们一起Talk Flutter吧。 概念介绍 我们在这里说的Checkbox也是叫复选框,没有选中时是一个正方形边框,边框内容是空白的,选中时会…