[英]Simple low-latency audio playback in iOS Swift
我是iOS的初學者,我正在嘗試用Swift設計一個鼓組應用程序。 我用一個按鈕設計了一個視圖並編寫了下面的代碼,但它有一些問題:
AVAudioPlayer
不是低延遲音頻的最佳選擇,但作為初學者,很難學習OpenAL
,沒有代碼示例的AudioUnit
或Swift
教程。 問題與此類似: 我應該使用哪種框架在iOS中播放低延遲的音頻文件(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單例上有一個非常簡單的解決方案(在應用程序啟動時會自動實例化):
首先,使用此類方法獲取對應用程序的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
的值func setPrefferedIOBufferDuration(_ duration: NSTimeInterval) throws
方法,取決於不返回錯誤的函數, 以及其他我不完全清楚的因素關於。 另外 - 在我的測試中 - 我發現如果你將這個值設置為一個非常低的值,那么值(或接近它的值)確實會被設置,但是當播放文件時(例如使用AVAudioPlayerNode)聲音不會玩了。 沒有錯誤,只是沒有聲音。 這顯然是個問題。 我還沒有發現如何測試這個問題,除非注意到在實際設備上測試時聽不到聲音。 我會調查一下。 但就目前而言,我建議將首選持續時間設置為不小於.002或.0015。 .0015的值似乎適用於我正在測試的iPad Air(型號A1474)。 雖然低至.0012似乎在我的iPhone 6S上運行良好。
從CPU開銷角度考慮的另一件事是音頻文件的格式。 未壓縮的格式在播放時將具有非常低的CPU開銷。 Apple建議您使用CAF文件以獲得最高質量和最低開銷。 對於壓縮文件和最低開銷,您應該使用IMA4壓縮:
(來自iOS多媒體編程指南 ):
iOS中的首選音頻格式對於未壓縮(最高質量)音頻,請使用打包在CAF文件中的16位小端線性PCM音頻數據。 您可以使用afconvert命令行工具在Mac OS X中將音頻文件轉換為此格式,如下所示:
/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]
我花了一個下午嘗試通過玩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。 但它似乎確實有效!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.