Как захватить аудио образцы в iOS с Swift?

В Интернете я нашел много примеров работы со звуком в iOS, но большинство из них устарели и не соответствуют тому, что я пытаюсь выполнить. Вот мой проект:

Мне нужно захватить аудиосэмплы из двух источников - микрофонный вход и сохраненные аудиофайлы. Мне нужно выполнить БПФ на этих образцах, чтобы получить "отпечаток" для всего клипа, а также применить некоторые дополнительные фильтры. Конечная цель - создать программное обеспечение для распознавания песен, подобное Shazam и т. Д.

Каков наилучший способ захвата отдельных аудиосэмплов в iOS 8 для выполнения быстрого преобразования Фурье? Я полагаю, что в конечном итоге их получится много, но я подозреваю, что это может не сработать. Во-вторых, как я могу использовать платформу Accelerate для обработки аудио? Похоже, это самый эффективный способ выполнить сложный анализ аудио в iOS.

Все примеры, которые я видел в Интернете, используют более старые версии iOS и Objective-C, и я не смог успешно перевести их на Swift. Предлагает ли iOS 8 новые фреймворки для такого рода вещей?

2 ответа

AVAudioEngine - способ пойти на это. Из документов Apple:

  • Для воспроизведения и записи одной дорожки используйте AVAudioPlayer и AVAudioRecorder.
  • Для более сложной обработки аудио используйте AVAudioEngine. AVAudioEngine включает в себя AVAudioInputNode и AVAudioOutputNode для ввода и вывода звука. Вы также можете использовать объекты AVAudioNode для обработки и микширования эффектов в аудио

Я буду с вами откровенен: AVAudioEngine - чрезвычайно привередливый API с расплывчатой ​​документацией, редко полезными сообщениями об ошибках и почти без примеров кода в Интернете, демонстрирующих больше, чем самые простые задачи. НО, если вы потратите время, чтобы преодолеть небольшую кривую обучения, вы действительно сможете сделать с ней несколько волшебных вещей относительно легко.

Я построил простой контроллер вида "детская площадка", который демонстрирует сэмплирование как микрофона, так и аудиофайла, работая в тандеме:

import UIKit

class AudioEnginePlaygroundViewController: UIViewController {
    private var audioEngine: AVAudioEngine!
    private var mic: AVAudioInputNode!
    private var micTapped = false
    override func viewDidLoad() {
        super.viewDidLoad()
        configureAudioSession()
        audioEngine = AVAudioEngine()
        mic = audioEngine.inputNode!
    }

    static func getController() -> AudioEnginePlaygroundViewController {
        let me = AudioEnginePlaygroundViewController(nibName: "AudioEnginePlaygroundViewController", bundle: nil)
        return me
    }

    @IBAction func toggleMicTap(_ sender: Any) {
        if micTapped {
            mic.removeTap(onBus: 0)
            micTapped = false
            return
        }

        let micFormat = mic.inputFormat(forBus: 0)
        mic.installTap(onBus: 0, bufferSize: 2048, format: micFormat) { (buffer, when) in
            let sampleData = UnsafeBufferPointer(start: buffer.floatChannelData![0], count: Int(buffer.frameLength))
        }
        micTapped = true
        startEngine()
    }

    @IBAction func playAudioFile(_ sender: Any) {
        stopAudioPlayback()
        let playerNode = AVAudioPlayerNode()

        let audioUrl = Bundle.main.url(forResource: "test_audio", withExtension: "wav")!
        let audioFile = readableAudioFileFrom(url: audioUrl)
        audioEngine.attach(playerNode)
        audioEngine.connect(playerNode, to: audioEngine.outputNode, format: audioFile.processingFormat)
        startEngine()

        playerNode.scheduleFile(audioFile, at: nil) {
            playerNode .removeTap(onBus: 0)
        }
        playerNode.installTap(onBus: 0, bufferSize: 4096, format: playerNode.outputFormat(forBus: 0)) { (buffer, when) in
            let sampleData = UnsafeBufferPointer(start: buffer.floatChannelData![0], count: Int(buffer.frameLength))
        }
        playerNode.play()
    }

    // MARK: Internal Methods

    private func configureAudioSession() {
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, with: [.mixWithOthers, .defaultToSpeaker])
            try AVAudioSession.sharedInstance().setActive(true)
        } catch { }
    }

    private func readableAudioFileFrom(url: URL) -> AVAudioFile {
        var audioFile: AVAudioFile!
        do {
            try audioFile = AVAudioFile(forReading: url)
        } catch { }
        return audioFile
    }

    private func startEngine() {
        guard !audioEngine.isRunning else {
            return
        }

        do {
            try audioEngine.start()
        } catch { }
    }

    private func stopAudioPlayback() {
        audioEngine.stop()
        audioEngine.reset()
    }
}

Аудио сэмплы передаются вам через обработчик завершения installTap, который непрерывно вызывается, когда аудио проходит через подключенный узел (либо микрофон, либо проигрыватель аудиофайлов) в режиме реального времени. Вы можете получить доступ к отдельным образцам, проиндексировав указатель sampleData, который я создал в каждом блоке.

быстрый

Запись в iOS:

  • Создать и поддерживать экземпляр AVAudioRecorder, как в var audioRecorder: AVAudioRecorder? = nil
  • Инициализируйте ваш AVAudioRecorder с URL-адресом для хранения образцов и некоторыми настройками записи

Последовательность записи сеанса:

  1. взывать prepareToRecord()
  2. взывать record()
  3. взывать stop()

Полный пример Swift/AVAudioRecorder

В основе вашего метода записи вы могли бы иметь:

func record() {
    self.prepareToRecord()
    if let recorder = self.audioRecorder {
        recorder.record()
    }
}

Чтобы подготовить запись (потоковая передача на file), вы могли бы иметь:

func prepareToRecord() {
    var error: NSError?
    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! NSString
    let soundFileURL: NSURL? = NSURL.fileURLWithPath("\(documentsPath)/recording.caf")

    self.audioRecorder = AVAudioRecorder(URL: soundFileURL, settings: recordSettings as [NSObject : AnyObject], error: &error)
    if let recorder = self.audioRecorder {
        recorder.prepareToRecord()
    }
}

Наконец, чтобы остановить запись, используйте это:

func stopRecording() {
    if let recorder = self.audioRecorder {
        recorder.stop()
    }
}

Пример выше также нуждается import AVFoundation и немного recordSettingsоставлено на ваше усмотрение. Пример recordSettings может выглядеть так:

let recordSettings = [
    AVFormatIDKey: kAudioFormatAppleLossless,
    AVEncoderAudioQualityKey : AVAudioQuality.Max.rawValue,
    AVEncoderBitRateKey : 320000,
    AVNumberOfChannelsKey: 2,
    AVSampleRateKey : 44100.0
]

Сделай это, все готово.


Вы также можете проверить этот ответ переполнения стека, который включает в себя демонстрационный проект.

Другие вопросы по тегам