我正在编写一个应用程序,我应该在其中播放部分音频文件。每个音频文件都包含一个单独轨道的音频数据。这些部分是有开始时间和结束时间的部分,我正在尝试按照我选择的顺序播放这些部分。
例如,假设我有 4 个部分:
A B C D
然后我激活B和D,我想玩,B,然后D,然后再B,然后D,等等。
为了在播放中平滑“跳跃”,我认为淡入/淡出开始/结束部分缓冲区很重要。
所以,我有一个基本的AVAudioEngine
设置,带有AVAudioPlayerNode
, 和一个混音器。对于每个音频部分,我缓存了一些信息:
- 该部分中第一个样本的缓冲区(我手动淡入)
- 的
AVAudioFramePosition
, 和AVAudioFrameCount
中间段的元组 - 音频部分中结束样本的缓冲区(我手动淡出)
现在,当我安排播放部分时,我会说AVAudioPlayerNode
:
- 安排启动缓冲区(
scheduleBuffer(_:completionHandler:)
无选项) - 安排中间段 (
scheduleSegment(_:startingFrame:frameCount:at:completionHandler:)
) - 最后安排结束缓冲区(
scheduleBuffer(_:completionHandler:)
无选项)
都在“时间” nil
。
这里的问题是我可以在音频部分边界听到 clic 和蹩脚的声音,我看不出我做错了什么。我的第一个想法是我手动进行的淡入淡出(基本上是将样本值乘以音量因子),但不这样做的结果相同。我以为我没有及时安排,但提前安排部分,例如事先 A - B - C 有相同的结果。然后我尝试了不同的帧位置计算,使用音频格式设置,结果相同。
所以我在这里没有想法,也许我没有得到正确的时间表机制。
任何人都可以确认我可以混合调度缓冲区和段AVAudioPlayerNode
吗?还是我应该只安排缓冲区或段?我可以确认只安排分段有效,播放非常好。
关于我如何缓存音频部分信息的一点上下文。在下面的代码中,是从 a 加载到磁盘上file
的类型,并且是值,代表我的音频部分的开始/结束。AVAudioFile
URL
begin
end
TimeInterval
let format = file.processingFormat
let startBufferFrameCount: AVAudioFrameCount = 4096
let endBufferFrameCount: AVAudioFrameCount = 4096
let audioSectionStartFrame = framePosition(at: begin, format: format)
let audioSectionEndFrame = framePosition(at: end, format: format)
let segmentStartFrame = audioSectionStartFrame + AVAudioFramePosition(startBufferFrameCount)
let segmentEndFrame = audioSectionEndFrame - AVAudioFramePosition(endBufferFrameCount)
startBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: startBufferFrameCount)
endBuffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: endBufferFrameCount)
file.framePosition = audioSectionStartFrame
try file.read(into: startBuffer)
file.framePosition = segmentEndFrame
try file.read(into: endBuffer)
middleSegment = (segmentStartFrame, AVAudioFrameCount(segmentEndFrame - segmentStartFrame))
frameCount = AVAudioFrameCount(audioSectionEndFrame - audioSectionStartFrame)
此外,将值framePosition(at:format:)
乘以传入TimeInterval
的采样率。AVAudioFormat
我为每个音频部分缓存此信息,但我在部分边界处听到咔嗒声,无论我是否提前安排它们。我也尝试在调度时不混合缓冲区和段,但我没有改变任何东西,所以我开始认为我在做错误的帧计算。