前篇导航:【CPAL 使用笔记 ②】制作一个振荡器(一)(http://t.csdnimg.cn/qu1F5)]
在继续振荡器的制作之前,我们需要对音频流的处理深入学习
预备知识
帧(frame)
一帧音频数据是指在一个时间点上所有声道(Channel)的采样值(Sample)集合。比如对于立体声系统,一帧可能包含两个样本(左声道一个,右声道一个)。
如何发送音频流数据?
build_output_stream
fn build_output_stream<T, D, E> (
&self,
config: &StreamConfig,
data_callback: D,
error_callback: E,
timeout: Option<Duration>
) -> Result<Self::Stream, BuildStreamError>
where
T: SizedSample,
D: FnMut(&mut [T], &OutputCallbackInfo + Send + 'static),
E: FnMut(StreamError) + Send + 'static,
以上是 build_output_stream()
的签名,不难发现,音频数据的处理交给了 error_callback
这个闭包。
data_callback
这个参数要求传入一个闭包,我们的处理逻辑全交给了这里。我们主要关心第一个参数 &mut [SizedSample]
这个参数传入一个包含一个缓冲区所有采样的数组。在这里采样实际上就是一个数,它具体是哪个类型(如 f32
或 f64
)取决于该输出流配置。例如如果你的输出流配置为 SampleFormat::F32
格式,那么你在 data_callback
中处理的缓冲区中的每个元素都是 f32
类型的,便可以直接对其进行赋值操作以修改音频数据。
当涉及到多个声道时,我们在某个时刻实际上处理的是是一帧的信号。此时 &mut [SizedSample]
便以通道数为步长来组织数据。比如说一个双声道音频流,我们按2个为一组划分采样数组,其中每一组的第一个值代表左声道,第二个代表右声道。
以代码为例:
for frame in sample_data.chunks_mut(num_channels) { // 以声道数为步长遍历数据
// value = ... // 计算值
for sample in frame.iter_mut() { // 遍历声道
//*sample = ... // 修改值(通过对 sample 解引用)
}
}
综上所述,每当音频设备需要更多数据时,就会调用该闭包并传递一个新的缓冲区。通过修改缓冲区内的样本值,即可实现对音频数据的发送或处理。
有了这个框架,我们就可以随心所欲地传输数据啦!