用C处理音频wav文件

2023-11-22

我正在处理 wav 文件的幅度并按某个小数因子缩放它。我正在努力思考如何以节省内存的方式读取和重写文件,同时也尝试解决该语言的细微差别(我是 C 语言的新手)。该文件可以是 8 位或 16 位格式。我想到这样做的方法是首先阅读标头数据到一些预定义的结构中,然后在循环中处理实际数据,在循环中我将把一大块数据读入缓冲区,执行所需的操作,然后将其写入输出。

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


typedef struct header 
{
    char chunk_id[4];
    int chunk_size;
    char format[4];
    char subchunk1_id[4];
    int subchunk1_size;
    short int audio_format;
    short int num_channels;
    int sample_rate;
    int byte_rate;
    short int block_align;
    short int bits_per_sample;
    short int extra_param_size;
    char subchunk2_id[4];
    int subchunk2_size;
} header;

typedef struct header* header_p;

void scale_wav_file(char * input, float factor, int is_8bit)
{
    FILE * infile = fopen(input, "rb");
    FILE * outfile = fopen("outfile.wav", "wb");

    int BUFSIZE = 4000, i, MAX_8BIT_AMP = 255, MAX_16BIT_AMP = 32678;

    // used for processing 8-bit file
    unsigned char inbuff8[BUFSIZE], outbuff8[BUFSIZE];

    // used for processing 16-bit file
    short int inbuff16[BUFSIZE], outbuff16[BUFSIZE];

    // header_p points to a header struct that contains the file's metadata fields
    header_p meta = (header_p)malloc(sizeof(header));

    if (infile)
    {

        // read and write header data
        fread(meta, 1, sizeof(header), infile);
        fwrite(meta, 1, sizeof(meta), outfile);

        while (!feof(infile))
        {
            if (is_8bit)
            {
                fread(inbuff8, 1, BUFSIZE, infile);   
            } else {
                fread(inbuff16, 1, BUFSIZE, infile);      
            }

            // scale amplitude for 8/16 bits
            for (i=0; i < BUFSIZE; ++i)
            {
                if (is_8bit)
                {
                    outbuff8[i] = factor * inbuff8[i];
                    if ((int)outbuff8[i] > MAX_8BIT_AMP)
                    {
                        outbuff8[i] = MAX_8BIT_AMP;
                    }
                } else {
                    outbuff16[i] = factor * inbuff16[i];
                    if ((int)outbuff16[i] > MAX_16BIT_AMP)
                    {
                        outbuff16[i] = MAX_16BIT_AMP;
                    } else if ((int)outbuff16[i] < -MAX_16BIT_AMP) {
                        outbuff16[i] = -MAX_16BIT_AMP;
                    }
                }
            }

            // write to output file for 8/16 bit
            if (is_8bit)
            {
                fwrite(outbuff8, 1, BUFSIZE, outfile);
            } else {
                fwrite(outbuff16, 1, BUFSIZE, outfile);
            }
        }
    }

    // cleanup
    if (infile) { fclose(infile); }
    if (outfile) { fclose(outfile); }
    if (meta) { free(meta); }
}

int main (int argc, char const *argv[])
{
    char infile[] = "file.wav";
    float factor = 0.5;
    scale_wav_file(infile, factor, 0);
    return 0;
}

我最后得到的文件大小不同(对于 40Mb 文件,文件大小为 1k 左右),我怀疑这是因为我将整个缓冲区写入输出,即使文件可能已终止在填充整个缓冲区大小之前。另外,输出文件很混乱 - 无法播放或打开 - 所以我可能做错了整个事情。任何关于我搞砸的地方的提示都会很棒。谢谢!


1您在此 else 分支中读取的是字节而不是 16 位样本:

