Skip to main content

Lab 4: 音量 - CS50x 2023

写一个程序来调整音频文件的音量。

$ ./volume INPUT.wav OUTPUT.wav 2.0

其中,INPUT.wav 是原始音频文件的文件名,OUTPUT.wav 是调整音量后的音频文件名。音量调整的比例因子是给定的值 (例如 2.0)。

WAV 文件

WAV 文件 是一种常见的音频表示文件格式。WAV 文件将音频存储为一系列“样本”,这些样本是用数字表示的音频信号在特定时间点的值。标头之后是 WAV 文件的样本数据。每个样本是 2 字节 (16 位) 的整数,代表特定时间点的音频信号值。

按照给定的比例因子调整每个样本的值,就可以改变音频的音量。例如,将每个样本值乘以 2.0 会使原始音频的音量增加一倍 (或加倍)。 相应地,乘以 0.5 会使音量减小一半。

类型

到目前为止,我们已经在 C 中看到了许多不同的类型,包括 intboolchardoublefloatlong。 在名为 stdint.h 的头文件中,声明了许多其他类型,这些类型允许我们非常精确地定义整数的大小(以位为单位)和符号(有符号或无符号)。 在这个实验里,我们会用到下面这两种数据类型:

  • uint8_t 是一种存储 8 位无符号(即非负数)整数的类型。 我们可以将 WAV 文件标头的每个字节视为 uint8_t 值。
  • int16_t 是一种存储 16 位有符号(即正数或负数)整数的类型。 我们可以将 WAV 文件中的每个音频样本视为 int16_t 值。

开始

打开 VS Code

首先单击终端窗口内部,然后单独执行 cd。 您应该发现其“提示符”类似于以下内容。

单击该终端窗口内部,然后执行

wget https://cdn.cs50.net/2022/fall/labs/4/volume.zip

然后按 Enter 键,以便在您的 codespace 中下载一个名为 volume.zip 的 ZIP 文件。 请注意不要忽略 wget 和以下 URL 之间的空格,或任何其他字符!

现在执行

以创建一个名为 volume 的文件夹。 您不再需要该 ZIP 文件,因此您可以执行

并在提示符下回复“y”,然后按 Enter 键以删除您下载的 ZIP 文件。

现在输入

然后按 Enter 键,将您自己移动(即打开)到该目录中。 您的提示符现在应类似于以下内容。

如果一切顺利,您应该执行

您应该看到一个 volume.c 文件以及一个 input.wav 文件。

如果您遇到任何问题,请再次按照这些相同的步骤操作,看看您是否可以确定自己出错的地方!

实现细节

完成 volume.c 的实现,使其按给定的因子更改声音文件的音量。

  • 这个程序接受三个命令行参数:input 是原始音频文件名,output 是要生成的新音频文件名,而 factor 是音量缩放的比例因子。
    • 例如,如果 factor2.0,则您的程序应将 input 中音频文件的音量加倍,并将新生成的音频文件保存在 output 中。
  • 您的程序应首先从输入文件读取标头,并将标头写入输出文件。 回想一下,此标头始终正好是 44 字节长。
    • volume.c 文件里已经定义了一个名为 HEADER_SIZE 的变量,它的值是文件头的字节数。
  • 然后,您的程序应从 WAV 文件读取剩余的数据,一次读取一个 16 位(2 字节)样本。 您的程序应将每个样本乘以 factor,并将新样本写入输出文件。
    • 你可以认为 WAV 文件里的样本数据都是 16 位的有符号整数. 实际上,WAV 文件的每个样本可能具有不同数量的位,但对于本实验,我们将假设 16 位样本。
  • 您的程序(如果使用 malloc)不得泄漏任何内存。

演练

提示

  • 你可能需要创建一个字节数组,用来存放从输入文件读取的 WAV 文件头数据.
  • 你可能需要创建一个“缓冲区”,用于存储从 WAV 文件读取的音频样本。为了存储音频样本,你可以使用 int16_t 类型,并通过如下语法创建一个缓冲区变量:

然后,你可以将 &buffer 作为 freadfwrite 的参数,从而从缓冲区读取或向缓冲区写入数据。(请注意,& 运算符用于获取变量的地址。)

  • 你可能会发现 freadfwrite 的文档对你有所帮助。
    • 需要特别注意的是,这两个函数都接受以下参数:
      • ptr: 指针,指向内存中用于存储(从文件读取时)或写入(向文件写入时)数据的位置。
      • size: 数据项的字节数
      • nmemb: 要读取或写入的数据项的数量(每个数据项的大小为 size 字节)
      • stream: 要读取或写入的文件指针
    • 根据文档,fread 会返回成功读取的数据项数量。这在你检查是否到达文件末尾时会很有帮助!

不确定如何解决?

如何测试你的代码

你的程序应该按照以下示例运行。

$ ./volume input.wav output.wav 2.0

当你收听 output.wav 时(例如,在文件浏览器中按住 Control 键点击 output.wav 并选择“下载”,然后在电脑上的音频播放器中打开),它的音量应该是 input.wav 的两倍!

$ ./volume input.wav output.wav 0.5

当你收听 output.wav 时,它的音量应该是 input.wav 的一半!

执行以下命令以使用 check50 评估代码的正确性。但请务必自己编译并测试它!

check50 cs50/labs/2023/x/volume

执行以下命令以使用 style50 评估代码的风格。

如何提交

在你的终端中,执行以下命令以提交你的工作。

submit50 cs50/labs/2023/x/volume