我是iOS的初学者,我正在尝试使用Swift设计一个鼓组应用程序。我用一个按钮设计了一个视图并编写了下面的代码,但它有一些问题:
AVAudioPlayer
不是低延迟音频的最佳选择,但作为初学者,如果没有OpenAL
中的代码示例或教程,很难学习AudioUnit
,Swift
。问题与此类似:Which framework should I use to play an audio file (WAV, MP3, AIFF) in iOS with low latency?。代码:
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()
}
答案 0 :(得分:5)
如果您需要极低的延迟,我发现AVAudioSession单例上有一个非常简单的解决方案(在应用启动时会自动实例化):
首先,使用此类方法获取对应用程序的AVAudioSession单例的引用:
(来自AVAudioSession Class Reference):
获取共享音频会话
宣言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属性。 有关详细信息,请参阅Configuring the Audio Session。
请记住上面的注释 - IOBufferDuration
属性实际是否设置为传递给func setPrefferedIOBufferDuration(_ duration: NSTimeInterval) throws
方法的值,取决于函数未返回错误, 和 其他我不太清楚的因素。另外 - 在我的测试中 - 我发现如果你将这个值设置为一个非常低的值,那么值(或接近它的值)确实会被设置,但是当播放文件时(例如使用AVAudioPlayerNode)声音不会玩了。没有错误,只是没有声音。这显然是个问题。我还没有发现如何测试这个问题,除非注意到在实际设备上测试时听不到声音。我会调查一下。但就目前而言,我建议将首选持续时间设置为不小于.002或.0015。 .0015的值似乎适用于我正在测试的iPad Air(型号A1474)。虽然低至.0012似乎在我的iPhone 6S上运行良好。
从CPU开销角度考虑的另一件事是音频文件的格式。未压缩的格式在播放时将具有非常低的CPU开销。 Apple建议您使用CAF文件以获得最高质量和最低开销。对于压缩文件和最低开销,您应该使用IMA4压缩:
(来自iOS Multimedia Programming Guide):
iOS中的首选音频格式适用于未压缩(最高质量) 音频,使用16位,小端,线性PCM音频数据打包在一个 CAF文件。您可以在Mac OS X中将音频文件转换为此格式 使用afconvert命令行工具,如下所示:
/usr/bin/afconvert -f caff -d LEI16 {INPUT} {OUTPUT}
当您需要播放多个声音时,可以减少内存使用量 同时,使用IMA4(IMA / ADPCM)压缩。这会减少文件 大小但在解压缩期间需要最小的CPU影响。和。一样 线性PCM数据,包装CAF文件中的IMA4数据。
您也可以使用afconvert转换为IMA4:
/usr/bin/afconvert -f AIFC -d ima4 [file]
答案 1 :(得分:0)
我花了一个下午试图通过玩AVAudioPlayer&来解决这个问题。 AVAudioSession,但我无法随心所欲。 (不幸的是,这里设置IO缓冲持续时间似乎没有帮助。)我也尝试过AudioToolbox,但我发现结果表现几乎相同 - 相关用户操作之间明显可察觉的延迟和音频。
在搜索了一下互联网之后,我发现了这个:
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。但它似乎确实有效!