while (!feof(infile))
    {
        if (is_8bit)
        {
            fread(inbuff8, 1, BUFSIZE, infile);   
        } else {
            fread(inbuff16, 1, BUFSIZE, infile); // <-- should be BUFSIZE*2     
        }

2缩放时不会使值饱和,例如原始 16 位样本 = 32000 且因子 = 1.5 将环绕整数值,而不是将其限制为最大值 32767。

3你根本不看 RIFF 和其他标题。在 WAV 文件中,音频数据后面可能是一些信息页脚,或者前面是附加标头。或者换句话说:你的header结构体过于静态。您还应该从文件中读取 WAV 格式,而不是使用参数说明它是 8 位样本。

4这不会发生:

                outbuff16[i] = factor * inbuff16[i];
                if ((int)outbuff16[i] > MAX_16BIT_AMP)

8 位/16 位值永远不会大于 255/32768,除非您的计算机在整数溢出时将一些魔术位插入内存:P

并且音频样本已签名,因此范围为 -128;127 和 -32768;32767。溢出检查必须发生在乘法表达式中。您还对浮点到整数舍入模式做出假设,该模式是可配置的并且应该予以考虑。就像是if(roundf(factor * inbuff16[i]) > 32767 || roundf(factor * inbuff16[i]) < -32768), maybe.

5您不存储结果fread,因此您将向输出文件写入太多样本。

6最后一点,你正在重新发明轮子。只要是为了学习,就没问题。否则你应该使用现有的库。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

用C处理音频wav文件 的相关文章

  • boost::asio + std::future - 关闭套接字后访问冲突

    我正在编写一个简单的 TCP 客户端来发送和接收单行文本 异步操作由 std future 处理 以便于超时阻塞查询 不幸的是 我的测试应用程序在破坏服务器对象时因访问冲突而崩溃 这是我的代码 TCP客户端 hpp ifndef TCPCL
  • c和java语言中的换行符

    现在行分隔符取决于系统 但在 C 程序中我使用 n 作为行分隔符 无论我在 Windows 还是 Linux 中运行它都可以正常工作 为什么 在java中 我们必须使用 n 因为它与系统相关 那么为什么我们在c中使用 n 作为新行 而不管我
  • 用 C++ 进行服装建模 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在编写一些软件 最终会绘制一个人体框架 可以配置各种参数 并且计划是在假人身上放置某种衣服 我研究
  • 在 C# 中创建具有单独列的分隔文本

    我一直在尝试在 C 中创建一个制表符限制的文本文件 以便数据正确显示在单独的列中 Firstname Lastname Age John Smith 17 James Sawyer 31 我尝试过 t 字符 但我得到的只是 Firstnam
  • 如何使用MemoryCache代替Timer来触发一个方法?

    以下方法通过等待已运行操作的结果来处理并发请求 对数据的请求可能会使用相同 不同的凭据同时出现 对于每组唯一的凭据 最多可以有一个GetCurrentInternal呼叫正在进行中 当准备就绪时 该呼叫的结果将返回给所有排队的服务员 pri
  • VB.NET 相当于 C# 属性简写吗?

    是否有与 C 等效的 VB NET public string FirstName get set 我知道你能做到 Public Property name As String Get Return name ToString End Ge
  • 如何读取扩展文件属性/文件元数据

    因此 我按照教程使用 ASP net core 将文件 上传 到本地路径 这是代码 public IActionResult About IList
  • 如何检查QProcess是否正确执行?

    QProcess process sdcompare QString command sdcompare QStringList args sdcompare command sdcompare diff args sdcompare lt
  • 如何为 C 分配的 numpy 数组注册析构函数?

    我想在 C C 中为 numpy 数组分配数字 并将它们作为 numpy 数组传递给 python 我可以做的PyArray SimpleNewFromData http docs scipy org doc numpy reference
  • 在 C# 中循环遍历文件文件夹的最简单方法是什么?

    我尝试编写一个程序 使用包含相关文件路径的配置文件来导航本地文件系统 我的问题是 在 C 中执行文件 I O 这将是从桌面应用程序到服务器并返回 和文件系统导航时使用的最佳实践是什么 我知道如何谷歌 并且找到了几种解决方案 但我想知道各种功
  • 使用 C 语言使用 strftime() 获取缩写时区

    我看过this https stackoverflow com questions 34408909 how to get abbreviated timezone and this https stackoverflow com ques
  • 获取 WPF 控件的所有附加事件处理程序

    我正在开发一个应用程序 在其中动态分配按钮的事件 现在的问题是 我希望获取按钮单击事件的所有事件 因为我希望删除以前的处理程序 我尝试将事件处理程序设置为 null 如下所示 Button Click null 但是我收到了一个无法分配 n
  • 如何将整数转换为 void 指针?

    在 C 中使用线程时 我面临警告 警告 从不同大小的整数转换为指针 代码如下 include
  • 使用 Moq 使用内部构造函数模拟类型

    我正在尝试模拟 Microsoft Sync Framework 中的一个类 它只有一个内部构造函数 当我尝试以下操作时 var fullEnumerationContextMock new Mock
  • 等待线程完成

    private void button1 Click object sender EventArgs e for int i 0 i lt 15 i Thread nova new Thread Method nova Start list
  • 如何从main方法调用业务对象类?

    我已将代码分为业务对象 访问层 如下所示 void Main Business object public class ExpenseBO public void MakeExpense ExpensePayload payload var
  • .NET中的LinkedList是循环链表吗?

    我需要一个循环链表 所以我想知道是否LinkedList是循环链表吗 每当您想要移动列表中的 下一个 块时 以循环方式使用它的快速解决方案 current current Next current List First 电流在哪里Linke
  • memset 未填充数组

    u32 iterations 5 u32 ecx u32 malloc sizeof u32 iterations memset ecx 0xBAADF00D sizeof u32 iterations printf 8X n ecx 0
  • 使用 GROUP 和 SUM 的 LINQ 查询

    请帮助我了解如何使用带有 GROUP 和 SUM 的 LINQ 进行查询 Query the database IEnumerable
  • 如何使用 Word Automation 获取页面范围

    如何使用办公自动化找到 Microsoft Word 中第 n 页的范围 似乎没有 getPageRange n 函数 并且不清楚它们是如何划分的 这就是您从 VBA 执行此操作的方法 转换为 Matlab COM 调用应该相当简单 Pub

随机推荐

  • Scikit-learn:在 GridSearchCV 中评分

    看起来GridSearchCVscikit learn 收集其 内部 交叉验证折叠的分数 然后对所有折叠的分数进行平均 我想知道这背后的理由 乍一看 收集其交叉验证折叠的预测 然后将所选的评分指标应用于所有折叠的预测似乎更灵活 我偶然发现这
  • 雪花中的断言

    有没有办法在 Snowflake 中执行断言 基本上 我正在尝试做一些测试 TDD 并且我想要一种类似于其他语言中的断言的机制 如果断言成功 则返回 true 和 或打印成功消息 如果断言失败 则会引发异常 我找不到任何在 Snowflak
  • PowerShell 无法识别 Java

    我在 Windows 2012 服务器上使用 PowerShell 并从 System32 中删除了所有 java 命令 重新安装了 jdk 将 JAVA HOME 和 Path 设置为指向新安装 我仍然收到以下错误 java The te
  • 如何知道设备的密度?

    我们可以在android中获取分辨率 但是我们如何知道设备 屏幕 的密度呢 thanks 执行以下代码 float scale getApplicationContext getResources getDisplayMetrics den
  • SQL - SELECT MAX() 和附带字段

    我所遇到的基本上是一个可以用多个表轻松解决的问题 但我只有一个表来完成它 考虑以下数据库表 UserID UserName EmailAddress Source 3K3S9 Ben email protected user SF13F H
  • android 中的椭圆是什么意思?

    我添加了一个EditText到我的布局 并添加了一个提示 并使其水平居中 运行应用程序时 提示是不可见的 我发现我应该做ellipsize的值TextView to be start
  • qemu-system-x86_64 :地址解析失败 ::1:46189:名称或服务未知

    我有以下问题 当我跑步时emulator avdname抛出以下错误 qemu system x86 64 chardev socket port 46189 host 1 nowait nodelay ipv6 id modem addr
  • 应用程序传输安全策略要求使用安全连接 - IOS 9

    我在使用 IP 地址连接 API 时遇到问题 即使我将以下代码添加到 plist 它仍然显示错误 如下所示 http xx3 xx xx8 xx7 xxx xxx 错误 无法加载资源 因为应用程序传输安全策略需要使用安全连接 这是我添加到
  • 获取特定用户的 CVS 历史记录

    如何获取特定用户对存储库所做的提交历史记录 我可以通过命令行或 TortoiseCVS 访问 CVS 因此使用任一方法的解决方案就足够了 作为一名编码员 我最感兴趣的是提交更改 而不是标记 分支等 因此我通常包括 c还有提交选项 cvs h
  • “ng-select”不是已知元素

    这是我的代码 我想在我的表格上添加https github com ng select ng select多选标签输入 组件 模块 ts import NgModule from angular core import CommonModu
  • 运行 shell 脚本时如何在 Jenkins 中标记构建不稳定

    在我正在进行的一个项目中 我们使用 shell 脚本来执行不同的任务 有些是运行 rsync 的 sh bash 脚本 有些是 PHP 脚本 PHP 脚本之一正在运行一些集成测试 这些测试输出到 JUnit XML 代码覆盖率报告等 詹金斯
  • meteorJS 从服务器调用 shell 命令

    我正在使用 MeteorJS 我想从 javascript 服务器端调用 bash 命令 这对于 NodeJS 来说似乎是可能的 http www dzone com snippets execute unix command nodejs
  • 获取多选中最后单击的选项

    在页面上 我有一个包含许多选项的选择 多个 框 现在我想对最后单击的项目做出反应 以使用 ajax 显示一些数据 由于选项元素上的 click 事件在 IE 中不起作用 我目前使用 change 事件 问题是 value 和 selecte
  • 如何使用 jQuery 在单击按钮时显示表格的另外 5 行

    我预加载了一个表及其所有行 但是 我只想显示其中的前 10 行 tbody 标签 现在每个 tr 在表中 这是我到目前为止所做的 var trs internalActivities gt table gt tbody gt tr trs
  • 骆驼、JMS、CLIENT_ACKNOWLEDGE 模式

    我知道Camel的JMS组件用于接收消息 使用Springs DefaultMessageListenerContainer 可以将其配置为使用 CLIENT ACKNOWLEDGE 模式来确认消息 我的问题是 什么时候调用 message
  • 从 PhpStorm 2017.1 检索保存(隐藏)的 SSH 密码

    我忘记了远程服务器的 SSH 密码 但它以隐藏方式保存在 IDE PhpStorm 2017 1 中 有什么办法可以查看隐藏密码吗 我终于可以从 PhpStorm 2017 1 检索密码 Go to 设置 首选项 外观与行为 系统设置 密码
  • PHP MySQLi multi_query 准备好的语句

    我想知道是否可以为 MySQLi multi query 准备多个语句 No mysqli multi query采用查询字符串作为其参数 而不是准备好的语句 mysql prepare只能准备一个语句 查询必须由单个 SQL 语句组成
  • 将 API 函数包装在 RxJs Observable 中

    我是 RxJs 的新手 我有一个用于地理编码的 API 它提供了如下所示的功能 simpleGeocode options where options address addr success Function failure Functi
  • ro.sf.lcd_密度必须定义为构建属性

    在模拟器上运行应用程序时 我遇到了这些错误 我该如何解决这个问题才能使我的程序更加可靠 error 01 16 11 06 49 211 E SurfaceFlinger 37 ro sf lcd density must be defin
  • 用C处理音频wav文件

    我正在处理 wav 文件的幅度并按某个小数因子缩放它 我正在努力思考如何以节省内存的方式读取和重写文件 同时也尝试解决该语言的细微差别 我是 C 语言的新手 该文件可以是 8 位或 16 位格式 我想到这样做的方法是首先阅读标头数据到一些预