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

Posted

技术标签:

【中文标题】Swift:从 url 检索音频文件标记列表?【英文标题】:Swift: Retrieve audio file marker list from url? 【发布时间】:2017-06-25 04:40:29 【问题描述】:

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

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 和大小(它们都是有效的),但它总是无法检索标记。对此的任何帮助都会很棒!

编辑: 这种工作,但所有数据完全错误,我无法理解单个音频文件如何具有多个 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

【问题讨论】:

为什么“重复”? developer.apple.com/documentation/swift/array/… 据我了解,您用新的 AudioFileMarkerList 实例的长度实例填充数组,然后返回它们。这是你想要的吗? @3000 我真正想要的是获取音频文件中所有标记的列表,但对我来说,看起来我正在为每个标记创建一个标记列表(这甚至是什么AudioFileMarkerList 是?)。主要问题是所有数据都不正确。我尝试在带有标记的音频文件上使用此功能,并且大多数名称为空,并且所有时间都完全关闭。该文件没有损坏,但我使用 AudioFileMarkerList 的方式有问题。我完全不明白它是如何工作的 看看这里的实例属性:developer.apple.com/documentation/audiotoolbox/… 【参考方案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)

仿照this answer

【讨论】:

这真的有效!经过3个月的等待!太感谢了!不过很抱歉,我不能给你赏金,因为它今天过期了,我直到回家才看到这个。 工作代码示例比赏金更有用。【参考方案2】:

编辑: 最简单的解决方案是使用 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

【讨论】:

你认为你可以向苹果报告/问这个问题吗?这似乎是一个内部错误,因为我没有开发者订阅,所以我无法询问 为什么不直接使用 Objective C?但是,是的,最好也向他们提交错误。 我不推荐我发布的代码,因为它基本上只是伪 C。最好直接使用 C。这是 Swift 做的一件简单的事情,所以我不明白你为什么不这样做。 检查我的编辑。使用 AudioKit,或者如果您不想嵌入框架,您可以直接从框架源代码中使用 EZAudioFile* 类。示例项目:u101308273.dl.dropboxusercontent.com/u/101308273/MarkerTest.zip 老实说......你应该按照我的建议去做。在 Swift 中调用这些东西只是使用了不同的语法,如果你不了解如何管理内存,这种语法会更加不稳定。有人可以制作一个更好的 swift 包装器,但归根结底,它将是被调用的 C。如果你直接自己做就更好了。如果有任何疑问,请使用我提到的 EZAudio 方法。它很坚固。

以上是关于Swift:从 url 检索音频文件标记列表?的主要内容,如果未能解决你的问题,请参考以下文章

播放存储在谷歌驱动器中的音频文件(远程 URL) - Swift

如何从 Swift 文件管理器播放音频文件?

是否可以从列出文件夹内容的 URL 中检索 JSON 格式的文件列表

如何使用 Swift 中的 url 从 firebase 正确检索数据?

如何从 iOS 中的音频剪辑中检索 PCM 样本数据?

如何在 SwiftUI 中使用 .fileimporter 保存音频文件并检索文件并在 SwiftUI 列表中显示它们的文件名?