AVAudioPCMBuffer 以编程方式构建,不以立体声播放

AVAudioPCMBuffer built programmatically, not playing back in stereo

我正在尝试在 Swift 中以编程方式填充 AVAudioPCMBuffer 以构建节拍器。这是我尝试构建的第一个真正的应用程序,所以它也是我的第一个音频应用程序。现在我正在尝试使用不同的框架和方法来让节拍器准确循环。

我正在尝试构建一个长度为 measure/bar 的 AVAudioPCMBuffer,以便我可以使用 AVAudioPlayerNode 的 scheduleBuffer 方法的 .Loops 选项。我首先将我的文件(2 ch、44100 Hz、Float32、non-inter、*.wav 和 *.m4a 都有相同的问题)加载到缓冲区中,然后将该缓冲区逐帧复制到 barBuffer 中,帧由空帧分隔。下面的循环是我如何完成这个。

如果我安排原始缓冲区播放,它将以立体声播放,但是当我安排barBuffer 时,我只得到左声道。正如我所说,我是编程初学者,没有音频编程经验,所以这可能是我对 32 位浮点通道或这种数据类型 UnsafePointer<UnsafeMutablePointer<float>> 缺乏了解。当我查看 swift 中的 floatChannelData 属性 时,描述听起来像是应该复制两个通道。

var j = 0
for i in 0..<Int(capacity) {
    barBuffer.floatChannelData.memory[j] = buffer.floatChannelData.memory[i]
    j += 1
}
j += Int(silenceLengthInSamples)
// loop runs 4 times for 4 beats per bar. 

编辑:感谢 hotpaw2,我删除了明显的错误 i += 1。但是,当播放 barBuffer 时,右声道仍然丢失。

这看起来像是对 Swift "for" 循环的误解。 Swift "for" 循环自动递增 "i" 数组索引。但是您在循环体中再次递增它,这意味着您最终会跳过初始缓冲区中的所有其他样本(右通道)。

swift 中的不安全指针很难习惯。

floatChannelData.memory[j]只访问第一个通道的数据。要访问其他频道,您有几个选择:

使用 advancedBy

// Where current channel is at 0

// Get a channel pointer aka UnsafePointer<UnsafeMutablePointer<Float>> 
let channelN = floatChannelData.advancedBy( channelNumber )

// Get channel data aka UnsafeMutablePointer<Float>
let channelNData = channelN.memory

// Get first two floats of channel channelNumber
let floatOne = channelNData.memory
let floatTwo = channelNData.advancedBy(1).memory

使用下标

// Get channel data aka UnsafeMutablePointer<Float>
let channelNData = floatChannelData[ channelNumber ]

// Get first two floats of channel channelNumber
let floatOne = channelNData[0]
let floatTwo = channelNData[1]

用下标就更清楚了,一步步前进再手动 访问内存是隐式的。


对于您的循环,尝试通过执行以下操作来访问缓冲区的所有通道:

for i in 0..<Int(capacity) {
    for n in 0..<Int(buffer.format.channelCount) {
         barBuffer.floatChannelData[n][j] = buffer.floatChannelData[n][i]
    }
}

希望对您有所帮助!