【问题标题】:How to export sound from timeline of sounds on iOS with OpenAL如何使用 OpenAL 从 iOS 上的声音时间线中导出声音
【发布时间】:2011-12-28 04:21:03
【问题描述】:

我不确定是否可以实现我想要的,但基本上我有一个代表录音的 NSDictionary。这是在什么时间点播放什么声音 ID 的时间线。

我有它,这样你就可以播放这个时间线/录音,而且效果很好。

我想知道是否有任何方法可以获取此时间线,并将其导出为单个声音,如果设备与 iTunes 同步,则可以将其保存到计算机。

所以基本上我在问我是否可以制作声音的时间线,回放并将这些声音拼接成一个声音,然后可以导出。

我使用 OpenAL 作为我的声音框架,声音文件都是 CAF。

感谢任何帮助或指导。

谢谢!

【问题讨论】:

    标签: iphone objective-c ios openal


    【解决方案1】:

    你需要:

    • 对线性 PCM 音频格式有很好的理解(参见 Wikipedia's Linear PCM page)。
    • 对音频采样率和一些基本数学有很好的理解,可以将您的时间转换为采样偏移量。
    • 了解two's-complement 二进制数(有符号/无符号、16 位、32 位等)如何存储在计算机中,以及处理器的endian-ness 对此有何影响。
    • 有耐心、对学习有兴趣,并且有强烈的愿望去完成这项工作。

    这是怎么做的:

    1. 在您的应用程序中启用文件共享(UIFileSharingEnabled=YES in info.plist 并将文件写入/Documents 目录)。

    2. 将使用过的声音渲染到包含线性 PCM 音频数据的内存缓冲区中(如果它们还没有,即如果它们被压缩)。您可以使用音频队列的离线渲染功能来做到这一点(请参阅Apple audio queue docs)。如果您将它们全部渲染为相同的 PCM 格式和采样率(例如 16 位签名采样 @44,100Hz,我将在所有示例中使用此格式),这将使事情变得更容易很多,并为您的输出使用相同的格式。我建议从单声道格式开始,然后在你开始工作后添加立体声。

    3. 选择一种未压缩的输出格式并将您的声音混合成一个流:

      3.1。分配一个足够大的缓冲区,或者打开一个文件流来写入。

      3.2。写出任何标题(例如,如果使用 WAV 格式输出而不是原始 PCM)并在第一个声音开始之前为任何初始静音写零(或者如果不使用带符号的样本格式,则为样本范围的中点)。例如,如果您希望在第一个声音之前保持 0.1 秒的静音,请写入 4410 (0.1 * 44100) 个零样本,即写入 4410 个短裤(16 位)全部为零。

      3.3。现在跟踪所有“当前播放”的声音并将它们混合在一起。从“当前播放声音”的空列表开始,并跟踪您正在混合的样本的“当前时间”,对于您写出的每个样本,将“当前时间”增加1.0/sample_rate。当有时间开始另一个声音时,将其添加到“当前播放”列表中,样本偏移量为 0。现在要进行混合,您遍历所有“当前播放”声音并将它们的当前样本加在一起,然后为它们中的每一个增加样本偏移量。将求和值写入输出缓冲区。例如,如果 soundA 从 0.1 秒开始(在静音之后),而 soundB 从 0.2 秒开始,您将对样本 8820 执行相当于 output[8820] = soundA[4410] + soundB[0]; 的操作,然后对样本 8821 执行 output[8821] = soundA[4411] + soundB[1]; 等操作。当声音结束时(您到达其样本的末尾)只需将其从“当前播放”列表中删除并继续播放直到您的音频数据结束。

      3.4。上面描述的简单混合(样本总和)确实存在一些问题。例如,如果两个样本的值加起来大于 32767,则不能将其存储在有符号的 16 位数字中,这称为剪裁。现在,只需将值钳制为 32767,然后让它工作......稍后再回来实现一个简单的限制器(见末尾的描述)。

    4. 既然您有一个未压缩线性 PCM 格式的混合版本的曲目,这可能就足够了,所以请将其写入/Documents。如果您想以压缩格式编写它,则需要获取音频编码器的源并通过它运行线性 PCM 输出。

    简单的限制器:

    让我们选择限制样本范围的前 10%,因此如果绝对值大于 29490 (int limitBegin = (int)(32767 * 0.9f);),我们将按比例缩小该值。可能的最大峰值为int maxSampleValue = 32767 * numPlayingSounds;,我们希望将limitBegin 以上的值缩放至峰值32767。因此按照上述非常简单的混合器将总和到sampleValue,然后:

    if(sampleValue > limitBegin)
    {
        float overLimit = (sampleValue - limitBegin) / (float)(maxSampleValue - limitBegin);
        sampleValue = limitBegin + (int)(overLimit * (32767 - limitBegin));
    }
    

    如果您留心,您会注意到当numPlayingSounds 发生变化时(例如,当新声音开始时),限制器变得更加(或不那么)刺耳,这可能会导致音量突然变化(在范围有限)以容纳额外的声音。您可以改为使用最大数量的播放声音,或者设计一些巧妙的方法在几毫秒内提升限制器。

    请记住,这是对sampleValue 的绝对值进行操作(在有符号格式中可能为负),所以这里的代码只是为了演示这个想法。您需要正确编写它以处理样本范围两端(峰和谷)的限制。此外,在混音过程中,您可以采取一些技巧来优化上述所有内容 - 您可能会在编写混音器时发现这些,小心并首先使其工作,然后在需要时返回并重构/优化。

    还记得考虑您正在使用的平台的字节顺序和您正在写入的文件格式,因为您可能需要进行一些字节交换。

    【讨论】:

    • 对峰值使用int maxSampleValue = 32767 * numPlayingSounds; 效果不是很好:它确实可以防止溢出,但组合文件的体积会远低于限制。更好的方法是最初将源音频文件组合成一个 4 字节整数(或浮点数)数组(而不是 CD 音频的普通 2 字节整数),然后扫描数组以找到真正的峰值,然后将 4 字节音频缩放为最终的 2 字节/样本数组。
    • 我需要一段时间才能弄清楚这一切,但我会继续假设您的答案是正确的。 :)
    • @MusiGenesis 你说得对,我描述的限制器会稍微降低音量,但它只会在 90% 以上开始,并根据播放样本的数量进行缩放。当然,它可以使用一些改进,例如通过前瞻提高限制器,或更改切入值,或提高按比例缩小效果以“四舍五入”顶部。根据最高峰缩放所有内容是防止削波的另一种有效方法,但这会降低整体音量,除非您使用像压缩器这样可以预见局部峰值的斜坡缩放器。最好先学习简单的东西:)
    【解决方案2】:

    如果您的文件以简单的格式存储,一种不太难的方法就是手动将它们组合在一起。也就是说,创建一个caf格式的新文件,然后手动将你想要的部分放在一起。

    如果声音未压缩(线性 PCM),这将非常容易。但是,请在此处阅读有关 caf 文件格式的文档:

    http://developer.apple.com/library/mac/#documentation/MusicAudio/Reference/CAFSpec/CAF_spec/CAF_spec.html#//apple_ref/doc/uid/TP40001862-CH210-SW1

    【讨论】:

    • 声音是使用 IMA4 压缩的 CAF。是否有可能创建一个新的 CAF,该 CAF 还考虑到每个单独的声音应该在什么时候播放,这样它就不仅仅是一个没有中间没有中断的混杂的声音?
    • 还有很多工作要做。在这个 SO 问题中有关于解码 IMA4 格式的信息:stackoverflow.com/questions/2130831/decoding-ima4-audio-format
    • 是否可以通过设备的扬声器播放录音并使用 AVAudioRecorder 或类似的工具进行录制?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多