12 个版本 (6 个稳定版本)
1.2.2 | 2024年1月7日 |
---|---|
1.2.0 | 2023年12月30日 |
1.1.1 | 2023年9月13日 |
1.1.0 | 2023年7月12日 |
0.1.1 | 2021年10月18日 |
#35 in 音频
每月 1,591 次下载
在 4 个 crate 中使用 (通过 web-audio-api)
125KB
2K SLoC
🏞️ creek 🏞️
磁盘上的音频文件实时安全流读写。
内置的解码器使用 Symphonia。有关支持的编解码器,请参阅 Symphonia 的文档。Symphonia 的 Cargo 功能以 decode-
为前缀暴露,除了 creek 目前无法处理的 aac
和 isomp4
。例如,要在 creek 中启用 MP3 解码,启用 decode-mp3
功能。
内置的编码器仅支持 WAV 格式。
读取流的原理
流内部有两种类型的缓冲区:一个 cache
缓冲区和 look-ahead
缓冲区。
一个 cache
缓冲区是预先加载的文件中用户定义的样本范围。
流可以有任意多的 cache
缓冲区。这个用例的常见用途是缓存文件的开头或循环区域,以实现无缝循环。当在文件中搜索帧时,creek 会检查是否存在包含该帧的缓存。如果存在,则播放可以立即恢复,无需缓冲。
使用 look-ahead
缓冲区自动提前加载播放头之前的帧,确保即使在最坏的情况下 I/O 延迟场景下,数据也始终可用。
如果没有找到合适的缓存(或缓存尚未加载),则需要先填充 look-ahead
缓冲区,然后才能读取更多数据。在这种情况下,您可以选择继续播放(这将输出静音)或暂时暂停播放,直到数据可用。
Creek 会自动启动一个“IO 服务器”线程来处理非实时操作。当流被丢弃时,该服务器会自动关闭。
写入流的原理
写入流的工作方式符合您的预期。一旦一个块被数据填满,它就会被发送到IO服务器线程进行写入。一旦服务器线程处理完那个块,它就会将其发送回写入流以便重用。
示例
简单使用示例
use creek::{
SymphoniaDecoder, SeekMode, ReadDiskStream,
WriteDiskStream, WavEncoder, wav_bit_depth
};
// Open a read stream.
let mut read_disk_stream = ReadDiskStream::<SymphoniaDecoder>::new(
"./test_files/wav_f32_stereo.wav", // Path to file.
0, // The frame in the file to start reading from.
Default::default(), // Use default read stream options.
).unwrap();
// Cache the start of the file into cache with index `0`.
let _ = read_disk_stream.cache(0, 0);
// Tell the stream to seek to the beginning of file. This will also alert the stream to the existence
// of the cache with index `0`.
read_disk_stream.seek(0, Default::default()).unwrap();
// Wait until the buffer is filled before sending it to the process thread.
//
// NOTE: Do ***not*** use this method in a real-time thread.
read_disk_stream.block_until_ready().unwrap();
// (Send `read_stream` to the audio processing thread)
// Open a write stream.
WriteDiskStream::<WavEncoder<wav_bit_depth::Float32>>::new(
"./test_files/wav_f32_stereo_out.wav", // Path to file.
2, // The number of channels in the file
44100, // The sample rate of the file
Default::default(), // Use default write stream options.
).unwrap();
// (Send `write_stream` to the audio processing thread)
// -------------------------------------------------------------
// In the realtime audio processing thread:
// Update read client and check if it is ready.
//
// NOTE: You should avoid using `unwrap()` in realtime code.
if !read_disk_stream.is_ready().unwrap() {
// If the look-ahead buffer is still buffering, We can choose to either continue
// reading (which will return silence), or pause playback until the buffer is filled.
}
let read_data = read_disk_stream.read(num_frames_in_output_buffer).unwrap();
println!("{}", read_data.num_frames());
println!("{}", read_data.num_channels());
// Seek to a new position in the file.
read_disk_stream.seek(50000, SeekMode::Auto};
assert_eq!(read_dist_stream.playhead(), 50000);
// Send stereo data to be written to disk.
write_disk_stream.write(
&[read_data.read_channel(0), read_data.read_channel(1)]
).unwrap();
演示
- 一个基本的
player 应用程序
,可以播放单个WAV文件,并提供可调整的循环区域。 - 一个基本的
writer 应用程序
,可以将声音录制到WAV文件中。
贡献
我们使用各种pre-commit钩子来确保代码风格和格式的一致性。
请遵循安装说明,了解如何在您的开发系统中设置pre-commit。这允许您在提交代码之前在本地运行检查,并等待CI结果。
pre-commit run --all-files
Git
与Git合作的约定
依赖项
~0–0.8MB
~23K SLoC