AudioKit FFTTap дает нули на физическом устройстве при повторном запуске - PullRequest
0 голосов
/ 19 сентября 2018

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

На симуляторе это работает без проблем.На физическом устройстве (iPad 12.9 1-го поколения) только первый сеанс дает действительные данные, тогда как начиная со второго я получаю только нули.

Я создал пример проекта Xcode, который продемонстрировал проблему.Его можно скачать здесь: https://drive.google.com/file/d/1rR2zWPREwXbXfZFocubwMgQ8SyFrXt2V/view?usp=sharing

Вот код ViewController:

import UIKit
import AudioKit

class ViewController: UIViewController, UIApplicationDelegate {
    @IBOutlet weak var toggleLearn: UIButton!

    var listenTimer : Timer?

    let mic = AKMicrophone()

    var compressor = AKCompressor()

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func listen() {

        compressor.start()
        compressor = AKCompressor(mic)

        if let inputs = AudioKit.inputDevices {
            do {
                try AudioKit.setInputDevice(inputs[0])
            } catch {
                print ("Could not set audio inputs: \(error)")
            }
            do {
                try mic.setDevice(inputs[0])
            } catch {
                print ("Could not set the audio input device to the AKMic: \(error)")
            }
        }

        AudioKit.output = AKBooster(compressor, gain: 0)

        if !AudioKit.engine.isRunning {
            do {
                try AudioKit.start()
            } catch {
                print ("Could not start AudioKit: \(error)")
            }
        }

        compressor.threshold = 3
        compressor.headRoom = 3
        compressor.masterGain = 1
        compressor.attackDuration = 0.001
        compressor.releaseDuration = 0.01

        mic.start()

        let fft = AKFFTTap(compressor)

        if listenTimer == nil {
            listenTimer = Timer.scheduledTimer(withTimeInterval: 0.08, repeats: true, block: { _ in

                let i = fft.fftData

                print (i[100...200])

            })
        }
    }

    @IBAction func learnPage(_ sender: UIButton) {
        if AudioKit.engine.isRunning {
            if listenTimer != nil {
                listenTimer?.invalidate()
                listenTimer = nil
            }
            mic.stop()
            compressor.stop()
            try! AudioKit.stop()

            toggleLearn.setTitle("Listen", for: .normal)
        } else {
            toggleLearn.setTitle("Stop", for: .normal)
            listen()
        }
    }
}

Помощь будет принята с благодарностью.

1 Ответ

0 голосов
/ 19 сентября 2018

Я нашел очень уродливый хак, на данный момент:

  1. Отредактируйте публичную инициализацию файла AKFFTTap.swift и добавьте в него еще один параметр, который удалитнажмите:

    public init(_ input: AKNode, activate: Bool) {
        super.init()
    
        if activate == true {
    
            fft = EZAudioFFT(maximumBufferSize: vDSP_Length(bufferSize), sampleRate: Float(AKSettings.sampleRate), delegate: self)
            input.avAudioNode.installTap(onBus: 0, bufferSize: bufferSize, format: AudioKit.format) { [weak self] (buffer, time) -> Void in
                guard let strongSelf = self else { return }
                buffer.frameLength = strongSelf.bufferSize
                let offset = Int(buffer.frameCapacity - buffer.frameLength)
                let tail = buffer.floatChannelData?[0]
                strongSelf.fft!.computeFFT(withBuffer: &tail![offset],
                                       withBufferSize: strongSelf.bufferSize)
            }
    
        } else {
    
            input.avAudioNode.removeTap(onBus: 0)
        }
    }
    
  2. При первом вызове крана не забудьте добавить новый параметр:

    let fft = AKFFTTap(compressor, activate: true)
    
  3. В коде, которыйосвобождает ресурсы AudioKit, используйте параметр для удаления крана:

    func stopListening() {
        // The first block nullifies the timer from my example above. Not necessary unless you use the timer.
        if listenTimer != nil {
            listenTimer?.invalidate()
            listenTimer = nil
        }
        if AudioKit.engine.isRunning {
            mic.stop()
            compressor.stop()
    
            let _ = AKFFTTap(compressor, activate: false)
        }
    }
    

Этот хак испортит цепочку, установленную AudioKit, и если вы остановите и перезапустите AudioKit, вы получите оченьстранное поведение (в моем случае - мой фиктивный вывод стал очень не фиктивным).Однако, если вы повторите все настройки без остановки AudioKit, результат останется прежним.Это означает, что в случае с кодом в моем исходном вопросе вызывается функция listen ().

...