【问题标题】:How do I upsample a wav file using AS3?如何使用 AS3 对 wav 文件进行上采样?
【发布时间】:2010-12-27 10:47:25
【问题描述】:

我正在尝试在 AS3 中将 8000hz、16 位 wav 文件上采样到 11025hz。在这一点上,我并不担心应用我知道我最终会需要的低通滤波器。

我一直是referencing this wiki page

这是我到目前为止所做的:

  1. 计算出最小公倍数为 3528000
  2. 计算出的 L 为 441
  3. 计算出的 M 为 320
  4. 在样本之间添加了 440 个零
  5. 每 320 个样本写入一个新的字节数组

但是,当我去播放新的 wav 时,它是无法区分的噪音。这是我的代码:

const sourceRate:uint = 8000;
const targetRate:uint = 11025;
var lcm:uint = lcm(targetRate, sourceRate); // = 3528000
var l:uint = lcm / sourceRate; // = 441
var m:uint = lcm / targetRate; // = 320

// upsample by factor of l
var upsampleData:ByteArray = new ByteArray();
upsampleData.endian = Endian.LITTLE_ENDIAN;

// originalWavData is a ByteArray of the source wav data
// fill is a ByteArray that contains 440 zeroes, written using writeShort(0x0)

while(originalWavData.bytesAvailable > 1) {
    upsampleData.writeBytes(fill);
    upsampleData.writeShort(originalWavData.readShort());
}

// downsample by factor of m
var downsampleData:ByteArray = new ByteArray();
downsampleData.endian = Endian.LITTLE_ENDIAN;

upsampleData.position = 0;

for(var k:uint=0; k<upsampleData.length; k++) {
    upsampleData.position = k * m;
    if(upsampleData.bytesAvailable < 2) break;
    downsampleData.writeShort(upsampleData.readShort());
}

谁能告诉我我在代码中做错了什么?这是我的第一个问题帖子,所以如果我忘记了什么,或者需要提供更多信息,请告诉我。

谢谢!

更新:

我简化了 Aric 的答案,现在使用以下代码成功地进行了上采样:

/**
 * Generates a ByteArray containing numSamples of
 * data using linear interpolation between points
 * y0 and y1.
 */
function interpolate(y0:int, y1:int, numSamples:uint):ByteArray {
    var b:ByteArray = new ByteArray();
    b.endian = Endian.LITTLE_ENDIAN;
    var m:Number = Math.round((y1-y0)/numSamples);
    for(var i:uint=0; i<numSamples; i++) {
        var n:int = m * i + y0;
        b.writeShort(n);
    }
    b.position = 0;
    return 0;
}

// upsample by factor of l
var n1:int = 0;
while(originalWavData.bytesAvailable > 1) {
    var sample:int = originalWavData.readShort();
    upsampleData.writeBytes(interpolate(n1, sample, (l-1)));
    n1 = sample;
}

// downsample by factor of m
while(upsampleData.bytesAvailable > 1) {
    downsampleData.writeShort(upsampleData.readShort());
    upsampleData.position += ((m-1)*2);
}

关于此解决方案的几点注意事项:我正在对音频验证码进行上采样,因此音质并不是非常重要。此外,第一个样本只是静音,所以我不需要计算第一个样本左侧的值。这就是为什么 n1 最初等于 0 的原因。此外,我没有在我的下采样中对生成的样本进行平均,而是抓住了每个第 M 个样本,这听起来对我的目的来说很好。

我确信有 1000 种更好的方法可以做到这一点,但对于我需要的,它确实有效。再次感谢 Aric 的回答。

【问题讨论】:

  • 添加零无疑会产生错误的数据。试着把它看成一个图像,想象一下如果你被告知将图像的水平分辨率提高四倍,然后在每个正常像素之间添加 3 个黑色像素,会发生什么情况?您需要查看插值。我读过的一种更好的方法是将声音文件转换为其频域,然后以更高的分辨率将其转换回来。
  • 感谢您的信息。我读过同样的东西。不幸的是,我对数字信号处理一无所知,在这里阅读 FFT (en.wikipedia.org/wiki/Fast_Fourier_transform) 让我更加困惑。您是否有任何示例/资源可以让我学习 DFT 的基础知识?我理解转换到频域的概念,但我无法掌握如何以更高分辨率转换回来。任何信息都非常感谢。谢谢!
  • 当您尝试在 Wikipedia 中执行该方法时,您是否通过低通滤波器运行上采样数据?我现在正在使用 Aric 的方法,但我对 add-zeroes-then-filter 方法很好奇。

标签: actionscript-3 audio wav


【解决方案1】:

您是否有特定原因要进行上采样?上采样不会为您提供更好的音频,就像将手机对话录制到 CD 上一样会给您“CD 质量”的音频。

如果您确实想要上采样,那么正如 Lasse 所说,不要只插入零。正如维基百科指出的那样,您需要插值采样到 3528000 Hz,然后再下采样回 11025 Hz。

对于上采样,一种方法是在每个点之间使用线算法。假设 8 kHz 录音中的前三个样本是:[15,25,33]。

要将这三个样本上采样到 3528000 Hz,您需要输出 (441 * 3 = 1323) 个样本。第 220 个样本应该是 15,第 661 个样本应该是 25,第 1102 个样本应该是 33。在这些点之间画一条直线,你会得到一个不错的上采样。

完成此操作后,您需要将 1323 个样本采样到大约 4 个样本。第一个应该是样本160-480的平均值,第二个应该是481-800的平均值,第三个应该是801-1160的平均值,第四个应该是1161-1480的平均值。当然,您缺少样本 1324-1480,您可以使用原始数据中的样本 #4 生成这些样本。

这至少应该让你接近。但在你这样做之前,问问自己是否真的需要上采样。除非您使用一些只采用某些格式的奇怪软件,否则最好只提供您拥有的数据。

【讨论】:

  • 非常感谢您的回答。我明白上采样不会提高质量。这就是我正在做的事情:我正在使用 FreeTTS 在中间层生成一个音频验证码。我使用的声音是 8khz 声音,这就是为什么我有一个 8khz wav 文件。 Flash 不会原生播放 wav 文件,因此我必须在运行时生成 SWF 并将 wav 文件嵌入到生成的 SWF 中。 Flash 仅支持 5512.5 hz 的倍数的采样率,因此我决定对音频客户端进行上采样(为我们的应用服务器节省一些时钟周期)。我用你的答案让它工作了。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-25
  • 1970-01-01
  • 2020-03-08
  • 1970-01-01
  • 2011-05-31
  • 2015-08-17
相关资源
最近更新 更多