Skip to content

Instantly share code, notes, and snippets.

@LuoZijun
Created June 12, 2019 07:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LuoZijun/37149dc0b906296b614268ab7e6f5c2c to your computer and use it in GitHub Desktop.
Save LuoZijun/37149dc0b906296b614268ab7e6f5c2c to your computer and use it in GitHub Desktop.
[package]
name = "audio"
version = "0.1.0"
authors = ["luozijun <luozijun.assistant@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
hound = "3.4"
cpal = "0.9"
minimp3 = "0.3"
extern crate cpal;
extern crate hound;
extern crate minimp3;
use std::env;
use std::fs::File;
use std::path::Path;
fn play_wav(filename: &str) {
let wav_reader = hound::WavReader::open(filename).unwrap();
let wav_spec = wav_reader.spec();
println!("WAV Reader Spec: {:?}", wav_spec );
// NOTE: 确保频率和声道数量是我们的硬件所支持的。
assert_eq!(wav_spec.channels == 1 || wav_spec.channels == 2, true);
assert_eq!(wav_spec.bits_per_sample, 16);
assert_eq!(wav_spec.sample_format, hound::SampleFormat::Int);
let device = cpal::default_output_device().expect("Failed to get default output device");
let format = device.default_output_format().expect("Failed to get default output format");
let event_loop = cpal::EventLoop::new();
println!("device name: {:?}", device.name() );
println!("device default output format: {:?}", format);
println!("supported_input_formats: {:?}",
device.supported_input_formats().unwrap().collect::<Vec<cpal::SupportedFormat>>()
);
println!("supported_output_formats: {:?}",
device.supported_output_formats().unwrap().collect::<Vec<cpal::SupportedFormat>>()
);
let stream_id = event_loop.build_output_stream(&device, &format).unwrap();
event_loop.play_stream(stream_id.clone());
let _sample_rate = format.sample_rate.0 as f32;
let mut samples = wav_reader.into_samples::<i16>()
.map(|sample| sample.expect("failed to decode WAV stream"));
event_loop.run(move |_stream_id, stream_data| {
match stream_data {
cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer) } => {
for sample in buffer.chunks_mut(format.channels as usize) {
// NOTE: 2 个声道
let left = samples.next().unwrap_or(0 as _) as f64; // NOTE: 这里需要处理当样本数据读取完毕的时候终止播放。
let right = samples.next().unwrap_or(0 as _) as f64;
let value: f64 = ( left + right ) / 2.0 / (std::i16::MAX as f64);
for out in sample.iter_mut() {
*out = value as f32;
}
}
},
_ => {
unreachable!()
},
};
// NOTE: 通过信号通知主线程停止播放。
// event_loop.destroy_stream(stream_id);
});
}
fn play_mp3(filename: &str) {
let mut decoder = minimp3::Decoder::new(File::open(Path::new(filename)).unwrap());
let device = cpal::default_output_device().expect("Failed to get default output device");
let format = device.default_output_format().expect("Failed to get default output format");
let event_loop = cpal::EventLoop::new();
let stream_id = event_loop.build_output_stream(&device, &format).unwrap();
event_loop.play_stream(stream_id.clone());
// NOTE: 这里应该使用 Iter Trait 来设计,这里就不考虑性能了。
let mut samples: Vec<i16> = vec![];
loop {
match decoder.next_frame() {
Ok(mut frame) => {
// NOTE: 确保频率和声道数量是我们的硬件所支持的。
assert_eq!(frame.sample_rate, 44100);
assert_eq!(frame.channels, 2);
samples.append(&mut frame.data);
},
Err(minimp3::Error::Eof) => {
break;
},
Err(e) => {
panic!("[ERROR] MP3 Decode Error: {:?}", e);
}
}
}
let mut samples = samples.iter();
event_loop.run(move |_stream_id, stream_data| {
match stream_data {
cpal::StreamData::Output { buffer: cpal::UnknownTypeOutputBuffer::F32(mut buffer) } => {
for sample in buffer.chunks_mut(format.channels as usize) {
// NOTE: 2 个声道
let left = *samples.next().unwrap_or(&0i16) as f64; // NOTE: 这里需要处理当样本数据读取完毕的时候终止播放。
let right = *samples.next().unwrap_or(&0i16) as f64;
let value: f64 = ( left + right ) / 2.0 / (std::i16::MAX as f64);
for out in sample.iter_mut() {
*out = value as f32;
}
}
},
_ => {
unreachable!()
},
};
});
}
fn main() {
let filename = env::args().nth(1).expect("play ./sample.wav\nplay ./sample.mp3");
if filename.ends_with("wav") {
play_wav(&filename);
} else if filename.ends_with("mp3") {
play_mp3(&filename);
} else {
println!("不支持的音频格式!");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment