首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >iOS Swift中简单的低延迟音频回放

iOS Swift中简单的低延迟音频回放
EN

Stack Overflow用户
提问于 2016-01-08 15:01:14
回答 2查看 6.2K关注 0票数 5

我是iOS的初学者,我正在尝试用Swift设计一个鼓集应用程序。我用一个按钮设计了一个视图,并编写了下面的代码,但是它有一些问题:

  1. 当我像滚筒一样快速地触摸按钮时,有些声音就会消失。
  2. 在“鼓卷”中,每次按下按钮时,声音都会被打断,而不是让样本一直播放到结束。举个例子,这是很糟糕的。我想听到每一个样本听起来完全,即使我再次触摸按钮。
  3. 触觉和声音之间有一个延迟。我知道AVAudioPlayer不是低延迟音频的最佳选择,但作为初学者,如果没有Swift的代码示例或教程,很难学习OpenALAudioUnit。问题与此类似:在低延迟的iOS中,我应该使用哪个框架来播放音频文件(WAV、MP3、AIFF)?

守则:

代码语言:javascript
复制
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()
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-02-06 18:53:11

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

首先,使用以下类方法获取对应用程序的AVAudioSession单例的引用:

(来自AVAudioSession类引用):

获取共享音频会话 声明SWIFT class func sharedInstance() -> AVAudioSession

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

设置首选音频I/O缓冲区持续时间(以秒为单位)。 声明SWIFT func setPreferredIOBufferDuration(_ duration: NSTimeInterval) throws 参数 duration要使用的音频I/O缓冲区持续时间(以秒为单位)。 outError在输入时,指向错误对象的指针。如果发生错误,则将指针设置为描述错误的NSError对象。如果不需要错误信息,请输入零。如果请求成功,则返回值true,否则返回false。 讨论 此方法请求更改I/O缓冲区持续时间。若要确定更改是否生效,请使用IOBufferDuration属性。有关详细信息,请参阅配置音频会话

请记住上面的注意事项-- IOBufferDuration属性是否实际上设置为传递给func setPrefferedIOBufferDuration(_ duration: NSTimeInterval) throws方法的值,取决于函数不返回错误、其他我不完全清楚的因素。而且--在我的测试中--我发现如果将这个值设置为极低的值,值(或接近它的值)确实会被设置,但是当播放一个文件(例如使用AVAudioPlayerNode)时,声音将不会被播放。没有错误,只是没有声音。这显然是个问题。我还没有发现如何测试这个问题,除非我注意到在实际设备上测试时耳朵里没有声音。我将会调查此事。但就目前而言,我建议将首选持续时间设置为不少于.002或.0015。.0015的价值似乎适用于我正在测试的iPad Air (型号A1474)。虽然.0012很低,但在我的iPhone 6S上工作得很好。

从CPU开销的角度考虑的另一件事是音频文件的格式。未压缩格式在播放时具有非常低的CPU开销。Apple建议,为了获得最高的质量和最低的开销,您应该使用CAF文件。对于压缩文件和最低开销,您应该使用IMA4压缩:

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

首选的音频格式在iOS为未压缩(最高质量)音频,使用16位,小终端,线性PCM音频数据打包在一个CAF文件。您可以使用af红外命令行工具在Mac中将音频文件转换为此格式,如下所示:

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

当您需要同时播放多个声音时,若要减少内存使用量,请使用IMA4 (IMA/ADPCM)压缩。这减少了文件大小,但在解压缩过程中对CPU的影响最小。与线性PCM数据一样,将IMA4数据打包到CAF文件中。

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

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

票数 7
EN

Stack Overflow用户

发布于 2017-12-08 20:57:05

我花了一个下午的时间试图通过玩AVAudioPlayer & AVAudioSession来解决这个问题,但是我没有办法解决这个问题。(不幸的是,按照这里接受的答案所建议的设置IO缓冲区持续时间似乎没有帮助。)我也尝试过AudioToolbox,但是我发现结果的性能几乎是一样的--在相关的用户操作和音频之间显然是一个明显的延迟。

在浏览了一下互联网之后,我发现:

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

关于AVAudioEngine的部分被证明是非常有用的。下面的代码是稍加修改的:

代码语言:javascript
复制
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")
    }
}

}

这可能不是完美的,因为我是一个斯威夫特新手,以前没有使用过AVAudioEngine。不过,它似乎确实有效!

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34680007

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档