Swift: получить список маркеров звукового файла из URL-адреса?
Я просто хочу получить список маркеров в аудио файле. Я думал, что это будет простая общая задача, которая не будет слишком сложной. Тем не менее, я едва могу найти какой-либо пример кода или документации, поэтому я закончил с этим:
private func getMarkers(_ url: CFURL) -> AudioFileMarkerList {
var file: AudioFileID?
var size: UInt32 = 0
var markers = AudioFileMarkerList()
AudioFileOpenURL(url, .readPermission, kAudioFileWAVEType, &file)
AudioFileGetPropertyInfo(file!, kAudioFilePropertyMarkerList, &size, nil)
AudioFileGetProperty(file!, kAudioFilePropertyMarkerList, &size, &markers)
return markers
}
К сожалению, это не работает: error: memory read failed for 0x0
.
Я просто не могу понять проблему. Я проверил URL и размер (оба они действительны), но всегда не удается получить маркеры. Любая помощь с этим была бы фантастической!
EDIT:
Такие работы, но все данные полностью неправильны, и я не могу понять, как один аудиофайл может иметь несколько AudioFileMarkerLists маркеров:
private func getMarkers(_ url: CFURL) -> [AudioFileMarkerList] {
var file: AudioFileID?
var size: UInt32 = 0
AudioFileOpenURL(url, .readPermission, kAudioFileWAVEType, &file)
AudioFileGetPropertyInfo(file!, kAudioFilePropertyMarkerList, &size, nil)
let length = NumBytesToNumAudioFileMarkers(Int(size))
var markers = [AudioFileMarkerList](repeating: AudioFileMarkerList(), count: length)
AudioFileGetProperty(file!, kAudioFilePropertyMarkerList, &size, &markers)
return markers
}
EDIT 2: В соответствии с большинством ответов, которые я видел до сих пор, это должно работать, но оно возвращает пустой массив:
private func getMarkers(_ url: CFURL) -> [AudioFileMarkerList] {
var file: AudioFileID?
var size: UInt32 = 0
AudioFileOpenURL(url, .readPermission, kAudioFileWAVEType, &file)
AudioFileGetPropertyInfo(file!, kAudioFilePropertyMarkerList, &size, nil)
let length = NumBytesToNumAudioFileMarkers(Int(size))
var markers = [AudioFileMarkerList]()
markers.reserveCapacity(length)
AudioFileGetProperty(file!, kAudioFilePropertyMarkerList, &size, &markers)
return markers
}
ИЗМЕНИТЬ 3:
Я избавился от кучи ошибок и полезных вещей из кода Райана для тех, кто хочет быстро попробовать и найти проблему:
private func getMarkers(_ url: CFURL) -> [AudioFileMarker]? {
var file: AudioFileID?
var size: UInt32 = 0
var markers: [AudioFileMarker] = []
AudioFileOpenURL(url, .readPermission, kAudioFileWAVEType, &file)
AudioFileGetPropertyInfo(file!, kAudioFilePropertyMarkerList, &size, nil)
let length = NumBytesToNumAudioFileMarkers(Int(size))
let data = UnsafeMutablePointer<AudioFileMarkerList>.allocate(capacity: length)
AudioFileGetProperty(file!, kAudioFilePropertyMarkerList, &size, data)
markers.append(data.pointee.mMarkers)
data.deallocate(capacity: length)
return markers
}
Я просто надеюсь, что Apple действительно проверила AudioFileMarkerList в первую очередь.
РЕДАКТИРОВАТЬ 4:
РЕШЕНИЕ благодаря ритмическому Фистману и Райану Франческони! Конечный результат:
private func getMarkers(_ url: CFURL) -> [AudioFileMarker]? {
var file: AudioFileID?
var size: UInt32 = 0
var markerList: [AudioFileMarker] = []
AudioFileOpenURL(url, .readPermission, kAudioFileWAVEType, &file)
AudioFileGetPropertyInfo(file!, kAudioFilePropertyMarkerList, &size, nil)
let length = NumBytesToNumAudioFileMarkers(Int(size))
let data = UnsafeMutablePointer<AudioFileMarkerList>.allocate(capacity: length)
AudioFileGetProperty(file!, kAudioFilePropertyMarkerList, &size, data)
let markers = UnsafeBufferPointer<AudioFileMarker>(start: &data.pointee.mMarkers, count: length)
for marker in markers {
markerList.append(marker)
}
data.deallocate(capacity: length)
return markerList
}
Ответы
Ответ 1
Похоже, вам нужно использовать UnsafeBufferPointer
для доступа к массивам переменной длины (например, mMarkers
). Поэтому вместо
out.append(markerList.mMarkers)
который добавляет только первый элемент, сделайте это
let markersBuffer = UnsafeBufferPointer<AudioFileMarker>(start: &data.pointee.mMarkers,
count: Int(data.pointee.mNumberMarkers))
for marker in markersBuffer {
markers.append(marker)
}
Настроен на этот ответ
Ответ 2
EDIT: Самое простое решение - использовать версию AudioKit для EZAudioFile.markers. Обратите внимание, что это не то же самое, что исходная структура EZAudio, поскольку я добавил этот код маркера только в версию AudioKit.
import AudioKit
...
if let file = EZAudioFile(url: url) {
if let markers = file.markers as? [EZAudioFileMarker] {
for m in markers {
Swift.print("NAME: \(m.name) FRAME: \(m.framePosition)")
}
}
}
Если вы ДЕЙСТВИТЕЛЬНО хотите попробовать в Swift, это будет выглядеть примерно так: Я не эксперт в этом, но насколько я могу судить, есть проблема, связанная с AudioFileMarkerList struct для Swift. Это может быть разрешимо, но мне кажется, что лучше всего использовать Objective C для выполнения этих вызовов. Вот почти законченная функция в Swift. Я рекомендую использовать AudioKit для выполнения того, что вам нужно, поскольку я добавил код маркера в EZAudioFile. Проверьте: https://github.com/AudioKit/AudioKit/blob/master/AudioKit/Common/Internals/EZAudio/EZAudioFile.m
Но для записи здесь находится код Swift! Обратите внимание, что он жестко закодирован в WAVE файлы на данный момент... Может быть, кто-то еще может это закончить?
class func getAudioFileMarkers(_ url: URL) -> [AudioFileMarker]? {
Swift.print("getAudioFileMarkers() \(url)")
var err: OSStatus = noErr
var audioFileID: AudioFileID?
err = AudioFileOpenURL(url as CFURL,
.readPermission,
kAudioFileWAVEType,
&audioFileID)
if err != noErr {
Swift.print("AudioFileOpenURL FAILED, Error: \(err)")
return nil
}
guard audioFileID != nil else {
return nil
}
Swift.print("audioFileID: \(audioFileID)")
var outSize: UInt32 = 0
var writable: UInt32 = 0
err = AudioFileGetPropertyInfo(audioFileID!, kAudioFilePropertyMarkerList, &outSize, &writable)
if err != noErr {
Swift.print("AudioFileGetPropertyInfo kAudioFilePropertyMarkerList FAILED, Error: \(err)")
return nil
}
Swift.print("outSize: \(outSize), writable: \(writable)")
guard outSize != 0 else { return nil }
let length = NumBytesToNumAudioFileMarkers( Int(outSize) )
Swift.print("Found \(length) markers")
let theData = UnsafeMutablePointer<AudioFileMarkerList>.allocate(capacity: length)
if length == 0 {
return nil
}
// pull marker list
err = AudioFileGetProperty(audioFileID!, kAudioFilePropertyMarkerList, &outSize, theData)
if err != noErr {
Swift.print("AudioFileGetProperty kAudioFilePropertyMarkerList FAILED, Error: \(err)")
return nil
}
let markerList: AudioFileMarkerList = theData.pointee
Swift.print("markerList.mMarkers: \(markerList.mMarkers)")
// this is only showing up as a single AudioFileMarker, not an array of them.
// I DON'T KNOW WHY. It works in Obj-C. I'm obviously missing something, or there is a problem in translation
var out = [AudioFileMarker]()
let mirror = Mirror(reflecting: markerList.mMarkers)
for m in mirror.children {
Swift.print( "label: \(m.label) value: \(m.value)" )
}
// for now just append the first one.
// :(
out.append(markerList.mMarkers)
// done with this now
theData.deallocate(capacity: length)
return out
}