Created
June 12, 2019 07:25
-
-
Save LuoZijun/37149dc0b906296b614268ab7e6f5c2c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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