如何将一个音频文件添加到另一个?
Posted
技术标签:
【中文标题】如何将一个音频文件添加到另一个?【英文标题】:How to add one audio file on to other one? 【发布时间】:2012-11-25 00:40:34 【问题描述】:我有一个音乐的音频文件,我需要录制歌曲的小片段并在不同的时间添加到音乐文件中。您可以这样理解,我有一条长条纸,我必须将小纸条粘贴在大纸条的不同位置。 请提出一些方法。
让我在这里提供更多细节。假设我有 10 个 5 秒的小声音片段,我有一个 50 秒的音乐文件。所以我总共有 11 个声音文件。现在,我必须通过在音乐文件的不同时间后添加 10 个小片段来创建一个最终的音频文件。第一个文件应该在 5.22 秒添加,第二个文件应该在 10.34 秒添加。
【问题讨论】:
这是一个非常笼统的问题,就目前而言。您需要更明确地说明,在模糊描述的过程中,目前让您感到困惑的是什么。说明您的解决方案的一些要求也会有所帮助(例如,仅将其标记为“iphone”)。 未压缩的音频文件只是定期进行的一系列电压测量——从大约每秒 8000 次到 44.1Khz。立体声文件可以是左右交替测量的两条轨道。每个测量值都有一定的长度——8、12、16、24 或 32 位。该文件有一个标题,提供有关文件的信息——数据速率、通道数、每个通道的位数等。“拼接”将是剥离标题的问题,将测量序列安排回-返回,然后为组合数据创建一个新标头。 @MatthewHall Mathew,我已经进一步解释了这个问题。 很容易做到,一旦你破解了文件格式。但是,如果文件被压缩,它会变得有点混乱,因为 Apple 的内置工具似乎不能很好地处理压缩,至少在模拟器上没有。 @HotLicks 你能详细说明一下吗? 【参考方案1】:我会根据你在cmets中的说明来回答:
假设我有两个 mp3 文件,a.mp3 为 5 秒,b.mp3 为 7 秒,我想混合它们以生成持续时间的 c.mp3 7 秒。
如 cmets 中所述,我无法为您提供任何 ios 细节,但我可以让您了解执行此过程所需的逻辑,无论使用何种平台和库。我将使用简单的 C++ sn-ps 来演示。但是,听起来您想要做的是将 a.mp3(以下简称 A)混入 b.mp3(以下简称 B)的某处——假设将 A 混合到 B 的开头——以产生最终的音频剪辑 C。
首先,由于您提到它们是 MP3 文件,而不是 WAV 或其他未压缩的 PCM 格式,例如 RAW 或 AIFF,您首先需要将 A 和 B 转换为未压缩的格式,例如 S16_LE
PCM (CD音频格式——带符号的 16 位整数样本,小端序),这意味着您将使用一组样本值——如果是立体声音频,则左右声道交错——对于 A 和 B,因此对于 C,当您完成混音后,其中的最后一个可以选择性地重新编码为 MP3。
您应该使用库来为您处理文件编码/格式问题,但是在使用它们时,它们都——包括用于直接录制或回放的系统接口——产生(即,在读取时)或期望 (即,在写入时)本质上是相同的基本未压缩 PCM 样本流格式。对于一般开发,无处不在的libsndfile
C 库可用于为您处理约 47 种文件格式的所有这些,包括 Ogg Vorbis 和 FLAC(但不直接支持 MP3)以及您使用的 WAV 格式变体应该是专注吧。
为简单起见,我们只考虑单声道声音片段 A 和 B(即,它只是 A 和 B 的样本值的直接数组,我们不必担心交错的左/右声道);如果重要的话,您可以通过独立考虑每个立体声通道(A.left 与 B.left 混合,A.right 与 B.right 混合)轻松地将概念扩展到立体声。如果您的特定 A 和 B 是立体声但 C 不需要,您也可以简单地将两个输入音频剪辑预先转换为单声道,具体取决于应用程序。
此外,将音频样本作为浮点值处理通常更容易,因此将未压缩的样本格式转换(或者,通常,您的音频文件库会为您完成 - libsndfile
会)将未压缩的样本格式转换为浮点在 [-1.0, +1.0] 范围内,其中 1.0 的绝对值表示最大可能的样本值,0.0 表示静音。这些样本值包含随时间推移(即在阵列上)的任意音频波形的演变。
首先,您需要确保在混音前有足够的“动态余量”(防止输出中出现削波)。为什么?混合采用信号叠加(加法)的原理来组合信号/声音:我们将为每个重叠样本将 A 和 B 加在一起,因此如果来自 A 和 B 的相应样本的总和,则混合输出样本可能会“剪辑”超过 1.0 或低于 -1.0。
有几种方法可以防止削波,具体取决于您各自的输入电平,以及您是想保持它们的音量比还是简单地将它们相等地组合(或者您是否正在使用立体声并想使用声音最大的那个A 或 B 的通道作为参考点——这是我们最后听到的立体声)。
我们将采用最简单的方法,将 A 和 B 的音量标准化为不超过满量程 (0.5) 的一半,这样当它们加在一起时,它们就永远不会削波(即没有混合输出样本将永远超出范围 [-1.0, +1.0])。如果不是 2 个输入,而是有 3 个输入音频片段 X、Y 和 Z 要使用此方法同时混合在一起,我们将在峰值 (0.33) 处将每个片段归一化为满量程的 1/3。
通过迭代它们各自的样本缓冲区/数组并确定每个样本缓冲区/数组中的最大样本值,找到 A 和 B、A_peak
和 B_peak
的峰值。 [要遵循的代码。]
分别为每个采样缓冲区 A 和 B 确定缩放值 A_scale
和 B_scale
,以便它们与各自的峰值相乘产生半比例。 [要遵循的代码。]
A_scale * A_peak == 0.5
B_scale * B_peak == 0.5
尔格:
A_scale = 1 / (2 * A_peak)
B_scale = 1 / (2 * B_peak)
现在,我们可以将整个样本缓冲区 A 和 B 分别乘以 A_scale
和 B_scale
,它们将被归一化为每个正好半刻度的峰值,并且两者的混合样本永远不会超过全刻度 -规模。也就是说,即使 A 和 B 的最大值与样本对齐,它们的缩放和总和混合输出也将恰好为 1.0,并且永远不会更大。这种比例系数通常被称为“增益”。
同样,有多种方法可以在混合时标准化两个或多个样本缓冲区(音频剪辑)之间的增益,但这是最简单和最通用的演示方法。另外,它很容易适应将 N 个不同的音频剪辑混合在一起(如上所述),并且稍微简化后,可以实时混合样本流(其中整个音频剪辑的样本缓冲区不是t 可用,并且样本处理是分块完成的,这在录制时通常是这种情况)。
现在,我们可以开始混音了。
在这种情况下,A(5 秒)适合 B(7 秒),因此我们可以将混合直接输出到 B 中,但为了一般性,让我们将混合输出到单独的样本缓冲区 C(7 秒),留下输入 A 和 B 未作为浮点样本缓冲区(可能被重用)。
让A_len
是样本计数中 A 的长度(这是很容易确定的——当你加载文件时,库会告诉你,尽管从根本上说它只取决于持续时间和采样率),同样适用于 B_len
和 B,对于输出 C,C_len == B_len
,因为您的问题陈述中有 B_len > A_len
。
分配C,我们的混合输出:
unsigned int C_len = max(A_len, B_len);
double C[] = new double[ C_len ];
找出A和B中样本绝对值的峰值:
double A_peak = -1.0, B_peak = -1.0;
for (unsigned int i = 0; i < A_len; ++i) A_peak = max( A_peak, fabs(A[i]) );
for (unsigned int i = 0; i < B_len; ++i) B_peak = max( B_peak, fabs(B[i]) );
求 A 和 B 的半标度归一化增益:
double A_scale = 1 / ( 2 * A_peak );
double B_scale = 1 / ( 2 * B_peak );
将 A 和 B 混合到 C 中:
assert(A_len <= B_len);
assert(B_len == C_len);
unsigned int x = 0;
for (; x < A_len; ++x)
C[x] = A_scale * A[x] + B_scale * B[x]; // actual mixing of A and B, finally
for (; x < B_len; ++x)
C[x] = B_scale * B[x]; // as if A[x] were zero & no abrupt gain change
请注意,浮点缓冲区 A 和 B 在混合和归一化后仍然保持不变。
A 在任何地方都可以被认为是零/无声,它没有被混合。
如果我们想在 B 内的任意偏移处开始混合 A(而不是在开始时,假设这里),那么我们只需计算对应于我们的时间偏移的样本数(t_offset
以秒为单位, s_offset = t * sample_rate
整数样本),并在上述循环结构中的 x == s_offset
处开始包含 A。 [假设s_offset + A_len <= C_len
防止溢出。]
我们鼓励尝试更多特定于应用程序的方法来规范化混合输入,因为有很多可能性。例如,如果我计算了 A 和 B 样本之和的峰值,而不是分别计算每个峰值(基本上是先混合然后校正),该怎么办?这种[更好的]技术什么时候不可能实现?
最后,每当您混合信号时,在混合开始和结束的过渡点(例如,咔嗒声)处(例如,在 A 结束但 B 继续进入 C 的点)处总是存在伪影的可能性。这是一个相对较低的风险。但是,此类伪影的一般解决方案是对混音的输入/离开输入进行短时间淡入和淡出,通过平滑混合波形来消除伪影,并且可以快速完成以至听不见.
【讨论】:
以上是关于如何将一个音频文件添加到另一个?的主要内容,如果未能解决你的问题,请参考以下文章
如何将类文件添加到另一个 jar 中的 jar 文件中 [关闭]
如何将可播放的音频文件添加到 TableListBox 播放列表(JUCE C++)