如何使用AVAudioPlayer更快地播放音频*和*更高音调?

时间:2014-04-16 08:11:04

标签: ios objective-c core-audio avaudioplayer openal

问题陈述:

我的应用程序中有一组声音效果存储为.m4a文件(AAC格式,48 KHz,16位),我希望以各种速度和音高播放,而不必预先播放将所有变体生成为单独的文件。

虽然.rate对象的AVAudioPlayer属性可以改变播放速度,但它始终保持原始音高,这不是我想要的。相反,我只是想要更快或更慢地播放声音样本并让音调上下移动以匹配 - 就像加速或减慢老式卷轴到磁带录音机一样。换句话说,我需要一些方法来基本上改变音频采样率,如+2半音(快12%),-5半音(慢33%),+ 12半音(快2倍)等等。

问题:

是否有某种方式从AVAudioPlayer对象获取线性PCM音频数据,使用不同的iOS框架应用采样率转换,并将生成的音频数据填充到新的AVAudioPlayer对象中,然后正常播放?

可能的途径:

我正在阅读AudioConverterConvertComplexBuffer。特别是kAudioConverterSampleRateConverterComplexity_MasteringkAudioConverterQuality_MaxAudioConverterFillComplexBuffer()引起了我的注意。所以这个音频转换框架看起来很可能。这是我应该进一步探索的途径吗?

要求:

  1. 我实际上不需要立即开始播放。如果采样率转换引起轻微延迟,那很好。我所有的样本都是4秒或更短的时间,所以我认为任何即时重新采样都会很快发生,大约为1/10秒或更短。 (但是,超过1/2会是太多了。)

  2. 如果使用iOS提供的转换框架有更简单的方法,我真的不想进入OpenAL或Core Audio等重量级的东西。但是,如果使用OpenAL或Core Audio存在简单解决此问题的解决方案,我很乐意考虑这一点。 “简单”是指可以在50-100行代码中实现的东西,并且不需要启动额外的线程来将数据提供给声音设备。我宁愿自动处理所有事情 - 这就是为什么我愿意在播放前转换音频片段。

  3. 我想在这里避免使用任何第三方库,因为这不是火箭科学,我知道必须以某种方式使用原生iOS框架。

  4. 同样,我需要一起调整音高和播放速率,而不是单独调整。因此,如果播放速度减慢了2倍,那么人声会变得非常深沉且说话速度慢。如果播放速度加快2-3倍,那么人类的声音听起来就像一个快速说话的花栗鼠。换句话说,我绝对不希望在保持音频持续时间相同的同时改变音调,因为当向上弯曲音调超过一对半音时,该操作导致不期望的“细小”声音。我只想加快整个过程的速度,并将音调调高为一种自然的副作用,就像过去常用的老式录音机一样。

  5. 需要在iOS 6及更高版本中运行,虽然iOS 5支持将是一个不错的奖励。

2 个答案:

答案 0 :(得分:2)

Jack Wu提到的论坛链接有一个建议,涉及直接覆盖AIFF标题数据。这可能有效,但您需要拥有AIFF文件,因为它依赖于要写入的特定范围的AIFF标头。这也需要在创建AVAudioPlayer之前完成,这意味着一旦它运行就不能修改音高。

如果你愿意去AudioUnits路线,一个完整的简单解决方案可能是〜200行(请注意,这假设具有一个功能的代码样式最多需要7行,每行有一个参数)。有一个Varispeed AudioUnit,它通过锁定音高来完成您想要的速度。您基本上需要查看API,docs和一些示例AudioUnit代码才能熟悉,然后:

  1. 创建/初始化音频图和流格式(~100行)
  2. 创建并向图表添加RemoteIO AudioUnit(kAudioUnitSubType_RemoteIO)(此输出到发言人)
  3. 创建并添加一个varispeed单元,并将varispeed单元的输出(kAudioUnitSubType_Varispeed)连接到RemoteIO Unit的输入
  4. 创建并向图形添加AudioFilePlayer(kAudioUnitSubType_AudioFilePlayer)单元以读取文件并将其连接到varispeed单元
  5. 启动图表以开始播放
  6. 当你想改变音高时,通过AudioUnitSetParameter进行改变,音高和播放速率的变化将在播放时生效
  7. 请注意,还有一个TimePitch音频单元,可以独立控制音高和速率。

答案 1 :(得分:2)

对于iOS 7,您需要查看名为audioTimePitchAlgorithm的AVPlayerItem的时间间距算法(AVAudioTimePitchAlgorithmVarispeed)。不幸的是,早期系统无法使用此功能。