2016-06-22 65 views
1

對於快捷和編程一般來說還是新手。swift AVFoundation pitched audio memory issue

我有這個功能,可以播放指定音高的一段音頻。它被一個NStimer調用,所以每秒播放一次。 (包含在聲音播放類函數,然後設置的NSTimer和使用的viewController)

func playPitchedAudio(audioFile: AVAudioFile, pitch: Float){ 
    audioEngine.stop() 
    audioEngine.reset() 

    let audioPlayerNode = AVAudioPlayerNode() 
    let changePitchEffect = AVAudioUnitTimePitch() 

    changePitchEffect.pitch = pitch 

    audioEngine.attachNode(audioPlayerNode) 
    audioEngine.attachNode(changePitchEffect) 

    audioEngine.connect(audioPlayerNode, to: changePitchEffect, format: nil) 

    audioEngine.connect(changePitchEffect, to: audioEngine.outputNode, format: nil) 

    audioPlayerNode.scheduleFile(audioFile, atTime: nil, completionHandler: nil) 

    do { 
     try audioEngine.start() 
    } catch { 
     print("error") 
    } 

    audioPlayerNode.play() 

} 

運行良好和作品,但其每次調用時增加了一個幾MB內存,從來沒有恢復的空間。做了一些關於內存泄漏的研究,但找不到任何有助於我的具體情況的東西,所以希望有人能指引我朝着正確的方向發展。

我認爲這是創建一個新的節點和TimePitch每次調用它,所以將它們移動到包含該函數的類中,但得到了「libC++ abi.dylib:終止於類型爲NSException的未捕獲異常」當聲音嘗試第二次播放時發生錯誤。

任何幫助非常感謝,謝謝!

額外的東西。

// defined in class to be used by function 
var pitchedAudioPlayer = AVAudioPlayerNode() 
var audioEngine = AVAudioEngine() 

//Timer Start 
self.timer.invalidate() 
self.timer = NSTimer.scheduledTimerWithTimeInterval(tempo, target: self,  selector: #selector(ViewController.timeTriggerPointer), userInfo: nil, repeats: true) 

//Timer calls... (along with some other unrelated stuff) 
func timeTriggerPointer() { 
    soundPlayer.playPitchedAudio(pitchFilePath, pitch: -1000.0) 
} 

解決方案 -

import AVFoundation 

class SoundPlayer { 
    var pitchedAudioPlayer = AVAudioPlayerNode() 
    var audioEngine = AVAudioEngine() 

    let audioPlayerNode = AVAudioPlayerNode() 
    let changePitchEffect = AVAudioUnitTimePitch() 

    init() { 
     audioEngine.attachNode(audioPlayerNode) 
     audioEngine.attachNode(changePitchEffect) 

     audioEngine.connect(audioPlayerNode, to: changePitchEffect, format: nil) 
     audioEngine.connect(changePitchEffect, to: audioEngine.outputNode, format: nil) 
    } 

    func playPitchedAudio(audioFile: AVAudioFile, pitch: Float){ 
     audioPlayerNode.stop() 

     changePitchEffect.pitch = pitch 

     audioPlayerNode.scheduleFile(audioFile, atTime: nil,  completionHandler: nil) 



     do { 
      try audioEngine.start() 
     } catch { 
      print("error") 
     } 

     audioPlayerNode.play() 
    } 
} 
+0

這是什麼問題?這是泄漏還是崩潰? – matt

+0

對不起,泄漏是個問題。崩潰是我修復泄漏的失敗嘗試。 –

+0

我如何插上它?會是個問題。我不知道我有泄漏,但是當我運行計時器時,內存使用量不斷增加,直到大約120mb,然後崩潰。當我使用AVAudioPlayer時,該應用程序工作正常(但不允許音高改變),所以它肯定是導致它的這個功能。我不知道如何查找泄漏,我嘗試過使用儀器,但是在這個階段它超出了我的意思,並且似乎指向我沒有寫的代碼,所以並沒有真正幫助我。不知道如何直接在Xcode中完成。謝謝您的幫助! –

回答

2

A 「泄漏」 是一個技術性很強的東西:一塊未引用的內存,可永遠不會被釋放(因爲它是未引用)。我懷疑你有泄漏。你只是有更多的對象,就這些。

你一遍又一遍地(每次定時器觸發一次)重複此代碼:

let audioPlayerNode = AVAudioPlayerNode() 
let changePitchEffect = AVAudioUnitTimePitch() 
audioEngine.attachNode(audioPlayerNode) 
audioEngine.attachNode(changePitchEffect) 

音頻引擎本身,但是,仍然在地方(它聲明的功能外,作爲一個屬性的視圖控制器)。因此,每當計時器觸發時,您就會在同一個音頻引擎中再添加兩個節點。節點佔用內存,所以你的記憶力不斷上升。這裏毫不奇怪。如果你不希望發生這種情況,不要這樣做。

我以爲這是其所謂的

你去那裏是與創建一個新的節點和TimePitch每次。所以你已經解決了你自己的問題。

想一想你想做什麼。你有一個帶有節點的音頻引擎。唯一可能在每次運行中改變的是AVAudioUnitTimePitch節點的音調和要播放的文件。因此,事先創建整個音頻引擎和節點,並將其保留原位。保留對AVAudioUnitTimePitch的引用作爲屬性。在計時器功能中,只需更改該音高值即可!

let theTimePitchNode = AVAudioUnitTimePitch() 
// and you have also added that node to the engine in your setup 
func playPitchedAudio(audioFile: AVAudioFile, pitch: Float){ 

    audioPlayerNode.stop() 

    theTimePitchNode.pitch = pitch 
    audioPlayerNode.scheduleFile(audioFile, atTime: nil, completionHandler: nil) 

    do { 
     try audioEngine.start() 
    } catch { 
     print("error") 
    } 

    audioPlayerNode.play() 

} 
+0

感謝您清理我對泄漏的錯誤理解!這有助於。所以我明白了這個問題,但我的嘗試修復無效!生病嘗試一些其他的事情。 –

+0

但你沒有顯示你的嘗試修復,那麼我怎麼知道你做錯了什麼?我已經修改了我的答案,以顯示您需要在此處遵循的策略。 – matt

+0

所以我試過這個..但聲音現在只播放一次(在第一次定時器命中)和內存問題仍然存在。 (在問題中增加了代碼) –