Swift:从url中检索音频文件标记列表?

时间:2017-06-25 04:40:30

标签: ios swift audiotoolbox

我只想获得音频文件中的标记列表。我认为这将是一项不太难的简单的共同任务。但是,我几乎找不到任何示例代码或文档,所以我最终得到了这个:

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

我无法弄清楚问题。我检查了网址和大小(两者都有效),但总是无法检索标记。任何帮助都会很棒!

修改 这种工作,但所有的数据都是完全错误的,我无法理解单个音频文件如何有多个标记的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
}

编辑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: 我从Ryan的代码中删除了一堆错误检查和有用的东西,以便任何想要快速尝试找到问题的人:

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: 感谢Rhythmic Fistman和Ryan Francesconi!最终结果:

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
}

2 个答案:

答案 0 :(得分:3)

看起来您需要使用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)
}

this answer

为模型

答案 1 :(得分:1)

编辑:最简单的解决方案是使用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结构转换为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
}