0

我正在为 iOS 制作音乐应用程序。我想同时播放 5 个文件。这已经在这个线程的帮助下工作了:如何使用连接到 AVAudioEngine 混音器的节点同时从缓冲区播放多个声音

现在我需要使用 AVAudioUnitVarispeed 更快地播放所有文件。我不能让它工作。

这是代码:

import AVFoundation

class Audio {

    // MARK: AUDIO VARIABLEN
    var engineFirst: AVAudioEngine = AVAudioEngine()
    var audioFilePlayer = [AVAudioPlayerNode]()
    var noteFilePath = [String]()

    // Speed
    var speedControl = [AVAudioUnitVarispeed]()

    var noteFileURL = [URL]()
    var noteAudioFile = [AVAudioFile]()
    var noteAudioFileBuffer = [AVAudioPCMBuffer]()
    let tonAnzahl = 5
    let latency = 0.03
    var playing = false

    // MARK: PLAY SOUND
    func playSound() {
        playing = true
        for i in 0...tonAnzahl - 1 {
            audioFilePlayer[i].scheduleBuffer(noteAudioFileBuffer[i], at: nil, completionHandler: nil)
            audioFilePlayer[i].play()
        }

    }

    // MARK: STOP SOUND
    func stopSound() {
        playing = false
        for i in 0...tonAnzahl - 1 {
            audioFilePlayer[i].volume = 0
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + latency, execute: {
            for i in 0...self.tonAnzahl - 1 {
                self.audioFilePlayer[i].stop()
            }
        })
    }

    // MARK: SETUP AUDIO ENGINE
    func setupAudioEngine(bassFile: String, terzFile: String, septimeFile: String, tensionOneFile: String, tensionTwoFile: String) {
        do {
            noteAudioFile.removeAll()
            noteFileURL.removeAll()
            // For each note, read the note URL into an AVAudioFile,
            // setup the AVAudioPCMBuffer using data read from the file,
            // and read the AVAudioFile into the corresponding buffer
            for i in 0...tonAnzahl - 1 {
                noteFilePath = [
                    Bundle.main.path(forResource: bassFile, ofType: "mp3")!,
                    Bundle.main.path(forResource: terzFile, ofType: "mp3")!,
                    Bundle.main.path(forResource: septimeFile, ofType: "mp3")!,
                    Bundle.main.path(forResource: tensionOneFile, ofType: "mp3")!,
                    Bundle.main.path(forResource: tensionTwoFile, ofType: "mp3")!]
                noteFileURL.append(URL(fileURLWithPath: noteFilePath[i]))

                try noteAudioFile.append(AVAudioFile(forReading: noteFileURL[i]))

                let noteAudioFormat = noteAudioFile[i].processingFormat
                let noteAudioFrameCount = UInt32(noteAudioFile[i].length)
                noteAudioFileBuffer.append(AVAudioPCMBuffer(pcmFormat: noteAudioFormat, frameCapacity: noteAudioFrameCount)!)

                try noteAudioFile[i].read(into: noteAudioFileBuffer[i])
            }
            // For each note, attach the corresponding node to the engineFirst, and connect the node to the engineFirst's mixer.
            for i in 0...tonAnzahl - 1 {
                audioFilePlayer.append(AVAudioPlayerNode())
                engineFirst.attach(audioFilePlayer[i])


                // HERE IS THE PROBLEM
                speedControl.append(AVAudioUnitVarispeed())
                speedControl[i].rate = 2
                engineFirst.attach(speedControl[i])

                engineFirst.connect(audioFilePlayer[i], to: speedControl[i], fromBus: 0, toBus: i, format: noteAudioFileBuffer[i].format)
                engineFirst.connect(speedControl[i], to: engineFirst.mainMixerNode, fromBus: 0, toBus: i, format: noteAudioFileBuffer[i].format)

            }

            // Audio Engine Start
            try engineFirst.start()

            // Setup the audio session to play sound in the app, and activate the audio session
            try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.soloAmbient)
            try AVAudioSession.sharedInstance().setMode(AVAudioSession.Mode.default)
            try AVAudioSession.sharedInstance().setActive(true)
        }
        catch let error {
            print(error.localizedDescription)
        }
    }
}

如果我替换此代码同时播放 5 个音符

// HERE IS THE PROBLEM
speedControl.append(AVAudioUnitVarispeed())
speedControl[i].rate = 2
engineFirst.attach(speedControl[i])

engineFirst.connect(audioFilePlayer[i], to: speedControl[i], fromBus: 0, toBus: i, format: noteAudioFileBuffer[i].format)
engineFirst.connect(speedControl[i], to: engineFirst.mainMixerNode, fromBus: 0, toBus: i, format: noteAudioFileBuffer[i].format)

engineFirst.connect(audioFilePlayer[i], to: engineFirst.mainMixerNode, fromBus: 0, toBus: i, format: noteAudioFileBuffer[i].format)

...并调用 playSound()。

但后来我没有实现 AVAudioUnitVarispeed (speedControl) ......

那么,如何添加 AVAudioUnitVarispeed 的代码呢?

谢谢您的帮助。

4

1 回答 1

0

我也一直在尝试这个。首先,我看到您正在尝试将您连接audioFilePlayersAVAudioUnitVarispeed. 我不确定这是否可行,因为所有不同的速度控制都会获得不同的输入和输出到不同的总线。总线旨在将源信号拆分到不同的路径。例如,如果您希望您的音频输出到 mainMixerNode,但也要绕道(即使用另一条总线作为输出)例如混响效果,然后到 mainMixerNode。您可以尝试将所有的连接audioFilePlayers到 的总线 0 speedControl,然后将所有连接speedControl 到 的总线 0...i .mainMixerNode。看看这是否有效。或者,如果您希望在所有轨道上发生速度变化,您可以制作speedControl主效果。所以你会做类似的事情:

engineFirst.connect(audioFilePlayer[i], to: engineFirst.mainMixerNode, format: noteAudioFileBuffer[i].format)  

// And then put this outside of the loop to connect all audioFilePlayers

engineFirst.connect(engineFirst.mainMixerNode, to: speedControl, format: noteAudioFileBuffer[0].format)
engineFirst.connect(speedcontrol, to: engineFirst.outputNode, format: noteAudioFileBuffer[0].format)

这样,您只需要一个实例,AVAudioUnitVarispeed而不必单独更改每个玩家的速度。

或者您可以尝试将所有 audioFilePlayers 连接到单个 speedControl 的总线 0...i 并将 speedControl 连接到 mainMixerNode 的总线 0...i。

希望这对你有用。

于 2020-05-08T11:56:55.853 回答