4

我正在尝试将 MIDI 文件转换为 Swift 中的音频文件 (.m4a)。

现在我使用MIKMIDI作为排序和播放 MIDI 文件的工具,但是它不包括将播放保存到文件中的能力。MIKMID 的创建者在此处概述了执行此操作的过程。为了尝试捕获输出并将其保存到音频文件,我按照此示例尝试将 MIKMIDI Graph 的 RemoteIO 节点替换为 Swift 中的 GeneralIO 节点。当我尝试使用 AudioUnitRender 和 ExtAudioFileWrite 将输出保存到文件时,它们都返回错误 -50 (kAudio_ParamError)。

    var channels = 2
    var buffFrames = 512
    var bufferList = AudioBufferList.allocate(maximumBuffers: 1)

         for i in 0...bufferList.count-1{

               var buffer = AudioBuffer()
               buffer.mNumberChannels = 2
               buffer.mDataByteSize = UInt32(buffFrames*sizeofValue(AudioUnitSampleType))
               buffer.mData = calloc(buffFrames, sizeofValue(AudioUnitSampleType))

               bufferList[i] = buffer

               result = AudioUnitRender(generalIOAudioUnit, &flags, &inTimeStamp, busNum, UInt32(buffFrames), bufferList.unsafeMutablePointer)
               inTimeStamp.mSampleTime += 1

               result = ExtAudioFileWrite(extAudioFile, UInt32(buffFrames), bufferList.unsafeMutablePointer)

         }

是什么导致了错误 -50,我该如何解决它以将 MIDI(离线)渲染为 .m4a 文件?

更新:我通过将 mNumberChannels 和通道更改为 = 1 解决了 ExtAudioFileWrite 错误 -50。现在我得到了一个带有噪音的一秒音频文件。AudioUnitRender 仍然返回错误 -50。

4

1 回答 1

1

您的代码有几个问题:

  1. AudioBufferList不同意客户端格式,请尝试

    let bufferList = AudioBufferList.allocate(maximumBuffers: Int(clientFormat.mChannelsPerFrame)) 
    
  2. 您正在从 替换错误的节点AUGraph,并将剩余节点连接到自身,从而导致AudioUnitRender.

但主要问题是您没有实施作者建议的解决方案。您希望您可以AudioUnitRender使用示例时间戳进行调用,比实时更快,但作者说不,您必须手动将采样时间转换为主机时间,并根据需要实现 midi 播放器的更好部分。

因此,您可以这样做(听起来很难),或者提交功能请求,或者通过向图形的远程 IO 音频单元添加渲染通知并在该阶段AudioUnitAddRenderNotify写入样本来实时记录到文件中。kAudioUnitRenderAction_PostRender

于 2016-09-07T13:03:57.813 回答