Есть ли ограничение на количество узлов, которые может создать AVAudioEngine?

В моем коде ниже я создал два звука: sound1 и sound2. Каждый звук содержит несколько сэмплов, позволяющих воспроизводить один и тот же звук одновременно. Проблема в том, что если я создаю более 6-8 узлов AVAudioPlayerNodes с AVAudioUnitTimePitch для каждого, звук будет полностью испорчен. Я даже не могу воспроизвести ни один звук, когда слишком увеличиваю количество сэмплов. Я не уверен, что мой код неправильный, или каков предел узла AVAudioEngine.

class AudioManager{
    var audioEngine:AVAudioEngine!;
    var mixer:AVAudioMixerNode!;
    var sound1:Sound!;
    var sound2:Sound!;
    init(){
        audioEngine = AVAudioEngine();
        mixer = audioEngine.mainMixerNode; //automatically creates instance of mixer node, output node, and connects

        do{
            try audioEngine.start();
        }catch let e as NSError{
            print("Error Starting AudioEngine \(e)");
        }

        sound1 = Sound(aManager: self, path: "assets/sounds/waterRefill", ofType: "mp3", numOfSamples: 7);
        sound2 = Sound(aManager: self, path: "assets/sounds/balloonCreate", ofType: "mp3", numOfSamples: 2);


    }

    func playSound(){
        sound1.play(1.0, pitch: 1.0);
    }

    func playSound2(){
        sound2.play(1.0, pitch: 1.0);
    }

    class Sound {
        var audioManager:AudioManager!;
        var audioFileBuffer:AVAudioPCMBuffer!;
        var numSamples:Int = 1;
        var audioIndex:Int = 0;
        var sampleList:[Sample] = [Sample]();

        init(aManager:AudioManager, path:String, ofType:String, numOfSamples:Int){
            audioManager = aManager;
            if(numOfSamples < 1){
                numSamples = 1;
            }else{
                numSamples = numOfSamples;
            }
            audioFileBuffer = createAudioBuffer(path, ofType: ofType);
            for (var i = 0; i < numSamples; i++){
                sampleList.append(Sample(sound: self));
            }
        }

        func createAudioBuffer(path:String, ofType:String)-> AVAudioPCMBuffer?{
            let filePath: String = NSBundle.mainBundle().pathForResource(path, ofType: ofType)!
            let fileURL: NSURL = NSURL(fileURLWithPath: filePath)
            do{
                let audioFile = try AVAudioFile(forReading: fileURL)
                let audioFormat = audioFile.processingFormat
                let audioFrameCount = UInt32(audioFile.length)
                let audioFileBuffer = AVAudioPCMBuffer(PCMFormat: audioFormat, frameCapacity: audioFrameCount)
                do{
                    try audioFile.readIntoBuffer(audioFileBuffer)
                    return audioFileBuffer;
                }catch let e as NSError{
                    print("Error loading Audio Into Buffer: \(e)");
                }
            }catch let e as NSError{
                print("Error loading Audio File: \(e)");
            }
            return nil;
        }

        private func runIndex(){
            if(audioIndex < (numSamples-1)){
                audioIndex++;
            }else{
                audioIndex = 0;
            }
        }

        func play(volume:Float, pitch:Float){

            var count:Int = 0;
            while(count < numSamples){
                if(numSamples > 1){
                    runIndex();
                }
                if (!sampleList[audioIndex].pitchPlayer.playing) {
                    sampleList[audioIndex].volume = volume;
                    sampleList[audioIndex].pitch = pitch;
                    sampleList[audioIndex].playSample();
                    break;
                }
                count++;
            }

        }

        class Sample{
            var parentSound:Sound!
            var pitchPlayer:AVAudioPlayerNode!;
            var timePitch:AVAudioUnitTimePitch!;
            var volume:Float = 1.0
            var pitch:Float = 1.0

            init(sound:Sound){
                parentSound = sound;
                pitchPlayer = AVAudioPlayerNode();
                timePitch = AVAudioUnitTimePitch();

                parentSound.audioManager.audioEngine.attachNode(pitchPlayer);
                parentSound.audioManager.audioEngine.attachNode(timePitch);

                parentSound.audioManager.audioEngine.connect(pitchPlayer, to: timePitch, format: parentSound.audioFileBuffer.format);
                parentSound.audioManager.audioEngine.connect(timePitch, to: parentSound.audioManager.mixer, format: parentSound.audioFileBuffer.format);


            }

            func playSample(){
                pitchPlayer.volume = volume;
                timePitch.pitch = pitch;
                print("Sample Play");

                pitchPlayer.play();
                pitchPlayer.scheduleBuffer(parentSound.audioFileBuffer, atTime: nil, options:.Interrupts, completionHandler: {[unowned self]() in
                    print("Is Stopped: \(self.pitchPlayer.playing)");
                    self.pitchPlayer.stop();
                    print("Is Stopped: \(self.pitchPlayer.playing)");
                    });
            }
        }
    }
}

person NJGUY    schedule 03.12.2015    source источник
comment
Мое приложение, которое воспроизводит файлы с помощью AVAAudioEngine и AVAudioPlayerNodes, не воспроизводило звуки на некоторых устройствах (хотя отлично работало на новых устройствах, таких как iPhone 6s Plus). Когда я уменьшил количество узлов на двигателе, он работал на старых устройствах. Я изучаю, как я могу настроить свой код, чтобы он работал на этих старых устройствах, потому что количество узлов, похоже, является проблемой.   -  person vikzilla    schedule 20.02.2016


Ответы (1)


Я никогда не слышал об ограничении количества узлов в графе AVAudioEngine, но я видел действительно плохую производительность после добавления нескольких сотен узлов. Решение, которое я нашел, заключалось в том, чтобы удалить эти узлы после того, как они закончили играть.

Обработчик завершения scheduleBuffer - хорошее место для этого, но я бы обернул удаление вызовом dispatch_async-to-the-main-queue, поскольку звуковой движок может по-прежнему использовать узел при вызове обработчика завершения.

Другой вариант - повторно использовать узлы проигрывателя после того, как они закончили воспроизведение образца, вместо создания нового для следующего образца, но этот подход может быть немного сложнее для реализации.

person Alex Machado    schedule 14.06.2016