1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::error;
6 use std::fmt;
7 use std::fs::File;
8 use std::io::{self, BufReader, BufWriter, Read, Write};
9 use std::os::raw::c_int;
10 use std::path::Path;
11 use std::sync::atomic::{AtomicBool, Ordering};
12
13 use audio_streams::{SampleFormat, StreamSource};
14 use hound::{WavReader, WavSpec, WavWriter};
15 use libcras::{BoxError, CrasClient, CrasNodeType};
16 use sys_util::{register_signal_handler, set_rt_prio_limit, set_rt_round_robin};
17
18 use crate::arguments::{AudioOptions, FileType, LoopbackType};
19
20 #[derive(Debug)]
21 pub enum Error {
22 CreateStream(BoxError),
23 FetchStream(BoxError),
24 FloatingPointSamples,
25 InvalidWavFile(hound::Error),
26 Io(io::Error),
27 Libcras(libcras::Error),
28 NoLoopbackNode(CrasNodeType),
29 OpenFile(hound::Error),
30 SampleBits(u16),
31 SysUtil(sys_util::Error),
32 }
33
34 impl error::Error for Error {}
35
36 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result37 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38 use Error::*;
39 match self {
40 CreateStream(e) => write!(f, "Failed to create stream: {}", e),
41 FetchStream(e) => write!(f, "Failed to fetch buffer from stream: {}", e),
42 FloatingPointSamples => write!(f, "Floating point audio samples are not supported"),
43 InvalidWavFile(e) => write!(f, "Could not open file as WAV file: {}", e),
44 Io(e) => write!(f, "IO Error: {}", e),
45 Libcras(e) => write!(f, "Libcras Error: {}", e),
46 NoLoopbackNode(typ) => write!(f, "No loopback node found with type {:?}", typ),
47 OpenFile(e) => write!(f, "Could not open WAV file for writing: {}", e),
48 SampleBits(bits) => write!(
49 f,
50 "Sample size {} is not supported, only 8, 16, 24, and 32 bit samples are supported",
51 bits
52 ),
53 SysUtil(e) => write!(f, "SysUtil Error: {}", e),
54 }
55 }
56 }
57
58 type Result<T> = std::result::Result<T, Error>;
59
60 static INTERRUPTED: AtomicBool = AtomicBool::new(false);
61
sigint_handler(_: c_int)62 extern "C" fn sigint_handler(_: c_int) {
63 // Check if we've already received one SIGINT. If we have, the program may
64 // be misbehaving and not terminating, so to be safe we'll forcefully exit.
65 if INTERRUPTED.load(Ordering::Acquire) {
66 std::process::exit(1);
67 }
68 INTERRUPTED.store(true, Ordering::Release);
69 }
70
add_sigint_handler() -> Result<()>71 fn add_sigint_handler() -> Result<()> {
72 const SIGINT: c_int = 2;
73 let result = unsafe { register_signal_handler(SIGINT, sigint_handler) };
74 result.map_err(Error::SysUtil)
75 }
76
set_priority_to_realtime()77 fn set_priority_to_realtime() {
78 const AUDIO_THREAD_RTPRIO: u16 = 10;
79 if set_rt_prio_limit(AUDIO_THREAD_RTPRIO as u64).is_err()
80 || set_rt_round_robin(AUDIO_THREAD_RTPRIO as i32).is_err()
81 {
82 println!("Attempt to use real-time priority failed, running with default scheduler.");
83 }
84 }
85
channel_string(num_channels: usize) -> String86 fn channel_string(num_channels: usize) -> String {
87 match num_channels {
88 1 => "Mono".to_string(),
89 2 => "Stereo".to_string(),
90 _ => format!("{} Channels", num_channels),
91 }
92 }
93
94 struct WavSource {
95 wav_reader: WavReader<BufReader<File>>,
96 format: SampleFormat,
97 num_channels: usize,
98 frame_rate: u32,
99 }
100
101 impl WavSource {
try_new(opts: &AudioOptions) -> Result<Self>102 fn try_new(opts: &AudioOptions) -> Result<Self> {
103 let wav_reader = WavReader::open(&opts.file_name).map_err(Error::InvalidWavFile)?;
104 let spec = wav_reader.spec();
105 if spec.sample_format == hound::SampleFormat::Float {
106 return Err(Error::FloatingPointSamples);
107 }
108
109 let format = match spec.bits_per_sample {
110 8 => SampleFormat::U8,
111 16 => SampleFormat::S16LE,
112 24 => SampleFormat::S24LE,
113 32 => SampleFormat::S32LE,
114 s => return Err(Error::SampleBits(s)),
115 };
116 if opts.format.is_some() && Some(format) != opts.format {
117 eprintln!("Warning: format changed to {:?}", format);
118 }
119
120 let num_channels = spec.channels as usize;
121 if opts.num_channels.is_some() && Some(num_channels) != opts.num_channels {
122 eprintln!("Warning: number of channels changed to {}", num_channels);
123 }
124
125 let frame_rate = spec.sample_rate;
126 if opts.frame_rate.is_some() && Some(frame_rate) != opts.frame_rate {
127 eprintln!("Warning: frame rate changed to {}", frame_rate);
128 }
129
130 Ok(Self {
131 wav_reader,
132 format,
133 num_channels,
134 frame_rate,
135 })
136 }
137
format(&self) -> SampleFormat138 fn format(&self) -> SampleFormat {
139 self.format
140 }
141
num_channels(&self) -> usize142 fn num_channels(&self) -> usize {
143 self.num_channels
144 }
145
frame_rate(&self) -> u32146 fn frame_rate(&self) -> u32 {
147 self.frame_rate
148 }
149 }
150
151 impl Read for WavSource {
read(&mut self, mut buf: &mut [u8]) -> io::Result<usize>152 fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
153 let frame_size = self.format.sample_bytes() * self.num_channels;
154 let read_len = buf.len() - buf.len() % frame_size;
155 let num_samples = read_len / self.format.sample_bytes();
156 let samples = self.wav_reader.samples::<i32>();
157 let mut read = 0;
158 for s in samples.take(num_samples) {
159 match s {
160 Ok(sample) => {
161 let result = match self.format {
162 SampleFormat::U8 => buf.write_all(&((sample + 128) as u8).to_le_bytes()),
163 SampleFormat::S16LE => buf.write_all(&(sample as i16).to_le_bytes()),
164 SampleFormat::S24LE | SampleFormat::S32LE => {
165 buf.write_all(&sample.to_le_bytes())
166 }
167 };
168
169 match result {
170 Ok(()) => read += self.format.sample_bytes(),
171 Err(_) => return Ok(read),
172 };
173 }
174 Err(_) => return Ok(read),
175 };
176 }
177 Ok(read)
178 }
179 }
180
playback(opts: AudioOptions) -> Result<()>181 pub fn playback(opts: AudioOptions) -> Result<()> {
182 let num_channels;
183 let frame_rate;
184 let format;
185 let mut sample_source: Box<dyn Read> = match opts.file_type {
186 FileType::Wav => {
187 let wav_source = WavSource::try_new(&opts)?;
188 num_channels = wav_source.num_channels();
189 frame_rate = wav_source.frame_rate();
190 format = wav_source.format();
191 Box::new(wav_source)
192 }
193 FileType::Raw => {
194 num_channels = opts.num_channels.unwrap_or(2);
195 frame_rate = opts.frame_rate.unwrap_or(48000);
196 format = opts.format.unwrap_or(SampleFormat::S16LE);
197 Box::new(BufReader::new(
198 File::open(&opts.file_name).map_err(Error::Io)?,
199 ))
200 }
201 };
202
203 println!(
204 "Playing {} '{}' : {}, Rate {} Hz, {}",
205 opts.file_type,
206 opts.file_name.display(),
207 format,
208 frame_rate,
209 channel_string(num_channels)
210 );
211
212 let mut cras_client = CrasClient::new().map_err(Error::Libcras)?;
213 let (_control, mut stream) = cras_client
214 .new_playback_stream(
215 num_channels,
216 format,
217 frame_rate,
218 opts.buffer_size.unwrap_or(256),
219 )
220 .map_err(Error::CreateStream)?;
221 set_priority_to_realtime();
222
223 add_sigint_handler()?;
224 while !INTERRUPTED.load(Ordering::Acquire) {
225 let mut buffer = stream.next_playback_buffer().map_err(Error::FetchStream)?;
226
227 let frame_size = num_channels * format.sample_bytes();
228 let frames = buffer.frame_capacity();
229
230 let mut chunk = (&mut sample_source).take((frames * frame_size) as u64);
231 let transferred = io::copy(&mut chunk, &mut buffer).map_err(Error::Io)?;
232 if transferred == 0 {
233 break;
234 }
235 }
236 // Stream and client should gracefully be closed out of this scope
237
238 Ok(())
239 }
240
241 struct WavSink {
242 wav_writer: WavWriter<BufWriter<File>>,
243 format: SampleFormat,
244 }
245
246 impl WavSink {
try_new<P: AsRef<Path>>( path: P, num_channels: usize, format: SampleFormat, frame_rate: u32, ) -> Result<Self>247 fn try_new<P: AsRef<Path>>(
248 path: P,
249 num_channels: usize,
250 format: SampleFormat,
251 frame_rate: u32,
252 ) -> Result<Self> {
253 let spec = WavSpec {
254 channels: num_channels as u16,
255 sample_rate: frame_rate,
256 bits_per_sample: (format.sample_bytes() * 8) as u16,
257 sample_format: hound::SampleFormat::Int,
258 };
259 let wav_writer = WavWriter::create(path, spec).map_err(Error::OpenFile)?;
260 Ok(Self { wav_writer, format })
261 }
262 }
263
264 impl Write for WavSink {
write(&mut self, samples: &[u8]) -> io::Result<usize>265 fn write(&mut self, samples: &[u8]) -> io::Result<usize> {
266 let sample_bytes = self.format.sample_bytes();
267 if samples.len() % sample_bytes != 0 {
268 return Err(io::Error::new(
269 io::ErrorKind::InvalidInput,
270 format!(
271 "u8 samples vector of length {} cannot be interpreted as {:?} samples",
272 samples.len(),
273 self.format
274 ),
275 ));
276 }
277 let num_samples = samples.len() / sample_bytes;
278 match self.format {
279 SampleFormat::U8 => {
280 for sample in samples {
281 self.wav_writer.write_sample(*sample as i8).map_err(|e| {
282 io::Error::new(
283 io::ErrorKind::Other,
284 format!("Failed to write sample: {}", e),
285 )
286 })?;
287 }
288 }
289 SampleFormat::S16LE => {
290 // hound offers an optimized i16 writer, so special case here.
291 let mut writer = self.wav_writer.get_i16_writer(num_samples as u32);
292 for i in 0..num_samples {
293 let sample = i16::from_le_bytes([
294 samples[sample_bytes * i],
295 samples[sample_bytes * i + 1],
296 ]);
297 writer.write_sample(sample);
298 }
299 // I16Writer buffers internally and must be explicitly flushed to write
300 // samples to the backing writer. Flush is not called automatically
301 // on drop.
302 // The flush method only writes data from the i16_writer to the underlying
303 // WavWriter, it does not actually guarantee a flush to disk.
304 writer.flush().map_err(|e| {
305 io::Error::new(
306 io::ErrorKind::Other,
307 format!("Failed to flush SampleWriter: {}", e),
308 )
309 })?;
310 }
311 SampleFormat::S24LE | SampleFormat::S32LE => {
312 for i in 0..num_samples {
313 let mut sample = i32::from_le_bytes([
314 samples[sample_bytes * i],
315 samples[sample_bytes * i + 1],
316 samples[sample_bytes * i + 2],
317 samples[sample_bytes * i + 3],
318 ]);
319
320 // Upsample to 32 bit since CRAS doesn't support S24_3LE.
321 // Our wav encoder/decoder, hound, does have support for
322 // S24_LE, but it hasn't released a new version since the
323 // support was added. If getting that support is an issue,
324 // push upstream to cut a new a release.
325 if self.format == SampleFormat::S24LE {
326 sample <<= 8;
327 }
328
329 self.wav_writer.write_sample(sample).map_err(|e| {
330 io::Error::new(
331 io::ErrorKind::Other,
332 format!("Failed to write sample: {}", e),
333 )
334 })?;
335 }
336 }
337 }
338
339 Ok(samples.len())
340 }
341
flush(&mut self) -> io::Result<()>342 fn flush(&mut self) -> io::Result<()> {
343 self.wav_writer.flush().map_err(|e| {
344 io::Error::new(
345 io::ErrorKind::Other,
346 format!("Failed to flush WavWriter: {}", e),
347 )
348 })
349 }
350 }
351
capture(opts: AudioOptions) -> Result<()>352 pub fn capture(opts: AudioOptions) -> Result<()> {
353 let num_channels = opts.num_channels.unwrap_or(2);
354 let format = opts.format.unwrap_or(SampleFormat::S16LE);
355 let frame_rate = opts.frame_rate.unwrap_or(48000);
356 let buffer_size = opts.buffer_size.unwrap_or(256);
357
358 let mut sample_sink: Box<dyn Write> = match opts.file_type {
359 FileType::Raw => Box::new(BufWriter::new(
360 File::create(&opts.file_name).map_err(Error::Io)?,
361 )),
362 FileType::Wav => Box::new(WavSink::try_new(
363 &opts.file_name,
364 num_channels,
365 format,
366 frame_rate,
367 )?),
368 };
369
370 println!(
371 "Recording {} '{}' : {}, Rate {} Hz, {}",
372 opts.file_type,
373 opts.file_name.display(),
374 format,
375 frame_rate,
376 channel_string(num_channels)
377 );
378
379 let mut cras_client = CrasClient::new().map_err(Error::Libcras)?;
380 cras_client.enable_cras_capture();
381 let (_control, mut stream) = match opts.loopback_type {
382 Some(loopback_type) => {
383 let node_type = match loopback_type {
384 LoopbackType::PreDsp => CrasNodeType::CRAS_NODE_TYPE_POST_MIX_PRE_DSP,
385 LoopbackType::PostDsp => CrasNodeType::CRAS_NODE_TYPE_POST_DSP,
386 };
387
388 let loopback_node = cras_client
389 .input_nodes()
390 .find(|node| node.node_type == node_type)
391 .ok_or(Error::NoLoopbackNode(node_type))?;
392
393 cras_client
394 .new_pinned_capture_stream(
395 loopback_node.iodev_index,
396 num_channels,
397 format,
398 frame_rate,
399 buffer_size,
400 )
401 .map_err(Error::CreateStream)?
402 }
403 None => cras_client
404 .new_capture_stream(num_channels, format, frame_rate, buffer_size)
405 .map_err(Error::CreateStream)?,
406 };
407 set_priority_to_realtime();
408 add_sigint_handler()?;
409 while !INTERRUPTED.load(Ordering::Acquire) {
410 let mut buf = stream.next_capture_buffer().map_err(Error::FetchStream)?;
411 io::copy(&mut buf, &mut sample_sink).map_err(Error::Io)?;
412 }
413 Ok(())
414 }
415