iOS Swift中简单的低延迟audio播放

我是iOS的初学者,我试图用Swiftdevise一个鼓组应用程序。 我用一个buttondevise了一个视图,并编写了下面的代码,但它有一些问题:

  1. 当我像滚鼓一样快速地触摸button时,有些声音会丢失。
  2. 仍然在“滚筒滚动”中,每次触摸button都会中断声音,而不是让样本播放直到结束。 例如,在cy片中这很糟糕。 即使再次触摸button,我也想听完所有的声音。
  3. 触摸和声音之间存在延迟。 我知道AVAudioPlayer并不是低延迟audio的最佳select,但是作为初学者,很难在没有代码示例或Swift教程的情况下学习OpenALAudioUnit 。 问题与此类似: 我应该使用哪个框架在iOS中播放低延迟的audio文件(WAV,MP3,AIFF)? 。

代码:

 override func viewDidLoad() { super.viewDidLoad() // Enable multiple touch for the button for v in view.subviews { if v.isKindOfClass(UIButton) { v.multipleTouchEnabled = true } } // Init audio audioURL = NSBundle.mainBundle().URLForResource("snareDrum", withExtension: "wav")! do { player = try AVAudioPlayer(contentsOfURL: audioURL) player?.prepareToPlay() } catch { print("AVAudioPlayer Error") } } override func viewDidDisappear(animated: Bool) { super.viewDidDisappear(animated) player?.stop() player = nil } @IBAction func playSound(sender: UIButton) { player?.currentTime = 0 player?.play() } 

如果您需要极低的延迟时间,我发现AVAudioSession单例中有一个非常简单的解决scheme(在应用程序启动时会自动实例化):

首先,使用这个类的方法获得对你的应用程序的AVAudioSession单例的引用:

(来自AVAudioSession类参考 ):

获取共享audio会话

声明SWIFT

class func sharedInstance() -> AVAudioSession

然后,尝试使用此实例方法将首选的IO缓冲区持续时间设置为很短的时间(例如.002):

设置首选audioI / O缓冲区持续时间,以秒为单位。

声明SWIFT

func setPreferredIOBufferDuration(_ duration: NSTimeInterval) throws

参数

duration您要使用的audioI / O缓冲区持续时间(以秒为单位)。

outErrorinput时,指向错误对象的指针。 如果发生错误,指针将被设置为描述错误的NSError对象。 如果你不想要错误信息,传入nil。 返回值如果请求成功,则返回true,否则返回false。

讨论

此方法请求更改I / O缓冲区持续时间。 要确定更改是否生效,请使用IOBufferDuration属性。 有关详情,请参阅configurationaudio会话 。


请牢记上面的注释 – IOBufferDuration属性实际上是否设置为传递给func setPrefferedIOBufferDuration(_ duration: NSTimeInterval) throws的值func setPrefferedIOBufferDuration(_ duration: NSTimeInterval) throws方法,取决于不返回错误的函数以及其他我不完全清楚的因素关于。 另外,在我的testing中,我发现如果将此值设置为非常低的值,则值(或与其相近的值)的确设置,但是在播放文件时(例如使用AVAudioPlayerNode),声音不会被播放。 没有错误,只是没有声音。 这显然是一个问题。 而且我还没有发现如何testing这个问题,除非在实际的设备上进行testing时发现缺less声音。 我会研究它。 但现在,我build议设置首选持续时间不低于.002或.0015。 .0015的值似乎适用于我testing的iPad Air(型号A1474)。 虽然低至0.0012似乎在我的iPhone 6S上运行良好。

另一个需要考虑CPU开销的是audio文件的格式。 播放时,未压缩格式的CPU开销非常低。 Applebuild议为了获得最高的质量和最低的开销,您应该使用CAF文件。 对于压缩文件和最低开销,您应该使用IMA4压缩:

(来自iOS多媒体编程指南 ):

iOS中的首选audio格式对于未压缩(最高质量)的audio,使用打包在CAF文件中的16位,小端,线性PCMaudio数据。 您可以使用afconvert命令行工具在Mac OS X中将audio文件转换为此格式,如下所示:

/usr/bin/afconvert -f caff -d LEI16 {INPUT} {OUTPUT}

为了在需要同时播放多个声音时使用更less的内存,请使用IMA4(IMA / ADPCM)压缩。 这样可以减小文件大小,但是在解压缩过程中CPU占用的影响最小。 与线性PCM数据一样,将IMA4数据封装在CAF文件中。

您也可以使用afconvert转换为IMA4:

/usr/bin/afconvert -f AIFC -d ima4 [file]

我花了一个下午的时间试图通过玩AVAudioPlayer和AVAudioSession来解决这个问题,但我无法得到它的任何地方。 (设置的IO缓冲区持续时间如接受的答案build议这里似乎没有帮助,不幸的是。)我也尝试了AudioToolbox,但我发现,结果performance几乎相同 – 相关的用户行为之间明显可察觉的延迟和audio。

在互联网上search了一下之后,我碰到了这个:

http://www.rockhoppertech.com/blog/swift-avfoundation/

AVAudioEngine部分原来是非常有用的。 下面的代码是一个小小的改进:

 import UIKit import AVFoundation class ViewController: UIViewController { var engine = AVAudioEngine() var playerNode = AVAudioPlayerNode() var mixerNode: AVAudioMixerNode? var audioFile: AVAudioFile? @IBOutlet var button: UIButton! override func viewDidLoad() { super.viewDidLoad() engine.attach(playerNode) mixerNode = engine.mainMixerNode engine.connect(playerNode, to: mixerNode!, format: mixerNode!.outputFormat(forBus: 0)) do { try engine.start() } catch let error { print("Error starting engine: \(error.localizedDescription)") } let url = Bundle.main.url(forResource: "click_04", withExtension: ".wav") do { try audioFile = AVAudioFile(forReading: url!) } catch let error { print("Error opening audio file: \(error.localizedDescription)") } } @IBAction func playSound(_ sender: Any) { engine.connect(playerNode, to: engine.mainMixerNode, format: audioFile?.processingFormat) playerNode.scheduleFile(audioFile!, at: nil, completionHandler: nil) if engine.isRunning{ playerNode.play() } else { print ("engine not running") } } 

}

这可能不完美,因为我是一个Swift新手,并没有使用过AVAudioEngine。 它似乎工作,但!

Interesting Posts