Как захватить аудио-образцы в iOS с помощью Swift?
Я нашел множество примеров онлайн для работы с аудио в iOS, но большинство из них довольно устарели и не относятся к тому, что я пытаюсь выполнить. Здесь мой проект:
Мне нужно захватить образцы звука из двух источников - вход для микрофона и сохраненные аудиофайлы. Мне нужно выполнить FFT на этих образцах, чтобы создать "отпечаток пальца" для всего клипа, а также применить некоторые дополнительные фильтры. Конечной целью является создание своего рода программного обеспечения для распознавания песен, похожего на Shazam и т.д.
Каков наилучший способ захвата отдельных образцов аудио в iOS 8 для выполнения быстрого преобразования Фурье? Я предполагаю, что в итоге получился большой массив из них, но я подозреваю, что это может сработать совсем не так. Во-вторых, как я могу использовать структуру Accelerate для обработки аудио? Это, пожалуй, самый эффективный способ выполнить сложный анализ звука в iOS.
Все примеры, которые я видел в Интернете, используют более старые версии iOS и Objective-C, и я не смог их перевести в Swift. Предоставляет ли iOS 8 некоторые новые рамки для такого рода вещей?
Ответы
Ответ 1
стремительные
Запись в iOS:
- Создайте и сохраните экземпляр
AVAudioRecorder
, как в var audioRecorder: AVAudioRecorder? = nil
- Инициализируйте свой
AVAudioRecorder
URL-адресом для хранения образцов и некоторых настроек записи
Последовательность сеанса записи:
- invoke
prepareToRecord()
- invoke
record()
- invoke
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
]
Сделайте это, все готово.
Вы также можете проверить этот ответ, который включает демонстрационный проект .
Ответ 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, который непрерывно вызывается как аудиопроход через прослушиваемый node (либо микрофон, либо проигрыватель аудиофайлов) в реальном времени. Вы можете получить доступ к отдельным образцам, указав указатель sampleData, который я создал в каждом блоке.