diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2021-03-03 05:57:31 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-03-03 05:57:31 +0000 |
commit | 0aaab56eba431583969e73f177a0efdcebf46cac (patch) | |
tree | d5ef5f1d516d12301b0a65b5dbfea16938588270 | |
parent | e46060c4528226af1a1089a123a21e537544aaa0 (diff) | |
parent | 26b34cb4fe3996e9d05e4111ad98a64875ed06dd (diff) | |
download | adhd-0aaab56eba431583969e73f177a0efdcebf46cac.tar.gz |
Merge "Revert^2 "Merge branch 'upstream-master'""
128 files changed, 2527 insertions, 986 deletions
diff --git a/audio_streams/README.md b/audio_streams/README.md index b62c2946..d3a02e8f 100644 --- a/audio_streams/README.md +++ b/audio_streams/README.md @@ -2,5 +2,5 @@ The `audio_streams` crate provides a basic interface for playing audio. This will be used to enable playback to various audio subsystems such as -Alsa and cras. To start, an empty playback example `DummyStreamSource` +Alsa and cras. To start, an empty playback example `NoopStreamSource` is provided. diff --git a/audio_streams/src/audio_streams.rs b/audio_streams/src/audio_streams.rs index 3cb11739..5290357e 100644 --- a/audio_streams/src/audio_streams.rs +++ b/audio_streams/src/audio_streams.rs @@ -13,14 +13,14 @@ //! the samples written to it are committed to the `PlaybackBufferStream` it came from. //! //! ``` -//! use audio_streams::{BoxError, SampleFormat, StreamSource, DummyStreamSource}; +//! use audio_streams::{BoxError, SampleFormat, StreamSource, NoopStreamSource}; //! use std::io::Write; //! //! const buffer_size: usize = 120; //! const num_channels: usize = 2; //! //! # fn main() -> std::result::Result<(), BoxError> { -//! let mut stream_source = DummyStreamSource::new(); +//! let mut stream_source = NoopStreamSource::new(); //! let sample_format = SampleFormat::S16LE; //! let frame_size = num_channels * sample_format.sample_bytes(); //! @@ -55,7 +55,7 @@ pub enum SampleFormat { } impl SampleFormat { - pub fn sample_bytes(&self) -> usize { + pub fn sample_bytes(self) -> usize { use SampleFormat::*; match self { U8 => 1, @@ -145,7 +145,7 @@ pub trait StreamSource: Send { /// Returns a stream control and buffer generator object. These are separate as the buffer /// generator might want to be passed to the audio stream. - /// Default implementation returns `DummyStreamControl` and `DummyCaptureStream`. + /// Default implementation returns `NoopStreamControl` and `NoopCaptureStream`. #[allow(clippy::type_complexity)] fn new_capture_stream( &mut self, @@ -161,8 +161,8 @@ pub trait StreamSource: Send { BoxError, > { Ok(( - Box::new(DummyStreamControl::new()), - Box::new(capture::DummyCaptureStream::new( + Box::new(NoopStreamControl::new()), + Box::new(capture::NoopCaptureStream::new( num_channels, format, frame_rate, @@ -295,28 +295,28 @@ impl<'a> Drop for PlaybackBuffer<'a> { } /// Stream that accepts playback samples but drops them. -pub struct DummyStream { +pub struct NoopStream { buffer: Vec<u8>, frame_size: usize, interval: Duration, next_frame: Duration, start_time: Option<Instant>, - buffer_drop: DummyBufferDrop, + buffer_drop: NoopBufferDrop, } -/// DummyStream data that is needed from the buffer complete callback. -struct DummyBufferDrop { +/// NoopStream data that is needed from the buffer complete callback. +struct NoopBufferDrop { which_buffer: bool, } -impl BufferDrop for DummyBufferDrop { +impl BufferDrop for NoopBufferDrop { fn trigger(&mut self, _nwritten: usize) { // When a buffer completes, switch to the other one. self.which_buffer ^= true; } } -impl DummyStream { +impl NoopStream { pub fn new( num_channels: usize, format: SampleFormat, @@ -325,20 +325,20 @@ impl DummyStream { ) -> Self { let frame_size = format.sample_bytes() * num_channels; let interval = Duration::from_millis(buffer_size as u64 * 1000 / frame_rate as u64); - DummyStream { + NoopStream { buffer: vec![0; buffer_size * frame_size], frame_size, interval, next_frame: interval, start_time: None, - buffer_drop: DummyBufferDrop { + buffer_drop: NoopBufferDrop { which_buffer: false, }, } } } -impl PlaybackBufferStream for DummyStream { +impl PlaybackBufferStream for NoopStream { fn next_playback_buffer(&mut self) -> Result<PlaybackBuffer, BoxError> { if let Some(start_time) = self.start_time { if start_time.elapsed() < self.next_frame { @@ -357,7 +357,8 @@ impl PlaybackBufferStream for DummyStream { } } -/// No-op control for `DummyStream`s. +/// No-op control for `NoopStream`s. +/// Should be deprecated once all existing use of DummyStreamControl removed. #[derive(Default)] pub struct DummyStreamControl; @@ -369,17 +370,29 @@ impl DummyStreamControl { impl StreamControl for DummyStreamControl {} -/// Source of `DummyStream` and `DummyStreamControl` objects. +/// No-op control for `NoopStream`s. #[derive(Default)] -pub struct DummyStreamSource; +pub struct NoopStreamControl; -impl DummyStreamSource { +impl NoopStreamControl { pub fn new() -> Self { - DummyStreamSource {} + NoopStreamControl {} } } -impl StreamSource for DummyStreamSource { +impl StreamControl for NoopStreamControl {} + +/// Source of `NoopStream` and `NoopStreamControl` objects. +#[derive(Default)] +pub struct NoopStreamSource; + +impl NoopStreamSource { + pub fn new() -> Self { + NoopStreamSource {} + } +} + +impl StreamSource for NoopStreamSource { #[allow(clippy::type_complexity)] fn new_playback_stream( &mut self, @@ -389,8 +402,8 @@ impl StreamSource for DummyStreamSource { buffer_size: usize, ) -> Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> { Ok(( - Box::new(DummyStreamControl::new()), - Box::new(DummyStream::new( + Box::new(NoopStreamControl::new()), + Box::new(NoopStream::new( num_channels, format, frame_rate, @@ -408,7 +421,7 @@ mod tests { fn invalid_buffer_length() { // Playback buffers can't be created with a size that isn't divisible by the frame size. let mut pb_buf = [0xa5u8; 480 * 2 * 2 + 1]; - let mut buffer_drop = DummyBufferDrop { + let mut buffer_drop = NoopBufferDrop { which_buffer: false, }; assert!(PlaybackBuffer::new(2, &mut pb_buf, &mut buffer_drop).is_err()); @@ -436,7 +449,7 @@ mod tests { #[test] fn sixteen_bit_stereo() { - let mut server = DummyStreamSource::new(); + let mut server = NoopStreamSource::new(); let (_, mut stream) = server .new_playback_stream(2, SampleFormat::S16LE, 48000, 480) .unwrap(); @@ -448,7 +461,7 @@ mod tests { #[test] fn consumption_rate() { - let mut server = DummyStreamSource::new(); + let mut server = NoopStreamSource::new(); let (_, mut stream) = server .new_playback_stream(2, SampleFormat::S16LE, 48000, 480) .unwrap(); diff --git a/audio_streams/src/capture.rs b/audio_streams/src/capture.rs index 3f2de3f3..930f1828 100644 --- a/audio_streams/src/capture.rs +++ b/audio_streams/src/capture.rs @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! ``` -//! use audio_streams::{BoxError, SampleFormat, StreamSource, DummyStreamSource}; +//! use audio_streams::{BoxError, SampleFormat, StreamSource, NoopStreamSource}; //! use std::io::Read; //! //! const buffer_size: usize = 120; //! const num_channels: usize = 2; //! //! # fn main() -> std::result::Result<(),BoxError> { -//! let mut stream_source = DummyStreamSource::new(); +//! let mut stream_source = NoopStreamSource::new(); //! let sample_format = SampleFormat::S16LE; //! let frame_size = num_channels * sample_format.sample_bytes(); //! @@ -34,7 +34,7 @@ use std::{ time::{Duration, Instant}, }; -use super::{AudioBuffer, BoxError, BufferDrop, DummyBufferDrop, SampleFormat}; +use super::{AudioBuffer, BoxError, BufferDrop, NoopBufferDrop, SampleFormat}; /// `CaptureBufferStream` provides `CaptureBuffer`s to read with audio samples from capture. pub trait CaptureBufferStream: Send { @@ -123,16 +123,16 @@ impl<'a> Drop for CaptureBuffer<'a> { } /// Stream that provides null capture samples. -pub struct DummyCaptureStream { +pub struct NoopCaptureStream { buffer: Vec<u8>, frame_size: usize, interval: Duration, next_frame: Duration, start_time: Option<Instant>, - buffer_drop: DummyBufferDrop, + buffer_drop: NoopBufferDrop, } -impl DummyCaptureStream { +impl NoopCaptureStream { pub fn new( num_channels: usize, format: SampleFormat, @@ -141,20 +141,20 @@ impl DummyCaptureStream { ) -> Self { let frame_size = format.sample_bytes() * num_channels; let interval = Duration::from_millis(buffer_size as u64 * 1000 / frame_rate as u64); - DummyCaptureStream { + NoopCaptureStream { buffer: vec![0; buffer_size * frame_size], frame_size, interval, next_frame: interval, start_time: None, - buffer_drop: DummyBufferDrop { + buffer_drop: NoopBufferDrop { which_buffer: false, }, } } } -impl CaptureBufferStream for DummyCaptureStream { +impl CaptureBufferStream for NoopCaptureStream { fn next_capture_buffer(&mut self) -> Result<CaptureBuffer, BoxError> { if let Some(start_time) = self.start_time { if start_time.elapsed() < self.next_frame { @@ -182,7 +182,7 @@ mod tests { fn invalid_buffer_length() { // Capture buffers can't be created with a size that isn't divisible by the frame size. let mut cp_buf = [0xa5u8; 480 * 2 * 2 + 1]; - let mut buffer_drop = DummyBufferDrop { + let mut buffer_drop = NoopBufferDrop { which_buffer: false, }; assert!(CaptureBuffer::new(2, &mut cp_buf, &mut buffer_drop).is_err()); @@ -212,7 +212,7 @@ mod tests { #[test] fn sixteen_bit_stereo() { - let mut server = DummyStreamSource::new(); + let mut server = NoopStreamSource::new(); let (_, mut stream) = server .new_capture_stream(2, SampleFormat::S16LE, 48000, 480) .unwrap(); @@ -224,7 +224,7 @@ mod tests { #[test] fn consumption_rate() { - let mut server = DummyStreamSource::new(); + let mut server = NoopStreamSource::new(); let (_, mut stream) = server .new_capture_stream(2, SampleFormat::S16LE, 48000, 480) .unwrap(); diff --git a/cras/README.dbus-api b/cras/README.dbus-api index 243107a0..c55a8df6 100644 --- a/cras/README.dbus-api +++ b/cras/README.dbus-api @@ -151,9 +151,9 @@ Methods void SetOutputVolume(int32 volume) int32 IsAudioOutputActive() Returns 1 if there are currently any active output streams, - excluding 'dummy' streams that are not actually outputting any + excluding 'fake' streams that are not actually outputting any audio. Returns 0 if there are no active streams, or all active - streams are 'dummy' streams. + streams are 'fake' streams. void SetGlobalOutputChannelRemix(int32 num_channels, array:double coefficient) diff --git a/cras/client/cras-sys/generator/src/main.rs b/cras/client/cras-sys/generator/src/main.rs index 1a022459..e562691d 100644 --- a/cras/client/cras-sys/generator/src/main.rs +++ b/cras/client/cras-sys/generator/src/main.rs @@ -36,6 +36,7 @@ fn copy_headers(src_dir: &Path, dst_dir: &Path) -> Result<(), String> { "cras_shm.h", "cras_types.h", "cras_util.h", + "packet_status_logger.h", ]; for header in &header_files { @@ -133,7 +134,11 @@ fn write_output(output_path: &Path, output: String) -> std::io::Result<()> { * cras_shm.h * cras_types.h * cras_util.h + * packet_status_logger.h */ + +#![allow(clippy::unreadable_literal)] +#![allow(clippy::cognitive_complexity)] "; let mut output_file = File::create(output_path)?; diff --git a/cras/client/cras-sys/src/gen.rs b/cras/client/cras-sys/src/gen.rs index 129a91a8..0375a0bd 100644 --- a/cras/client/cras-sys/src/gen.rs +++ b/cras/client/cras-sys/src/gen.rs @@ -10,7 +10,11 @@ * cras_shm.h * cras_types.h * cras_util.h + * packet_status_logger.h */ + +#![allow(clippy::unreadable_literal)] +#![allow(clippy::cognitive_complexity)] /* automatically generated by rust-bindgen */ pub const CRAS_IODEV_NAME_BUFFER_SIZE: u32 = 64; @@ -609,6 +613,78 @@ fn bindgen_test_layout_cras_audio_format_packed() { ) ); } +#[repr(C)] +#[derive(Copy, Clone)] +pub struct packet_status_logger { + pub data: [u8; 64usize], + pub size: ::std::os::raw::c_int, + pub wp: ::std::os::raw::c_int, + pub num_wraps: ::std::os::raw::c_int, + pub ts: timespec, +} +#[test] +fn bindgen_test_layout_packet_status_logger() { + assert_eq!( + ::std::mem::size_of::<packet_status_logger>(), + 96usize, + concat!("Size of: ", stringify!(packet_status_logger)) + ); + assert_eq!( + ::std::mem::align_of::<packet_status_logger>(), + 8usize, + concat!("Alignment of ", stringify!(packet_status_logger)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<packet_status_logger>())).data as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(packet_status_logger), + "::", + stringify!(data) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<packet_status_logger>())).size as *const _ as usize }, + 64usize, + concat!( + "Offset of field: ", + stringify!(packet_status_logger), + "::", + stringify!(size) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<packet_status_logger>())).wp as *const _ as usize }, + 68usize, + concat!( + "Offset of field: ", + stringify!(packet_status_logger), + "::", + stringify!(wp) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<packet_status_logger>())).num_wraps as *const _ as usize }, + 72usize, + concat!( + "Offset of field: ", + stringify!(packet_status_logger), + "::", + stringify!(num_wraps) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<packet_status_logger>())).ts as *const _ as usize }, + 80usize, + concat!( + "Offset of field: ", + stringify!(packet_status_logger), + "::", + stringify!(ts) + ) + ); +} #[repr(C, packed)] #[derive(Debug, Copy, Clone)] pub struct cras_timespec { @@ -845,13 +921,17 @@ pub enum CRAS_BT_LOG_EVENTS { BT_HFP_REQUEST_DISCONNECT = 13, BT_HFP_SUPPORTED_FEATURES = 14, BT_HFP_HF_INDICATOR = 15, - BT_HSP_NEW_CONNECTION = 16, - BT_HSP_REQUEST_DISCONNECT = 17, - BT_NEW_AUDIO_PROFILE_AFTER_CONNECT = 18, - BT_RESET = 19, - BT_SCO_CONNECT = 20, - BT_TRANSPORT_ACQUIRE = 21, - BT_TRANSPORT_RELEASE = 22, + BT_HFP_SET_SPEAKER_GAIN = 16, + BT_HFP_UPDATE_SPEAKER_GAIN = 17, + BT_HSP_NEW_CONNECTION = 18, + BT_HSP_REQUEST_DISCONNECT = 19, + BT_NEW_AUDIO_PROFILE_AFTER_CONNECT = 20, + BT_RESET = 21, + BT_SCO_CONNECT = 22, + BT_TRANSPORT_ACQUIRE = 23, + BT_TRANSPORT_RELEASE = 24, + BT_TRANSPORT_SET_VOLUME = 25, + BT_TRANSPORT_UPDATE_VOLUME = 26, } #[repr(C, packed)] #[derive(Debug, Copy, Clone)] @@ -1589,12 +1669,13 @@ pub struct main_thread_event { pub nsec: u32, pub data1: u32, pub data2: u32, + pub data3: u32, } #[test] fn bindgen_test_layout_main_thread_event() { assert_eq!( ::std::mem::size_of::<main_thread_event>(), - 16usize, + 20usize, concat!("Size of: ", stringify!(main_thread_event)) ); assert_eq!( @@ -1642,6 +1723,16 @@ fn bindgen_test_layout_main_thread_event() { stringify!(data2) ) ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<main_thread_event>())).data3 as *const _ as usize }, + 16usize, + concat!( + "Offset of field: ", + stringify!(main_thread_event), + "::", + stringify!(data3) + ) + ); } #[repr(C, packed)] #[derive(Copy, Clone)] @@ -1654,7 +1745,7 @@ pub struct main_thread_event_log { fn bindgen_test_layout_main_thread_event_log() { assert_eq!( ::std::mem::size_of::<main_thread_event_log>(), - 16392usize, + 20488usize, concat!("Size of: ", stringify!(main_thread_event_log)) ); assert_eq!( @@ -1702,7 +1793,7 @@ pub struct main_thread_debug_info { fn bindgen_test_layout_main_thread_debug_info() { assert_eq!( ::std::mem::size_of::<main_thread_debug_info>(), - 16392usize, + 20488usize, concat!("Size of: ", stringify!(main_thread_debug_info)) ); assert_eq!( @@ -1836,12 +1927,13 @@ fn bindgen_test_layout_cras_bt_event_log() { #[derive(Copy, Clone)] pub struct cras_bt_debug_info { pub bt_log: cras_bt_event_log, + pub wbs_logger: packet_status_logger, } #[test] fn bindgen_test_layout_cras_bt_debug_info() { assert_eq!( ::std::mem::size_of::<cras_bt_debug_info>(), - 16392usize, + 16488usize, concat!("Size of: ", stringify!(cras_bt_debug_info)) ); assert_eq!( @@ -1859,6 +1951,16 @@ fn bindgen_test_layout_cras_bt_debug_info() { stringify!(bt_log) ) ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<cras_bt_debug_info>())).wbs_logger as *const _ as usize }, + 16392usize, + concat!( + "Offset of field: ", + stringify!(cras_bt_debug_info), + "::", + stringify!(wbs_logger) + ) + ); } #[repr(u32)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -2013,13 +2115,14 @@ pub struct cras_server_state { pub snapshot_buffer: cras_audio_thread_snapshot_buffer, pub bt_debug_info: cras_bt_debug_info, pub bt_wbs_enabled: i32, + pub deprioritize_bt_wbs_mic: i32, pub main_thread_debug_info: main_thread_debug_info, } #[test] fn bindgen_test_layout_cras_server_state() { assert_eq!( ::std::mem::size_of::<cras_server_state>(), - 1410096usize, + 1414292usize, concat!("Size of: ", stringify!(cras_server_state)) ); assert_eq!( @@ -2383,7 +2486,7 @@ fn bindgen_test_layout_cras_server_state() { unsafe { &(*(::std::ptr::null::<cras_server_state>())).bt_wbs_enabled as *const _ as usize }, - 1393700usize, + 1393796usize, concat!( "Offset of field: ", stringify!(cras_server_state), @@ -2393,10 +2496,23 @@ fn bindgen_test_layout_cras_server_state() { ); assert_eq!( unsafe { + &(*(::std::ptr::null::<cras_server_state>())).deprioritize_bt_wbs_mic as *const _ + as usize + }, + 1393800usize, + concat!( + "Offset of field: ", + stringify!(cras_server_state), + "::", + stringify!(deprioritize_bt_wbs_mic) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<cras_server_state>())).main_thread_debug_info as *const _ as usize }, - 1393704usize, + 1393804usize, concat!( "Offset of field: ", stringify!(cras_server_state), @@ -2857,187 +2973,6 @@ fn bindgen_test_layout_cras_connect_message() { } #[repr(C, packed)] #[derive(Debug, Copy, Clone)] -pub struct cras_connect_message_old { - pub header: cras_server_message, - pub proto_version: u32, - pub direction: CRAS_STREAM_DIRECTION, - pub stream_id: cras_stream_id_t, - pub stream_type: CRAS_STREAM_TYPE, - pub buffer_frames: u32, - pub cb_threshold: u32, - pub flags: u32, - pub format: cras_audio_format_packed, - pub dev_idx: u32, - pub effects: u64, - pub client_type: CRAS_CLIENT_TYPE, - pub client_shm_size: u32, -} -#[test] -fn bindgen_test_layout_cras_connect_message_old() { - assert_eq!( - ::std::mem::size_of::<cras_connect_message_old>(), - 79usize, - concat!("Size of: ", stringify!(cras_connect_message_old)) - ); - assert_eq!( - ::std::mem::align_of::<cras_connect_message_old>(), - 1usize, - concat!("Alignment of ", stringify!(cras_connect_message_old)) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<cras_connect_message_old>())).header as *const _ as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(header) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<cras_connect_message_old>())).proto_version as *const _ as usize - }, - 8usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(proto_version) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<cras_connect_message_old>())).direction as *const _ as usize - }, - 12usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(direction) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<cras_connect_message_old>())).stream_id as *const _ as usize - }, - 16usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(stream_id) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<cras_connect_message_old>())).stream_type as *const _ as usize - }, - 20usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(stream_type) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<cras_connect_message_old>())).buffer_frames as *const _ as usize - }, - 24usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(buffer_frames) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<cras_connect_message_old>())).cb_threshold as *const _ as usize - }, - 28usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(cb_threshold) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<cras_connect_message_old>())).flags as *const _ as usize }, - 32usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(flags) - ) - ); - assert_eq!( - unsafe { &(*(::std::ptr::null::<cras_connect_message_old>())).format as *const _ as usize }, - 36usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(format) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<cras_connect_message_old>())).dev_idx as *const _ as usize - }, - 59usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(dev_idx) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<cras_connect_message_old>())).effects as *const _ as usize - }, - 63usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(effects) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<cras_connect_message_old>())).client_type as *const _ as usize - }, - 71usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(client_type) - ) - ); - assert_eq!( - unsafe { - &(*(::std::ptr::null::<cras_connect_message_old>())).client_shm_size as *const _ - as usize - }, - 75usize, - concat!( - "Offset of field: ", - stringify!(cras_connect_message_old), - "::", - stringify!(client_shm_size) - ) - ); -} -#[repr(C, packed)] -#[derive(Debug, Copy, Clone)] pub struct cras_disconnect_stream_message { pub header: cras_server_message, pub stream_id: cras_stream_id_t, diff --git a/cras/client/cras_tests/src/audio.rs b/cras/client/cras_tests/src/audio.rs index 261ad631..5ab22474 100644 --- a/cras/client/cras_tests/src/audio.rs +++ b/cras/client/cras_tests/src/audio.rs @@ -317,9 +317,11 @@ impl Write for WavSink { samples[sample_bytes * i + 3], ]); - // Upsample to 32 bit since CRAS doesn't support S24_3LE, - // even though the wav encoder does. - // TODO(fletcherw): add S24_LE support to hound. + // Upsample to 32 bit since CRAS doesn't support S24_3LE. + // Our wav encoder/decoder, hound, does have support for + // S24_LE, but it hasn't released a new version since the + // support was added. If getting that support is an issue, + // push upstream to cut a new a release. if self.format == SampleFormat::S24LE { sample <<= 8; } diff --git a/cras/client/libcras/src/cras_client_message.rs b/cras/client/libcras/src/cras_client_message.rs index d42b6f95..c1c5ec5c 100644 --- a/cras/client/libcras/src/cras_client_message.rs +++ b/cras/client/libcras/src/cras_client_message.rs @@ -195,6 +195,6 @@ impl CrasClientMessage { if self.len != mem::size_of::<T>() { return Err(Error::InvalidSize); } - T::from_slice(&self.data[..mem::size_of::<T>()]).ok_or_else(|| Error::MessageFromSliceError) + T::from_slice(&self.data[..mem::size_of::<T>()]).ok_or(Error::MessageFromSliceError) } } diff --git a/cras/client/libcras/src/libcras.rs b/cras/client/libcras/src/libcras.rs index 62e8fdda..80d2cff7 100644 --- a/cras/client/libcras/src/libcras.rs +++ b/cras/client/libcras/src/libcras.rs @@ -126,9 +126,9 @@ use std::{error, fmt}; pub use audio_streams::BoxError; use audio_streams::{ - capture::{CaptureBufferStream, DummyCaptureStream}, + capture::{CaptureBufferStream, NoopCaptureStream}, shm_streams::{NullShmStream, ShmStream, ShmStreamSource}, - BufferDrop, DummyStreamControl, PlaybackBufferStream, SampleFormat, StreamControl, + BufferDrop, NoopStreamControl, PlaybackBufferStream, SampleFormat, StreamControl, StreamDirection, StreamEffect, StreamSource, }; use cras_sys::gen::*; @@ -475,7 +475,7 @@ impl<'a> CrasClient<'a> { ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> { Ok(( - Box::new(DummyStreamControl::new()), + Box::new(NoopStreamControl::new()), Box::new(self.create_stream::<CrasPlaybackData>( Some(device_index), buffer_size as u32, @@ -509,7 +509,7 @@ impl<'a> CrasClient<'a> { buffer_size: usize, ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> { Ok(( - Box::new(DummyStreamControl::new()), + Box::new(NoopStreamControl::new()), Box::new(self.create_stream::<CrasCaptureData>( Some(device_index), buffer_size as u32, @@ -535,7 +535,7 @@ impl<'a> CrasClient<'a> { let tokens: Vec<Token> = events.iter_readable().map(|e| e.token()).collect(); tokens .get(0) - .ok_or_else(|| Error::UnexpectedExit) + .ok_or(Error::UnexpectedExit) .and_then(|ref token| { match token { Token::ServerMsg => ServerResult::handle_server_message(socket), @@ -562,7 +562,7 @@ impl<'a> StreamSource for CrasClient<'a> { ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> { Ok(( - Box::new(DummyStreamControl::new()), + Box::new(NoopStreamControl::new()), Box::new(self.create_stream::<CrasPlaybackData>( None, buffer_size as u32, @@ -584,7 +584,7 @@ impl<'a> StreamSource for CrasClient<'a> { ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> { if self.cras_capture { Ok(( - Box::new(DummyStreamControl::new()), + Box::new(NoopStreamControl::new()), Box::new(self.create_stream::<CrasCaptureData>( None, buffer_size as u32, @@ -596,8 +596,8 @@ impl<'a> StreamSource for CrasClient<'a> { )) } else { Ok(( - Box::new(DummyStreamControl::new()), - Box::new(DummyCaptureStream::new( + Box::new(NoopStreamControl::new()), + Box::new(NoopCaptureStream::new( num_channels, format, frame_rate, diff --git a/cras/src/Makefile.am b/cras/src/Makefile.am index 874389c6..69fea5ff 100644 --- a/cras/src/Makefile.am +++ b/cras/src/Makefile.am @@ -46,6 +46,7 @@ noinst_PROGRAMS = if HAVE_DBUS CRAS_DBUS_SOURCES = \ common/cras_sbc_codec.c \ + common/packet_status_logger.c \ server/cras_bt_manager.c \ server/cras_bt_adapter.c \ server/cras_bt_device.c \ @@ -155,6 +156,7 @@ cras_server_SOURCES = \ server/cras_volume_curve.c \ server/dev_io.c \ server/dev_stream.c \ + server/ewma_power.c \ server/input_data.c \ server/linear_resampler.c \ server/polled_interval_checker.c \ @@ -280,6 +282,7 @@ include_HEADERS = \ common/cras_types.h \ common/cras_util.h \ common/edid_utils.h \ + common/packet_status_logger.h \ common/utlist.h \ libcras/cras_client.h \ libcras/cras_helpers.h @@ -437,6 +440,7 @@ TESTS = \ edid_utils_unittest \ empty_iodev_unittest \ expr_unittest \ + ewma_power_unittest \ file_wait_unittest \ float_buffer_unittest \ fmt_conv_unittest \ @@ -880,6 +884,14 @@ buffer_share_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \ -I$(top_srcdir)/src/common -I$(top_srcdir)/src/server buffer_share_unittest_LDADD = -lgtest -liniparser -lpthread +ewma_power_unittest_SOURCES = tests/ewma_power_unittest.cc \ + common/cras_audio_format.c server/cras_audio_area.c \ + server/ewma_power.c + +ewma_power_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \ + -I$(top_srcdir)/src/common -I$(top_srcdir)/src/server +ewma_power_unittest_LDADD = -lgtest + iodev_list_unittest_SOURCES = tests/iodev_list_unittest.cc \ server/cras_iodev_list.c iodev_list_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) -I$(top_srcdir)/src/common \ @@ -949,8 +961,9 @@ control_rclient_unittest_SOURCES = tests/control_rclient_unittest.cc \ server/cras_rstream_config.c control_rclient_unittest_CPPFLAGS = $(COMMON_CPPFLAGS) \ -I$(top_srcdir)/src/common \ - -I$(top_srcdir)/src/server $(CRAS_UT_TMPDIR_CFLAGS) -control_rclient_unittest_LDADD = -lgtest -lpthread + -I$(top_srcdir)/src/server $(CRAS_UT_TMPDIR_CFLAGS) \ + $(DBUS_CFLAGS) +control_rclient_unittest_LDADD = -lgtest -lpthread $(DBUS_LIBS) playback_rclient_unittest_SOURCES = tests/playback_rclient_unittest.cc \ server/cras_rstream_config.c diff --git a/cras/src/alsa_plugin/pcm_cras.c b/cras/src/alsa_plugin/pcm_cras.c index 715db2cc..7bc960bc 100644 --- a/cras/src/alsa_plugin/pcm_cras.c +++ b/cras/src/alsa_plugin/pcm_cras.c @@ -124,7 +124,7 @@ static int pcm_cras_process_cb(struct cras_client *client, struct snd_pcm_cras *pcm_cras; const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t copied_frames; - char dummy_byte; + char empty_byte; size_t chan, frame_bytes, sample_bytes; int rc; uint8_t *samples; @@ -196,7 +196,7 @@ static int pcm_cras_process_cb(struct cras_client *client, copied_frames += frames; } - rc = write(pcm_cras->fd, &dummy_byte, 1); /* Wake up polling clients. */ + rc = write(pcm_cras->fd, &empty_byte, 1); /* Wake up polling clients. */ if (rc < 0 && errno != EWOULDBLOCK && errno != EAGAIN) fprintf(stderr, "%s write failed %d\n", __func__, errno); diff --git a/cras/src/common/cras_config.c b/cras/src/common/cras_config.c index 55caee2f..75fa24e7 100644 --- a/cras/src/common/cras_config.c +++ b/cras/src/common/cras_config.c @@ -44,6 +44,12 @@ int cras_fill_socket_path(enum CRAS_CONNECTION_TYPE conn_type, char *sock_path) case CRAS_VMS_UNIFIED: sock_file = CRAS_VMS_UNIFIED_SOCKET_FILE; break; + case CRAS_PLUGIN_PLAYBACK: + sock_file = CRAS_PLUGIN_PLAYBACK_SOCKET_FILE; + break; + case CRAS_PLUGIN_UNIFIED: + sock_file = CRAS_PLUGIN_UNIFIED_SOCKET_FILE; + break; default: return -EINVAL; } diff --git a/cras/src/common/cras_config.h b/cras/src/common/cras_config.h index 7f0aa23f..1c8e55fa 100644 --- a/cras/src/common/cras_config.h +++ b/cras/src/common/cras_config.h @@ -20,6 +20,9 @@ /* Socket file paths for VMs. */ #define CRAS_VMS_LEGACY_SOCKET_FILE "vms/.cras_socket" #define CRAS_VMS_UNIFIED_SOCKET_FILE "vms/.cras_unified" +/* Socket file paths for pluginVM. */ +#define CRAS_PLUGIN_PLAYBACK_SOCKET_FILE "vms/plugin/playback/.cras_socket" +#define CRAS_PLUGIN_UNIFIED_SOCKET_FILE "vms/plugin/unified/.cras_socket" /* Maximum socket_path size, which is equals to sizeof(sun_path) in sockaddr_un * structure. diff --git a/cras/src/common/cras_messages.h b/cras/src/common/cras_messages.h index ee09d930..50cbe7cd 100644 --- a/cras/src/common/cras_messages.h +++ b/cras/src/common/cras_messages.h @@ -120,28 +120,6 @@ struct __attribute__((__packed__)) cras_connect_message { uint64_t buffer_offsets[2]; }; -/* - * Old version of connect message without 'buffer_offsets'. - * Used to check against when receiving invalid size of connect message. - * Expected to have proto_version set to 5. - * TODO(fletcherw): remove when all clients migrate to latest libcras. - */ -struct __attribute__((__packed__)) cras_connect_message_old { - struct cras_server_message header; - uint32_t proto_version; - enum CRAS_STREAM_DIRECTION direction; /* input/output/loopback */ - cras_stream_id_t stream_id; /* unique id for this stream */ - enum CRAS_STREAM_TYPE stream_type; /* media, or call, etc. */ - uint32_t buffer_frames; /* Buffer size in frames. */ - uint32_t cb_threshold; /* callback client when this much is left */ - uint32_t flags; - struct cras_audio_format_packed format; /* rate, channel, sample size */ - uint32_t dev_idx; /* device to attach stream, 0 if none */ - uint64_t effects; /* Bit map of requested effects. */ - enum CRAS_CLIENT_TYPE client_type; /* chrome, or arc, etc. */ - uint32_t client_shm_size; /* Size of client-provided samples shm, if any */ -}; - static inline void cras_fill_connect_message( struct cras_connect_message *m, enum CRAS_STREAM_DIRECTION direction, cras_stream_id_t stream_id, enum CRAS_STREAM_TYPE stream_type, diff --git a/cras/src/common/cras_observer_ops.h b/cras/src/common/cras_observer_ops.h index a54d044c..e73845c9 100644 --- a/cras/src/common/cras_observer_ops.h +++ b/cras/src/common/cras_observer_ops.h @@ -47,6 +47,10 @@ struct cras_observer_ops { void (*num_active_streams_changed)(void *context, enum CRAS_STREAM_DIRECTION dir, uint32_t num_active_streams); + /* Number of input streams with permission changed. */ + void (*num_input_streams_with_permission_changed)( + void *context, + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]); /* Hotword triggered. */ void (*hotword_triggered)(void *context, int64_t tv_sec, int64_t tv_nsec); diff --git a/cras/src/common/cras_types.h b/cras/src/common/cras_types.h index c3482e8d..90a04741 100644 --- a/cras/src/common/cras_types.h +++ b/cras/src/common/cras_types.h @@ -15,6 +15,7 @@ #include "cras_audio_format.h" #include "cras_iodev_info.h" +#include "packet_status_logger.h" /* Architecture independent timespec */ struct __attribute__((__packed__)) cras_timespec { @@ -50,6 +51,8 @@ enum CRAS_CONNECTION_TYPE { CRAS_CAPTURE, // For capture client. CRAS_VMS_LEGACY, // For legacy client in vms. CRAS_VMS_UNIFIED, // For unified client in vms. + CRAS_PLUGIN_PLAYBACK, // For playback client in vms/plugin. + CRAS_PLUGIN_UNIFIED, // For unified client in vms/plugin. CRAS_NUM_CONN_TYPE, }; @@ -165,8 +168,15 @@ enum CRAS_CLIENT_TYPE { CRAS_CLIENT_TYPE_CROSVM, /* CROSVM */ CRAS_CLIENT_TYPE_SERVER_STREAM, /* Server stream */ CRAS_CLIENT_TYPE_LACROS, /* LaCrOS */ + CRAS_CLIENT_TYPE_PLUGIN, /* PluginVM */ + CRAS_NUM_CLIENT_TYPE, /* numbers of CRAS_CLIENT_TYPE */ }; +static inline bool cras_validate_client_type(enum CRAS_CLIENT_TYPE client_type) +{ + return 0 <= client_type && client_type < CRAS_NUM_CLIENT_TYPE; +} + #define ENUM_STR(x) \ case x: \ return #x; @@ -202,6 +212,7 @@ cras_client_type_str(enum CRAS_CLIENT_TYPE client_type) ENUM_STR(CRAS_CLIENT_TYPE_CROSVM) ENUM_STR(CRAS_CLIENT_TYPE_SERVER_STREAM) ENUM_STR(CRAS_CLIENT_TYPE_LACROS) + ENUM_STR(CRAS_CLIENT_TYPE_PLUGIN) default: return "INVALID_CLIENT_TYPE"; } @@ -479,6 +490,7 @@ struct __attribute__((__packed__)) cras_bt_event_log { struct __attribute__((__packed__)) cras_bt_debug_info { struct cras_bt_event_log bt_log; + struct packet_status_logger wbs_logger; }; /* @@ -556,7 +568,11 @@ struct __attribute__((__packed__)) cras_audio_thread_snapshot_buffer { * snapshot_buffer - ring buffer for storing audio thread snapshots. * bt_debug_info - ring buffer for storing bluetooth event logs. * bt_wbs_enabled - Whether or not bluetooth wideband speech is enabled. + * deprioritize_bt_wbs_mic - Whether Bluetooth wideband speech mic + * should be deprioritized for selecting as default audio input. * main_thread_debug_info - ring buffer for storing main thread event logs. + * num_input_streams_with_permission - An array containing numbers of input + * streams with permission in each client type. */ #define CRAS_SERVER_STATE_VERSION 2 struct __attribute__((packed, aligned(4))) cras_server_state { @@ -593,7 +609,9 @@ struct __attribute__((packed, aligned(4))) cras_server_state { struct cras_audio_thread_snapshot_buffer snapshot_buffer; struct cras_bt_debug_info bt_debug_info; int32_t bt_wbs_enabled; + int32_t deprioritize_bt_wbs_mic; struct main_thread_debug_info main_thread_debug_info; + uint32_t num_input_streams_with_permission[CRAS_NUM_CLIENT_TYPE]; }; /* Actions for card add/remove/change. */ diff --git a/cras/src/common/packet_status_logger.c b/cras/src/common/packet_status_logger.c new file mode 100644 index 00000000..f1be6965 --- /dev/null +++ b/cras/src/common/packet_status_logger.c @@ -0,0 +1,35 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <string.h> +#include <time.h> + +#include "cras_util.h" +#include "packet_status_logger.h" + +void packet_status_logger_init(struct packet_status_logger *logger) +{ + memset(logger->data, 0, PACKET_STATUS_LEN_BYTES); + logger->size = PACKET_STATUS_LEN_BYTES * 8; + logger->wp = 0; + logger->num_wraps = 0; + clock_gettime(CLOCK_MONOTONIC_RAW, &logger->ts); +} + +void packet_status_logger_update(struct packet_status_logger *logger, bool val) +{ + if (val) { + logger->data[logger->wp / 8] |= 1UL << (logger->wp % 8); + } else { + logger->data[logger->wp / 8] &= ~(1UL << (logger->wp % 8)); + } + logger->wp++; + if (logger->wp >= logger->size) { + logger->wp %= logger->size; + logger->num_wraps += 1; + } + if (logger->wp == 0 || (logger->num_wraps == 0 && logger->wp == 1)) + clock_gettime(CLOCK_MONOTONIC_RAW, &logger->ts); +} diff --git a/cras/src/common/packet_status_logger.h b/cras/src/common/packet_status_logger.h new file mode 100644 index 00000000..3bc90041 --- /dev/null +++ b/cras/src/common/packet_status_logger.h @@ -0,0 +1,127 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef PACKET_STATUS_LOGGER_ +#define PACKET_STATUS_LOGGER_ + +#include <stdint.h> +#include <stdbool.h> + +#define PACKET_STATUS_LEN_BYTES 64 +#define WBS_FRAME_NS 7500000 + +/* Avoid 32, 40, 64 consecutive hex characters so CrOS feedback redact + * tool doesn't trim our dump. */ +#define PACKET_STATUS_LOG_LINE_WRAP 50 + +/* + * Object to log consecutive packets' status. + * Members: + * data - Bytes to store packets' status. + * size - Total number of bits in |data|. + * wp - Position of the next bit to log packet status. + * num_wraps - Number of times the ring buffer has wrapped. + * ts - The timestamp of the last time when the first bit of |data| updated. + */ +struct packet_status_logger { + uint8_t data[PACKET_STATUS_LEN_BYTES]; + int size; + int wp; + int num_wraps; + struct timespec ts; +}; + +/* Initializes the packet status logger. */ +void packet_status_logger_init(struct packet_status_logger *logger); + +/* Updates the next packet status to logger. */ +void packet_status_logger_update(struct packet_status_logger *logger, bool val); + +/* Rewinds logger's time stamp to calculate the beginning. + * If logger's ring buffer hasn't wrapped, simply return logger_ts. + * Otherwise beginning_ts = logger_ts - WBS_FRAME_NS * (size - wp) + */ +static inline void +packet_status_logger_begin_ts(const struct packet_status_logger *logger, + struct timespec *ts) +{ + long nsec = WBS_FRAME_NS * (logger->size - logger->wp); + + *ts = logger->ts; + if (logger->num_wraps == 0) + return; + while (nsec > 1000000000L) { + ts->tv_sec--; + nsec -= 1000000000L; + } + ts->tv_nsec -= nsec; + if (ts->tv_nsec < 0) { + ts->tv_sec--; + ts->tv_nsec += 1000000000L; + } +} + +/* Fast-forwards the logger's time stamp to calculate the end. + * In other words, end_ts = logger_ts + WBS_FRAME_NS * wp + */ +static inline void +packet_status_logger_end_ts(const struct packet_status_logger *logger, + struct timespec *ts) +{ + *ts = logger->ts; + ts->tv_nsec += WBS_FRAME_NS * logger->wp; + while (ts->tv_nsec > 1000000000L) { + ts->tv_sec++; + ts->tv_nsec -= 1000000000L; + } +} + +/* Prints the logger data in hex format */ +static inline void +packet_status_logger_dump_hex(const struct packet_status_logger *logger) +{ + int i = logger->wp / 8; + + /* Print the bits after wp only if buffer has wrapped. */ + if (logger->num_wraps) { + if (logger->wp % 8) + printf("%.2x", + logger->data[i] & (0xff << (logger->wp % 8))); + for (; i < PACKET_STATUS_LEN_BYTES; i++) + printf("%.2x", logger->data[i]); + } + for (i = 0; i < logger->wp / 8; i++) + printf("%.2x", logger->data[i]); + if (logger->wp % 8) + printf("%.2x", logger->data[i] & (~(0xff << (logger->wp % 8)))); + printf("\n"); +} + +/* Prints the logger data in binary format */ +static inline void +packet_status_logger_dump_binary(const struct packet_status_logger *logger) +{ + /* Don't print the bits after wp if buffer hasn't wrapped. */ + int head = logger->num_wraps ? logger->wp : 0; + int len = logger->num_wraps ? logger->size : logger->wp; + int i, j; + + for (i = 0; i < len; ++i) { + j = (head + i) % logger->size; + printf("%d", (logger->data[j / 8] >> (j % 8)) & 1U); + if ((i + 1) % PACKET_STATUS_LOG_LINE_WRAP == 0) + printf("\n"); + } + /* Fill indicator digit 'D' until the last line wraps. */ + if (len % PACKET_STATUS_LOG_LINE_WRAP) { + while (len % PACKET_STATUS_LOG_LINE_WRAP) { + printf("D"); + ++len; + } + printf("\n"); + } +} + +#endif /* PACKET_STATUS_LOGGER_ */ diff --git a/cras/src/plc/cras_plc.c b/cras/src/plc/cras_plc.c index 4bc9fb76..74c3568b 100644 --- a/cras/src/plc/cras_plc.c +++ b/cras/src/plc/cras_plc.c @@ -22,6 +22,9 @@ #define PLC_SBCRL 36 /* SBC Reconvergence sample Length */ #define PLC_OLAL 16 /* OverLap-Add Length */ +#define PLC_WINDOW_SIZE 5 +#define PLC_PL_THRESHOLD 2 + /* The pre-computed zero input bit stream of mSBC codec, per HFP 1.7 spec. * This mSBC frame will be decoded into all-zero input PCM. */ static const uint8_t msbc_zero_frame[] = { @@ -40,6 +43,18 @@ static const float rcos[PLC_OLAL] = { 0.99148655f, 0.96623611f, 0.92510857f, 0.13049554f, 0.07489143f, 0.03376389f, 0.00851345f }; +/* This structure tracks the packet loss information for last PLC_WINDOW_SIZE + * of packets: + * loss_hist - The packet loss history of receiving packets. 1 means lost. + * ptr - The index of the to be updated packet loss status. + * count - The count of lost packets in the window. + */ +struct packet_window { + uint8_t loss_hist[PLC_WINDOW_SIZE]; + unsigned int ptr; + unsigned int count; +}; + /* The PLC is specifically designed for mSBC. The algorithm searches the * history of receiving samples to find the best match samples and constructs * substitutions for the lost samples. The selection is based on pattern @@ -57,23 +72,30 @@ static const float rcos[PLC_OLAL] = { 0.99148655f, 0.96623611f, 0.92510857f, * frame. * zero_frame - A buffer used for storing the samples from decoding the * mSBC zero frame packet. + * pl_window - A window monitoring how many packets are bad within the recent + * PLC_WINDOW_SIZE of packets. This is used to determine if we + * want to disable the PLC temporarily. */ struct cras_msbc_plc { int16_t hist[PLC_HL + MSBC_FS + PLC_SBCRL + PLC_OLAL]; unsigned int best_lag; int handled_bad_frames; int16_t zero_frame[MSBC_FS]; + struct packet_window *pl_window; }; struct cras_msbc_plc *cras_msbc_plc_create() { struct cras_msbc_plc *plc = (struct cras_msbc_plc *)calloc(1, sizeof(*plc)); + plc->pl_window = + (struct packet_window *)calloc(1, sizeof(*plc->pl_window)); return plc; } void cras_msbc_plc_destroy(struct cras_msbc_plc *plc) { + free(plc->pl_window); free(plc); } @@ -94,14 +116,33 @@ void overlap_add(int16_t *output, float scaler_d, const int16_t *desc, } } +void update_plc_state(struct packet_window *w, uint8_t is_packet_loss) +{ + uint8_t *curr = &w->loss_hist[w->ptr]; + if (is_packet_loss != *curr) { + w->count += (is_packet_loss - *curr); + *curr = is_packet_loss; + } + w->ptr = (w->ptr + 1) % PLC_WINDOW_SIZE; +} + +int possibly_pause_plc(struct packet_window *w) +{ + /* The packet loss count comes from a time window and we use it as an + * indicator of our confidence of the PLC algorithm. It is known to + * generate poorer and robotic feeling sounds, when the majority of + * samples in the PLC history buffer are from the concealment results. + */ + return w->count >= PLC_PL_THRESHOLD; +} + int cras_msbc_plc_handle_good_frames(struct cras_msbc_plc *state, const uint8_t *input, uint8_t *output) { int16_t *frame_head, *input_samples, *output_samples; if (state->handled_bad_frames == 0) { - /* If there was no packet loss before this good frame, there - * is nothing we need to do to the frame so we'll just pass - * the input to output. + /* If there was no packet concealment before this good frame, + * we just simply copy the input to output without reconverge. */ memmove(output, input, MSBC_FS * MSBC_SAMPLE_SIZE); } else { @@ -129,6 +170,7 @@ int cras_msbc_plc_handle_good_frames(struct cras_msbc_plc *state, (PLC_HL - MSBC_FS) * MSBC_SAMPLE_SIZE); memcpy(&state->hist[PLC_HL - MSBC_FS], output, MSBC_FS * MSBC_SAMPLE_SIZE); + update_plc_state(state->pl_window, 0); return MSBC_CODE_SIZE; } @@ -184,37 +226,60 @@ int cras_msbc_plc_handle_bad_frames(struct cras_msbc_plc *state, int16_t *frame_head = &state->hist[PLC_HL]; size_t pcm_decoded = 0; + /* mSBC codec is stateful, the history of signal would contribute to the + * decode result state->zero_frame. + */ codec->decode(codec, msbc_zero_frame, MSBC_PKT_LEN, state->zero_frame, MSBC_FS, &pcm_decoded); - if (state->handled_bad_frames == 0) { - /* Finds the best matching samples and amplitude */ - state->best_lag = pattern_match(state->hist) + PLC_TL; - best_match_hist = &state->hist[state->best_lag]; - scaler = amplitude_match(&state->hist[PLC_HL - MSBC_FS], - best_match_hist); - - /* Constructs the substitution samples */ - overlap_add(frame_head, 1.0, state->zero_frame, scaler, - best_match_hist); - for (int i = PLC_OLAL; i < MSBC_FS; i++) - state->hist[PLC_HL + i] = - f_to_s16(scaler * best_match_hist[i]); - overlap_add(&frame_head[MSBC_FS], scaler, - &best_match_hist[MSBC_FS], 1.0, - &best_match_hist[MSBC_FS]); - - memmove(&frame_head[MSBC_FS + PLC_OLAL], - &best_match_hist[MSBC_FS + PLC_OLAL], - PLC_SBCRL * MSBC_SAMPLE_SIZE); + /* The PLC algorithm is more likely to generate bad results that sound + * robotic after severe packet losses happened. Only applying it when + * we are confident. + */ + if (!possibly_pause_plc(state->pl_window)) { + if (state->handled_bad_frames == 0) { + /* Finds the best matching samples and amplitude */ + state->best_lag = pattern_match(state->hist) + PLC_TL; + best_match_hist = &state->hist[state->best_lag]; + scaler = amplitude_match(&state->hist[PLC_HL - MSBC_FS], + best_match_hist); + + /* Constructs the substitution samples */ + overlap_add(frame_head, 1.0, state->zero_frame, scaler, + best_match_hist); + for (int i = PLC_OLAL; i < MSBC_FS; i++) + state->hist[PLC_HL + i] = + f_to_s16(scaler * best_match_hist[i]); + overlap_add(&frame_head[MSBC_FS], scaler, + &best_match_hist[MSBC_FS], 1.0, + &best_match_hist[MSBC_FS]); + + memmove(&frame_head[MSBC_FS + PLC_OLAL], + &best_match_hist[MSBC_FS + PLC_OLAL], + PLC_SBCRL * MSBC_SAMPLE_SIZE); + } else { + memmove(frame_head, &state->hist[state->best_lag], + (MSBC_FS + PLC_SBCRL + PLC_OLAL) * + MSBC_SAMPLE_SIZE); + } + state->handled_bad_frames++; } else { - memmove(frame_head, &state->hist[state->best_lag], - (MSBC_FS + PLC_SBCRL + PLC_OLAL) * MSBC_SAMPLE_SIZE); + /* This is a case similar to receiving a good frame with all + * zeros, we set handled_bad_frames to zero to prevent the + * following good frame from being concealed to reconverge with + * the zero frames we fill in. The concealment result sounds + * more artificial and weird than simply writing zeros and + * following samples. + */ + memmove(frame_head, state->zero_frame, MSBC_CODE_SIZE); + memset(frame_head + MSBC_CODE_SIZE, 0, + (PLC_SBCRL + PLC_OLAL) * MSBC_SAMPLE_SIZE); + state->handled_bad_frames = 0; } - state->handled_bad_frames++; memcpy(output, frame_head, MSBC_CODE_SIZE); memmove(state->hist, &state->hist[MSBC_FS], (PLC_HL + PLC_SBCRL + PLC_OLAL) * MSBC_SAMPLE_SIZE); + update_plc_state(state->pl_window, 1); return MSBC_CODE_SIZE; } diff --git a/cras/src/plc/cras_plc_test.c b/cras/src/plc/cras_plc_test.c index 458f1254..4b7a6a77 100644 --- a/cras/src/plc/cras_plc_test.c +++ b/cras/src/plc/cras_plc_test.c @@ -4,11 +4,13 @@ */ #include <errno.h> +#include <getopt.h> #include <math.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> @@ -29,32 +31,65 @@ static const uint8_t msbc_zero_frame[] = { 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c }; -bool *generate_pl_seq(unsigned pk_count, unsigned loss_count) +bool *generate_pl_seq(int input_file_size, float pl_percent) { - bool *seq = (bool *)calloc(pk_count, sizeof(*seq)); + unsigned pk_count, pl_count; + bool *seq; + + pk_count = input_file_size / MSBC_CODE_SIZE; + pl_count = pk_count * (pl_percent / 100.0); + seq = (bool *)calloc(pk_count, sizeof(*seq)); srand(RND_SEED); - while (loss_count > 0) { + while (pl_count > 0) { bool *missed = &seq[rand() % pk_count]; if (!*missed) { *missed = true; - loss_count--; + pl_count--; + } + } + return seq; +} + +/* pl_hex is expected to be consecutive bytes(two chars) in hex format.*/ +bool *parse_pl_hex(int input_file_size, const char *pl_hex) +{ + char tmp[3]; + uint8_t val = 0; + int i, pl_hex_len, seq_len; + bool *seq; + + pl_hex_len = strlen(pl_hex); + seq_len = MAX(1 + input_file_size / MSBC_CODE_SIZE, pl_hex_len * 4); + seq = (bool *)calloc(seq_len, sizeof(*seq)); + + for (i = 0; i < seq_len; i++) { + /* If sequence is longer then the provided pl_hex, leave the + * rest to all zeros. */ + if (i > pl_hex_len * 4) + break; + if (i % 8 == 0) { + memcpy(tmp, pl_hex + i / 4, 2); + tmp[2] = '\0'; + val = strtol(tmp, NULL, 16); } + seq[i] = val & 1U; + val >>= 1; } + printf("pl_hex string maps to %ld ms, total sequence size %f ms\n", + strlen(pl_hex) * 30, seq_len * 7.5f); return seq; } -void plc_experiment(char *input_filename, float pl_percent, bool with_plc) +void plc_experiment(const char *input_filename, bool *pl_seq, bool with_plc) { char output_filename[255]; int input_fd, output_fd, rc; - struct stat st; - bool *pl_seq; struct cras_audio_codec *msbc_input = cras_msbc_codec_create(); struct cras_audio_codec *msbc_output = cras_msbc_codec_create(); struct cras_msbc_plc *plc = cras_msbc_plc_create(); uint8_t buffer[MSBC_CODE_SIZE], packet_buffer[MSBC_PKT_FRAME_LEN]; size_t encoded, decoded; - unsigned pk_count, pl_count, count = 0; + unsigned count = 0; input_fd = open(input_filename, O_RDONLY); if (input_fd == -1) { @@ -63,9 +98,9 @@ void plc_experiment(char *input_filename, float pl_percent, bool with_plc) } if (with_plc) - sprintf(output_filename, "output_%2.2f_plc.raw", pl_percent); + sprintf(output_filename, "output_with_plc.raw"); else - sprintf(output_filename, "output_%2.2f_zero.raw", pl_percent); + sprintf(output_filename, "output_with_zero.raw"); output_fd = open(output_filename, O_CREAT | O_RDWR | O_TRUNC, 0644); if (output_fd == -1) { @@ -74,11 +109,6 @@ void plc_experiment(char *input_filename, float pl_percent, bool with_plc) return; } - fstat(input_fd, &st); - pk_count = st.st_size / MSBC_CODE_SIZE; - pl_count = pk_count * (pl_percent / 100.0); - pl_seq = generate_pl_seq(pk_count, pl_count); - while (1) { rc = read(input_fd, buffer, MSBC_CODE_SIZE); if (rc < 0) { @@ -117,19 +147,77 @@ void plc_experiment(char *input_filename, float pl_percent, bool with_plc) } } +static void show_usage() +{ + printf("This test only supports reading/writing raw audio with format:\n" + "\t16000 sample rate, mono channel, S16_LE\n"); + printf("--help - Print this usage.\n"); + printf("--input_file - path to an audio file.\n"); + printf("--pattern - Hex string representing consecutive packets'" + "status.\n"); + printf("--random - Percentage of packet loss.\n"); +} + int main(int argc, char **argv) { - if (argc != 3) { - printf("Usage: cras_plc_test input.raw pl_percentage\n" - "This test only supports reading/writing files with " - "format:\n" - "- raw pcm\n" - "- 16000 sample rate\n" - "- mono channel\n" - "- S16_LE sample format\n"); + int fd; + struct stat st; + float pl_percent; + int pl_percent_set = 0; + int option_character; + int option_index = 0; + const char *input_file = NULL; + const char *pl_hex = NULL; + bool *pl_seq = NULL; + static struct option long_options[] = { + { "help", no_argument, NULL, 'h' }, + { "input", required_argument, NULL, 'i' }, + { "pattern", required_argument, NULL, 'p' }, + { "random", required_argument, NULL, 'r' }, + { NULL, 0, NULL, 0 }, + }; + + while (true) { + option_character = getopt_long(argc, argv, "i:r:p:h", + long_options, &option_index); + if (option_character == -1) + break; + switch (option_character) { + case 'h': + show_usage(); + break; + case 'i': + input_file = optarg; + break; + case 'p': + pl_hex = optarg; + break; + case 'r': + pl_percent = atof(optarg); + pl_percent_set = 1; + break; + default: + break; + } + } + + if ((!pl_percent_set && !pl_hex) || !input_file) { + show_usage(); return 1; } - plc_experiment(argv[1], atof(argv[2]), true); - plc_experiment(argv[1], atof(argv[2]), false); + fd = open(input_file, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "Cannout open input file %s\n", input_file); + return 1; + } + fstat(fd, &st); + close(fd); + if (pl_percent_set) + pl_seq = generate_pl_seq(st.st_size, pl_percent); + else if (pl_hex) + pl_seq = parse_pl_hex(st.st_size, pl_hex); + + plc_experiment(input_file, pl_seq, true); + plc_experiment(input_file, pl_seq, false); } diff --git a/cras/src/server/audio_thread.c b/cras/src/server/audio_thread.c index df713ca5..cd155e82 100644 --- a/cras/src/server/audio_thread.c +++ b/cras/src/server/audio_thread.c @@ -555,8 +555,11 @@ static void append_stream_dump_info(struct audio_debug_info *info, si->runtime_nsec = time_since.tv_nsec; } -/* Handle a message sent to the playback thread */ -static int handle_playback_thread_message(struct audio_thread *thread) +/* Handle a message sent from main thread to the audio thread. + * Returns: + * Error code when reading or sending message fails. + */ +static int handle_audio_thread_message(struct audio_thread *thread) { uint8_t buf[256]; struct audio_thread_msg *msg = (struct audio_thread_msg *)buf; @@ -711,7 +714,7 @@ static int handle_playback_thread_message(struct audio_thread *thread) err = audio_thread_send_response(thread, ret); if (err < 0) return err; - return ret; + return 0; } /* Returns the number of active streams plus the number of active devices. */ @@ -912,7 +915,7 @@ static void *audio_io_thread(void *arg) continue; if (thread->pollfds[0].revents & POLLIN) { - rc = handle_playback_thread_message(thread); + rc = handle_audio_thread_message(thread); if (rc < 0) syslog(LOG_ERR, "handle message %d", rc); } diff --git a/cras/src/server/config/cras_board_config.c b/cras/src/server/config/cras_board_config.c index d04d626b..14d3fa0c 100644 --- a/cras/src/server/config/cras_board_config.c +++ b/cras/src/server/config/cras_board_config.c @@ -13,12 +13,14 @@ static const int32_t DEFAULT_OUTPUT_BUFFER_SIZE = 512; static const int32_t AEC_SUPPORTED_DEFAULT = 0; static const int32_t AEC_GROUP_ID_DEFAULT = -1; static const int32_t BLUETOOTH_WBS_ENABLED_INI_DEFAULT = 1; +static const int32_t BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_DEFAULT = 0; #define CONFIG_NAME "board.ini" #define DEFAULT_OUTPUT_BUF_SIZE_INI_KEY "output:default_output_buffer_size" #define AEC_SUPPORTED_INI_KEY "processing:aec_supported" #define AEC_GROUP_ID_INI_KEY "processing:group_id" #define BLUETOOTH_WBS_ENABLED_INI_KEY "bluetooth:wbs_enabled" +#define BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_KEY "bluetooth:deprioritize_wbs_mic" #define UCM_IGNORE_SUFFIX_KEY "ucm:ignore_suffix" void cras_board_config_get(const char *config_path, @@ -34,6 +36,8 @@ void cras_board_config_get(const char *config_path, board_config->aec_group_id = AEC_GROUP_ID_DEFAULT; board_config->ucm_ignore_suffix = NULL; board_config->bt_wbs_enabled = BLUETOOTH_WBS_ENABLED_INI_DEFAULT; + board_config->deprioritize_bt_wbs_mic = + BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_DEFAULT; if (config_path == NULL) return; @@ -66,6 +70,12 @@ void cras_board_config_get(const char *config_path, board_config->bt_wbs_enabled = iniparser_getint( ini, ini_key, BLUETOOTH_WBS_ENABLED_INI_DEFAULT); + snprintf(ini_key, MAX_INI_KEY_LENGTH, + BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_KEY); + ini_key[MAX_INI_KEY_LENGTH] = 0; + board_config->deprioritize_bt_wbs_mic = iniparser_getint( + ini, ini_key, BLUETOOTH_DEPRIORITIZE_WBS_MIC_INI_DEFAULT); + snprintf(ini_key, MAX_INI_KEY_LENGTH, UCM_IGNORE_SUFFIX_KEY); ini_key[MAX_INI_KEY_LENGTH] = 0; ptr = iniparser_getstring(ini, ini_key, ""); diff --git a/cras/src/server/config/cras_board_config.h b/cras/src/server/config/cras_board_config.h index ed80bec5..2ecde265 100644 --- a/cras/src/server/config/cras_board_config.h +++ b/cras/src/server/config/cras_board_config.h @@ -13,6 +13,7 @@ struct cras_board_config { int32_t aec_supported; int32_t aec_group_id; int32_t bt_wbs_enabled; + int32_t deprioritize_bt_wbs_mic; char *ucm_ignore_suffix; }; diff --git a/cras/src/server/cras_a2dp_iodev.c b/cras/src/server/cras_a2dp_iodev.c index 683fa31f..6c434758 100644 --- a/cras/src/server/cras_a2dp_iodev.c +++ b/cras/src/server/cras_a2dp_iodev.c @@ -664,7 +664,7 @@ struct cras_iodev *a2dp_iodev_create(struct cras_bt_transport *transport) iodev->start = start; iodev->frames_to_play_in_sleep = frames_to_play_in_sleep; - /* Create a dummy ionode */ + /* Create an empty ionode */ node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; strcpy(node->name, iodev->info.name); @@ -684,6 +684,8 @@ struct cras_iodev *a2dp_iodev_create(struct cras_bt_transport *transport) iodev->info.max_supported_channels = (a2dp.channel_mode == SBC_CHANNEL_MODE_MONO) ? 1 : 2; + ewma_power_disable(&iodev->ewma); + return iodev; error: if (a2dpio) { diff --git a/cras/src/server/cras_alsa_helpers.c b/cras/src/server/cras_alsa_helpers.c index 4f402498..6cdc165a 100644 --- a/cras/src/server/cras_alsa_helpers.c +++ b/cras/src/server/cras_alsa_helpers.c @@ -556,7 +556,7 @@ int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format, return 0; } -int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp) +int cras_alsa_set_swparams(snd_pcm_t *handle) { int err; snd_pcm_sw_params_t *swparams; @@ -593,50 +593,7 @@ int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp) return err; } - if (*enable_htimestamp) { - /* Use MONOTONIC_RAW time-stamps. */ - err = snd_pcm_sw_params_set_tstamp_type( - handle, swparams, SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW); - if (err < 0) { - syslog(LOG_ERR, "set_tstamp_type: %s\n", - snd_strerror(err)); - return err; - } - err = snd_pcm_sw_params_set_tstamp_mode(handle, swparams, - SND_PCM_TSTAMP_ENABLE); - if (err < 0) { - syslog(LOG_ERR, "set_tstamp_mode: %s\n", - snd_strerror(err)); - return err; - } - } - - /* This hack is required because ALSA-LIB does not provide any way to - * detect whether MONOTONIC_RAW timestamps are supported by the kernel. - * In ALSA-LIB, the code checks the hardware protocol version. */ err = snd_pcm_sw_params(handle, swparams); - if (err == -EINVAL && *enable_htimestamp) { - *enable_htimestamp = 0; - syslog(LOG_WARNING, - "MONOTONIC_RAW timestamps are not supported."); - - err = snd_pcm_sw_params_set_tstamp_type( - handle, swparams, SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY); - if (err < 0) { - syslog(LOG_ERR, "set_tstamp_type: %s\n", - snd_strerror(err)); - return err; - } - err = snd_pcm_sw_params_set_tstamp_mode(handle, swparams, - SND_PCM_TSTAMP_NONE); - if (err < 0) { - syslog(LOG_ERR, "set_tstamp_mode: %s\n", - snd_strerror(err)); - return err; - } - - err = snd_pcm_sw_params(handle, swparams); - } if (err < 0) { syslog(LOG_ERR, "sw_params: %s\n", snd_strerror(err)); diff --git a/cras/src/server/cras_alsa_helpers.h b/cras/src/server/cras_alsa_helpers.h index 38976749..01a42aea 100644 --- a/cras/src/server/cras_alsa_helpers.h +++ b/cras/src/server/cras_alsa_helpers.h @@ -135,13 +135,10 @@ int cras_alsa_set_hwparams(snd_pcm_t *handle, struct cras_audio_format *format, /* Sets up the swparams to alsa. * Args: * handle - The open PCM to configure. - * enable_htimestamp - If non-zero, enable and configure hardware timestamps, - * updated to reflect whether MONOTONIC RAW htimestamps - * are supported by the kernel implementation. * Returns: * 0 on success, negative error on failure. */ -int cras_alsa_set_swparams(snd_pcm_t *handle, int *enable_htimestamp); +int cras_alsa_set_swparams(snd_pcm_t *handle); /* Get the number of used frames in the alsa buffer. * diff --git a/cras/src/server/cras_alsa_io.c b/cras/src/server/cras_alsa_io.c index 792305bb..da4ef630 100644 --- a/cras/src/server/cras_alsa_io.c +++ b/cras/src/server/cras_alsa_io.c @@ -113,7 +113,6 @@ struct alsa_input_node { * is_first - true if this is the first iodev on the card. * fully_specified - true if this device and it's nodes were fully specified. * That is, don't automatically create nodes for it. - * enable_htimestamp - True when the device's htimestamp is used. * handle - Handle to the opened ALSA device. * num_severe_underruns - Number of times we have run out of data badly. Unlike num_underruns which records for the duration @@ -148,7 +147,6 @@ struct alsa_io { enum CRAS_ALSA_CARD_TYPE card_type; int is_first; int fully_specified; - int enable_htimestamp; snd_pcm_t *handle; unsigned int num_severe_underruns; snd_pcm_stream_t alsa_stream; @@ -331,8 +329,7 @@ static int frames_queued(const struct cras_iodev *iodev, aio->num_severe_underruns++; return rc; } - if (!aio->enable_htimestamp) - clock_gettime(CLOCK_MONOTONIC_RAW, tstamp); + clock_gettime(CLOCK_MONOTONIC_RAW, tstamp); if (iodev->direction == CRAS_STREAM_INPUT) return (int)frames; @@ -374,7 +371,7 @@ static int close_dev(struct cras_iodev *iodev) return 0; } -static int dummy_hotword_cb(void *arg, int revents) +static int empty_hotword_cb(void *arg, int revents) { /* Only need this once. */ struct alsa_io *aio = (struct alsa_io *)arg; @@ -449,7 +446,7 @@ static int configure_dev(struct cras_iodev *iodev) return rc; /* Configure software params. */ - rc = cras_alsa_set_swparams(aio->handle, &aio->enable_htimestamp); + rc = cras_alsa_set_swparams(aio->handle); if (rc < 0) return rc; @@ -490,7 +487,7 @@ static int configure_dev(struct cras_iodev *iodev) if (aio->poll_fd >= 0) audio_thread_add_events_callback( - aio->poll_fd, dummy_hotword_cb, aio, POLLIN); + aio->poll_fd, empty_hotword_cb, aio, POLLIN); } /* Capture starts right away, playback will wait for samples. */ @@ -1549,7 +1546,7 @@ static void jack_output_plug_event(const struct cras_alsa_jack *jack, * For HDMI plug event cases, update max supported channels according * to the current active node. */ - if (node->base.type == CRAS_NODE_TYPE_HDMI) + if (node->base.type == CRAS_NODE_TYPE_HDMI && plugged) update_max_supported_channels(&aio->base); } @@ -2134,8 +2131,6 @@ alsa_iodev_create(size_t card_index, const char *card_name, size_t device_index, rc = ucm_get_min_buffer_level(ucm, &level); if (!rc && direction == CRAS_STREAM_OUTPUT) iodev->min_buffer_level = level; - - aio->enable_htimestamp = ucm_get_enable_htimestamp_flag(ucm); } set_iodev_name(iodev, card_name, dev_name, card_index, device_index, @@ -2355,8 +2350,11 @@ void alsa_iodev_ucm_complete_init(struct cras_iodev *iodev) set_default_hotword_model(iodev); + node = iodev->active_node; + /* Record max supported channels into cras_iodev_info. */ - update_max_supported_channels(iodev); + if (node && node->plugged) + update_max_supported_channels(iodev); } void alsa_iodev_destroy(struct cras_iodev *iodev) diff --git a/cras/src/server/cras_alsa_jack.c b/cras/src/server/cras_alsa_jack.c index 52a227e7..6d4d7bf5 100644 --- a/cras/src/server/cras_alsa_jack.c +++ b/cras/src/server/cras_alsa_jack.c @@ -734,6 +734,16 @@ static snd_hctl_elem_t *find_eld_control_by_dev_index(snd_hctl_t *hctl, return snd_hctl_find_elem(hctl, elem_id); } +/* For non-gpio jack, check if it's of type hdmi/dp by + * matching jack name. */ +static int is_jack_hdmi_dp(const char *jack_name) +{ + // TODO(hychao): Use the information provided in UCM instead of + // name matching. + static const char *hdmi_dp = "HDMI"; + return !!strstr(jack_name, hdmi_dp); +} + /* Find GPIO jacks for this jack_list. * Args: * jack_list - Jack list to add to. @@ -772,8 +782,9 @@ static int find_gpio_jacks(struct cras_alsa_jack_list *jack_list, if (result_jack) { *result_jack = data.result_jack; - /* Find ELD control for HDMI/DP gpio jack. */ - if (*result_jack) + /* Find ELD control only for HDMI/DP gpio jack. */ + if (*result_jack && + is_jack_hdmi_dp((*result_jack)->gpio.device_name)) (*result_jack)->eld_control = find_eld_control_by_dev_index( jack_list->hctl, @@ -833,14 +844,6 @@ static unsigned int hctl_jack_device_index(const char *name) return (unsigned int)device_index; } -/* For non-gpio jack, check if it's of type hdmi/dp by - * matching jack name. */ -static int is_jack_hdmi_dp(const char *jack_name) -{ - static const char *hdmi_dp = "HDMI/DP"; - return strncmp(jack_name, hdmi_dp, strlen(hdmi_dp)) == 0; -} - /* Checks if the given control name is in the supplied list of possible jack * control base names. */ static int is_jack_control_in_list(const char *const *list, diff --git a/cras/src/server/cras_alsa_plugin_io.c b/cras/src/server/cras_alsa_plugin_io.c index 9c557a40..32c1ae11 100644 --- a/cras/src/server/cras_alsa_plugin_io.c +++ b/cras/src/server/cras_alsa_plugin_io.c @@ -24,9 +24,9 @@ #define PLUGIN_KEY_PCM "pcm" #define PLUGIN_KEY_CARD "card" -#define DUMMY_USB_VID 0x00 -#define DUMMY_USB_PID 0x00 -#define DUMMY_USB_SERIAL_NUMBER "serial-number-not-used" +#define NULL_USB_VID 0x00 +#define NULL_USB_PID 0x00 +#define NULL_USB_SERIAL_NUMBER "serial-number-not-used" struct hctl_poll_fd { int fd; @@ -159,12 +159,11 @@ void alsa_plugin_io_create(enum CRAS_STREAM_DIRECTION direction, "section %s mixer_name %s", section->name, section->mixer_name); } - plugin->iodev = - alsa_iodev_create(0, card_name, 0, pcm_name, "", "", - ALSA_CARD_TYPE_USB, 1, /* is first */ - plugin->mixer, NULL, plugin->ucm, - plugin->hctl, direction, DUMMY_USB_VID, - DUMMY_USB_PID, DUMMY_USB_SERIAL_NUMBER); + plugin->iodev = alsa_iodev_create(0, card_name, 0, pcm_name, "", "", + ALSA_CARD_TYPE_USB, 1, /* is first */ + plugin->mixer, NULL, plugin->ucm, + plugin->hctl, direction, NULL_USB_VID, + NULL_USB_PID, NULL_USB_SERIAL_NUMBER); DL_FOREACH (ucm_sections, section) { if (section->dir != plugin->iodev->direction) diff --git a/cras/src/server/cras_alsa_ucm.c b/cras/src/server/cras_alsa_ucm.c index 3782cb24..9759a50f 100644 --- a/cras/src/server/cras_alsa_ucm.c +++ b/cras/src/server/cras_alsa_ucm.c @@ -57,7 +57,6 @@ static const char default_node_gain[] = "DefaultNodeGain"; static const char hotword_model_prefix[] = "Hotword Model"; static const char fully_specified_ucm_var[] = "FullySpecifiedUCM"; static const char main_volume_names[] = "MainVolumeNames"; -static const char enable_htimestamp_var[] = "EnableHtimestamp"; /* Use case verbs corresponding to CRAS_STREAM_TYPE. */ static const char *use_case_verbs[] = { @@ -1121,15 +1120,3 @@ unsigned int ucm_get_dma_period_for_dev(struct cras_use_case_mgr *mgr, return 0; return value; } - -unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr *mgr) -{ - char *flag; - int ret = 0; - flag = ucm_get_flag(mgr, enable_htimestamp_var); - if (!flag) - return 0; - ret = !strcmp(flag, "1"); - free(flag); - return ret; -} diff --git a/cras/src/server/cras_alsa_ucm.h b/cras/src/server/cras_alsa_ucm.h index 48dc6550..99a8b440 100644 --- a/cras/src/server/cras_alsa_ucm.h +++ b/cras/src/server/cras_alsa_ucm.h @@ -472,12 +472,4 @@ unsigned int ucm_get_dma_period_for_dev(struct cras_use_case_mgr *mgr, */ unsigned int ucm_get_optimize_no_stream_flag(struct cras_use_case_mgr *mgr); -/* Retrieve the flag that enables use of htimestamp. - * Args: - * mgr - The cras_use_case_mgr pointer returned from alsa_ucm_create. - * Returns: - * 1 if the flag is enabled. 0 otherwise. - */ -unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr *mgr); - #endif /* _CRAS_ALSA_UCM_H */ diff --git a/cras/src/server/cras_apm_list.h b/cras/src/server/cras_apm_list.h index b9a7fe2f..7a36ceae 100644 --- a/cras/src/server/cras_apm_list.h +++ b/cras/src/server/cras_apm_list.h @@ -162,7 +162,7 @@ void cras_apm_list_set_aec_dump(struct cras_apm_list *list, void *dev_ptr, /* * If webrtc audio processing library is not available then define all - * cras_apm_list functions as dummy. As long as cras_apm_list_add returns + * cras_apm_list functions as empty. As long as cras_apm_list_add returns * NULL, non of the other functions should be called. */ static inline int cras_apm_list_init(const char *device_config_dir) diff --git a/cras/src/server/cras_bt_device.c b/cras/src/server/cras_bt_device.c index 0607ac8e..70c87479 100644 --- a/cras/src/server/cras_bt_device.c +++ b/cras/src/server/cras_bt_device.c @@ -61,9 +61,7 @@ static const unsigned int CONN_WATCH_MAX_RETRIES = 30; static const unsigned int SCO_SUSPEND_DELAY_MS = 5000; static const unsigned int CRAS_SUPPORTED_PROFILES = - CRAS_BT_DEVICE_PROFILE_A2DP_SINK | - CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE | - CRAS_BT_DEVICE_PROFILE_HSP_AUDIOGATEWAY; + CRAS_BT_DEVICE_PROFILE_A2DP_SINK | CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE; /* Object to represent a general bluetooth device, and used to * associate with some CRAS modules if it supports audio. @@ -79,6 +77,8 @@ static const unsigned int CRAS_SUPPORTED_PROFILES = * connected - If this devices is connected. * connected_profiles - OR'ed all connected audio profiles. * profiles - OR'ed by all audio profiles this device supports. + * hidden_profiles - OR'ed by all audio profiles this device actually + * supports but is not scanned by BlueZ. * bt_iodevs - The pointer to the cras_iodevs of this device. * active_profile - The flag to indicate the active audio profile this * device is currently using. @@ -102,8 +102,9 @@ struct cras_bt_device { int paired; int trusted; int connected; - enum cras_bt_device_profile connected_profiles; - enum cras_bt_device_profile profiles; + unsigned int connected_profiles; + unsigned int profiles; + unsigned int hidden_profiles; struct cras_iodev *bt_iodevs[CRAS_NUM_DIRECTIONS]; unsigned int active_profile; int use_hardware_volume; @@ -512,14 +513,18 @@ int cras_bt_device_audio_gateway_initialized(struct cras_bt_device *device) * behavior on qualification test software. */ if (!cras_bt_device_supports_profile( device, CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE)) { - cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); + unsigned int profiles = + device->profiles | CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE; + cras_bt_device_set_supported_profiles(device, profiles); + device->hidden_profiles |= CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE; bt_device_conn_watch_cb(NULL, (void *)device); } return 0; } -int cras_bt_device_get_active_profile(const struct cras_bt_device *device) +unsigned int +cras_bt_device_get_active_profile(const struct cras_bt_device *device) { return device->active_profile; } @@ -569,6 +574,19 @@ static void cras_bt_device_log_profile(const struct cras_bt_device *device, } } +static void cras_bt_device_log_profiles(const struct cras_bt_device *device, + unsigned int profiles) +{ + unsigned int profile; + + while (profiles) { + /* Get the LSB of profiles */ + profile = profiles & -profiles; + cras_bt_device_log_profile(device, profile); + profiles ^= profile; + } +} + static int cras_bt_device_is_profile_connected(const struct cras_bt_device *device, enum cras_bt_device_profile profile) @@ -708,7 +726,7 @@ void cras_bt_device_set_connected(struct cras_bt_device *device, int value) void cras_bt_device_notify_profile_dropped(struct cras_bt_device *device, enum cras_bt_device_profile profile) { - device->connected_profiles &= !profile; + device->connected_profiles &= ~profile; /* Do nothing if device already disconnected. */ if (!device->connected) @@ -723,37 +741,33 @@ void cras_bt_device_notify_profile_dropped(struct cras_bt_device *device, UNEXPECTED_PROFILE_DROP); } -/* - * Check if the uuid is of a new audio profile that isn't listed - * as supported by device. +/* Refresh the list of known supported profiles. * Args: - * device - The BT device holding supported profiles bitmap. - * uuid - UUID string from the device properties notified by BlueZ. + * device - The BT device holding scanned profiles bitmap. + * profiles - The OR'ed profiles the device claims to support as is notified + * by BlueZ. * Returns: - * True if uuid is a new audio profiles not already supported by device. + * The OR'ed profiles that are both supported by Cras and isn't previously + * supported by the device. */ -int cras_bt_device_add_supported_profiles(struct cras_bt_device *device, - const char *uuid) +int cras_bt_device_set_supported_profiles(struct cras_bt_device *device, + unsigned int profiles) { - enum cras_bt_device_profile profile = - cras_bt_device_profile_from_uuid(uuid); - - if (profile == 0) + /* Do nothing if no new profiles. */ + if ((device->profiles & profiles) == profiles) return 0; - /* Do nothing if this profile is not new. */ - if (device->profiles & profile) - return 0; + unsigned int new_profiles = profiles & ~device->profiles; /* Log this event as we might need to re-intialize the BT audio nodes * if new audio profile is reported for already connected device. */ - if (device->connected && (profile & CRAS_SUPPORTED_PROFILES)) + if (device->connected && (new_profiles & CRAS_SUPPORTED_PROFILES)) BTLOG(btlog, BT_NEW_AUDIO_PROFILE_AFTER_CONNECT, - device->profiles, profile); - device->profiles |= profile; - cras_bt_device_log_profile(device, profile); + device->profiles, new_profiles); + cras_bt_device_log_profiles(device, new_profiles); + device->profiles = profiles | device->hidden_profiles; - return (profile & CRAS_SUPPORTED_PROFILES); + return (new_profiles & CRAS_SUPPORTED_PROFILES); } void cras_bt_device_update_properties(struct cras_bt_device *device, @@ -821,6 +835,7 @@ void cras_bt_device_update_properties(struct cras_bt_device *device, "as") == 0 && strcmp(key, "UUIDs") == 0) { DBusMessageIter uuid_array_iter; + unsigned int profiles = 0; dbus_message_iter_recurse(&variant_iter, &uuid_array_iter); @@ -830,22 +845,21 @@ void cras_bt_device_update_properties(struct cras_bt_device *device, dbus_message_iter_get_basic(&uuid_array_iter, &uuid); - - /* - * If updated properties includes new audio - * profile, and device is connected, we need - * to start connection watcher. This is needed - * because on some bluetooth device, supported - * profiles do not present when device - * interface is added and they are updated - * later. - */ - if (cras_bt_device_add_supported_profiles( - device, uuid)) - watch_needed = device->connected; + profiles |= + cras_bt_device_profile_from_uuid(uuid); dbus_message_iter_next(&uuid_array_iter); } + + /* If updated properties includes new audio profile and + * device is connected, we need to start connection + * watcher. This is needed because on some bluetooth + * devices, supported profiles do not present when + * device interface is added and they are updated later. + */ + if (cras_bt_device_set_supported_profiles(device, + profiles)) + watch_needed = device->connected; } dbus_message_iter_next(properties_array_iter); @@ -876,7 +890,7 @@ void cras_bt_device_update_properties(struct cras_bt_device *device, } else if (strcmp(key, "Connected") == 0) { device->connected = 0; } else if (strcmp(key, "UUIDs") == 0) { - device->profiles = 0; + device->profiles = device->hidden_profiles; } dbus_message_iter_next(invalidated_array_iter); diff --git a/cras/src/server/cras_bt_device.h b/cras/src/server/cras_bt_device.h index 3800927e..4202bc93 100644 --- a/cras/src/server/cras_bt_device.h +++ b/cras/src/server/cras_bt_device.h @@ -63,8 +63,8 @@ void cras_bt_device_update_properties(struct cras_bt_device *device, DBusMessageIter *invalidated_array_iter); /* Updates the supported profiles on dev. Expose for unit test. */ -int cras_bt_device_add_supported_profiles(struct cras_bt_device *device, - const char *uuid); +int cras_bt_device_set_supported_profiles(struct cras_bt_device *device, + unsigned int profiles); /* Checks if profile is claimed supported by the device. */ int cras_bt_device_supports_profile(const struct cras_bt_device *device, @@ -133,7 +133,8 @@ void cras_bt_device_rm_iodev(struct cras_bt_device *device, struct cras_iodev *iodev); /* Gets the active profile of the bt device. */ -int cras_bt_device_get_active_profile(const struct cras_bt_device *device); +unsigned int +cras_bt_device_get_active_profile(const struct cras_bt_device *device); /* Sets the active profile of the bt device. */ void cras_bt_device_set_active_profile(struct cras_bt_device *device, diff --git a/cras/src/server/cras_bt_io.c b/cras/src/server/cras_bt_io.c index 3cffe148..9f5c2f79 100644 --- a/cras/src/server/cras_bt_io.c +++ b/cras/src/server/cras_bt_io.c @@ -518,7 +518,7 @@ struct cras_iodev *cras_bt_io_create(struct cras_bt_device *device, iodev->set_volume = set_bt_volume; } - /* Create the dummy node so it's the only node exposed to UI, and + /* Create the fake node so it's the only node exposed to UI, and * point it to the first profile dev. */ active = (struct bt_node *)calloc(1, sizeof(*active)); if (!active) diff --git a/cras/src/server/cras_control_rclient.c b/cras/src/server/cras_control_rclient.c index 3906a23b..cd0c4d3b 100644 --- a/cras/src/server/cras_control_rclient.c +++ b/cras/src/server/cras_control_rclient.c @@ -15,6 +15,7 @@ #include "cras_dsp.h" #include "cras_iodev.h" #include "cras_iodev_list.h" +#include "cras_hfp_ag_profile.h" #include "cras_main_thread_log.h" #include "cras_messages.h" #include "cras_observer.h" @@ -298,15 +299,11 @@ static int ccr_handle_message_from_client(struct cras_rclient *client, switch (msg->id) { case CRAS_SERVER_CONNECT_STREAM: { int client_shm_fd = num_fds > 1 ? fds[1] : -1; - struct cras_connect_message cmsg; if (MSG_LEN_VALID(msg, struct cras_connect_message)) { rclient_handle_client_stream_connect( client, (const struct cras_connect_message *)msg, fd, client_shm_fd); - } else if (!convert_connect_message_old(msg, &cmsg)) { - rclient_handle_client_stream_connect(client, &cmsg, fd, - client_shm_fd); } else { return -EINVAL; } @@ -422,10 +419,15 @@ static int ccr_handle_message_from_client(struct cras_rclient *client, state = cras_system_state_get_no_lock(); #ifdef CRAS_DBUS memcpy(&state->bt_debug_info.bt_log, btlog, - sizeof(struct cras_bt_debug_info)); + sizeof(struct cras_bt_event_log)); + memcpy(&state->bt_debug_info.wbs_logger, + cras_hfp_ag_get_wbs_logger(), + sizeof(struct packet_status_logger)); #else memset(&state->bt_debug_info.bt_log, 0, sizeof(struct cras_bt_debug_info)); + memset(&state->bt_debug_info.wbs_logger, 0, + sizeof(struct packet_status_logger)); #endif cras_fill_client_audio_debug_info_ready(&msg); diff --git a/cras/src/server/cras_dbus_control.c b/cras/src/server/cras_dbus_control.c index 628ec221..3479c3c6 100644 --- a/cras/src/server/cras_dbus_control.c +++ b/cras/src/server/cras_dbus_control.c @@ -75,6 +75,9 @@ " <method name=\"GetSystemAecGroupId\">\n" \ " <arg name=\"group_id\" type=\"i\" direction=\"out\"/>\n" \ " </method>\n" \ + " <method name=\"GetDeprioritizeBtWbsMic\">\n" \ + " <arg name=\"deprioritized\" type=\"b\" direction=\"out\"/>\n" \ + " </method>\n" \ " <method name=\"SetActiveOutputNode\">\n" \ " <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \ " </method>\n" \ @@ -105,6 +108,9 @@ " <method name=\"GetNumberOfActiveInputStreams\">\n" \ " <arg name=\"num\" type=\"i\" direction=\"out\"/>\n" \ " </method>\n" \ + " <method name=\"GetNumberOfInputStreamsWithPermission\">\n" \ + " <arg name=\"num\" type=\"a{sv}\" direction=\"out\"/>\n" \ + " </method>\n" \ " <method name=\"SetGlobalOutputChannelRemix\">\n" \ " <arg name=\"num_channels\" type=\"i\" direction=\"in\"/>\n" \ " <arg name=\"coefficient\" type=\"ad\" direction=\"in\"/>\n" \ @@ -671,6 +677,27 @@ static DBusHandlerResult handle_get_system_aec_group_id(DBusConnection *conn, } static DBusHandlerResult +handle_get_deprioritize_bt_wbs_mic(DBusConnection *conn, DBusMessage *message, + void *arg) +{ + DBusMessage *reply; + dbus_uint32_t serial = 0; + dbus_bool_t deprioritized; + + reply = dbus_message_new_method_return(message); + + deprioritized = cras_system_get_deprioritize_bt_wbs_mic(); + dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &deprioritized, + DBUS_TYPE_INVALID); + + dbus_connection_send(conn, reply, &serial); + + dbus_message_unref(reply); + + return DBUS_HANDLER_RESULT_HANDLED; +} + +static DBusHandlerResult handle_set_active_node(DBusConnection *conn, DBusMessage *message, void *arg, enum CRAS_STREAM_DIRECTION direction) { @@ -784,6 +811,65 @@ handle_get_num_active_streams_use_output_hw(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } +static bool append_num_input_streams_with_permission( + DBusMessage *message, uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) +{ + DBusMessageIter array; + DBusMessageIter dict; + unsigned type; + + dbus_message_iter_init_append(message, &array); + for (type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) { + const char *client_type_str = cras_client_type_str(type); + if (!is_utf8_string(client_type_str)) { + syslog(LOG_ERR, + "Non-utf8 clinet_type_str '%s' cannot be sent " + "via dbus", + client_type_str); + client_type_str = ""; + } + + if (!dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY, + "{sv}", &dict)) + return false; + if (!append_key_value(&dict, "ClientType", DBUS_TYPE_STRING, + DBUS_TYPE_STRING_AS_STRING, + &client_type_str)) + return false; + if (!append_key_value(&dict, "NumStreamsWithPermission", + DBUS_TYPE_UINT32, + DBUS_TYPE_UINT32_AS_STRING, + &num_input_streams[type])) + return false; + if (!dbus_message_iter_close_container(&array, &dict)) + return false; + } + return true; +} + +static DBusHandlerResult +handle_get_num_input_streams_with_permission(DBusConnection *conn, + DBusMessage *message, void *arg) +{ + DBusMessage *reply; + dbus_uint32_t serial = 0; + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE] = {}; + + reply = dbus_message_new_method_return(message); + + cras_system_state_get_input_streams_with_permission(num_input_streams); + if (!append_num_input_streams_with_permission(reply, num_input_streams)) + goto error; + + dbus_connection_send(conn, reply, &serial); + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_HANDLED; + +error: + dbus_message_unref(reply); + return DBUS_HANDLER_RESULT_NEED_MEMORY; +} + static DBusHandlerResult handle_set_global_output_channel_remix(DBusConnection *conn, DBusMessage *message, void *arg) @@ -1052,6 +1138,9 @@ static DBusHandlerResult handle_control_message(DBusConnection *conn, "GetSystemAecGroupId")) { return handle_get_system_aec_group_id(conn, message, arg); } else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE, + "GetDeprioritizeBtWbsMic")) { + return handle_get_deprioritize_bt_wbs_mic(conn, message, arg); + } else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE, "SetActiveOutputNode")) { return handle_set_active_node(conn, message, arg, CRAS_STREAM_OUTPUT); @@ -1088,6 +1177,11 @@ static DBusHandlerResult handle_control_message(DBusConnection *conn, arg); } else if (dbus_message_is_method_call( message, CRAS_CONTROL_INTERFACE, + "GetNumberOfInputStreamsWithPermission")) { + return handle_get_num_input_streams_with_permission( + conn, message, arg); + } else if (dbus_message_is_method_call( + message, CRAS_CONTROL_INTERFACE, "GetNumberOfActiveOutputStreams")) { return handle_get_num_active_streams_use_output_hw( conn, message, arg); @@ -1315,6 +1409,25 @@ static void signal_num_active_streams_changed(void *context, dbus_message_unref(msg); } +static void signal_num_input_streams_with_permission_changed( + void *context, uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) +{ + struct cras_dbus_control *control = (struct cras_dbus_control *)context; + dbus_uint32_t serial = 0; + DBusMessage *msg; + + msg = create_dbus_message("NumberOfInputStreamsWithPermissionChanged"); + if (!msg) + return; + + if (!append_num_input_streams_with_permission(msg, num_input_streams)) + goto error; + + dbus_connection_send(control->conn, msg, &serial); +error: + dbus_message_unref(msg); +} + static void signal_hotword_triggered(void *context, int64_t tv_sec, int64_t tv_nsec) { @@ -1399,6 +1512,8 @@ void cras_dbus_control_start(DBusConnection *conn) observer_ops.capture_mute_changed = signal_capture_mute; observer_ops.num_active_streams_changed = signal_num_active_streams_changed; + observer_ops.num_input_streams_with_permission_changed = + signal_num_input_streams_with_permission_changed; observer_ops.nodes_changed = signal_nodes_changed; observer_ops.active_node_changed = signal_active_node_changed; observer_ops.input_node_gain_changed = signal_node_capture_gain_changed; diff --git a/cras/src/server/cras_dsp.c b/cras/src/server/cras_dsp.c index d0b26264..9c4cc7b5 100644 --- a/cras/src/server/cras_dsp.c +++ b/cras/src/server/cras_dsp.c @@ -208,15 +208,15 @@ void cras_dsp_load_pipeline(struct cras_dsp_context *ctx) cmd_load_pipeline(ctx, global_ini); } -void cras_dsp_load_dummy_pipeline(struct cras_dsp_context *ctx, - unsigned int num_channels) +void cras_dsp_load_mock_pipeline(struct cras_dsp_context *ctx, + unsigned int num_channels) { - struct ini *dummy_ini; - dummy_ini = create_dummy_ini(ctx->purpose, num_channels); - if (dummy_ini == NULL) - syslog(LOG_ERR, "Failed to create dummy ini"); + struct ini *mock_ini; + mock_ini = create_mock_ini(ctx->purpose, num_channels); + if (mock_ini == NULL) + syslog(LOG_ERR, "Failed to create mock ini"); else - cmd_load_pipeline(ctx, dummy_ini); + cmd_load_pipeline(ctx, mock_ini); } struct pipeline *cras_dsp_get_pipeline(struct cras_dsp_context *ctx) diff --git a/cras/src/server/cras_dsp.h b/cras/src/server/cras_dsp.h index 9a72f42b..366e2e67 100644 --- a/cras/src/server/cras_dsp.h +++ b/cras/src/server/cras_dsp.h @@ -54,11 +54,11 @@ void cras_dsp_set_variable_boolean(struct cras_dsp_context *ctx, * blocking the audio thread. */ void cras_dsp_load_pipeline(struct cras_dsp_context *ctx); -/* Loads a dummy pipeline of source directly connects to sink, of given +/* Loads a mock pipeline of source directly connects to sink, of given * number of channels. */ -void cras_dsp_load_dummy_pipeline(struct cras_dsp_context *ctx, - unsigned int num_channels); +void cras_dsp_load_mock_pipeline(struct cras_dsp_context *ctx, + unsigned int num_channels); /* Locks the pipeline in the context for access. Returns NULL if the * pipeline is still being loaded or cannot be loaded. */ diff --git a/cras/src/server/cras_dsp_ini.c b/cras/src/server/cras_dsp_ini.c index 0b844d16..a331acf8 100644 --- a/cras/src/server/cras_dsp_ini.c +++ b/cras/src/server/cras_dsp_ini.c @@ -11,7 +11,7 @@ #define MAX_NR_PORT 128 /* the max number of ports for a plugin */ #define MAX_PORT_NAME_LENGTH 20 /* names like "output_32" */ -#define MAX_DUMMY_INI_CH 20 /* Max number of channels to create dummy ini */ +#define MAX_MOCK_INI_CH 20 /* Max number of channels to create mock ini */ /* Format of the ini file (See dsp.ini.sample for an example). @@ -305,22 +305,21 @@ static int insert_swap_lr_plugin(struct ini *ini) return 0; } -struct ini *create_dummy_ini(const char *purpose, unsigned int num_channels) +struct ini *create_mock_ini(const char *purpose, unsigned int num_channels) { - static char dummy_flow_names[MAX_DUMMY_INI_CH][9] = { - "{tmp:0}", "{tmp:1}", "{tmp:2}", "{tmp:3}", - "{tmp:4}", "{tmp:5}", "{tmp:6}", "{tmp:7}", - "{tmp:8}", "{tmp:9}", "{tmp:10}", "{tmp:11}", - "{tmp:12}", "{tmp:13}", "{tmp:14}", "{tmp:15}", - "{tmp:16}", "{tmp:17}", "{tmp:18}", "{tmp:19}", + static char mock_flow_names[MAX_MOCK_INI_CH][9] = { + "{tmp:0}", "{tmp:1}", "{tmp:2}", "{tmp:3}", "{tmp:4}", + "{tmp:5}", "{tmp:6}", "{tmp:7}", "{tmp:8}", "{tmp:9}", + "{tmp:10}", "{tmp:11}", "{tmp:12}", "{tmp:13}", "{tmp:14}", + "{tmp:15}", "{tmp:16}", "{tmp:17}", "{tmp:18}", "{tmp:19}", }; struct ini *ini; struct plugin *source, *sink; - int tmp_flow_ids[MAX_DUMMY_INI_CH]; + int tmp_flow_ids[MAX_MOCK_INI_CH]; int i; - if (num_channels > MAX_DUMMY_INI_CH) { - syslog(LOG_ERR, "Unable to create %u channels of dummy ini", + if (num_channels > MAX_MOCK_INI_CH) { + syslog(LOG_ERR, "Unable to create %u channels of mock ini", num_channels); return NULL; } @@ -332,7 +331,7 @@ struct ini *create_dummy_ini(const char *purpose, unsigned int num_channels) } for (i = 0; i < num_channels; i++) - tmp_flow_ids[i] = add_new_flow(ini, dummy_flow_names[i]); + tmp_flow_ids[i] = add_new_flow(ini, mock_flow_names[i]); source = ARRAY_APPEND_ZERO(&ini->plugins); source->title = "source"; diff --git a/cras/src/server/cras_dsp_ini.h b/cras/src/server/cras_dsp_ini.h index 51deefd6..c839d4b0 100644 --- a/cras/src/server/cras_dsp_ini.h +++ b/cras/src/server/cras_dsp_ini.h @@ -71,7 +71,7 @@ struct ini { }; /* - * Creates a dummy ini structure equivalent to: + * Creates a mock ini structure equivalent to: * * [src] * out0={tmp:0} @@ -86,7 +86,7 @@ struct ini { * The caller of this function is responsible to free the returned * ini by calling cras_dsp_ini_free(). */ -struct ini *create_dummy_ini(const char *purpose, unsigned int num_channels); +struct ini *create_mock_ini(const char *purpose, unsigned int num_channels); /* Reads the ini file into the ini structure */ struct ini *cras_dsp_ini_create(const char *ini_filename); diff --git a/cras/src/server/cras_empty_iodev.c b/cras/src/server/cras_empty_iodev.c index 76eab6c1..3471c756 100644 --- a/cras/src/server/cras_empty_iodev.c +++ b/cras/src/server/cras_empty_iodev.c @@ -199,7 +199,7 @@ struct cras_iodev *empty_iodev_create(enum CRAS_STREAM_DIRECTION direction, iodev->update_active_node = update_active_node; iodev->no_stream = cras_iodev_default_no_stream_playback; - /* Create a dummy ionode */ + /* Create an empty ionode */ node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; node->type = node_type; diff --git a/cras/src/server/cras_fmt_conv.c b/cras/src/server/cras_fmt_conv.c index 478452d4..509db1eb 100644 --- a/cras/src/server/cras_fmt_conv.c +++ b/cras/src/server/cras_fmt_conv.c @@ -9,6 +9,7 @@ #include <syslog.h> #include <endian.h> #include <limits.h> +#include <math.h> #include "cras_fmt_conv.h" #include "cras_fmt_conv_ops.h" @@ -58,21 +59,35 @@ static int is_channel_layout_equal(const struct cras_audio_format *a, return 1; } -static void normalize_buf(float *buf, size_t size) +/* + * Calculates the normalize_factor abs_sum(ci) from given coefficients. + * Since sum(ci / abs_sum(ci)) <= 1, this could prevent sample overflow while + * upmixing or downmixing. + */ +static float normalize_factor(float *buf, size_t n) { int i; - float squre_sum = 0.0; - for (i = 0; i < size; i++) - squre_sum += buf[i] * buf[i]; + float abs_sum = 0.0; + for (i = 0; i < n; i++) + abs_sum += fabs(buf[i]); - if (squre_sum == 0.0) - return; + return 1.0 / abs_sum; +} - for (i = 0; i < size; i++) - buf[i] /= squre_sum; +/* + * Normalize all channels with the same factor to maintain + * the energy ratio between original channels. + */ +static void normalize(float **mtx, size_t m, size_t n, float factor) +{ + int i, j; + for (i = 0; i < m; i++) + for (j = 0; j < n; j++) + mtx[i][j] *= factor; } -/* Populates the down mix matrix by rules: +/* + * Populates the down mix matrix by rules: * 1. Front/side left(right) channel will mix to left(right) of * full scale. * 2. Center and LFE will be split equally to left and right. @@ -106,9 +121,46 @@ static void surround51_to_stereo_downmix_mtx(float **mtx, mtx[STEREO_L][layout[CRAS_CH_LFE]] = 0.707; mtx[STEREO_R][layout[CRAS_CH_LFE]] = 0.707; } + normalize(mtx, 2, 6, normalize_factor(mtx[STEREO_L], 6)); +} + +/* Populates the down mix matrix by rules: + * 1. Front left(right) channel will mix to the front left(right) of + * full scale. + * 2. Rear and side left(right) channel will mix to the rear left(right) of + * full scale. + * 3. Center will be split equally to the front left and right. + * 4. LFE will be split equally to the other channels. + */ +static void surround51_to_quad_downmix_mtx(float **mtx, + int8_t layout[CRAS_CH_MAX]) +{ + if (layout[CRAS_CH_FL] != -1 && layout[CRAS_CH_FR] != -1) { + mtx[CRAS_CH_FL][layout[CRAS_CH_FL]] = 1.0; + mtx[CRAS_CH_FR][layout[CRAS_CH_FR]] = 1.0; + } + if (layout[CRAS_CH_RL] != -1 && layout[CRAS_CH_RR] != -1) { + mtx[CRAS_CH_RL][layout[CRAS_CH_RL]] = 1.0; + mtx[CRAS_CH_RR][layout[CRAS_CH_RR]] = 1.0; + } + if (layout[CRAS_CH_SL] != -1 && layout[CRAS_CH_SR] != -1) { + mtx[CRAS_CH_RL][layout[CRAS_CH_SL]] = 1.0; + mtx[CRAS_CH_RR][layout[CRAS_CH_SR]] = 1.0; + } + if (layout[CRAS_CH_FC] != -1) { + /* Split 1/2 power to the front L/R */ + mtx[CRAS_CH_FL][layout[CRAS_CH_FC]] = 0.707; + mtx[CRAS_CH_FR][layout[CRAS_CH_FC]] = 0.707; + } + if (layout[CRAS_CH_LFE] != -1) { + /* Split 1/4 power to the other channel */ + mtx[CRAS_CH_FL][layout[CRAS_CH_LFE]] = 0.5; + mtx[CRAS_CH_FR][layout[CRAS_CH_LFE]] = 0.5; + mtx[CRAS_CH_RL][layout[CRAS_CH_LFE]] = 0.5; + mtx[CRAS_CH_RR][layout[CRAS_CH_LFE]] = 0.5; + } - normalize_buf(mtx[STEREO_L], 6); - normalize_buf(mtx[STEREO_R], 6); + normalize(mtx, 4, 6, normalize_factor(mtx[CRAS_CH_FL], 6)); } static int is_supported_format(const struct cras_audio_format *fmt) @@ -170,6 +222,12 @@ static size_t _51_to_stereo(struct cras_fmt_conv *conv, const uint8_t *in, return s16_51_to_stereo(in, in_frames, out); } +static size_t _51_to_quad(struct cras_fmt_conv *conv, const uint8_t *in, + size_t in_frames, uint8_t *out) +{ + return s16_51_to_quad(in, in_frames, out); +} + static size_t stereo_to_quad(struct cras_fmt_conv *conv, const uint8_t *in, size_t in_frames, uint8_t *out) { @@ -340,7 +398,8 @@ struct cras_fmt_conv *cras_fmt_conv_create(const struct cras_audio_format *in, conv->channel_converter = quad_to_stereo; } else if (in->num_channels == 2 && out->num_channels == 6) { conv->channel_converter = stereo_to_51; - } else if (in->num_channels == 6 && out->num_channels == 2) { + } else if (in->num_channels == 6 && + (out->num_channels == 2 || out->num_channels == 4)) { int in_channel_layout_set = 0; /* Checks if channel_layout is set in the incoming format */ @@ -361,11 +420,20 @@ struct cras_fmt_conv *cras_fmt_conv_create(const struct cras_audio_format *in, return NULL; } conv->channel_converter = convert_channels; - surround51_to_stereo_downmix_mtx( - conv->ch_conv_mtx, - conv->in_fmt.channel_layout); + if (out->num_channels == 4) { + surround51_to_quad_downmix_mtx( + conv->ch_conv_mtx, + conv->in_fmt.channel_layout); + } else { + surround51_to_stereo_downmix_mtx( + conv->ch_conv_mtx, + conv->in_fmt.channel_layout); + } } else { - conv->channel_converter = _51_to_stereo; + if (out->num_channels == 4) + conv->channel_converter = _51_to_quad; + else + conv->channel_converter = _51_to_stereo; } } else if (in->num_channels <= 8 && out->num_channels <= 8) { // For average channel counts mix from all to all. diff --git a/cras/src/server/cras_fmt_conv_ops.c b/cras/src/server/cras_fmt_conv_ops.c index 87358af2..a306d216 100644 --- a/cras/src/server/cras_fmt_conv_ops.c +++ b/cras/src/server/cras_fmt_conv_ops.c @@ -235,20 +235,69 @@ size_t s16_51_to_stereo(const uint8_t *_in, size_t in_frames, uint8_t *_out) int16_t *out = (int16_t *)_out; static const unsigned int left_idx = 0; static const unsigned int right_idx = 1; - /* static const unsigned int left_surround_idx = 2; */ - /* static const unsigned int right_surround_idx = 3; */ - static const unsigned int center_idx = 4; - /* static const unsigned int lfe_idx = 5; */ - size_t i; + static const unsigned int center_idx = 2; + /* static const unsigned int lfe_idx = 3; */ + /* static const unsigned int left_surround_idx = 4; */ + /* static const unsigned int right_surround_idx = 5; */ + size_t i; + int16_t half_center; + /* Use the normalized_factor from the left channel = 1 / (|1| + |0.707|) + * to prevent mixing overflow. + */ + const float normalized_factor = 0.585; for (i = 0; i < in_frames; i++) { - unsigned int half_center; - - half_center = in[6 * i + center_idx] / 2; + half_center = + in[6 * i + center_idx] * 0.707 * normalized_factor; out[2 * i + left_idx] = - s16_add_and_clip(in[6 * i + left_idx], half_center); + in[6 * i + left_idx] * normalized_factor + half_center; out[2 * i + right_idx] = - s16_add_and_clip(in[6 * i + right_idx], half_center); + in[6 * i + right_idx] * normalized_factor + half_center; + } + return in_frames; +} + +/* + * Channel converter: 5.1 surround to quad (front L/R, rear L/R). + * + * The out buffer can have room for just quad samples. This convert function + * is used as the default behavior when channel layout is not set from the + * client side. + */ +size_t s16_51_to_quad(const uint8_t *_in, size_t in_frames, uint8_t *_out) +{ + const int16_t *in = (const int16_t *)_in; + int16_t *out = (int16_t *)_out; + static const unsigned int l_quad = 0; + static const unsigned int r_quad = 1; + static const unsigned int rl_quad = 2; + static const unsigned int rr_quad = 3; + + static const unsigned int l_51 = 0; + static const unsigned int r_51 = 1; + static const unsigned int center_51 = 2; + static const unsigned int lfe_51 = 3; + static const unsigned int rl_51 = 4; + static const unsigned int rr_51 = 5; + + /* Use normalized_factor from the left channel = 1 / (|1| + |0.707| + |0.5|) + * to prevent overflow. */ + const float normalized_factor = 0.453; + size_t i; + for (i = 0; i < in_frames; i++) { + int16_t half_center; + int16_t lfe; + + half_center = in[6 * i + center_51] * 0.707 * normalized_factor; + lfe = in[6 * i + lfe_51] * 0.5 * normalized_factor; + out[4 * i + l_quad] = normalized_factor * in[6 * i + l_51] + + half_center + lfe; + out[4 * i + r_quad] = normalized_factor * in[6 * i + r_51] + + half_center + lfe; + out[4 * i + rl_quad] = + normalized_factor * in[6 * i + rl_51] + lfe; + out[4 * i + rr_quad] = + normalized_factor * in[6 * i + rr_51] + lfe; } return in_frames; } diff --git a/cras/src/server/cras_fmt_conv_ops.h b/cras/src/server/cras_fmt_conv_ops.h index 8042ad59..a1a57487 100644 --- a/cras/src/server/cras_fmt_conv_ops.h +++ b/cras/src/server/cras_fmt_conv_ops.h @@ -51,6 +51,11 @@ size_t s16_stereo_to_51(size_t left, size_t right, size_t center, size_t s16_51_to_stereo(const uint8_t *in, size_t in_frames, uint8_t *out); /* + * Channel converter: 5.1 surround to quad. + */ +size_t s16_51_to_quad(const uint8_t *in, size_t in_frames, uint8_t *out); + +/* * Channel converter: stereo to quad (front L/R, rear L/R). */ size_t s16_stereo_to_quad(size_t front_left, size_t front_right, diff --git a/cras/src/server/cras_hfp_ag_profile.c b/cras/src/server/cras_hfp_ag_profile.c index 8cf4a431..9d59d40e 100644 --- a/cras/src/server/cras_hfp_ag_profile.c +++ b/cras/src/server/cras_hfp_ag_profile.c @@ -22,6 +22,7 @@ #include "cras_iodev_list.h" #include "cras_observer.h" #include "utlist.h" +#include "packet_status_logger.h" #define HFP_AG_PROFILE_NAME "Hands-Free Voice gateway" #define HFP_AG_PROFILE_PATH "/org/chromium/Cras/Bluetooth/HFPAG" @@ -103,6 +104,7 @@ struct audio_gateway { }; static struct audio_gateway *connected_ags; +static struct packet_status_logger wbs_logger; static int need_go_sco_pcm(struct cras_bt_device *device) { @@ -411,6 +413,7 @@ int cras_hfp_ag_start(struct cras_bt_device *device) ag->slc_handle, ag->profile); } else { ag->info = hfp_info_create(); + hfp_info_set_wbs_logger(ag->info, &wbs_logger); ag->idev = hfp_iodev_create(CRAS_STREAM_INPUT, ag->device, ag->slc_handle, ag->profile, ag->info); @@ -453,6 +456,11 @@ struct hfp_slc_handle *cras_hfp_ag_get_slc(struct cras_bt_device *device) return NULL; } +struct packet_status_logger *cras_hfp_ag_get_wbs_logger() +{ + return &wbs_logger; +} + void cras_hfp_ag_resend_device_battery_level() { struct audio_gateway *ag; diff --git a/cras/src/server/cras_hfp_ag_profile.h b/cras/src/server/cras_hfp_ag_profile.h index fb58efb6..50d27e05 100644 --- a/cras/src/server/cras_hfp_ag_profile.h +++ b/cras/src/server/cras_hfp_ag_profile.h @@ -53,6 +53,9 @@ struct hfp_slc_handle *cras_hfp_ag_get_active_handle(); /* Gets the SLC handle for given cras_bt_device. */ struct hfp_slc_handle *cras_hfp_ag_get_slc(struct cras_bt_device *device); +/* Gets the logger for WBS packet status. */ +struct packet_status_logger *cras_hfp_ag_get_wbs_logger(); + /* Iterate all possible AGs (theoratically only one) and signal its battery * level */ void cras_hfp_ag_resend_device_battery_level(); diff --git a/cras/src/server/cras_hfp_alsa_iodev.c b/cras/src/server/cras_hfp_alsa_iodev.c index 532b6c40..b80a88c7 100644 --- a/cras/src/server/cras_hfp_alsa_iodev.c +++ b/cras/src/server/cras_hfp_alsa_iodev.c @@ -309,6 +309,10 @@ struct cras_iodev *hfp_alsa_iodev_create(struct cras_iodev *aio, /* Record max supported channels into cras_iodev_info. */ iodev->info.max_supported_channels = 1; + /* Specifically disable EWMA calculation on this and the child iodev. */ + ewma_power_disable(&iodev->ewma); + ewma_power_disable(&aio->ewma); + return iodev; } diff --git a/cras/src/server/cras_hfp_info.c b/cras/src/server/cras_hfp_info.c index 550de586..fc407b29 100644 --- a/cras/src/server/cras_hfp_info.c +++ b/cras/src/server/cras_hfp_info.c @@ -19,6 +19,7 @@ #include "cras_sbc_codec.h" #include "cras_server_metrics.h" #include "utlist.h" +#include "packet_status_logger.h" /* The max buffer size. Note that the actual used size must set to multiple * of SCO packet size, and the packet size does not necessarily be equal to @@ -93,6 +94,7 @@ static const uint8_t h2_header_frames_count[] = { 0x08, 0x38, 0xc8, 0xf8 }; * read_align_cb - Callback used to align mSBC frame reading with read buf. * msbc_read_current_corrupted - Flag to mark if the current mSBC frame * read is corrupted. + * wbs_logger - The logger for packet status in WBS. */ struct hfp_info { int fd; @@ -119,6 +121,7 @@ struct hfp_info { size_t read_rp; int (*read_align_cb)(uint8_t *buf); bool msbc_read_current_corrupted; + struct packet_status_logger *wbs_logger; }; int hfp_info_add_iodev(struct hfp_info *info, @@ -403,6 +406,20 @@ static const uint8_t *extract_msbc_frame(const uint8_t *input, int len, return NULL; } +/* Log value 0 when packet is received. */ +static void log_wbs_packet_received(struct hfp_info *info) +{ + if (info->wbs_logger) + packet_status_logger_update(info->wbs_logger, 0); +} + +/* Log value 1 when packet is lost. */ +static void log_wbs_packet_lost(struct hfp_info *info) +{ + if (info->wbs_logger) + packet_status_logger_update(info->wbs_logger, 1); +} + /* * Handle the case when mSBC frame is considered lost. * Args: @@ -419,6 +436,8 @@ static int handle_packet_loss(struct hfp_info *info) info->msbc_num_in_frames++; info->msbc_num_lost_frames++; + log_wbs_packet_lost(info); + in_bytes = buf_write_pointer_size(info->capture_buf, &pcm_avail); if (pcm_avail < MSBC_CODE_SIZE) return 0; @@ -580,6 +599,7 @@ recv_msbc_bytes: pcm_read += err; } else { /* Good mSBC frame decoded. */ + log_wbs_packet_received(info); buf_increment_write(info->capture_buf, pcm_decoded); info->msbc_num_in_frames++; cras_msbc_plc_handle_good_frames(info->msbc_plc, capture_buf, @@ -723,6 +743,12 @@ error: return NULL; } +void hfp_info_set_wbs_logger(struct hfp_info *info, + struct packet_status_logger *wbs_logger) +{ + info->wbs_logger = wbs_logger; +} + int hfp_info_running(struct hfp_info *info) { return info->started; @@ -760,6 +786,8 @@ int hfp_info_start(int fd, unsigned int mtu, int codec, struct hfp_info *info) info->msbc_read = cras_msbc_codec_create(); info->msbc_write = cras_msbc_codec_create(); info->msbc_plc = cras_msbc_plc_create(); + + packet_status_logger_init(info->wbs_logger); } else { info->write_cb = hfp_write; info->read_cb = hfp_read; diff --git a/cras/src/server/cras_hfp_info.h b/cras/src/server/cras_hfp_info.h index 96110e2a..3472aeab 100644 --- a/cras/src/server/cras_hfp_info.h +++ b/cras/src/server/cras_hfp_info.h @@ -30,6 +30,10 @@ struct hfp_info *hfp_info_create(); /* Destroys given hfp_info instance. */ void hfp_info_destroy(struct hfp_info *info); +/* Sets the wbs_logger to hfp_info instance. */ +void hfp_info_set_wbs_logger(struct hfp_info *info, + struct packet_status_logger *wbs_logger); + /* Checks if given hfp_info is running. */ int hfp_info_running(struct hfp_info *info); diff --git a/cras/src/server/cras_hfp_iodev.c b/cras/src/server/cras_hfp_iodev.c index bf609cfb..7cce3736 100644 --- a/cras/src/server/cras_hfp_iodev.c +++ b/cras/src/server/cras_hfp_iodev.c @@ -350,6 +350,8 @@ struct cras_iodev *hfp_iodev_create(enum CRAS_STREAM_DIRECTION dir, /* Record max supported channels into cras_iodev_info. */ iodev->info.max_supported_channels = 1; + ewma_power_disable(&iodev->ewma); + return iodev; error: diff --git a/cras/src/server/cras_hfp_slc.c b/cras/src/server/cras_hfp_slc.c index 1cf003a1..e4f0127d 100644 --- a/cras/src/server/cras_hfp_slc.c +++ b/cras/src/server/cras_hfp_slc.c @@ -708,7 +708,14 @@ static int indicator_support(struct hfp_slc_handle *handle, const char *cmd) * check the Bluetooth SIG Assigned Numbers web page. */ BTLOG(btlog, BT_HFP_HF_INDICATOR, 1, 0); - err = hfp_send(handle, AT_CMD("+BIND: (2)")); + /* "2" is for HF Battery Level that we support. We don't + * support "1" but this is a workaround for Pixel Buds 2 + * which expects this exact combination for battery + * reporting (HFP 1.7 standard) to work. This workaround + * is fine since we don't enable Safety Drive with + * +BIND: 1,1 (b/172680041). + */ + err = hfp_send(handle, AT_CMD("+BIND: (1,2)")); if (err < 0) return err; } @@ -738,6 +745,14 @@ static int indicator_support(struct hfp_slc_handle *handle, const char *cmd) * indicator * 1 = enabled, value changes may be sent for this indicator */ + + /* We don't support Enhanced Driver Status, so explicitly + * disable it (b/172680041). + */ + err = hfp_send(handle, AT_CMD("+BIND: 1,0")); + if (err < 0) + return err; + BTLOG(btlog, BT_HFP_HF_INDICATOR, 0, 0); err = hfp_send(handle, AT_CMD("+BIND: 2,1")); @@ -927,7 +942,7 @@ static int terminate_call(struct hfp_slc_handle *handle, const char *cmd) * * An initialized service level connection is the pre-condition for all * call related procedures. Note that for the call related commands, - * we are good to just respond with a dummy "OK". + * we are good to just respond with a meaningless "OK". * * The procedure to establish a service level connection is described below: * diff --git a/cras/src/server/cras_iodev.c b/cras/src/server/cras_iodev.c index f426eb5f..fd1ce805 100644 --- a/cras/src/server/cras_iodev.c +++ b/cras/src/server/cras_iodev.c @@ -239,7 +239,7 @@ int cras_iodev_is_zero_volume(const struct cras_iodev *odev) * | ---------------- | device from * | | S1 Open | | audio_thread and * | ---------------- | closes device - * | Device with dummy start | | + * | Device with empty start | | * | ops transits into | Sample is ready | * | no stream state right V | * | after open. ---------------- | @@ -527,8 +527,8 @@ static void add_ext_dsp_module_to_pipeline(struct cras_iodev *iodev) if (!pipeline) { cras_iodev_alloc_dsp(iodev); - cras_dsp_load_dummy_pipeline(iodev->dsp_context, - iodev->format->num_channels); + cras_dsp_load_mock_pipeline(iodev->dsp_context, + iodev->format->num_channels); pipeline = cras_dsp_get_pipeline(iodev->dsp_context); } /* dsp_context mutex locked. Now it's safe to modify dsp @@ -953,6 +953,8 @@ int cras_iodev_open(struct cras_iodev *iodev, unsigned int cb_level, iodev->highest_hw_level = 0; iodev->input_dsp_offset = 0; + ewma_power_init(&iodev->ewma, iodev->format->frame_rate); + if (iodev->direction == CRAS_STREAM_OUTPUT) { /* If device supports start ops, device can be in open state. * Otherwise, device starts running right after opening. */ @@ -1097,6 +1099,9 @@ int cras_iodev_put_output_buffer(struct cras_iodev *iodev, uint8_t *frames, loopback->cb_data); } + ewma_power_calculate(&iodev->ewma, (int16_t *)frames, + iodev->format->num_channels, nframes); + rc = apply_dsp(iodev, frames, nframes); if (rc) return rc; @@ -1200,6 +1205,11 @@ int cras_iodev_get_input_buffer(struct cras_iodev *iodev, unsigned int *frames) *frames - iodev->input_dsp_offset); if (rc) return rc; + ewma_power_calculate_area( + &iodev->ewma, + (int16_t *)(hw_buffer + + iodev->input_dsp_offset * frame_bytes), + data->area, *frames - iodev->input_dsp_offset); } if (cras_system_get_capture_mute()) diff --git a/cras/src/server/cras_iodev.h b/cras/src/server/cras_iodev.h index 553cb807..db16a0f8 100644 --- a/cras/src/server/cras_iodev.h +++ b/cras/src/server/cras_iodev.h @@ -19,6 +19,7 @@ #include "cras_dsp.h" #include "cras_iodev_info.h" #include "cras_messages.h" +#include "ewma_power.h" struct buffer_share; struct cras_fmt_conv; @@ -237,7 +238,7 @@ struct cras_ionode { * stream side processing. * initial_ramp_request - The value indicates which type of ramp the device * should perform when some samples are ready for playback. - * + * ewma - The ewma instance to calculate iodev volume. */ struct cras_iodev { void (*set_volume)(struct cras_iodev *iodev); @@ -312,6 +313,7 @@ struct cras_iodev { unsigned int input_dsp_offset; unsigned int initial_ramp_request; struct input_data *input_data; + struct ewma_power ewma; struct cras_iodev *prev, *next; }; diff --git a/cras/src/server/cras_iodev_list.c b/cras/src/server/cras_iodev_list.c index bba793d1..ada29719 100644 --- a/cras/src/server/cras_iodev_list.c +++ b/cras/src/server/cras_iodev_list.c @@ -361,7 +361,8 @@ static void possibly_enable_echo_reference(struct cras_iodev *dev) if (dev->echo_reference_dev == NULL) return; - server_stream_create(stream_list, dev->echo_reference_dev->info.idx); + server_stream_create(stream_list, dev->echo_reference_dev->info.idx, + dev->format); } /* diff --git a/cras/src/server/cras_loopback_iodev.c b/cras/src/server/cras_loopback_iodev.c index 0947313f..cf3ba4ae 100644 --- a/cras/src/server/cras_loopback_iodev.c +++ b/cras/src/server/cras_loopback_iodev.c @@ -331,7 +331,7 @@ struct cras_iodev *loopback_iodev_create(enum CRAS_LOOPBACK_TYPE type) if (iodev == NULL) return NULL; - /* Create a dummy ionode */ + /* Create an empty ionode */ node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; node->type = node_type; diff --git a/cras/src/server/cras_observer.c b/cras/src/server/cras_observer.c index a73bcccf..0f17dc92 100644 --- a/cras/src/server/cras_observer.c +++ b/cras/src/server/cras_observer.c @@ -7,6 +7,7 @@ #include "cras_alert.h" #include "cras_iodev_list.h" +#include "cras_types.h" #include "utlist.h" struct cras_observer_client { @@ -35,6 +36,7 @@ struct cras_observer_alerts { struct cras_alert *num_active_streams[CRAS_NUM_DIRECTIONS]; struct cras_alert *non_empty_audio_state_changed; struct cras_alert *bt_battery_changed; + struct cras_alert *num_input_streams_with_permission; }; struct cras_observer_server { @@ -76,6 +78,10 @@ struct cras_observer_alert_data_streams { uint32_t num_active_streams; }; +struct cras_observer_alert_data_input_streams { + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]; +}; + struct cras_observer_alert_data_hotword_triggered { int64_t tv_sec; int64_t tv_nsec; @@ -253,6 +259,20 @@ static void num_active_streams_alert(void *arg, void *data) } } +static void num_input_streams_with_permission_alert(void *arg, void *data) +{ + struct cras_observer_client *client; + struct cras_observer_alert_data_input_streams *input_streams_data = + (struct cras_observer_alert_data_input_streams *)data; + + DL_FOREACH (g_observer->clients, client) { + if (client->ops.num_input_streams_with_permission_changed) + client->ops.num_input_streams_with_permission_changed( + client->context, + input_streams_data->num_input_streams); + } +} + static void hotword_triggered_alert(void *arg, void *data) { struct cras_observer_client *client; @@ -353,6 +373,7 @@ int cras_observer_server_init() CRAS_OBSERVER_SET_ALERT(hotword_triggered, NULL, 0); CRAS_OBSERVER_SET_ALERT(non_empty_audio_state_changed, NULL, 0); CRAS_OBSERVER_SET_ALERT(bt_battery_changed, NULL, 0); + CRAS_OBSERVER_SET_ALERT(num_input_streams_with_permission, NULL, 0); CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(num_active_streams, CRAS_STREAM_OUTPUT); @@ -386,6 +407,8 @@ void cras_observer_server_free() cras_alert_destroy(g_observer->alerts.non_empty_audio_state_changed); cras_alert_destroy(g_observer->alerts.bt_battery_changed); cras_alert_destroy( + g_observer->alerts.num_input_streams_with_permission); + cras_alert_destroy( g_observer->alerts.num_active_streams[CRAS_STREAM_OUTPUT]); cras_alert_destroy( g_observer->alerts.num_active_streams[CRAS_STREAM_INPUT]); @@ -562,6 +585,21 @@ void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir, cras_alert_pending_data(alert, &data, sizeof(data)); } +void cras_observer_notify_input_streams_with_permission( + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) +{ + struct cras_observer_alert_data_input_streams data; + struct cras_alert *alert; + + memcpy(&data.num_input_streams, num_input_streams, + sizeof(*num_input_streams) * CRAS_NUM_CLIENT_TYPE); + alert = g_observer->alerts.num_input_streams_with_permission; + if (!alert) + return; + + cras_alert_pending_data(alert, &data, sizeof(data)); +} + void cras_observer_notify_hotword_triggered(int64_t tv_sec, int64_t tv_nsec) { struct cras_observer_alert_data_hotword_triggered data; diff --git a/cras/src/server/cras_observer.h b/cras/src/server/cras_observer.h index 1da5eeaf..2dd013b8 100644 --- a/cras/src/server/cras_observer.h +++ b/cras/src/server/cras_observer.h @@ -92,6 +92,10 @@ void cras_observer_notify_suspend_changed(int suspended); void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir, uint32_t num_active_streams); +/* Notify observers of the number of input streams with permission. */ +void cras_observer_notify_input_streams_with_permission( + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]); + /* Notify observers of the timestamp when hotword triggered. */ void cras_observer_notify_hotword_triggered(int64_t tv_sec, int64_t tv_nsec); diff --git a/cras/src/server/cras_rclient.c b/cras/src/server/cras_rclient.c index 22fbb05c..d2646e75 100644 --- a/cras/src/server/cras_rclient.c +++ b/cras/src/server/cras_rclient.c @@ -59,9 +59,16 @@ int cras_rclient_send_message(const struct cras_rclient *client, return client->ops->send_message_to_client(client, msg, fds, num_fds); } +static void cras_rclient_set_client_type(struct cras_rclient *client, + enum CRAS_CLIENT_TYPE client_type) +{ + client->client_type = client_type; +} + struct cras_rclient *cras_rclient_create(int fd, size_t id, enum CRAS_CONNECTION_TYPE conn_type) { + struct cras_rclient *client; if (!cras_validate_connection_type(conn_type)) goto error; @@ -76,6 +83,14 @@ struct cras_rclient *cras_rclient_create(int fd, size_t id, return cras_playback_rclient_create(fd, id); case CRAS_VMS_UNIFIED: return cras_unified_rclient_create(fd, id); + case CRAS_PLUGIN_PLAYBACK: + client = cras_playback_rclient_create(fd, id); + cras_rclient_set_client_type(client, CRAS_CLIENT_TYPE_PLUGIN); + return client; + case CRAS_PLUGIN_UNIFIED: + client = cras_unified_rclient_create(fd, id); + cras_rclient_set_client_type(client, CRAS_CLIENT_TYPE_PLUGIN); + return client; default: goto error; } diff --git a/cras/src/server/cras_rclient.h b/cras/src/server/cras_rclient.h index 6cffb7d8..3a3988c2 100644 --- a/cras/src/server/cras_rclient.h +++ b/cras/src/server/cras_rclient.h @@ -20,6 +20,9 @@ struct cras_server_message; * fd - Connection for client communication. * ops - cras_rclient_ops for the cras_rclient. * supported_directions - Bit mask for supported stream directions. + * client_type - Client type of this rclient. If this is set to value other + * than CRAS_CLIENT_TYPE_UNKNOWN, rclient will overwrite incoming + * messages' client type. */ struct cras_rclient { struct cras_observer_client *observer; @@ -27,6 +30,7 @@ struct cras_rclient { int fd; const struct cras_rclient_ops *ops; int supported_directions; + enum CRAS_CLIENT_TYPE client_type; }; /* Operations for cras_rclient. diff --git a/cras/src/server/cras_rclient_util.c b/cras/src/server/cras_rclient_util.c index da991282..def645e3 100644 --- a/cras/src/server/cras_rclient_util.c +++ b/cras/src/server/cras_rclient_util.c @@ -80,6 +80,13 @@ rclient_validate_stream_connect_message(const struct cras_rclient *client, msg->direction, client->id); return -EINVAL; } + + if (!cras_validate_client_type(msg->client_type)) { + syslog(LOG_ERR, + "stream_connect: invalid stream client_type: %x for " + "client: %zx.\n", + msg->client_type, client->id); + } return 0; } @@ -155,6 +162,9 @@ int rclient_handle_client_stream_connect(struct cras_rclient *client, stream_config = cras_rstream_config_init_with_message( client, msg, &aud_fd, &client_shm_fd, &remote_fmt); + /* Overwrite client_type if client->client_type is set. */ + if (client->client_type != CRAS_CLIENT_TYPE_UNKNOWN) + stream_config.client_type = client->client_type; rc = stream_list_add(cras_iodev_list_get_stream_list(), &stream_config, &stream); if (rc) @@ -279,15 +289,11 @@ int rclient_handle_message_from_client(struct cras_rclient *client, switch (msg->id) { case CRAS_SERVER_CONNECT_STREAM: { int client_shm_fd = num_fds > 1 ? fds[1] : -1; - struct cras_connect_message cmsg; if (MSG_LEN_VALID(msg, struct cras_connect_message)) { rclient_handle_client_stream_connect( client, (const struct cras_connect_message *)msg, fd, client_shm_fd); - } else if (!convert_connect_message_old(msg, &cmsg)) { - rclient_handle_client_stream_connect(client, &cmsg, fd, - client_shm_fd); } else { return -EINVAL; } diff --git a/cras/src/server/cras_rclient_util.h b/cras/src/server/cras_rclient_util.h index e00f87c9..089c2ecb 100644 --- a/cras/src/server/cras_rclient_util.h +++ b/cras/src/server/cras_rclient_util.h @@ -122,33 +122,4 @@ int rclient_handle_message_from_client(struct cras_rclient *client, const struct cras_server_message *msg, int *fds, unsigned int num_fds); -/* - * Converts an old version of connect message to the correct - * cras_connect_message. Returns zero on success, negative on failure. - * Note that this is special check only for libcras transition in - * clients, from CRAS_PROTO_VER 5 to 7. - * TODO(fletcherw): clean up the function once transition is done. - */ -static inline int -convert_connect_message_old(const struct cras_server_message *msg, - struct cras_connect_message *cmsg) -{ - struct cras_connect_message_old *old; - - if (!MSG_LEN_VALID(msg, struct cras_connect_message_old)) - return -EINVAL; - - old = (struct cras_connect_message_old *)msg; - if (old->proto_version != 5 || CRAS_PROTO_VER != 7) - return -EINVAL; - - // We want to copy everything except the client_shm_size field, since - // that overlaps slightly with the now larger client_shm_size. - memcpy(cmsg, old, sizeof(*old) - sizeof(old->client_shm_size)); - cmsg->client_shm_size = old->client_shm_size; - cmsg->buffer_offsets[0] = 0; - cmsg->buffer_offsets[1] = 0; - return 0; -} - #endif /* CRAS_RCLIENT_UTIL_H_ */ diff --git a/cras/src/server/cras_rstream.c b/cras/src/server/cras_rstream.c index b5a64901..94adcead 100644 --- a/cras/src/server/cras_rstream.c +++ b/cras/src/server/cras_rstream.c @@ -292,6 +292,7 @@ int cras_rstream_create(struct cras_rstream_config *config, stream->num_missed_cb = 0; stream->is_pinned = (config->dev_idx != NO_DEVICE); stream->pinned_dev_idx = config->dev_idx; + ewma_power_init(&stream->ewma, stream->format.frame_rate); rc = setup_shm_area(stream, config); if (rc < 0) { @@ -312,7 +313,7 @@ int cras_rstream_create(struct cras_rstream_config *config, config->stream_id, config->buffer_frames, config->cb_threshold); *stream_out = stream; - cras_system_state_stream_added(stream->direction); + cras_system_state_stream_added(stream->direction, stream->client_type); clock_gettime(CLOCK_MONOTONIC_RAW, &stream->start_ts); @@ -324,7 +325,8 @@ int cras_rstream_create(struct cras_rstream_config *config, void cras_rstream_destroy(struct cras_rstream *stream) { cras_server_metrics_stream_destroy(stream); - cras_system_state_stream_removed(stream->direction); + cras_system_state_stream_removed(stream->direction, + stream->client_type); close(stream->fd); cras_audio_shm_destroy(stream->shm); cras_audio_area_destroy(stream->audio_area); @@ -472,9 +474,16 @@ void cras_rstream_update_input_write_pointer(struct cras_rstream *rstream) void cras_rstream_update_output_read_pointer(struct cras_rstream *rstream) { + size_t nfr = 0; + uint8_t *src; unsigned int nwritten = buffer_share_get_new_write_point(rstream->buf_state); + /* Retrieve the read pointer |src| start from which to calculate + * the EWMA power. */ + src = cras_shm_get_readable_frames(rstream->shm, 0, &nfr); + ewma_power_calculate(&rstream->ewma, (int16_t *)src, + rstream->format.num_channels, nwritten); cras_shm_buffer_read(rstream->shm, nwritten); } diff --git a/cras/src/server/cras_rstream.h b/cras/src/server/cras_rstream.h index 059f2bb7..3bf7df0b 100644 --- a/cras/src/server/cras_rstream.h +++ b/cras/src/server/cras_rstream.h @@ -14,6 +14,7 @@ #include "cras_shm.h" #include "cras_types.h" #include "cras_rstream_config.h" +#include "ewma_power.h" struct cras_connect_message; struct cras_rclient; @@ -55,6 +56,7 @@ struct master_dev_info { * first_missed_cb_ts - The time when the first missed callback happens. * buf_state - State of the buffer from all devices for this stream. * apm_list - List of audio processing module instances. + * ewma - The ewma instance to calculate stream volume. * num_attached_devs - Number of iodevs this stream has attached to. * num_missed_cb - Number of callback schedules have been missed. * queued_frames - Cached value of the number of queued frames in shm. @@ -85,6 +87,7 @@ struct cras_rstream { struct timespec first_missed_cb_ts; struct buffer_share *buf_state; struct cras_apm_list *apm_list; + struct ewma_power ewma; int num_attached_devs; int num_missed_cb; int queued_frames; diff --git a/cras/src/server/cras_server_metrics.c b/cras/src/server/cras_server_metrics.c index 91556d86..ef4011bd 100644 --- a/cras/src/server/cras_server_metrics.c +++ b/cras/src/server/cras_server_metrics.c @@ -268,7 +268,7 @@ metrics_device_type_str(enum CRAS_METRICS_DEVICE_TYPE device_type) return "NoDevice"; case CRAS_METRICS_DEVICE_ALSA_LOOPBACK: return "AlsaLoopback"; - /* Other dummy devices. */ + /* Other fallback devices. */ case CRAS_METRICS_DEVICE_NORMAL_FALLBACK: return "NormalFallback"; case CRAS_METRICS_DEVICE_ABNORMAL_FALLBACK: diff --git a/cras/src/server/cras_system_state.c b/cras/src/server/cras_system_state.c index a5834197..331ecb11 100644 --- a/cras/src/server/cras_system_state.c +++ b/cras/src/server/cras_system_state.c @@ -156,6 +156,8 @@ void cras_system_state_init(const char *device_config_dir, const char *shm_name, exp_state->aec_supported = board_config.aec_supported; exp_state->aec_group_id = board_config.aec_group_id; exp_state->bt_wbs_enabled = board_config.bt_wbs_enabled; + exp_state->deprioritize_bt_wbs_mic = + board_config.deprioritize_bt_wbs_mic; if ((rc = pthread_mutex_init(&state.update_lock, 0) != 0)) { syslog(LOG_ERR, "Fatal: system state mutex init"); @@ -382,6 +384,11 @@ bool cras_system_get_bt_wbs_enabled() return !!state.exp_state->bt_wbs_enabled; } +bool cras_system_get_deprioritize_bt_wbs_mic() +{ + return !!state.exp_state->deprioritize_bt_wbs_mic; +} + void cras_system_set_bt_fix_a2dp_packet_size_enabled(bool enabled) { state.bt_fix_a2dp_packet_size = enabled; @@ -511,7 +518,8 @@ void cras_system_rm_select_fd(int fd) state.fd_rm(fd, state.select_data); } -void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction) +void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type) { struct cras_server_state *s; @@ -521,13 +529,19 @@ void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction) s->num_active_streams[direction]++; s->num_streams_attached++; + if (direction == CRAS_STREAM_INPUT) { + s->num_input_streams_with_permission[client_type]++; + cras_observer_notify_input_streams_with_permission( + s->num_input_streams_with_permission); + } cras_system_state_update_complete(); cras_observer_notify_num_active_streams( direction, s->num_active_streams[direction]); } -void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction) +void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type) { struct cras_server_state *s; unsigned i, sum; @@ -545,6 +559,11 @@ void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction) cras_clock_gettime(CLOCK_MONOTONIC_RAW, &s->last_active_stream_time); s->num_active_streams[direction]--; + if (direction == CRAS_STREAM_INPUT) { + s->num_input_streams_with_permission[client_type]--; + cras_observer_notify_input_streams_with_permission( + s->num_input_streams_with_permission); + } cras_system_state_update_complete(); cras_observer_notify_num_active_streams( @@ -566,6 +585,15 @@ unsigned cras_system_state_get_active_streams_by_direction( return state.exp_state->num_active_streams[direction]; } +void cras_system_state_get_input_streams_with_permission( + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) +{ + unsigned type; + for (type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) + num_input_streams[type] = + state.exp_state->num_input_streams_with_permission[type]; +} + void cras_system_state_get_last_stream_active_time(struct cras_timespec *ts) { *ts = state.exp_state->last_active_stream_time; diff --git a/cras/src/server/cras_system_state.h b/cras/src/server/cras_system_state.h index 7f92625e..ff04606a 100644 --- a/cras/src/server/cras_system_state.h +++ b/cras/src/server/cras_system_state.h @@ -122,6 +122,12 @@ void cras_system_set_bt_wbs_enabled(bool enabled); /* Gets the elable flag of bluetooth wideband speech feature. */ bool cras_system_get_bt_wbs_enabled(); +/* + * Returns if Bluetooth WBS mic should be deprioritized for selecting + * as default audio input option. + */ +bool cras_system_get_deprioritize_bt_wbs_mic(); + /* Sets the flag to enable or disable Bluetooth fixed A2DP packet size. */ void cras_system_set_bt_fix_a2dp_packet_size_enabled(bool enabled); @@ -226,16 +232,20 @@ int cras_system_add_task(void (*callback)(void *data), void *callback_data); * subsystem is idle. * Args: * direction - Directions of audio streams. + * client_type - CRAS_CLIENT_TYPE of the audio stream. */ -void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction); +void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type); /* Signals that an audio input or output stream has been removed from the * system. This allows the count of active streams can be used to notice when * the audio subsystem is idle. * Args: * direction - Directions of audio stream. + * client_type - CRAS_CLIENT_TYPE of the audio stream. */ -void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction); +void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type); /* Returns the number of active playback and capture streams. */ unsigned cras_system_state_get_active_streams(); @@ -247,6 +257,16 @@ unsigned cras_system_state_get_active_streams(); unsigned cras_system_state_get_active_streams_by_direction( enum CRAS_STREAM_DIRECTION direction); +/* Returns the number of input streams with permission per CRAS_CLIENT_TYPE. + * + * Returns: + * num_input_streams - An array with length = CRAS_NUM_CLIENT_TYPE and each + * element is the number of the current input + * streams with permission in each client type. + */ +void cras_system_state_get_input_streams_with_permission( + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]); + /* Fills ts with the time the last stream was removed from the system, the time * the stream count went to zero. */ diff --git a/cras/src/server/dev_io.c b/cras/src/server/dev_io.c index 20b25f7e..42fe9558 100644 --- a/cras/src/server/dev_io.c +++ b/cras/src/server/dev_io.c @@ -126,6 +126,19 @@ static bool is_time_to_fetch(const struct dev_stream *dev_stream, return 0; } +/* The log only accepts uint32 arguments, so the float power + * must be written as bits and assumed to have a float when + * parsing the log. + */ +static uint32_t get_ewma_power_as_int(struct ewma_power *ewma) +{ + uint32_t pow_as_int = 0; + + if (sizeof(uint32_t) == sizeof(float)) + memcpy(&pow_as_int, &ewma->power, sizeof(uint32_t)); + return pow_as_int; +} + /* Asks any stream with room for more data. Sets the time stamp for all streams. * Args: * adev - The output device streams are attached to. @@ -194,7 +207,8 @@ static int fetch_streams(struct open_dev *adev) dev_stream_set_delay(dev_stream, delay); ATLOG(atlog, AUDIO_THREAD_FETCH_STREAM, rstream->stream_id, - cras_rstream_get_cb_threshold(rstream), delay); + cras_rstream_get_cb_threshold(rstream), + get_ewma_power_as_int(&rstream->ewma)); rc = dev_stream_request_playback_samples(dev_stream, &now); if (rc < 0) { @@ -550,7 +564,8 @@ static int capture_to_streams(struct open_dev *adev) break; } - ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_DONE, remainder, 0, 0); + ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_DONE, remainder, + get_ewma_power_as_int(&idev->ewma), 0); return 0; } @@ -733,7 +748,8 @@ int write_output_samples(struct open_dev **odevs, struct open_dev *adev, if (cras_iodev_update_rate(odev, hw_level, &hw_tstamp)) update_estimated_rate(adev); } - ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO, adev->dev->info.idx, hw_level, 0); + ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO, adev->dev->info.idx, hw_level, + odev->min_cb_level); /* Don't request more than hardware can hold. Note that min_buffer_level * has been subtracted from the actual hw_level so we need to take it @@ -797,7 +813,7 @@ int write_output_samples(struct open_dev **odevs, struct open_dev *adev, } ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_DONE, hw_level, total_written, - odev->min_cb_level); + get_ewma_power_as_int(&odev->ewma)); return total_written; } diff --git a/cras/src/server/dev_stream.c b/cras/src/server/dev_stream.c index f0fcb71e..025aeddd 100644 --- a/cras/src/server/dev_stream.c +++ b/cras/src/server/dev_stream.c @@ -87,9 +87,12 @@ struct dev_stream *dev_stream_create(struct cras_rstream *stream, } else { /* * For input, take into account the stream specific processing - * like AEC. Use the post processing format to configure format - * converter. + * like AEC. APM exists only in input path, and has no dependency + * to dev_stream. Starts APM in dev_stream's constructor just to + * align with its life cycle, and then gets the post processing + * format to configure format converter. */ + cras_apm_list_start_apm(stream->apm_list, dev_ptr); ofmt = cras_rstream_post_processing_format(stream, dev_ptr) ?: dev_fmt, rc = config_format_converter(&out->conv, stream->direction, @@ -123,9 +126,8 @@ struct dev_stream *dev_stream_create(struct cras_rstream *stream, stream_fmt->frame_rate, &stream->sleep_interval_ts); stream->next_cb_ts = *cb_ts; - /* Sets up the stream & dev pair and then start APM. */ + /* Sets up the stream & dev pair. */ cras_rstream_dev_attach(stream, dev_id, dev_ptr); - cras_apm_list_start_apm(stream->apm_list, dev_ptr); return out; } diff --git a/cras/src/server/ewma_power.c b/cras/src/server/ewma_power.c new file mode 100644 index 00000000..5270ef0e --- /dev/null +++ b/cras/src/server/ewma_power.c @@ -0,0 +1,81 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "ewma_power.h" +#include "math.h" + +/* One sample per 1ms. */ +#define EWMA_SAMPLE_RATE 1000 + +/* Smooth factor for EWMA, 1 - expf(-1.0/(rate * 0.01)) + * where the 0.01 corresponds to 10ms interval that is chosen and + * being used in Chrome for a long time. + * Here the |rate| is set to the down sampled EWMA_SAMPLE_RATE and + * whenever it changes the calculated |smooth_factor| should be updated + * accordingly. + */ +const static float smooth_factor = 0.095; + +void ewma_power_disable(struct ewma_power *ewma) +{ + ewma->enabled = 0; +} + +void ewma_power_init(struct ewma_power *ewma, unsigned int rate) +{ + ewma->enabled = 1; + ewma->power_set = 0; + ewma->step_fr = rate / EWMA_SAMPLE_RATE; +} + +void ewma_power_calculate(struct ewma_power *ewma, const int16_t *buf, + unsigned int channels, unsigned int size) +{ + int i, ch; + float power, f; + + if (!ewma->enabled) + return; + for (i = 0; i < size; i += ewma->step_fr * channels) { + power = 0.0f; + for (ch = 0; ch < channels; ch++) { + f = buf[i + ch] / 32768.0f; + power += f * f / channels; + } + if (!ewma->power_set) { + ewma->power = power; + ewma->power_set = 1; + } else { + ewma->power = smooth_factor * power + + (1 - smooth_factor) * ewma->power; + } + } +} + +void ewma_power_calculate_area(struct ewma_power *ewma, const int16_t *buf, + struct cras_audio_area *area, unsigned int size) +{ + int i, ch; + float power, f; + + if (!ewma->enabled) + return; + for (i = 0; i < size; i += ewma->step_fr * area->num_channels) { + power = 0.0f; + for (ch = 0; ch < area->num_channels; ch++) { + if (area->channels[ch].ch_set == 0) + continue; + f = buf[i + ch] / 32768.0f; + power += f * f / area->num_channels; + } + if (!ewma->power_set) { + ewma->power = power; + ewma->power_set = 1; + } else { + ewma->power = smooth_factor * power + + (1 - smooth_factor) * ewma->power; + } + } +} diff --git a/cras/src/server/ewma_power.h b/cras/src/server/ewma_power.h new file mode 100644 index 00000000..78d2e504 --- /dev/null +++ b/cras/src/server/ewma_power.h @@ -0,0 +1,65 @@ +/* Copyright 2020 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef EWMA_POWER_H_ +#define EWMA_POWER_H_ + +#include <stdbool.h> +#include <stdint.h> + +#include "cras_audio_area.h" + +/* + * The exponentially weighted moving average power module used to + * calculate the energe level in audio stream. + * Members: + * power_set - Flag to note if the first power value has set. + * enabled - Flag to enable ewma calculation. Set to false to + * make all calculations no-ops. + * power - The power value. + * step_fr - How many frames to sample one for EWMA calculation. + */ +struct ewma_power { + bool power_set; + bool enabled; + float power; + unsigned int step_fr; +}; + +/* + * Disables the ewma instance. + */ +void ewma_power_disable(struct ewma_power *ewma); + +/* + * Initializes the ewma_power object. + * Args: + * ewma - The ewma_power object to initialize. + * rate - The sample rate of the audio data that the ewma object + * will calculate power from. + */ +void ewma_power_init(struct ewma_power *ewma, unsigned int rate); + +/* + * Feeds an audio buffer to ewma_power object to calculate the + * latest power value. + * Args: + * ewma - The ewma_power object to calculate power. + * buf - Pointer to the audio data. + * channels - Number of channels of the audio data. + * size - Length in frames of the audio data. + */ +void ewma_power_calculate(struct ewma_power *ewma, const int16_t *buf, + unsigned int channels, unsigned int size); + +/* + * Feeds non-interleaved audio data to ewma_power to calculate the + * latest power value. This is similar to ewma_power_calculate but + * accepts cras_audio_area. + */ +void ewma_power_calculate_area(struct ewma_power *ewma, const int16_t *buf, + struct cras_audio_area *area, unsigned int size); + +#endif /* EWMA_POWER_H_ */ diff --git a/cras/src/server/server_stream.c b/cras/src/server/server_stream.c index 48dd6a30..6644c469 100644 --- a/cras/src/server/server_stream.c +++ b/cras/src/server/server_stream.c @@ -16,18 +16,6 @@ static unsigned int server_stream_block_size = 480; /* - * Server stream doesn't care what format is used, because no - * client is reading data from stream. The main point is to - * make pinned device open and let data flow through its dsp - * pipeline. - */ -static struct cras_audio_format format = { - SND_PCM_FORMAT_S16_LE, - 48000, - 2, -}; - -/* * Information of a stream created by server. Currently only * one server stream is allowed, for echo reference use. */ @@ -45,7 +33,8 @@ static void server_stream_add_cb(void *data) stream_list_add(stream_list, stream_config, &stream); } -void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx) +void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx, + struct cras_audio_format *format) { int audio_fd = -1; int client_shm_fd = -1; @@ -64,7 +53,7 @@ void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx) CRAS_STREAM_TYPE_DEFAULT, CRAS_CLIENT_TYPE_SERVER_STREAM, CRAS_STREAM_INPUT, dev_idx, /*flags=*/SERVER_ONLY, - /*effects=*/0, &format, server_stream_block_size, + /*effects=*/0, format, server_stream_block_size, server_stream_block_size, &audio_fd, &client_shm_fd, /*client_shm_size=*/0, buffer_offsets, stream_config); diff --git a/cras/src/server/server_stream.h b/cras/src/server/server_stream.h index 595987cb..e1eb8e10 100644 --- a/cras/src/server/server_stream.h +++ b/cras/src/server/server_stream.h @@ -14,8 +14,8 @@ struct stream_list; * stream_list - List of stream to add new server stream to. * dev_idx - The id of the device that new server stream will pin to. */ -void server_stream_create(struct stream_list *stream_list, - unsigned int dev_idx); +void server_stream_create(struct stream_list *stream_list, unsigned int dev_idx, + struct cras_audio_format *format); /* * Asynchronously destroys existing server stream pinned to device of given idx. diff --git a/cras/src/server/test_iodev.c b/cras/src/server/test_iodev.c index 266b62a8..cb7d5f3a 100644 --- a/cras/src/server/test_iodev.c +++ b/cras/src/server/test_iodev.c @@ -201,7 +201,7 @@ struct cras_iodev *test_iodev_create(enum CRAS_STREAM_DIRECTION direction, */ iodev->info.max_supported_channels = 1; - /* Create a dummy ionode */ + /* Create an empty ionode */ node = (struct cras_ionode *)calloc(1, sizeof(*node)); node->dev = iodev; node->plugged = 1; diff --git a/cras/src/tests/a2dp_iodev_unittest.cc b/cras/src/tests/a2dp_iodev_unittest.cc index f03a6147..523a62e4 100644 --- a/cras/src/tests/a2dp_iodev_unittest.cc +++ b/cras/src/tests/a2dp_iodev_unittest.cc @@ -44,7 +44,7 @@ static size_t cras_iodev_free_resources_called; static int a2dp_write_return_val[MAX_A2DP_WRITE_CALLS]; static unsigned int a2dp_write_index; static int a2dp_encode_called; -static cras_audio_area* dummy_audio_area; +static cras_audio_area* mock_audio_area; static thread_callback write_callback; static void* write_callback_data; static const char* fake_device_name = "fake device name"; @@ -79,9 +79,9 @@ void ResetStubData() { fake_transport = reinterpret_cast<struct cras_bt_transport*>(0x123); - if (!dummy_audio_area) { - dummy_audio_area = (cras_audio_area*)calloc( - 1, sizeof(*dummy_audio_area) + sizeof(cras_channel_area) * 2); + if (!mock_audio_area) { + mock_audio_area = (cras_audio_area*)calloc( + 1, sizeof(*mock_audio_area) + sizeof(cras_channel_area) * 2); } write_callback = NULL; @@ -108,8 +108,8 @@ class A2dpIodev : public testing::Test { } virtual void TearDown() { - free(dummy_audio_area); - dummy_audio_area = NULL; + free(mock_audio_area); + mock_audio_area = NULL; free(atlog); } }; @@ -899,7 +899,7 @@ int clock_gettime(clockid_t clk_id, struct timespec* tp) { } void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) { - iodev->area = dummy_audio_area; + iodev->area = mock_audio_area; } void cras_iodev_free_audio_area(struct cras_iodev* iodev) {} @@ -917,12 +917,15 @@ int cras_iodev_fill_odev_zeros(struct cras_iodev* odev, unsigned int frames) { void cras_audio_area_config_buf_pointers(struct cras_audio_area* area, const struct cras_audio_format* fmt, uint8_t* base_buffer) { - dummy_audio_area->channels[0].buf = base_buffer; + mock_audio_area->channels[0].buf = base_buffer; } struct audio_thread* cras_iodev_list_get_audio_thread() { return NULL; } +// From ewma_power +void ewma_power_disable(struct ewma_power* ewma) {} + // From audio_thread struct audio_thread_event_log* atlog; diff --git a/cras/src/tests/alsa_helpers_unittest.cc b/cras/src/tests/alsa_helpers_unittest.cc index 0e8112cd..32df30af 100644 --- a/cras/src/tests/alsa_helpers_unittest.cc +++ b/cras/src/tests/alsa_helpers_unittest.cc @@ -163,34 +163,12 @@ TEST(AlsaHelper, MatchChannelMapCapability51) { } TEST(AlsaHelper, Htimestamp) { - snd_pcm_t* dummy_handle = reinterpret_cast<snd_pcm_t*>(0x1); + snd_pcm_t* mock_handle = reinterpret_cast<snd_pcm_t*>(0x1); snd_pcm_uframes_t used; snd_pcm_uframes_t severe_underrun_frames = 480; struct timespec tstamp; - int htimestamp_enabled = 1; const char* dev_name = "dev_name"; - // Enable htimestamp use. - ResetStubData(); - EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled)); - EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 1); - EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 1); - EXPECT_EQ(1, htimestamp_enabled); - - // Try to enable htimestamp use: not supported. - ResetStubData(); - snd_pcm_sw_params_ret_vals.push_back(-EINVAL); - EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled)); - EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 2); - EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 2); - EXPECT_EQ(0, htimestamp_enabled); - - // Disable htimestamp use. - ResetStubData(); - EXPECT_EQ(0, cras_alsa_set_swparams(dummy_handle, &htimestamp_enabled)); - EXPECT_EQ(snd_pcm_sw_params_set_tstamp_mode_called, 0); - EXPECT_EQ(snd_pcm_sw_params_set_tstamp_type_called, 0); - ResetStubData(); tstamp.tv_sec = 0; tstamp.tv_nsec = 0; @@ -198,7 +176,7 @@ TEST(AlsaHelper, Htimestamp) { snd_pcm_htimestamp_tstamp_ret_val.tv_sec = 10; snd_pcm_htimestamp_tstamp_ret_val.tv_nsec = 10000; - cras_alsa_get_avail_frames(dummy_handle, 48000, severe_underrun_frames, + cras_alsa_get_avail_frames(mock_handle, 48000, severe_underrun_frames, dev_name, &used, &tstamp); EXPECT_EQ(used, snd_pcm_htimestamp_avail_ret_val); EXPECT_EQ(tstamp.tv_sec, snd_pcm_htimestamp_tstamp_ret_val.tv_sec); @@ -206,7 +184,7 @@ TEST(AlsaHelper, Htimestamp) { } TEST(AlsaHelper, GetAvailFramesSevereUnderrun) { - snd_pcm_t* dummy_handle = reinterpret_cast<snd_pcm_t*>(0x1); + snd_pcm_t* mock_handle = reinterpret_cast<snd_pcm_t*>(0x1); snd_pcm_uframes_t avail; snd_pcm_uframes_t severe_underrun_frames = 480; snd_pcm_uframes_t buffer_size = 48000; @@ -216,7 +194,7 @@ TEST(AlsaHelper, GetAvailFramesSevereUnderrun) { ResetStubData(); snd_pcm_htimestamp_avail_ret_val = buffer_size + severe_underrun_frames + 1; - rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size, + rc = cras_alsa_get_avail_frames(mock_handle, buffer_size, severe_underrun_frames, dev_name, &avail, &tstamp); // Returns -EPIPE when severe underrun happens. @@ -224,7 +202,7 @@ TEST(AlsaHelper, GetAvailFramesSevereUnderrun) { ResetStubData(); snd_pcm_htimestamp_avail_ret_val = buffer_size + severe_underrun_frames; - rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size, + rc = cras_alsa_get_avail_frames(mock_handle, buffer_size, severe_underrun_frames, dev_name, &avail, &tstamp); // Underrun which is not severe enough will be masked. @@ -234,7 +212,7 @@ TEST(AlsaHelper, GetAvailFramesSevereUnderrun) { ResetStubData(); snd_pcm_htimestamp_avail_ret_val = buffer_size - 1; - rc = cras_alsa_get_avail_frames(dummy_handle, buffer_size, + rc = cras_alsa_get_avail_frames(mock_handle, buffer_size, severe_underrun_frames, dev_name, &avail, &tstamp); // When avail < buffer_size, there is no underrun. diff --git a/cras/src/tests/alsa_io_unittest.cc b/cras/src/tests/alsa_io_unittest.cc index ba650a2f..b3059a23 100644 --- a/cras/src/tests/alsa_io_unittest.cc +++ b/cras/src/tests/alsa_io_unittest.cc @@ -137,7 +137,6 @@ static int cras_iodev_frames_queued_ret; static int cras_iodev_buffer_avail_ret; static int cras_alsa_resume_appl_ptr_called; static int cras_alsa_resume_appl_ptr_ahead; -static int ucm_get_enable_htimestamp_flag_ret; static const struct cras_volume_curve* fake_get_dBFS_volume_curve_val; static int cras_iodev_dsp_set_swap_mode_for_node_called; static std::map<std::string, long> ucm_get_default_node_gain_values; @@ -220,7 +219,6 @@ void ResetStubData() { cras_iodev_buffer_avail_ret = 0; cras_alsa_resume_appl_ptr_called = 0; cras_alsa_resume_appl_ptr_ahead = 0; - ucm_get_enable_htimestamp_flag_ret = 0; fake_get_dBFS_volume_curve_val = NULL; cras_iodev_dsp_set_swap_mode_for_node_called = 0; ucm_get_default_node_gain_values.clear(); @@ -2473,7 +2471,7 @@ int cras_alsa_set_hwparams(snd_pcm_t* handle, unsigned int dma_period_time) { return 0; } -int cras_alsa_set_swparams(snd_pcm_t* handle, int* enable_htimestamp) { +int cras_alsa_set_swparams(snd_pcm_t* handle) { return 0; } int cras_alsa_get_avail_frames(snd_pcm_t* handle, @@ -2768,10 +2766,6 @@ int ucm_get_min_buffer_level(struct cras_use_case_mgr* mgr, return 0; } -unsigned int ucm_get_enable_htimestamp_flag(struct cras_use_case_mgr* mgr) { - return ucm_get_enable_htimestamp_flag_ret; -} - unsigned int ucm_get_disable_software_volume(struct cras_use_case_mgr* mgr) { return 0; } diff --git a/cras/src/tests/alsa_ucm_unittest.cc b/cras/src/tests/alsa_ucm_unittest.cc index efb82652..44c35879 100644 --- a/cras/src/tests/alsa_ucm_unittest.cc +++ b/cras/src/tests/alsa_ucm_unittest.cc @@ -862,28 +862,6 @@ TEST(AlsaUcm, UseFullySpecifiedUCMConfig) { ASSERT_FALSE(fully_specified_flag); } -TEST(AlsaUcm, EnableHtimestampFlag) { - struct cras_use_case_mgr* mgr = &cras_ucm_mgr; - unsigned int enable_htimestamp_flag; - - std::string id = "=EnableHtimestamp//HiFi"; - ResetStubData(); - - /* Flag is not set */ - enable_htimestamp_flag = ucm_get_enable_htimestamp_flag(mgr); - ASSERT_FALSE(enable_htimestamp_flag); - - /* Flag is set to "1". */ - snd_use_case_get_value[id] = std::string("1"); - enable_htimestamp_flag = ucm_get_enable_htimestamp_flag(mgr); - ASSERT_TRUE(enable_htimestamp_flag); - - /* Flag is set to "0". */ - snd_use_case_get_value[id] = std::string("0"); - enable_htimestamp_flag = ucm_get_enable_htimestamp_flag(mgr); - ASSERT_FALSE(enable_htimestamp_flag); -} - TEST(AlsaUcm, GetMixerNameForDevice) { struct cras_use_case_mgr* mgr = &cras_ucm_mgr; const char *mixer_name_1, *mixer_name_2; diff --git a/cras/src/tests/audio_thread_unittest_obsolete.cc b/cras/src/tests/audio_thread_unittest_obsolete.cc index 0baeb38f..ae9f5ef3 100644 --- a/cras/src/tests/audio_thread_unittest_obsolete.cc +++ b/cras/src/tests/audio_thread_unittest_obsolete.cc @@ -52,8 +52,8 @@ static unsigned int dev_stream_mix_called; static struct timespec time_now; static int cras_fmt_conversion_needed_return_val; -static struct cras_audio_area* dummy_audio_area1; -static struct cras_audio_area* dummy_audio_area2; +static struct cras_audio_area* mock_audio_area1; +static struct cras_audio_area* mock_audio_area2; static struct cras_audio_format cras_iodev_set_format_val; static struct dev_stream_capture_call dev_stream_capture_call; @@ -95,18 +95,18 @@ class ReadStreamSuite : public testing::Test { SetupRstream(&rstream2_, 2); shm2_ = cras_rstream_input_shm(rstream2_); - dummy_audio_area1 = (cras_audio_area*)calloc( + mock_audio_area1 = (cras_audio_area*)calloc( 1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); - dummy_audio_area1->num_channels = 2; - channel_area_set_channel(&dummy_audio_area1->channels[0], CRAS_CH_FL); - channel_area_set_channel(&dummy_audio_area1->channels[1], CRAS_CH_FR); - rstream_->input_audio_area = dummy_audio_area1; - dummy_audio_area2 = (cras_audio_area*)calloc( + mock_audio_area1->num_channels = 2; + channel_area_set_channel(&mock_audio_area1->channels[0], CRAS_CH_FL); + channel_area_set_channel(&mock_audio_area1->channels[1], CRAS_CH_FR); + rstream_->input_audio_area = mock_audio_area1; + mock_audio_area2 = (cras_audio_area*)calloc( 1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); - dummy_audio_area2->num_channels = 2; - channel_area_set_channel(&dummy_audio_area2->channels[0], CRAS_CH_FL); - channel_area_set_channel(&dummy_audio_area2->channels[1], CRAS_CH_FR); - rstream2_->input_audio_area = dummy_audio_area2; + mock_audio_area2->num_channels = 2; + channel_area_set_channel(&mock_audio_area2->channels[0], CRAS_CH_FL); + channel_area_set_channel(&mock_audio_area2->channels[1], CRAS_CH_FR); + rstream2_->input_audio_area = mock_audio_area2; dev_stream_mix_dont_fill_next = 0; dev_stream_mix_count = 0; @@ -123,8 +123,8 @@ class ReadStreamSuite : public testing::Test { free(rstream_); free(shm2_->area); free(rstream2_); - free(dummy_audio_area1); - free(dummy_audio_area2); + free(mock_audio_area1); + free(mock_audio_area2); } void SetupRstream(struct cras_rstream** rstream, int fd) { @@ -1444,18 +1444,18 @@ class ActiveDevicesSuite : public testing::Test { rstream2_->buffer_frames -= 50; rstream2_->cb_threshold -= 50; - dummy_audio_area1 = (cras_audio_area*)calloc( + mock_audio_area1 = (cras_audio_area*)calloc( 1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); - dummy_audio_area1->num_channels = 2; - channel_area_set_channel(&dummy_audio_area1->channels[0], CRAS_CH_FL); - channel_area_set_channel(&dummy_audio_area1->channels[1], CRAS_CH_FR); - rstream_->input_audio_area = dummy_audio_area1; - dummy_audio_area2 = (cras_audio_area*)calloc( + mock_audio_area1->num_channels = 2; + channel_area_set_channel(&mock_audio_area1->channels[0], CRAS_CH_FL); + channel_area_set_channel(&mock_audio_area1->channels[1], CRAS_CH_FR); + rstream_->input_audio_area = mock_audio_area1; + mock_audio_area2 = (cras_audio_area*)calloc( 1, sizeof(cras_audio_area) + 2 * sizeof(cras_channel_area)); - dummy_audio_area2->num_channels = 2; - channel_area_set_channel(&dummy_audio_area2->channels[0], CRAS_CH_FL); - channel_area_set_channel(&dummy_audio_area2->channels[1], CRAS_CH_FR); - rstream2_->input_audio_area = dummy_audio_area2; + mock_audio_area2->num_channels = 2; + channel_area_set_channel(&mock_audio_area2->channels[0], CRAS_CH_FL); + channel_area_set_channel(&mock_audio_area2->channels[1], CRAS_CH_FR); + rstream2_->input_audio_area = mock_audio_area2; cras_iodev_set_format_called = 0; close_dev_called_ = 0; @@ -1483,8 +1483,8 @@ class ActiveDevicesSuite : public testing::Test { shm = cras_rstream_output_shm(rstream_); free(shm->header); free(rstream_); - free(dummy_audio_area1); - free(dummy_audio_area2); + free(mock_audio_area1); + free(mock_audio_area2); } void SetupRstream(struct cras_rstream** rstream) { diff --git a/cras/src/tests/bt_device_unittest.cc b/cras/src/tests/bt_device_unittest.cc index a9213f3d..ccb581cc 100644 --- a/cras/src/tests/bt_device_unittest.cc +++ b/cras/src/tests/bt_device_unittest.cc @@ -228,7 +228,8 @@ TEST_F(BtDeviceTestSuite, SetDeviceConnectedA2dpOnly) { device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); EXPECT_NE((void*)NULL, device); - cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID); + cras_bt_device_set_supported_profiles(device, + CRAS_BT_DEVICE_PROFILE_A2DP_SINK); cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); @@ -268,8 +269,9 @@ TEST_F(BtDeviceTestSuite, SetDeviceConnectedHfpHspOnly) { device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); EXPECT_NE((void*)NULL, device); - cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); - cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); + cras_bt_device_set_supported_profiles( + device, CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | + CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); @@ -310,9 +312,10 @@ TEST_F(BtDeviceTestSuite, SetDeviceConnectedA2dpHfpHsp) { device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); EXPECT_NE((void*)NULL, device); - cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID); - cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); - cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); + cras_bt_device_set_supported_profiles( + device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK | + CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | + CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); @@ -360,9 +363,10 @@ TEST_F(BtDeviceTestSuite, DevConnectedConflictCheck) { device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); EXPECT_NE((void*)NULL, device); - cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID); - cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); - cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); + cras_bt_device_set_supported_profiles( + device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK | + CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | + CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); @@ -398,9 +402,10 @@ TEST_F(BtDeviceTestSuite, A2dpDropped) { device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); EXPECT_NE((void*)NULL, device); - cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID); - cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); - cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); + cras_bt_device_set_supported_profiles( + device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK | + CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | + CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); @@ -433,9 +438,10 @@ TEST_F(BtDeviceTestSuite, DevConnectDisconnectBackToBack) { device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); EXPECT_NE((void*)NULL, device); - cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID); - cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); - cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); + cras_bt_device_set_supported_profiles( + device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK | + CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | + CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); @@ -500,9 +506,10 @@ TEST_F(BtDeviceTestSuite, ConnectionWatchTimeout) { device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); EXPECT_NE((void*)NULL, device); - cras_bt_device_add_supported_profiles(device, A2DP_SINK_UUID); - cras_bt_device_add_supported_profiles(device, HSP_HS_UUID); - cras_bt_device_add_supported_profiles(device, HFP_HF_UUID); + cras_bt_device_set_supported_profiles( + device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK | + CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | + CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); cur = msg_root = NewMockDBusConnectedMessage(1); cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); diff --git a/cras/src/tests/bt_io_unittest.cc b/cras/src/tests/bt_io_unittest.cc index 7ccb669e..ee013cf3 100644 --- a/cras/src/tests/bt_io_unittest.cc +++ b/cras/src/tests/bt_io_unittest.cc @@ -423,7 +423,8 @@ int cras_iodev_list_rm_input(struct cras_iodev* dev) { } // From bt device -int cras_bt_device_get_active_profile(const struct cras_bt_device* device) { +unsigned int cras_bt_device_get_active_profile( + const struct cras_bt_device* device) { return cras_bt_device_get_active_profile_ret; } diff --git a/cras/src/tests/capture_rclient_unittest.cc b/cras/src/tests/capture_rclient_unittest.cc index 4c1ab07f..b749f1a5 100644 --- a/cras/src/tests/capture_rclient_unittest.cc +++ b/cras/src/tests/capture_rclient_unittest.cc @@ -23,8 +23,8 @@ static unsigned int cras_observer_remove_called; static int stream_list_add_called; static int stream_list_add_return; static unsigned int stream_list_rm_called; -static struct cras_audio_shm dummy_shm; -static struct cras_rstream dummy_rstream; +static struct cras_audio_shm mock_shm; +static struct cras_rstream mock_rstream; void ResetStubData() { cras_make_fd_nonblocking_called = 0; @@ -177,44 +177,6 @@ TEST_F(CCRMessageSuite, StreamConnectMessageInvalidClientId) { EXPECT_EQ(stream_id, out_msg.stream_id); } -/* - * TODO(yuhsaun): Remove this test when there are no client uses the old - * craslib. (CRAS_PROTO_VER = 5) - */ -TEST_F(CCRMessageSuite, StreamConnectMessageOldProtocal) { - struct cras_client_stream_connected out_msg; - int rc; - - struct cras_connect_message_old msg; - cras_stream_id_t stream_id = 0x10002; - - msg.proto_version = 5; - msg.direction = CRAS_STREAM_INPUT; - msg.stream_id = stream_id; - msg.stream_type = CRAS_STREAM_TYPE_DEFAULT; - msg.buffer_frames = 480; - msg.cb_threshold = 240; - msg.flags = 0; - msg.effects = 0; - pack_cras_audio_format(&msg.format, &fmt); - msg.dev_idx = NO_DEVICE; - msg.client_shm_size = 0; - msg.client_type = CRAS_CLIENT_TYPE_TEST; - msg.header.id = CRAS_SERVER_CONNECT_STREAM; - msg.header.length = sizeof(struct cras_connect_message_old); - - fd_ = 100; - rc = - rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_, 1); - EXPECT_EQ(1, cras_make_fd_nonblocking_called); - EXPECT_EQ(1, stream_list_add_called); - EXPECT_EQ(0, stream_list_rm_called); - - rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg)); - EXPECT_EQ(sizeof(out_msg), rc); - EXPECT_EQ(stream_id, out_msg.stream_id); -} - TEST_F(CCRMessageSuite, StreamDisconnectMessage) { struct cras_disconnect_stream_message msg; cras_stream_id_t stream_id = 0x10002; @@ -290,16 +252,16 @@ int stream_list_add(struct stream_list* list, struct cras_rstream** stream) { int ret; - *stream = &dummy_rstream; + *stream = &mock_rstream; stream_list_add_called++; ret = stream_list_add_return; if (ret) stream_list_add_return = -EINVAL; - dummy_rstream.shm = &dummy_shm; - dummy_rstream.direction = config->direction; - dummy_rstream.stream_id = config->stream_id; + mock_rstream.shm = &mock_shm; + mock_rstream.direction = config->direction; + mock_rstream.stream_id = config->stream_id; return ret; } diff --git a/cras/src/tests/control_rclient_unittest.cc b/cras/src/tests/control_rclient_unittest.cc index b0d01fc6..d6b63aab 100644 --- a/cras/src/tests/control_rclient_unittest.cc +++ b/cras/src/tests/control_rclient_unittest.cc @@ -47,8 +47,8 @@ static unsigned int stream_list_add_stream_called; static unsigned int stream_list_disconnect_stream_called; static unsigned int cras_iodev_list_rm_input_called; static unsigned int cras_iodev_list_rm_output_called; -static struct cras_audio_shm dummy_shm; -static struct cras_rstream dummy_rstream; +static struct cras_audio_shm mock_shm; +static struct cras_rstream mock_rstream; static size_t cras_observer_num_ops_registered; static size_t cras_observer_register_notify_called; static size_t cras_observer_add_called; @@ -222,30 +222,6 @@ TEST_F(RClientMessagesSuite, ConnectMsgWithBadFd) { stream_list_disconnect_stream_called); } -TEST_F(RClientMessagesSuite, ConnectMsgFromOldClient) { - struct cras_client_stream_connected out_msg; - int rc; - - cras_rstream_create_stream_out = rstream_; - cras_iodev_attach_stream_retval = 0; - - connect_msg_.header.length = sizeof(struct cras_connect_message_old); - connect_msg_.proto_version = 5; - - fd_ = 100; - rc = rclient_->ops->handle_message_from_client(rclient_, &connect_msg_.header, - &fd_, 1); - EXPECT_EQ(0, rc); - EXPECT_EQ(1, cras_make_fd_nonblocking_called); - - rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg)); - EXPECT_EQ(sizeof(out_msg), rc); - EXPECT_EQ(stream_id_, out_msg.stream_id); - EXPECT_EQ(0, out_msg.err); - EXPECT_EQ(1, stream_list_add_stream_called); - EXPECT_EQ(0, stream_list_disconnect_stream_called); -} - TEST_F(RClientMessagesSuite, StreamConnectMessageValidDirection) { struct cras_client_stream_connected out_msg; int rc; @@ -911,16 +887,16 @@ int stream_list_add(struct stream_list* list, struct cras_rstream** stream) { int ret; - *stream = &dummy_rstream; + *stream = &mock_rstream; stream_list_add_stream_called++; ret = stream_list_add_stream_return; if (ret) stream_list_add_stream_return = -EINVAL; - dummy_rstream.shm = &dummy_shm; - dummy_rstream.direction = config->direction; - dummy_rstream.stream_id = config->stream_id; + mock_rstream.shm = &mock_shm; + mock_rstream.direction = config->direction; + mock_rstream.stream_id = config->stream_id; return ret; } @@ -987,4 +963,8 @@ bool cras_audio_format_valid(const struct cras_audio_format* fmt) { return true; } +struct packet_status_logger* cras_hfp_ag_get_wbs_logger() { + return NULL; +} + } // extern "C" diff --git a/cras/src/tests/dev_stream_unittest.cc b/cras/src/tests/dev_stream_unittest.cc index 9210021e..640ca932 100644 --- a/cras/src/tests/dev_stream_unittest.cc +++ b/cras/src/tests/dev_stream_unittest.cc @@ -340,7 +340,7 @@ TEST_F(CreateSuite, CreateSRC44to48) { // Converter tmp and output buffers are large enough for device output. unsigned int device_frames = cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames, out_fmt.frame_rate); - EXPECT_LE(kBufferFrames, device_frames); // Sanity check. + EXPECT_LE(kBufferFrames, device_frames); // Soundness check. EXPECT_LE(device_frames, config_format_converter_frames); EXPECT_LE(device_frames, dev_stream->conv_buffer_size_frames); dev_stream_destroy(dev_stream); @@ -364,7 +364,7 @@ TEST_F(CreateSuite, CreateSRC44from48Input) { // Converter tmp and output buffers are large enough for device input. unsigned int device_frames = cras_frames_at_rate(out_fmt.frame_rate, kBufferFrames, in_fmt.frame_rate); - EXPECT_LE(kBufferFrames, device_frames); // Sanity check. + EXPECT_LE(kBufferFrames, device_frames); // Soundness check. EXPECT_LE(device_frames, config_format_converter_frames); EXPECT_EQ(&processed_fmt, config_format_converter_from_fmt); EXPECT_LE(device_frames, dev_stream->conv_buffer_size_frames); @@ -420,7 +420,7 @@ TEST_F(CreateSuite, CreateSRC8to48) { // Converter tmp and output buffers are large enough for device output. unsigned int device_frames = cras_frames_at_rate(in_fmt.frame_rate, kBufferFrames, out_fmt.frame_rate); - EXPECT_LE(kBufferFrames, device_frames); // Sanity check. + EXPECT_LE(kBufferFrames, device_frames); // Soundness check. EXPECT_LE(device_frames, config_format_converter_frames); EXPECT_LE(device_frames, dev_stream->conv_buffer_size_frames); dev_stream_destroy(dev_stream); @@ -441,7 +441,7 @@ TEST_F(CreateSuite, CreateSRC8from48Input) { // Converter tmp and output buffers are large enough for device input. unsigned int device_frames = cras_frames_at_rate(out_fmt.frame_rate, kBufferFrames, in_fmt.frame_rate); - EXPECT_LE(kBufferFrames, device_frames); // Sanity check. + EXPECT_LE(kBufferFrames, device_frames); // Soundness check. EXPECT_LE(device_frames, config_format_converter_frames); EXPECT_LE(device_frames, dev_stream->conv_buffer_size_frames); dev_stream_destroy(dev_stream); diff --git a/cras/src/tests/empty_iodev_unittest.cc b/cras/src/tests/empty_iodev_unittest.cc index 585fba32..148d5301 100644 --- a/cras/src/tests/empty_iodev_unittest.cc +++ b/cras/src/tests/empty_iodev_unittest.cc @@ -13,7 +13,7 @@ extern "C" { static struct timespec clock_gettime_retspec; static struct cras_audio_format fake_format; -static cras_audio_area dummy_audio_area; +static cras_audio_area mock_audio_area; namespace { @@ -57,7 +57,7 @@ int cras_iodev_default_no_stream_playback(struct cras_iodev* odev, int enable) { } void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) { - iodev->area = &dummy_audio_area; + iodev->area = &mock_audio_area; } void cras_iodev_free_audio_area(struct cras_iodev* iodev) {} diff --git a/cras/src/tests/ewma_power_unittest.cc b/cras/src/tests/ewma_power_unittest.cc new file mode 100644 index 00000000..10f03189 --- /dev/null +++ b/cras/src/tests/ewma_power_unittest.cc @@ -0,0 +1,129 @@ +// Copyright (c) 2020 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <gtest/gtest.h> + +extern "C" { +#include "ewma_power.h" +} + +namespace { + +TEST(EWMAPower, RelativePowerValue) { + struct ewma_power ewma; + int16_t buf[480]; + float f; + int i; + + for (i = 0; i < 480; i++) + buf[i] = 0x00fe; + + ewma_power_init(&ewma, 48000); + EXPECT_EQ(48, ewma.step_fr); + + ewma_power_calculate(&ewma, buf, 1, 480); + EXPECT_LT(0.0f, ewma.power); + + // After 10ms of silence the power value decreases. + f = ewma.power; + for (i = 0; i < 480; i++) + buf[i] = 0x00; + ewma_power_calculate(&ewma, buf, 1, 480); + EXPECT_LT(ewma.power, f); + + // After 300ms of silence the power value decreases to insignificant low. + for (i = 0; i < 30; i++) + ewma_power_calculate(&ewma, buf, 1, 480); + EXPECT_LT(ewma.power, 1.0e-10); +} + +TEST(EWMAPower, PowerInStereoData) { + struct ewma_power ewma; + int16_t buf[960]; + int i; + float f; + + ewma_power_init(&ewma, 48000); + + for (i = 0; i < 960; i += 2) { + buf[i] = 0x0; + buf[i + 1] = 0x00fe; + } + ewma_power_calculate(&ewma, buf, 2, 480); + EXPECT_LT(0.0f, ewma.power); + + // After 10ms of silence the power value decreases. + f = ewma.power; + for (i = 0; i < 960; i++) + buf[i] = 0x0; + ewma_power_calculate(&ewma, buf, 2, 480); + EXPECT_LT(ewma.power, f); + + // After 300ms of silence the power value decreases to insignificant low. + for (i = 0; i < 30; i++) + ewma_power_calculate(&ewma, buf, 2, 480); + EXPECT_LT(ewma.power, 1.0e-10); + + // Assume the data is silent in the other channel. + ewma_power_init(&ewma, 48000); + + for (i = 0; i < 960; i += 2) { + buf[i] = 0x0ffe; + buf[i + 1] = 0x0; + } + ewma_power_calculate(&ewma, buf, 2, 480); + EXPECT_LT(0.0f, ewma.power); +} + +TEST(EWMAPower, PowerInAudioArea) { + struct ewma_power ewma; + struct cras_audio_area* area = cras_audio_area_create(4); + struct cras_audio_format* fmt = + cras_audio_format_create(SND_PCM_FORMAT_S16_LE, 48000, 4); + int8_t layout[CRAS_CH_MAX] = {0, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + int16_t buf[1920]; + int i; + float f; + + cras_audio_format_set_channel_layout(fmt, layout); + cras_audio_area_config_channels(area, fmt); + + for (i = 0; i < 1920; i += 4) { + buf[i] = 0x0ffe; + buf[i + 1] = 0x0; + buf[i + 2] = 0x0; + buf[i + 3] = 0x0ffe; + } + ewma_power_init(&ewma, 48000); + ewma_power_calculate_area(&ewma, buf, area, 480); + f = ewma.power; + EXPECT_LT(0.0f, f); + + /* Change the layout in the same audio area. Expect the power be lower because + * one of the channel is now silent. */ + layout[CRAS_CH_FR] = 2; + cras_audio_format_set_channel_layout(fmt, layout); + cras_audio_area_config_channels(area, fmt); + ewma_power_init(&ewma, 48000); + ewma_power_calculate_area(&ewma, buf, area, 480); + EXPECT_GT(f, ewma.power); + + /* Change layout to the two silent channels. Expect power is 0.0f. */ + layout[CRAS_CH_FL] = 1; + cras_audio_format_set_channel_layout(fmt, layout); + cras_audio_area_config_channels(area, fmt); + ewma_power_init(&ewma, 48000); + ewma_power_calculate_area(&ewma, buf, area, 480); + EXPECT_EQ(0.0f, ewma.power); + + cras_audio_format_destroy(fmt); + cras_audio_area_destroy(area); +} + +} // namespace + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/cras/src/tests/fmt_conv_ops_unittest.cc b/cras/src/tests/fmt_conv_ops_unittest.cc index d8feeab3..ebe8b65d 100644 --- a/cras/src/tests/fmt_conv_ops_unittest.cc +++ b/cras/src/tests/fmt_conv_ops_unittest.cc @@ -4,6 +4,8 @@ #include <gtest/gtest.h> #include <limits.h> +#include <math.h> +#include <stdint.h> #include <sys/param.h> #include <memory> @@ -477,7 +479,7 @@ TEST(FormatConverterOpsTest, _51ToStereoS16LE) { const size_t out_ch = 2; const size_t left = 0; const size_t right = 1; - const size_t center = 4; + const size_t center = 2; S16LEPtr src = CreateS16LE(frames * in_ch); S16LEPtr dst = CreateS16LE(frames * out_ch); @@ -486,11 +488,58 @@ TEST(FormatConverterOpsTest, _51ToStereoS16LE) { s16_51_to_stereo((uint8_t*)src.get(), frames, (uint8_t*)dst.get()); EXPECT_EQ(ret, frames); + /* Use the normalized_factor from the left channel = 1 / (|1| + |0.707|) + * to prevent mixing overflow. + */ + const float normalized_factor = 0.585; + + for (size_t i = 0; i < frames; ++i) { + int16_t half_center = src[i * 6 + center] * 0.707 * normalized_factor; + int16_t l = normalized_factor * src[i * 6 + left] + half_center; + int16_t r = normalized_factor * src[i * 6 + right] + half_center; + + EXPECT_EQ(l, dst[i * 2 + left]); + EXPECT_EQ(r, dst[i * 2 + right]); + } +} + +// Test 5.1 to Quad conversion. S16_LE. +TEST(FormatConverterOpsTest, _51ToQuadS16LE) { + const size_t frames = 4096; + const size_t in_ch = 6; + const size_t out_ch = 4; + const unsigned int fl_quad = 0; + const unsigned int fr_quad = 1; + const unsigned int rl_quad = 2; + const unsigned int rr_quad = 3; + + const unsigned int fl_51 = 0; + const unsigned int fr_51 = 1; + const unsigned int center_51 = 2; + const unsigned int lfe_51 = 3; + const unsigned int rl_51 = 4; + const unsigned int rr_51 = 5; + + S16LEPtr src = CreateS16LE(frames * in_ch); + S16LEPtr dst = CreateS16LE(frames * out_ch); + + size_t ret = s16_51_to_quad((uint8_t*)src.get(), frames, (uint8_t*)dst.get()); + EXPECT_EQ(ret, frames); + + /* Use normalized_factor from the left channel = 1 / (|1| + |0.707| + |0.5|) + * to prevent overflow. */ + const float normalized_factor = 0.453; for (size_t i = 0; i < frames; ++i) { - int16_t half_center = src[i * 6 + center] / 2; - EXPECT_EQ(S16AddAndClip(src[i * 6 + left], half_center), dst[i * 2 + left]); - EXPECT_EQ(S16AddAndClip(src[i * 6 + right], half_center), - dst[i * 2 + right]); + int16_t half_center = src[i * 6 + center_51] * 0.707 * normalized_factor; + int16_t lfe = src[6 * i + lfe_51] * 0.5 * normalized_factor; + int16_t fl = normalized_factor * src[6 * i + fl_51] + half_center + lfe; + int16_t fr = normalized_factor * src[6 * i + fr_51] + half_center + lfe; + int16_t rl = normalized_factor * src[6 * i + rl_51] + lfe; + int16_t rr = normalized_factor * src[6 * i + rr_51] + lfe; + EXPECT_EQ(fl, dst[4 * i + fl_quad]); + EXPECT_EQ(fr, dst[4 * i + fr_quad]); + EXPECT_EQ(rl, dst[4 * i + rl_quad]); + EXPECT_EQ(rr, dst[4 * i + rr_quad]); } } diff --git a/cras/src/tests/fmt_conv_unittest.cc b/cras/src/tests/fmt_conv_unittest.cc index 5474f172..c66984ee 100644 --- a/cras/src/tests/fmt_conv_unittest.cc +++ b/cras/src/tests/fmt_conv_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <gtest/gtest.h> +#include <math.h> #include <sys/param.h> extern "C" { @@ -459,6 +460,81 @@ TEST(FormatConverterTest, SurroundToStereo) { free(out_buff); } +// Test 5.1 to Quad mix. +TEST(FormatConverterTest, SurroundToQuad) { + struct cras_fmt_conv* c; + struct cras_audio_format in_fmt; + struct cras_audio_format out_fmt; + + size_t out_frames; + int16_t* in_buff; + int16_t* out_buff; + unsigned int i; + const size_t buf_size = 4096; + unsigned int in_buf_size = 4096; + + ResetStub(); + in_fmt.format = SND_PCM_FORMAT_S16_LE; + out_fmt.format = SND_PCM_FORMAT_S16_LE; + in_fmt.num_channels = 6; + out_fmt.num_channels = 4; + in_fmt.frame_rate = 48000; + out_fmt.frame_rate = 48000; + for (i = 0; i < CRAS_CH_MAX; i++) + in_fmt.channel_layout[i] = surround_channel_center_layout[i]; + + c = cras_fmt_conv_create(&in_fmt, &out_fmt, buf_size, 0); + ASSERT_NE(c, (void*)NULL); + + out_frames = cras_fmt_conv_out_frames_to_in(c, buf_size); + EXPECT_EQ(buf_size, out_frames); + + out_frames = cras_fmt_conv_in_frames_to_out(c, buf_size); + EXPECT_EQ(buf_size, out_frames); + + in_buff = (int16_t*)malloc(buf_size * 2 * cras_get_format_bytes(&in_fmt)); + + const int16_t in_fl = 100; + const int16_t in_fr = 200; + const int16_t in_rl = 200; + const int16_t in_rr = 300; + const int16_t in_fc = 60; + const int16_t in_lfe = 90; + + for (i = 0; i < buf_size; i++) { + in_buff[i * 6 + CRAS_CH_FL] = in_fl; + in_buff[i * 6 + CRAS_CH_FR] = in_fr; + in_buff[i * 6 + CRAS_CH_RL] = in_rl; + in_buff[i * 6 + CRAS_CH_RR] = in_rr; + in_buff[i * 6 + CRAS_CH_FC] = in_fc; + in_buff[i * 6 + CRAS_CH_LFE] = in_lfe; + } + out_buff = (int16_t*)malloc(buf_size * 2 * cras_get_format_bytes(&out_fmt)); + out_frames = cras_fmt_conv_convert_frames( + c, (uint8_t*)in_buff, (uint8_t*)out_buff, &in_buf_size, buf_size); + EXPECT_EQ(buf_size, out_frames); + + // This is the sum of mtx[CRAS_CH_FL] coefficients. + const float normalize_factor = 1.0 / (1 + 0.707 + 0.5); + + for (i = 0; i < buf_size; i++) { + int16_t lfe = 0.5 * normalize_factor * in_lfe; + int16_t center = 0.707 * normalize_factor * in_fc; + int16_t fl = normalize_factor * in_fl + center + lfe; + int16_t fr = normalize_factor * in_fr + center + lfe; + int16_t rl = normalize_factor * in_rl + lfe; + int16_t rr = normalize_factor * in_rr + lfe; + + EXPECT_EQ(fl, out_buff[i * 4 + CRAS_CH_FL]); + EXPECT_EQ(fr, out_buff[i * 4 + CRAS_CH_FR]); + EXPECT_EQ(rl, out_buff[i * 4 + CRAS_CH_RL]); + EXPECT_EQ(rr, out_buff[i * 4 + CRAS_CH_RR]); + } + cras_fmt_conv_destroy(&c); + free(in_buff); + free(out_buff); +} + // Test Quad to Stereo mix. TEST(FormatConverterTest, QuadToStereo) { struct cras_fmt_conv* c; diff --git a/cras/src/tests/hfp_ag_profile_unittest.cc b/cras/src/tests/hfp_ag_profile_unittest.cc index 1a115f04..3ecd2407 100644 --- a/cras/src/tests/hfp_ag_profile_unittest.cc +++ b/cras/src/tests/hfp_ag_profile_unittest.cc @@ -286,6 +286,9 @@ void cras_bt_device_notify_profile_dropped( cras_bt_device_notify_profile_dropped_profile = profile; } +void hfp_info_set_wbs_logger(struct hfp_info* info, + struct packet_status_logger* wbs_logger) {} + void cras_observer_notify_bt_battery_changed(const char* address, uint32_t level) { return; diff --git a/cras/src/tests/hfp_alsa_iodev_unittest.cc b/cras/src/tests/hfp_alsa_iodev_unittest.cc index 89756948..c5bd4e9a 100644 --- a/cras/src/tests/hfp_alsa_iodev_unittest.cc +++ b/cras/src/tests/hfp_alsa_iodev_unittest.cc @@ -477,6 +477,9 @@ void cras_iodev_set_active_node(struct cras_iodev* iodev, iodev->active_node = node; } +// From ewma_power +void ewma_power_disable(struct ewma_power* ewma) {} + size_t cras_system_get_volume() { return 0; } diff --git a/cras/src/tests/hfp_info_unittest.cc b/cras/src/tests/hfp_info_unittest.cc index 48a1c89c..24f536ae 100644 --- a/cras/src/tests/hfp_info_unittest.cc +++ b/cras/src/tests/hfp_info_unittest.cc @@ -3,10 +3,15 @@ * found in the LICENSE file. */ +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <stdint.h> #include <time.h> +using testing::MatchesRegex; +using testing::internal::CaptureStdout; +using testing::internal::GetCapturedStdout; + extern "C" { #include "cras_hfp_info.c" #include "sbc_codec_stub.h" @@ -504,6 +509,26 @@ TEST(HfpInfo, StartHfpInfoAndWriteMsbc) { hfp_info_destroy(info); } +TEST(HfpInfo, WBSLoggerPacketStatusDumpBinary) { + struct packet_status_logger logger; + char log_regex[64]; + int num_wraps[5] = {0, 0, 0, 1, 1}; + int wp[5] = {40, 150, 162, 100, 32}; + + /* Expect the log line wraps at correct length to avoid feedback redact. */ + snprintf(log_regex, 64, "([01D]{%d}\n)*", PACKET_STATUS_LOG_LINE_WRAP); + + packet_status_logger_init(&logger); + logger.size = PACKET_STATUS_LEN_BYTES * 8; + for (int i = 0; i < 5; i++) { + CaptureStdout(); + logger.num_wraps = num_wraps[i]; + logger.wp = wp[i]; + packet_status_logger_dump_binary(&logger); + EXPECT_THAT(GetCapturedStdout(), MatchesRegex(log_regex)); + } +} + } // namespace extern "C" { @@ -549,6 +574,10 @@ int cras_msbc_plc_handle_good_frames(struct cras_msbc_plc* plc, cras_msbc_plc_handle_good_frames_called++; return MSBC_CODE_SIZE; } +void packet_status_logger_init(struct packet_status_logger* logger) {} + +void packet_status_logger_update(struct packet_status_logger* logger, + bool val) {} } int main(int argc, char** argv) { diff --git a/cras/src/tests/hfp_iodev_unittest.cc b/cras/src/tests/hfp_iodev_unittest.cc index 2a61140e..18262bf9 100644 --- a/cras/src/tests/hfp_iodev_unittest.cc +++ b/cras/src/tests/hfp_iodev_unittest.cc @@ -43,7 +43,7 @@ static size_t hfp_fill_output_with_zeros_called; static size_t hfp_force_output_level_called; static size_t hfp_force_output_level_target; static size_t fake_buffer_size = 500; -static cras_audio_area* dummy_audio_area; +static cras_audio_area* mock_audio_area; void ResetStubData() { cras_bt_device_append_iodev_called = 0; @@ -73,9 +73,9 @@ void ResetStubData() { fake_info = reinterpret_cast<struct hfp_info*>(0x123); - if (!dummy_audio_area) { - dummy_audio_area = (cras_audio_area*)calloc( - 1, sizeof(*dummy_audio_area) + sizeof(cras_channel_area) * 2); + if (!mock_audio_area) { + mock_audio_area = (cras_audio_area*)calloc( + 1, sizeof(*mock_audio_area) + sizeof(cras_channel_area) * 2); } } @@ -86,8 +86,8 @@ class HfpIodev : public testing::Test { virtual void SetUp() { ResetStubData(); } virtual void TearDown() { - free(dummy_audio_area); - dummy_audio_area = NULL; + free(mock_audio_area); + mock_audio_area = NULL; } }; @@ -243,6 +243,9 @@ void cras_iodev_set_active_node(struct cras_iodev* iodev, iodev->active_node = node; } +// From ewma_power +void ewma_power_disable(struct ewma_power* ewma) {} + // From system_state. size_t cras_system_get_volume() { return 0; @@ -351,7 +354,7 @@ void hfp_force_output_level(struct hfp_info* info, unsigned int level) { } void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) { - iodev->area = dummy_audio_area; + iodev->area = mock_audio_area; } void cras_iodev_free_audio_area(struct cras_iodev* iodev) {} @@ -367,7 +370,7 @@ int cras_iodev_fill_odev_zeros(struct cras_iodev* odev, unsigned int frames) { void cras_audio_area_config_buf_pointers(struct cras_audio_area* area, const struct cras_audio_format* fmt, uint8_t* base_buffer) { - dummy_audio_area->channels[0].buf = base_buffer; + mock_audio_area->channels[0].buf = base_buffer; } int hfp_set_call_status(struct hfp_slc_handle* handle, int call) { diff --git a/cras/src/tests/hfp_slc_unittest.cc b/cras/src/tests/hfp_slc_unittest.cc index 4501b07a..966278f4 100644 --- a/cras/src/tests/hfp_slc_unittest.cc +++ b/cras/src/tests/hfp_slc_unittest.cc @@ -202,7 +202,7 @@ TEST(HfpSlc, InitializeSlcSupportsHfIndicator) { /* Assert "\r\n+BIND: (2)\r\n" response is received */ err = read(sock[1], buf, 256); - chp = strstr(buf, "\r\n+BIND: (2)\r\n"); + chp = strstr(buf, "\r\n+BIND: (1,2)\r\n"); ASSERT_NE((void*)NULL, (void*)chp); chp = strstr(buf, "\r\nOK\r\n"); ASSERT_NE((void*)NULL, (void*)chp); diff --git a/cras/src/tests/iodev_list_unittest.cc b/cras/src/tests/iodev_list_unittest.cc index d40facab..272537fc 100644 --- a/cras/src/tests/iodev_list_unittest.cc +++ b/cras/src/tests/iodev_list_unittest.cc @@ -44,8 +44,8 @@ static struct audio_thread thread; static struct cras_iodev loopback_input; static int cras_iodev_close_called; static struct cras_iodev* cras_iodev_close_dev; -static struct cras_iodev dummy_hotword_iodev; -static struct cras_iodev dummy_empty_iodev[2]; +static struct cras_iodev mock_hotword_iodev; +static struct cras_iodev mock_empty_iodev[2]; static stream_callback* stream_add_cb; static stream_callback* stream_rm_cb; static struct cras_rstream* stream_list_get_ret; @@ -233,11 +233,11 @@ class IoDevTestSuite : public testing::Test { DL_APPEND(fake_sco_out_dev.nodes, &fake_sco_out_node); fake_sco_in_node.is_sco_pcm = 0; fake_sco_out_node.is_sco_pcm = 0; - dummy_empty_iodev[0].state = CRAS_IODEV_STATE_CLOSE; - dummy_empty_iodev[0].update_active_node = update_active_node; - dummy_empty_iodev[1].state = CRAS_IODEV_STATE_CLOSE; - dummy_empty_iodev[1].update_active_node = update_active_node; - dummy_hotword_iodev.update_active_node = update_active_node; + mock_empty_iodev[0].state = CRAS_IODEV_STATE_CLOSE; + mock_empty_iodev[0].update_active_node = update_active_node; + mock_empty_iodev[1].state = CRAS_IODEV_STATE_CLOSE; + mock_empty_iodev[1].update_active_node = update_active_node; + mock_hotword_iodev.update_active_node = update_active_node; } virtual void TearDown() { @@ -681,8 +681,7 @@ TEST_F(IoDevTestSuite, InitDevFailShouldScheduleRetry) { EXPECT_EQ(2, cras_iodev_open_called); EXPECT_EQ(1, audio_thread_add_stream_called); EXPECT_EQ(0, update_active_node_called); - EXPECT_EQ(&dummy_empty_iodev[CRAS_STREAM_OUTPUT], - audio_thread_add_stream_dev); + EXPECT_EQ(&mock_empty_iodev[CRAS_STREAM_OUTPUT], audio_thread_add_stream_dev); EXPECT_NE((void*)NULL, cras_tm_timer_cb); EXPECT_EQ(1, cras_tm_create_timer_called); @@ -693,7 +692,7 @@ TEST_F(IoDevTestSuite, InitDevFailShouldScheduleRetry) { EXPECT_EQ(1, cras_tm_create_timer_called); EXPECT_EQ(1, audio_thread_add_stream_called); - dummy_empty_iodev[CRAS_STREAM_OUTPUT].format = &fmt_; + mock_empty_iodev[CRAS_STREAM_OUTPUT].format = &fmt_; cras_tm_timer_cb = NULL; cras_iodev_open_ret[3] = -5; stream_add_cb(&rstream); @@ -1752,7 +1751,7 @@ TEST_F(IoDevTestSuite, AddRemovePinnedStream) { EXPECT_EQ(2, update_active_node_called); // Unselect d1_ and select to d2_ EXPECT_EQ(&d2_, update_active_node_iodev_val[0]); - EXPECT_EQ(&dummy_empty_iodev[CRAS_STREAM_OUTPUT], + EXPECT_EQ(&mock_empty_iodev[CRAS_STREAM_OUTPUT], update_active_node_iodev_val[1]); // Remove pinned stream from d1, check d1 is closed after stream removed. @@ -1869,14 +1868,14 @@ TEST_F(IoDevTestSuite, HotwordStreamsAddedThenSuspendResume) { EXPECT_EQ(&d1_, audio_thread_disconnect_stream_dev); EXPECT_EQ(2, audio_thread_add_stream_called); EXPECT_EQ(&rstream, audio_thread_add_stream_stream); - EXPECT_EQ(&dummy_hotword_iodev, audio_thread_add_stream_dev); + EXPECT_EQ(&mock_hotword_iodev, audio_thread_add_stream_dev); /* Resume hotword streams, verify the stream disconnects from * the empty iodev and connects back to the real hotword iodev. */ EXPECT_EQ(0, cras_iodev_list_resume_hotword_stream()); EXPECT_EQ(2, audio_thread_disconnect_stream_called); EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream); - EXPECT_EQ(&dummy_hotword_iodev, audio_thread_disconnect_stream_dev); + EXPECT_EQ(&mock_hotword_iodev, audio_thread_disconnect_stream_dev); EXPECT_EQ(3, audio_thread_add_stream_called); EXPECT_EQ(&rstream, audio_thread_add_stream_stream); EXPECT_EQ(&d1_, audio_thread_add_stream_dev); @@ -1910,7 +1909,7 @@ TEST_F(IoDevTestSuite, HotwordStreamsAddedAfterSuspend) { /* Hotword stream connected, verify it is added to the empty iodev. */ EXPECT_EQ(0, stream_add_cb(&rstream)); EXPECT_EQ(1, audio_thread_add_stream_called); - EXPECT_EQ(&dummy_hotword_iodev, audio_thread_add_stream_dev); + EXPECT_EQ(&mock_hotword_iodev, audio_thread_add_stream_dev); EXPECT_EQ(&rstream, audio_thread_add_stream_stream); /* Resume hotword streams, now the existing hotword stream should disconnect @@ -1918,7 +1917,7 @@ TEST_F(IoDevTestSuite, HotwordStreamsAddedAfterSuspend) { EXPECT_EQ(0, cras_iodev_list_resume_hotword_stream()); EXPECT_EQ(1, audio_thread_disconnect_stream_called); EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream); - EXPECT_EQ(&dummy_hotword_iodev, audio_thread_disconnect_stream_dev); + EXPECT_EQ(&mock_hotword_iodev, audio_thread_disconnect_stream_dev); EXPECT_EQ(2, audio_thread_add_stream_called); EXPECT_EQ(&rstream, audio_thread_add_stream_stream); EXPECT_EQ(&d1_, audio_thread_add_stream_dev); @@ -2035,9 +2034,9 @@ struct cras_iodev* empty_iodev_create(enum CRAS_STREAM_DIRECTION direction, enum CRAS_NODE_TYPE node_type) { struct cras_iodev* dev; if (node_type == CRAS_NODE_TYPE_HOTWORD) { - dev = &dummy_hotword_iodev; + dev = &mock_hotword_iodev; } else { - dev = &dummy_empty_iodev[direction]; + dev = &mock_empty_iodev[direction]; } dev->direction = direction; if (dev->active_node == NULL) { diff --git a/cras/src/tests/iodev_unittest.cc b/cras/src/tests/iodev_unittest.cc index 03cfee62..21dc4d57 100644 --- a/cras/src/tests/iodev_unittest.cc +++ b/cras/src/tests/iodev_unittest.cc @@ -60,7 +60,7 @@ static unsigned int cras_mix_mute_count; static unsigned int cras_dsp_num_input_channels_return; static unsigned int cras_dsp_num_output_channels_return; struct cras_dsp_context* cras_dsp_context_new_return; -static unsigned int cras_dsp_load_dummy_pipeline_called; +static unsigned int cras_dsp_load_mock_pipeline_called; static unsigned int rate_estimator_add_frames_num_frames; static unsigned int rate_estimator_add_frames_called; static int cras_system_get_mute_return; @@ -148,7 +148,7 @@ void ResetStubData() { cras_dsp_num_input_channels_return = 2; cras_dsp_num_output_channels_return = 2; cras_dsp_context_new_return = NULL; - cras_dsp_load_dummy_pipeline_called = 0; + cras_dsp_load_mock_pipeline_called = 0; rate_estimator_add_frames_num_frames = 0; rate_estimator_add_frames_called = 0; cras_system_get_mute_return = 0; @@ -2240,12 +2240,12 @@ TEST(IoDev, SetExtDspMod) { EXPECT_EQ(3, cras_dsp_get_pipeline_called); EXPECT_EQ(3, cras_dsp_pipeline_set_sink_ext_module_called); - /* If pipeline doesn't exist, dummy pipeline should be loaded. */ + /* If pipeline doesn't exist, mock pipeline should be loaded. */ cras_dsp_get_pipeline_ret = 0x0; cras_iodev_set_ext_dsp_module(&iodev, &ext); EXPECT_EQ(3, ext_mod_configure_called); EXPECT_EQ(5, cras_dsp_get_pipeline_called); - EXPECT_EQ(1, cras_dsp_load_dummy_pipeline_called); + EXPECT_EQ(1, cras_dsp_load_mock_pipeline_called); EXPECT_EQ(4, cras_dsp_pipeline_set_sink_ext_module_called); } @@ -2462,9 +2462,11 @@ unsigned int buffer_share_id_offset(const struct buffer_share* mix, } // From cras_system_state. -void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction) {} +void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type) {} -void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction) {} +void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type) {} // From cras_dsp struct cras_dsp_context* cras_dsp_context_new(int sample_rate, @@ -2479,9 +2481,9 @@ void cras_dsp_context_free(struct cras_dsp_context* ctx) { } void cras_dsp_load_pipeline(struct cras_dsp_context* ctx) {} -void cras_dsp_load_dummy_pipeline(struct cras_dsp_context* ctx, - unsigned int num_channels) { - cras_dsp_load_dummy_pipeline_called++; +void cras_dsp_load_mock_pipeline(struct cras_dsp_context* ctx, + unsigned int num_channels) { + cras_dsp_load_mock_pipeline_called++; } void cras_dsp_set_variable_string(struct cras_dsp_context* ctx, @@ -2760,6 +2762,18 @@ int cras_server_metrics_device_volume(struct cras_iodev* iodev) { return 0; } +void ewma_power_init(struct ewma_power* ewma, unsigned int rate){}; + +void ewma_power_calculate(struct ewma_power* ewma, + const int16_t* buf, + unsigned int channels, + unsigned int size){}; + +void ewma_power_calculate_area(struct ewma_power* ewma, + const int16_t* buf, + struct cras_audio_area* area, + unsigned int size){}; + } // extern "C" } // namespace diff --git a/cras/src/tests/loopback_iodev_unittest.cc b/cras/src/tests/loopback_iodev_unittest.cc index 1c69ba57..fde50375 100644 --- a/cras/src/tests/loopback_iodev_unittest.cc +++ b/cras/src/tests/loopback_iodev_unittest.cc @@ -29,7 +29,7 @@ static const unsigned int kFrameBytes = 4; static const unsigned int kBufferSize = kBufferFrames * kFrameBytes; static struct timespec time_now; -static cras_audio_area* dummy_audio_area; +static cras_audio_area* mock_audio_area; static loopback_hook_data_t loop_hook; static struct cras_iodev* enabled_dev; static unsigned int cras_iodev_list_add_input_called; @@ -46,8 +46,8 @@ static char* atlog_name; class LoopBackTestSuite : public testing::Test { protected: virtual void SetUp() { - dummy_audio_area = (cras_audio_area*)calloc( - 1, sizeof(*dummy_audio_area) + sizeof(cras_channel_area) * 2); + mock_audio_area = (cras_audio_area*)calloc( + 1, sizeof(*mock_audio_area) + sizeof(cras_channel_area) * 2); for (unsigned int i = 0; i < kBufferSize; i++) { buf_[i] = rand(); } @@ -77,7 +77,7 @@ class LoopBackTestSuite : public testing::Test { EXPECT_EQ(1, cras_iodev_list_rm_input_called); EXPECT_EQ(NULL, device_enabled_callback_cb); EXPECT_EQ(NULL, device_disabled_callback_cb); - free(dummy_audio_area); + free(mock_audio_area); audio_thread_event_log_deinit(atlog, atlog_name); free(atlog_name); } @@ -224,7 +224,7 @@ extern "C" { void cras_audio_area_config_buf_pointers(struct cras_audio_area* area, const struct cras_audio_format* fmt, uint8_t* base_buffer) { - dummy_audio_area->channels[0].buf = base_buffer; + mock_audio_area->channels[0].buf = base_buffer; } void cras_iodev_free_audio_area(struct cras_iodev* iodev) {} @@ -232,7 +232,7 @@ void cras_iodev_free_audio_area(struct cras_iodev* iodev) {} void cras_iodev_free_format(struct cras_iodev* iodev) {} void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) { - iodev->area = dummy_audio_area; + iodev->area = mock_audio_area; } void cras_iodev_add_node(struct cras_iodev* iodev, struct cras_ionode* node) { diff --git a/cras/src/tests/observer_unittest.cc b/cras/src/tests/observer_unittest.cc index 2053e940..2a8fae2c 100644 --- a/cras/src/tests/observer_unittest.cc +++ b/cras/src/tests/observer_unittest.cc @@ -11,6 +11,8 @@ extern "C" { #include "cras_observer.c" +#include "cras_observer.h" +#include "cras_types.h" } namespace { @@ -56,6 +58,9 @@ static size_t cb_num_active_streams_changed_called; static std::vector<enum CRAS_STREAM_DIRECTION> cb_num_active_streams_changed_dir; static std::vector<uint32_t> cb_num_active_streams_changed_num; +static size_t cb_num_input_streams_with_permission_called; +static std::vector<std::vector<uint32_t>> + cb_num_input_streams_with_permission_array; static void ResetStubData() { cras_alert_destroy_called = 0; @@ -99,6 +104,8 @@ static void ResetStubData() { cb_num_active_streams_changed_called = 0; cb_num_active_streams_changed_dir.clear(); cb_num_active_streams_changed_num.clear(); + cb_num_input_streams_with_permission_called = 0; + cb_num_input_streams_with_permission_array.clear(); } /* System output volume changed. */ @@ -190,6 +197,15 @@ void cb_num_active_streams_changed(void* context, cb_num_active_streams_changed_num.push_back(num_active_streams); } +void cb_num_input_streams_with_permission_changed( + void* context, + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) { + cb_num_input_streams_with_permission_called++; + cb_context.push_back(context); + cb_num_input_streams_with_permission_array.push_back(std::vector<uint32_t>( + num_input_streams, num_input_streams + CRAS_NUM_CLIENT_TYPE)); +} + class ObserverTest : public testing::Test { protected: virtual void SetUp() { @@ -198,7 +214,7 @@ class ObserverTest : public testing::Test { ResetStubData(); rc = cras_observer_server_init(); ASSERT_EQ(0, rc); - EXPECT_EQ(16, cras_alert_create_called); + EXPECT_EQ(17, cras_alert_create_called); EXPECT_EQ(reinterpret_cast<void*>(output_volume_alert), cras_alert_add_callback_map[g_observer->alerts.output_volume]); EXPECT_EQ(reinterpret_cast<void*>(output_mute_alert), @@ -256,7 +272,7 @@ class ObserverTest : public testing::Test { virtual void TearDown() { cras_observer_server_free(); - EXPECT_EQ(16, cras_alert_destroy_called); + EXPECT_EQ(17, cras_alert_destroy_called); ResetStubData(); } @@ -578,6 +594,37 @@ TEST_F(ObserverTest, NotifyNumActiveStreams) { DoObserverRemoveClear(num_active_streams_alert, data); }; +TEST_F(ObserverTest, NotifyNumInputStreamsWithPermission) { + struct cras_observer_alert_data_input_streams* data; + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE] = {}; + for (unsigned type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) { + num_input_streams[type] = (uint32_t)type; + } + + cras_observer_notify_input_streams_with_permission(num_input_streams); + ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data)); + ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void*>(NULL)); + data = reinterpret_cast<struct cras_observer_alert_data_input_streams*>( + cras_alert_pending_data_value); + for (unsigned type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) { + EXPECT_EQ(data->num_input_streams[type], num_input_streams[type]); + } + + ops1_.num_input_streams_with_permission_changed = + cb_num_input_streams_with_permission_changed; + ops2_.num_input_streams_with_permission_changed = + cb_num_input_streams_with_permission_changed; + DoObserverAlert(num_input_streams_with_permission_alert, data); + ASSERT_EQ(2, cb_num_input_streams_with_permission_called); + for (auto cb_num_input_streams : cb_num_input_streams_with_permission_array) { + ASSERT_EQ(cb_num_input_streams.size(), (size_t)CRAS_NUM_CLIENT_TYPE); + for (unsigned type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) { + EXPECT_EQ(cb_num_input_streams[type], num_input_streams[type]); + } + } + DoObserverRemoveClear(num_input_streams_with_permission_alert, data); +} + TEST_F(ObserverTest, NotifyHotwordTriggered) { struct cras_observer_alert_data_hotword_triggered* data; diff --git a/cras/src/tests/playback_rclient_unittest.cc b/cras/src/tests/playback_rclient_unittest.cc index 7056d2fe..75cbe552 100644 --- a/cras/src/tests/playback_rclient_unittest.cc +++ b/cras/src/tests/playback_rclient_unittest.cc @@ -24,8 +24,8 @@ static unsigned int cras_observer_remove_called; static int stream_list_add_called; static int stream_list_add_return; static unsigned int stream_list_rm_called; -static struct cras_audio_shm dummy_shm; -static struct cras_rstream dummy_rstream; +static struct cras_audio_shm mock_shm; +static struct cras_rstream mock_rstream; void ResetStubData() { audio_format_valid = true; @@ -207,44 +207,6 @@ TEST_F(CPRMessageSuite, StreamConnectMessageInvalidAudioFormat) { EXPECT_EQ(stream_id, out_msg.stream_id); } -/* - * TODO(yuhsaun): Remove this test when there are no client uses the old - * craslib. (CRAS_PROTO_VER = 5) - */ -TEST_F(CPRMessageSuite, StreamConnectMessageOldProtocal) { - struct cras_client_stream_connected out_msg; - int rc; - - struct cras_connect_message_old msg; - cras_stream_id_t stream_id = 0x10002; - - msg.proto_version = 5; - msg.direction = CRAS_STREAM_OUTPUT; - msg.stream_id = stream_id; - msg.stream_type = CRAS_STREAM_TYPE_DEFAULT; - msg.buffer_frames = 480; - msg.cb_threshold = 240; - msg.flags = 0; - msg.effects = 0; - pack_cras_audio_format(&msg.format, &fmt); - msg.dev_idx = NO_DEVICE; - msg.client_shm_size = 0; - msg.client_type = CRAS_CLIENT_TYPE_TEST; - msg.header.id = CRAS_SERVER_CONNECT_STREAM; - msg.header.length = sizeof(struct cras_connect_message_old); - - fd_ = 100; - rc = - rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_, 1); - EXPECT_EQ(1, cras_make_fd_nonblocking_called); - EXPECT_EQ(1, stream_list_add_called); - EXPECT_EQ(0, stream_list_rm_called); - - rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg)); - EXPECT_EQ(sizeof(out_msg), rc); - EXPECT_EQ(stream_id, out_msg.stream_id); -} - TEST_F(CPRMessageSuite, StreamDisconnectMessage) { struct cras_disconnect_stream_message msg; cras_stream_id_t stream_id = 0x10002; @@ -320,16 +282,16 @@ int stream_list_add(struct stream_list* list, struct cras_rstream** stream) { int ret; - *stream = &dummy_rstream; + *stream = &mock_rstream; stream_list_add_called++; ret = stream_list_add_return; if (ret) stream_list_add_return = -EINVAL; - dummy_rstream.shm = &dummy_shm; - dummy_rstream.direction = config->direction; - dummy_rstream.stream_id = config->stream_id; + mock_rstream.shm = &mock_shm; + mock_rstream.direction = config->direction; + mock_rstream.stream_id = config->stream_id; return ret; } diff --git a/cras/src/tests/polled_interval_checker_unittest.cc b/cras/src/tests/polled_interval_checker_unittest.cc index c18fdf7e..a4aff09c 100644 --- a/cras/src/tests/polled_interval_checker_unittest.cc +++ b/cras/src/tests/polled_interval_checker_unittest.cc @@ -70,7 +70,7 @@ TEST(PolledIntervalCheckerTest, DoesNotResetAutomatically) { struct polled_interval* interval = create_interval(); - // Sanity check. + // Initial check. EXPECT_FALSE(pic_interval_elapsed(interval)); // Increment time so the interval elapses. @@ -100,7 +100,7 @@ TEST(PolledIntervalCheckerTest, Reset) { struct polled_interval* interval = create_interval(); - // Sanity check. + // Initial check. EXPECT_FALSE(pic_interval_elapsed(interval)); // Increment time so the interval elapses. diff --git a/cras/src/tests/rstream_unittest.cc b/cras/src/tests/rstream_unittest.cc index 77dd6adb..593c805d 100644 --- a/cras/src/tests/rstream_unittest.cc +++ b/cras/src/tests/rstream_unittest.cc @@ -390,10 +390,18 @@ unsigned int buffer_share_id_offset(const struct buffer_share* mix, unsigned int id) { return 0; } +void ewma_power_init(struct ewma_power* ewma, unsigned int rate) {} -void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction) {} +void ewma_power_calculate(struct ewma_power* ewma, + const int16_t* buf, + unsigned int channels, + unsigned int size) {} -void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction) {} +void cras_system_state_stream_added(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type) {} + +void cras_system_state_stream_removed(enum CRAS_STREAM_DIRECTION direction, + enum CRAS_CLIENT_TYPE client_type) {} int cras_server_metrics_stream_create( const struct cras_rstream_config* config) { @@ -405,9 +413,13 @@ int cras_server_metrics_stream_destroy(const struct cras_rstream* stream) { } #ifdef HAVE_WEBRTC_APM +#define FAKE_CRAS_APM_PTR reinterpret_cast<struct cras_apm*>(0x99) struct cras_apm_list* cras_apm_list_create(void* stream_ptr, uint64_t effects) { return NULL; } +struct cras_apm* cras_apm_list_get_active_apm(void* stream_ptr, void* dev_ptr) { + return FAKE_CRAS_APM_PTR; +} int cras_apm_list_destroy(struct cras_apm_list* list) { return 0; } diff --git a/cras/src/tests/system_state_unittest.cc b/cras/src/tests/system_state_unittest.cc index e61a43e2..0450df38 100644 --- a/cras/src/tests/system_state_unittest.cc +++ b/cras/src/tests/system_state_unittest.cc @@ -38,6 +38,7 @@ static size_t cras_observer_notify_output_mute_called; static size_t cras_observer_notify_capture_mute_called; static size_t cras_observer_notify_suspend_changed_called; static size_t cras_observer_notify_num_active_streams_called; +static size_t cras_observer_notify_input_streams_with_permission_called; static struct cras_board_config fake_board_config; static void ResetStubData() { @@ -58,6 +59,7 @@ static void ResetStubData() { cras_observer_notify_capture_mute_called = 0; cras_observer_notify_suspend_changed_called = 0; cras_observer_notify_num_active_streams_called = 0; + cras_observer_notify_input_streams_with_permission_called = 0; memset(&fake_board_config, 0, sizeof(fake_board_config)); } @@ -371,11 +373,11 @@ TEST(SystemSettingsStreamCount, StreamCount) { do_sys_init(); EXPECT_EQ(0, cras_system_state_get_active_streams()); - cras_system_state_stream_added(CRAS_STREAM_OUTPUT); + cras_system_state_stream_added(CRAS_STREAM_OUTPUT, CRAS_CLIENT_TYPE_CHROME); EXPECT_EQ(1, cras_system_state_get_active_streams()); struct cras_timespec ts1; cras_system_state_get_last_stream_active_time(&ts1); - cras_system_state_stream_removed(CRAS_STREAM_OUTPUT); + cras_system_state_stream_removed(CRAS_STREAM_OUTPUT, CRAS_CLIENT_TYPE_CHROME); EXPECT_EQ(0, cras_system_state_get_active_streams()); struct cras_timespec ts2; cras_system_state_get_last_stream_active_time(&ts2); @@ -388,9 +390,11 @@ TEST(SystemSettingsStreamCount, StreamCountByDirection) { do_sys_init(); EXPECT_EQ(0, cras_system_state_get_active_streams()); - cras_system_state_stream_added(CRAS_STREAM_OUTPUT); - cras_system_state_stream_added(CRAS_STREAM_INPUT); - cras_system_state_stream_added(CRAS_STREAM_POST_MIX_PRE_DSP); + cras_system_state_stream_added(CRAS_STREAM_OUTPUT, CRAS_CLIENT_TYPE_CHROME); + cras_system_state_stream_added(CRAS_STREAM_INPUT, CRAS_CLIENT_TYPE_CHROME); + cras_system_state_stream_added(CRAS_STREAM_POST_MIX_PRE_DSP, + CRAS_CLIENT_TYPE_CHROME); + EXPECT_EQ(1, cras_observer_notify_input_streams_with_permission_called); EXPECT_EQ( 1, cras_system_state_get_active_streams_by_direction(CRAS_STREAM_OUTPUT)); EXPECT_EQ( @@ -399,9 +403,11 @@ TEST(SystemSettingsStreamCount, StreamCountByDirection) { CRAS_STREAM_POST_MIX_PRE_DSP)); EXPECT_EQ(3, cras_system_state_get_active_streams()); EXPECT_EQ(3, cras_observer_notify_num_active_streams_called); - cras_system_state_stream_removed(CRAS_STREAM_OUTPUT); - cras_system_state_stream_removed(CRAS_STREAM_INPUT); - cras_system_state_stream_removed(CRAS_STREAM_POST_MIX_PRE_DSP); + cras_system_state_stream_removed(CRAS_STREAM_OUTPUT, CRAS_CLIENT_TYPE_CHROME); + cras_system_state_stream_removed(CRAS_STREAM_INPUT, CRAS_CLIENT_TYPE_CHROME); + cras_system_state_stream_removed(CRAS_STREAM_POST_MIX_PRE_DSP, + CRAS_CLIENT_TYPE_CHROME); + EXPECT_EQ(2, cras_observer_notify_input_streams_with_permission_called); EXPECT_EQ( 0, cras_system_state_get_active_streams_by_direction(CRAS_STREAM_OUTPUT)); EXPECT_EQ( @@ -511,6 +517,11 @@ void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir, cras_observer_notify_num_active_streams_called++; } +void cras_observer_notify_input_streams_with_permission( + uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE]) { + cras_observer_notify_input_streams_with_permission_called++; +} + void cras_board_config_get(const char* config_path, struct cras_board_config* board_config) { *board_config = fake_board_config; diff --git a/cras/src/tools/cras_test_client/cras_test_client.c b/cras/src/tools/cras_test_client/cras_test_client.c index 947b9458..5a7b3e06 100644 --- a/cras/src/tools/cras_test_client/cras_test_client.c +++ b/cras/src/tools/cras_test_client/cras_test_client.c @@ -493,6 +493,26 @@ static void convert_time(unsigned int *sec, unsigned int *nsec, *nsec = nsec_offset; } +static float get_ewma_power_as_float(uint32_t data) +{ + float f = 0.0f; + + /* Convert from the uint32_t log type back to float. + * If data cannot be assigned to float, default value will + * be printed as -inf to hint the problem. + */ + if (sizeof(uint32_t) == sizeof(float)) + memcpy(&f, &data, sizeof(float)); + else + printf("%-30s float to uint32_t\n", "MEMORY_NOT_ALIGNED"); + + /* Convert to dBFS and set to zero if it's + * insignificantly low. Picking the same threshold + * 1.0e-10f as in Chrome. + */ + return (f < 1.0e-10f) ? -INFINITY : 10.0f * log10f(f); +} + static void show_alog_tag(const struct audio_thread_event_log *log, unsigned int tag_idx, int32_t sec_offset, int32_t nsec_offset) @@ -552,25 +572,30 @@ static void show_alog_tag(const struct audio_thread_event_log *log, printf("%-30s dev:%u tstamp:%s.%09u\n", "READ_AUDIO_TSTAMP", data1, time_str, nsec); break; - case AUDIO_THREAD_READ_AUDIO_DONE: - printf("%-30s read_remainder:%u\n", "READ_AUDIO_DONE", data1); + case AUDIO_THREAD_READ_AUDIO_DONE: { + float f = get_ewma_power_as_float(data2); + printf("%-30s read_remainder:%u power:%f dBFS\n", + "READ_AUDIO_DONE", data1, f); break; + } case AUDIO_THREAD_READ_OVERRUN: printf("%-30s dev:%u stream:%x num_overruns:%u\n", "READ_AUDIO_OVERRUN", data1, data2, data3); break; case AUDIO_THREAD_FILL_AUDIO: - printf("%-30s dev:%u hw_level:%u\n", "FILL_AUDIO", data1, - data2); + printf("%-30s dev:%u hw_level:%u min_cb_level:%u\n", + "FILL_AUDIO", data1, data2, data3); break; case AUDIO_THREAD_FILL_AUDIO_TSTAMP: printf("%-30s dev:%u tstamp:%s.%09u\n", "FILL_AUDIO_TSTAMP", data1, time_str, nsec); break; - case AUDIO_THREAD_FILL_AUDIO_DONE: - printf("%-30s hw_level:%u total_written:%u min_cb_level:%u\n", - "FILL_AUDIO_DONE", data1, data2, data3); + case AUDIO_THREAD_FILL_AUDIO_DONE: { + float f = get_ewma_power_as_float(data3); + printf("%-30s hw_level:%u total_written:%u power:%f dBFS\n", + "FILL_AUDIO_DONE", data1, data2, f); break; + } case AUDIO_THREAD_WRITE_STREAMS_WAIT: printf("%-30s stream:%x\n", "WRITE_STREAMS_WAIT", data1); break; @@ -588,10 +613,12 @@ static void show_alog_tag(const struct audio_thread_event_log *log, printf("%-30s id:%x shm_frames:%u cb_pending:%u\n", "WRITE_STREAMS_STREAM", data1, data2, data3); break; - case AUDIO_THREAD_FETCH_STREAM: - printf("%-30s id:%x cbth:%u delay:%u\n", - "WRITE_STREAMS_FETCH_STREAM", data1, data2, data3); + case AUDIO_THREAD_FETCH_STREAM: { + float f = get_ewma_power_as_float(data3); + printf("%-30s id:%x cbth:%u power:%f dBFS\n", + "WRITE_STREAMS_FETCH_STREAM", data1, data2, f); break; + } case AUDIO_THREAD_STREAM_ADDED: printf("%-30s id:%x dev:%u\n", "STREAM_ADDED", data1, data2); break; @@ -966,7 +993,7 @@ static void show_btlog_tag(const struct cras_bt_event_log *log, printf("%-30s\n", "ADAPTER_REMOVED"); break; case BT_A2DP_CONFIGURED: - printf("%-30s connected profiles %u\n", "A2DP_CONFIGURED", + printf("%-30s connected profiles 0x%.2x\n", "A2DP_CONFIGURED", data1); break; case BT_A2DP_START: @@ -976,8 +1003,8 @@ static void show_btlog_tag(const struct cras_bt_event_log *log, printf("%-30s\n", "A2DP_SUSPENDED"); break; case BT_AUDIO_GATEWAY_INIT: - printf("%-30s supported profiles %u\n", "AUDIO_GATEWAY_INIT", - data1); + printf("%-30s supported profiles 0x%.2x\n", + "AUDIO_GATEWAY_INIT", data1); break; case BT_AUDIO_GATEWAY_START: printf("%-30s \n", "AUDIO_GATEWAY_START"); @@ -991,11 +1018,12 @@ static void show_btlog_tag(const struct cras_bt_event_log *log, data2); break; case BT_DEV_CONNECTED_CHANGE: - printf("%-30s profiles %u now %u\n", "DEV_CONNECTED_CHANGE", - data1, data2); + printf("%-30s supported profiles 0x%.2x now %s\n", + "DEV_CONNECTED_CHANGE", data1, + data2 ? "connected" : "disconnected"); break; case BT_DEV_CONN_WATCH_CB: - printf("%-30s %u retries left, supported profiles %u\n", + printf("%-30s %u retries left, supported profiles 0x%.2x\n", "DEV_CONN_WATCH_CB", data1, data2); break; case BT_DEV_SUSPEND_CB: @@ -1021,8 +1049,8 @@ static void show_btlog_tag(const struct cras_bt_event_log *log, printf("%-30s\n", "HFP_REQUEST_DISCONNECT"); break; case BT_HFP_SUPPORTED_FEATURES: - printf("%-30s role %s features %u\n", "HFP_SUPPORTED_FEATURES", - data1 ? "AG" : "HF", data2); + printf("%-30s role %s features 0x%.4x\n", + "HFP_SUPPORTED_FEATURES", data1 ? "AG" : "HF", data2); break; case BT_HSP_NEW_CONNECTION: printf("%-30s\n", "HSP_NEW_CONNECTION"); @@ -1031,7 +1059,7 @@ static void show_btlog_tag(const struct cras_bt_event_log *log, printf("%-30s\n", "HSP_REQUEST_DISCONNECT"); break; case BT_NEW_AUDIO_PROFILE_AFTER_CONNECT: - printf("%-30s old %u, new %u\n", + printf("%-30s old 0x%.2x, new 0x%.2x\n", "NEW_AUDIO_PROFILE_AFTER_CONNECT", data1, data2); break; case BT_RESET: @@ -1060,12 +1088,30 @@ static void show_btlog_tag(const struct cras_bt_event_log *log, } } +static void convert_to_time_str(const struct timespec *ts, time_t sec_offset, + int32_t nsec_offset) +{ + time_t lt = ts->tv_sec; + struct tm t; + unsigned int time_nsec; + + /* Assuming tv_nsec doesn't exceed 10^9 */ + time_nsec = ts->tv_nsec; + convert_time((unsigned int *)<, &time_nsec, sec_offset, nsec_offset); + localtime_r(<, &t); + strftime(time_str, 128, "%Y-%m-%dT%H:%M:%S", &t); + snprintf(time_str + strlen(time_str), 128 - strlen(time_str), ".%09u", + time_nsec); +} + static void cras_bt_debug_info(struct cras_client *client) { const struct cras_bt_debug_info *info; time_t sec_offset; int32_t nsec_offset; int i, j; + struct timespec ts; + struct packet_status_logger wbs_logger; info = cras_client_get_bt_debug_info(client); fill_time_offset(&sec_offset, &nsec_offset); @@ -1078,6 +1124,23 @@ static void cras_bt_debug_info(struct cras_client *client) j %= info->bt_log.len; } + printf("-------------WBS packet loss------------\n"); + wbs_logger = info->wbs_logger; + + packet_status_logger_begin_ts(&wbs_logger, &ts); + convert_to_time_str(&ts, sec_offset, nsec_offset); + printf("%s [begin]\n", time_str); + + packet_status_logger_end_ts(&wbs_logger, &ts); + convert_to_time_str(&ts, sec_offset, nsec_offset); + printf("%s [end]\n", time_str); + + printf("In hex format:\n"); + packet_status_logger_dump_hex(&wbs_logger); + + printf("In binary format:\n"); + packet_status_logger_dump_binary(&wbs_logger); + /* Signal main thread we are done after the last chunk. */ pthread_mutex_lock(&done_mutex); pthread_cond_signal(&done_cond); diff --git a/init/cras.conf b/init/cras.conf index c61d7894..20847494 100644 --- a/init/cras.conf +++ b/init/cras.conf @@ -10,6 +10,8 @@ author "chromium-os-dev@chromium.org" env CRAS_SOCKET_DIR=/run/cras env CRAS_VMS_SOCKET_DIR=/run/cras/vms +env CRAS_PLUGIN_DIR=/run/cras/vms/plugin +env CRAS_ARGS= start on starting system-services stop on stopping system-services @@ -23,6 +25,18 @@ pre-start script chown -R cras:cras "${CRAS_SOCKET_DIR}" mkdir -p -m 1770 "${CRAS_VMS_SOCKET_DIR}" chown -R cras:cras "${CRAS_VMS_SOCKET_DIR}" + for socket_dir in playback unified; do + mkdir -p -m 1770 "${CRAS_PLUGIN_DIR}/${socket_dir}" + chown -R cras:cras "${CRAS_PLUGIN_DIR}/${socket_dir}" + done + mkdir -m 0755 -p /var/lib/cras + chown -R cras:cras /var/lib/cras end script exec /bin/sh /usr/share/cros/init/cras.sh + +# sound_card_init uses CRAS stop timestamp as a criterion to skip boot time +# calibration for DSM. +post-stop script + echo "$(date +---%\nsecs:\ %s%\nnanos:\ %N)" > /var/lib/cras/stop +end script diff --git a/init/cras.sh b/init/cras.sh index b0ca430c..ca2d6b93 100644 --- a/init/cras.sh +++ b/init/cras.sh @@ -62,4 +62,4 @@ exec minijail0 -u cras -g cras -G --uts -v -l \ -- \ /usr/bin/cras \ ${DSP_CONFIG} ${DEVICE_CONFIG_DIR} ${DISABLE_PROFILE} \ - ${INTERNAL_UCM_SUFFIX} + ${INTERNAL_UCM_SUFFIX} ${CRAS_ARGS} diff --git a/scripts/audio_tuning/frontend/audio.js b/scripts/audio_tuning/frontend/audio.js index 2476f1a9..b90be953 100644 --- a/scripts/audio_tuning/frontend/audio.js +++ b/scripts/audio_tuning/frontend/audio.js @@ -1105,7 +1105,7 @@ function check_button(div, handler) { this.update = update; } -function dummy() { +function empty() { } /* Changes the opacity of a div. */ @@ -1185,7 +1185,7 @@ function drc_card(parent, index, lower_freq, freq_label) { var f_slider; if (lower_freq == 0) { /* Special case for the lowest band */ f_slider = new slider_input_log(table, freq_label, lower_freq, 0, 1, - 'Hz', 0, dummy); + 'Hz', 0, empty); f_slider.hide(true); } else { f_slider = new slider_input_log(table, freq_label, lower_freq, 1, diff --git a/scripts/volume_tuning/volume.js b/scripts/volume_tuning/volume.js index c5f05266..88f39984 100644 --- a/scripts/volume_tuning/volume.js +++ b/scripts/volume_tuning/volume.js @@ -116,7 +116,7 @@ function redraw() { var max = parseFloat(minmax_boxes[1].value); var step = parseFloat(minmax_boxes[2].value); - // Sanity checks + // Soundness checks if (isNaN(min) || isNaN(max) || isNaN(step)) return; if (min >= max || step <= 0 || (max - min) / step > 10000) return; diff --git a/seccomp/cras-seccomp-arm.policy b/seccomp/cras-seccomp-arm.policy index 16b6ca39..e823244b 100644 --- a/seccomp/cras-seccomp-arm.policy +++ b/seccomp/cras-seccomp-arm.policy @@ -85,7 +85,6 @@ getresgid32: 1 pipe2: 1 sched_get_priority_max: 1 sysinfo: 1 -flock: 1 # Allow ioctl command of type 'A' and 'U' for SNDRV_PCM_IOCTL_* and # SNDRV_CTL_IOCTL_*, and EVIOCGSW(8), EVIOCGNAME(256), EVIOCGBIT(0x05, 8), diff --git a/seccomp/cras-seccomp-arm64.policy b/seccomp/cras-seccomp-arm64.policy index d505d175..d06a7411 100644 --- a/seccomp/cras-seccomp-arm64.policy +++ b/seccomp/cras-seccomp-arm64.policy @@ -53,7 +53,6 @@ exit_group: 1 fchmod: 1 fchmodat: 1 flock: 1 -flock: 1 getegid: 1 geteuid: 1 getgid: 1 diff --git a/sound_card_init/.gitignore b/sound_card_init/.gitignore index f2e972dd..b6e87008 100644 --- a/sound_card_init/.gitignore +++ b/sound_card_init/.gitignore @@ -1,6 +1,6 @@ # Generated by Cargo # will have compiled files and executables -/target/ +**/target/ # These are backup files generated by rustfmt **/*.rs.bk diff --git a/sound_card_init/Cargo.lock b/sound_card_init/Cargo.lock index 8d0eeea8..06448e9d 100644 --- a/sound_card_init/Cargo.lock +++ b/sound_card_init/Cargo.lock @@ -106,6 +106,7 @@ dependencies = [ "serde", "serde_yaml", "sys_util", + "utils", ] [[package]] @@ -197,6 +198,7 @@ dependencies = [ "serde", "serde_yaml", "sys_util", + "utils", ] [[package]] @@ -250,6 +252,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" [[package]] +name = "utils" +version = "0.1.0" +dependencies = [ + "remain", + "serde", + "serde_yaml", +] + +[[package]] name = "yaml-rust" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/sound_card_init/Cargo.toml b/sound_card_init/Cargo.toml index d6ed9e2a..d4a2b376 100644 --- a/sound_card_init/Cargo.toml +++ b/sound_card_init/Cargo.toml @@ -12,6 +12,7 @@ getopts = "0.2" libcras = "*" remain = "0.2.1" max98390d = { path = "max98390d" } +utils = { path = "utils" } serde = { version = "1.0", features = ["derive"] } serde_yaml = "0.8.11" sys_util = "*" diff --git a/sound_card_init/max98390d/Cargo.toml b/sound_card_init/max98390d/Cargo.toml index 15660589..f3bbae69 100644 --- a/sound_card_init/max98390d/Cargo.toml +++ b/sound_card_init/max98390d/Cargo.toml @@ -13,3 +13,4 @@ remain = "0.2.1" serde = { version = "1.0", features = ["derive"]} serde_yaml = "0.8.11" sys_util = "*" +utils = { path = "../utils" }
\ No newline at end of file diff --git a/sound_card_init/max98390d/src/amp_calibration.rs b/sound_card_init/max98390d/src/amp_calibration.rs index 50f4227c..35340226 100644 --- a/sound_card_init/max98390d/src/amp_calibration.rs +++ b/sound_card_init/max98390d/src/amp_calibration.rs @@ -123,24 +123,10 @@ impl<'a> AmpCalibration<'a> { }; if !self.validate_temperature(temp_cali) { - match datastore { - None => return Err(Error::InvalidTemperature(temp_cali)), - Some(d) => match d { - Datastore::UseVPD => { - info!("invalid temperature: {}, use VPD values.", temp_cali); - return Ok(()); - } - Datastore::DSM { rdc, ambient_temp } => { - info!("invalid temperature: {}, use datastore values.", temp_cali); - self.card - .control_by_name::<IntControl>(&self.setting.amp.rdc_ctrl)? - .set(rdc)?; - self.card - .control_by_name::<IntControl>(&self.setting.amp.temp_ctrl)? - .set(ambient_temp)?; - return Ok(()); - } - }, + info!("invalid temperature: {}.", temp_cali); + return match datastore { + None => Err(Error::InvalidTemperature(temp_cali)), + Some(d) => self.apply_datastore(d), }; } @@ -149,22 +135,10 @@ impl<'a> AmpCalibration<'a> { } else if diff < CALI_ERROR_LOWER_LIMIT { match datastore { None => Datastore::UseVPD.save(self.card.name(), &self.setting.calib_file)?, - Some(d) => match d { - Datastore::UseVPD => { - info!("rdc diff: {}, use VPD values.", diff); - } - Datastore::DSM { rdc, ambient_temp } => { - info!("rdc diff: {}, use datastore values.", diff); - self.card - .control_by_name::<IntControl>(&self.setting.amp.rdc_ctrl)? - .set(rdc)?; - self.card - .control_by_name::<IntControl>(&self.setting.amp.temp_ctrl)? - .set(ambient_temp)?; - } - }, + Some(d) => self.apply_datastore(d)?, } } else { + info!("apply boot time calibration values."); self.card .control_by_name::<IntControl>(&self.setting.amp.rdc_ctrl)? .set(rdc_cali)?; @@ -180,6 +154,22 @@ impl<'a> AmpCalibration<'a> { Ok(()) } + fn apply_datastore(&mut self, d: Datastore) -> Result<()> { + info!("apply datastore values."); + match d { + Datastore::UseVPD => Ok(()), + Datastore::DSM { rdc, ambient_temp } => { + self.card + .control_by_name::<IntControl>(&self.setting.amp.rdc_ctrl)? + .set(rdc)?; + self.card + .control_by_name::<IntControl>(&self.setting.amp.temp_ctrl)? + .set(ambient_temp)?; + Ok(()) + } + } + } + fn validate_temperature(&self, temp: i32) -> bool { temp < self.setting.amp.temp_upper_limit && temp > self.setting.amp.temp_lower_limit } @@ -213,7 +203,7 @@ impl<'a> AmpCalibration<'a> { return Err(Error::StartPlaybackTimeout); } else { // Spurious wakes. Decrements the sleep duration by the amount slept. - timeout = timeout - start_time.elapsed(); + timeout -= start_time.elapsed(); } } } @@ -312,4 +302,18 @@ impl<'a> AmpCalibration<'a> { Ok(handle) } + + /// Skips max98390d boot time calibration when the speaker may be hot. + /// + /// If datastore exists, applies the stored value and sets volume to high. + /// If datastore does not exist, sets volume to low. + pub fn hot_speaker_workflow(&mut self) -> Result<()> { + if let Ok(sci_calib) = Datastore::from_file(self.card.name(), &self.setting.calib_file) { + self.apply_datastore(sci_calib)?; + self.set_volume(VolumeMode::High)?; + return Ok(()); + } + info!("no datastore, set volume low"); + self.set_volume(VolumeMode::Low) + } } diff --git a/sound_card_init/max98390d/src/datastore.rs b/sound_card_init/max98390d/src/datastore.rs index 6e221635..12ebff73 100644 --- a/sound_card_init/max98390d/src/datastore.rs +++ b/sound_card_init/max98390d/src/datastore.rs @@ -7,11 +7,10 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; use sys_util::info; +use utils::DATASTORE_DIR; use crate::error::{Error, Result}; -const DATASTORE_DIR: &str = "/var/lib/sound_card_init"; - /// `Datastore`, which stores and reads calibration values in yaml format. #[derive(Debug, Deserialize, Serialize, Copy, Clone)] pub enum Datastore { diff --git a/sound_card_init/max98390d/src/error.rs b/sound_card_init/max98390d/src/error.rs index 572340de..778ff961 100644 --- a/sound_card_init/max98390d/src/error.rs +++ b/sound_card_init/max98390d/src/error.rs @@ -6,6 +6,7 @@ use std::fmt; use std::io; use std::num::ParseIntError; use std::sync::PoisonError; +use std::time; use remain::sorted; @@ -21,8 +22,10 @@ pub enum Error { CrasClientFailed(libcras::Error), DeserializationFailed(String, serde_yaml::Error), FileIOFailed(String, io::Error), + HotSpeaker, InternalSpeakerNotFound, InvalidDatastore, + InvalidShutDownTime, InvalidTemperature(i32), LargeCalibrationDiff(i32, i32), MissingDSMParam, @@ -30,8 +33,10 @@ pub enum Error { NewPlayStreamFailed(libcras::BoxError), NextPlaybackBufferFailed(libcras::BoxError), PlaybackFailed(io::Error), + ReadTimestampFailed(utils::error::Error), SerializationFailed(serde_yaml::Error), StartPlaybackTimeout, + SystemTimeError(time::SystemTimeError), VPDParseFailed(String, ParseIntError), WorkerPanics, } @@ -67,6 +72,7 @@ impl fmt::Display for Error { CrasClientFailed(e) => write!(f, "failed to create cras client: {}", e), DeserializationFailed(file, e) => write!(f, "failed to parse {}: {}", file, e), FileIOFailed(file, e) => write!(f, "{}: {}", file, e), + InvalidShutDownTime => write!(f, "invalid shutdown time"), InternalSpeakerNotFound => write!(f, "internal speaker is not found in cras"), InvalidTemperature(temp) => write!( f, @@ -74,18 +80,21 @@ impl fmt::Display for Error { temp ), InvalidDatastore => write!(f, "invalid datastore format"), + HotSpeaker => write!(f, "skip boot time calibration as the speakers may be hot"), LargeCalibrationDiff(rdc, temp) => write!( f, "calibration difference is too large, rdc: {}, temp: {}", rdc, temp ), - MutexPoisonError => write!(f, "mutex is poisoned"), MissingDSMParam => write!(f, "missing dsm_param.bin"), + MutexPoisonError => write!(f, "mutex is poisoned"), NewPlayStreamFailed(e) => write!(f, "{}", e), NextPlaybackBufferFailed(e) => write!(f, "{}", e), PlaybackFailed(e) => write!(f, "{}", e), + ReadTimestampFailed(e) => write!(f, "{}", e), SerializationFailed(e) => write!(f, "failed to serialize yaml: {}", e), StartPlaybackTimeout => write!(f, "playback is not started in time"), + SystemTimeError(e) => write!(f, "{}", e), VPDParseFailed(file, e) => write!(f, "failed to parse vpd {}: {}", file, e), WorkerPanics => write!(f, "run_play_zero_worker panics"), } diff --git a/sound_card_init/max98390d/src/lib.rs b/sound_card_init/max98390d/src/lib.rs index 7e445c59..44b1c611 100644 --- a/sound_card_init/max98390d/src/lib.rs +++ b/sound_card_init/max98390d/src/lib.rs @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. //! `max98390d` crate implements the required initialization workflows. -//! It currently supports boot time calibration for amplifiers. +//! It currently supports boot time calibration for max98390d. #![deny(missing_docs)] mod amp_calibration; mod datastore; @@ -10,15 +10,20 @@ mod error; mod settings; mod vpd; -use std::path::Path; +use std::fs; +use std::path::{Path, PathBuf}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; use cros_alsa::Card; use sys_util::error; +use utils::{run_time, shutdown_time, DATASTORE_DIR}; use crate::amp_calibration::{AmpCalibration, VolumeMode}; use crate::error::{Error, Result}; use crate::settings::DeviceSettings; +const SPEAKER_COOL_DOWN_TIME: Duration = Duration::from_secs(180); + /// Performs max98390d boot time calibration. /// /// @@ -35,20 +40,27 @@ pub fn run_max98390d(snd_card: &str, conf: &str) -> Result<()> { let mut card = Card::new(snd_card)?; if !Path::new(&settings.dsm_param).exists() { - for s in &settings.amp_calibrations { - let mut amp_calib = match AmpCalibration::new(&mut card, s.clone()) { - Ok(amp) => amp, - Err(e) => { - error!("{}.", e); - continue; + set_all_volume_low(&mut card, &settings); + return Err(Error::MissingDSMParam); + } + + // Needs to check whether the speakers are over heated if it is not the first time boot. + if run_time::exists(snd_card) { + if let Err(err) = check_speaker_over_heated(snd_card, SPEAKER_COOL_DOWN_TIME) { + match err { + Error::HotSpeaker => run_all_hot_speaker_workflow(&mut card, &settings), + _ => { + // We cannot assume the speakers are not replaced or not over heated + // when the shutdown time file is invalid; therefore we can not use the datastore + // value anymore and we can not trigger boot time calibration. + del_all_datastore(snd_card, &settings); + set_all_volume_low(&mut card, &settings); } }; - if let Err(e) = amp_calib.set_volume(VolumeMode::Low) { - error!("failed to set volume to low: {}.", e); - } - } - return Err(Error::MissingDSMParam); + return Err(err); + }; } + // If some error occurs during the calibration, the iteration will continue running the // calibration for the next amp. let results: Vec<Result<()>> = settings @@ -74,3 +86,68 @@ pub fn run_max98390d(snd_card: &str, conf: &str) -> Result<()> { Ok(()) } + +fn del_all_datastore(snd_card: &str, settings: &DeviceSettings) { + for s in &settings.amp_calibrations { + if let Err(e) = fs::remove_file( + PathBuf::from(DATASTORE_DIR) + .join(snd_card) + .join(&s.calib_file), + ) { + error!("failed to remove datastore: {}.", e); + } + } +} + +fn run_all_hot_speaker_workflow(card: &mut Card, settings: &DeviceSettings) { + for s in &settings.amp_calibrations { + let mut amp_calib = match AmpCalibration::new(card, s.clone()) { + Ok(amp) => amp, + Err(e) => { + error!("{}.", e); + continue; + } + }; + if let Err(e) = amp_calib.hot_speaker_workflow() { + error!("failed to run hot_speaker_workflow: {}.", e); + } + } +} + +fn set_all_volume_low(card: &mut Card, settings: &DeviceSettings) { + for s in &settings.amp_calibrations { + let mut amp_calib = match AmpCalibration::new(card, s.clone()) { + Ok(amp) => amp, + Err(e) => { + error!("{}.", e); + continue; + } + }; + if let Err(e) = amp_calib.set_volume(VolumeMode::Low) { + error!("failed to set volume to low: {}.", e); + } + } +} + +// If (Current time - the latest CRAS shutdown time) < cool_down_time, we assume that +// the speakers may be over heated. +fn check_speaker_over_heated(snd_card: &str, cool_down_time: Duration) -> Result<()> { + let last_run = run_time::from_file(snd_card).map_err(Error::ReadTimestampFailed)?; + let last_shutdown = shutdown_time::from_file().map_err(Error::ReadTimestampFailed)?; + if last_shutdown < last_run { + return Err(Error::InvalidShutDownTime); + } + + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(Error::SystemTimeError)?; + + let elapsed = now + .checked_sub(last_shutdown) + .ok_or(Error::InvalidShutDownTime)?; + + if elapsed < cool_down_time { + return Err(Error::HotSpeaker); + } + Ok(()) +} diff --git a/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy b/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy index bddf6ea5..b1bcd6b8 100644 --- a/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy +++ b/sound_card_init/seccomp/sound_card_init-seccomp-amd64.policy @@ -34,6 +34,7 @@ getdents: 1 recvfrom: 1 set_robust_list: 1 umask: 1 +unlink: 1 sendto: 1 setgroups: 1 connect: 1 diff --git a/sound_card_init/sound_card_init.conf b/sound_card_init/sound_card_init.conf index 7ad1a804..7ab0211e 100644 --- a/sound_card_init/sound_card_init.conf +++ b/sound_card_init/sound_card_init.conf @@ -48,8 +48,8 @@ end script # -k: empty /sys tmpfs path. # -b: need /sys/firmware/vpd/ro/ access to read the default calibration value in vpd. # -k: get a writeable and empty /var tmpfs path. -# -b: need /var/lib/sound_card_init/$SOUND_CARD_ID writable -# access for datastore update. +# -b: need /var/lib/sound_card_init/$SOUND_CARD_ID writable access for datastore update. +# -b: need /var/lib/cras readable exec minijail0 \ --uts \ -e \ @@ -68,6 +68,7 @@ exec minijail0 \ -b /sys/firmware/vpd/ro/ \ -k 'tmpfs,/var,tmpfs,MS_NODEV|MS_NOEXEC|MS_NOSUID,mode=755,size=10M' \ -b /var/lib/sound_card_init/"${SOUND_CARD_ID}"/,,1 \ + -b /var/lib/cras/ \ -u sound_card_init -g sound_card_init -G \ -S /usr/share/policy/sound_card_init-seccomp.policy \ /usr/bin/sound_card_init "--id=${SOUND_CARD_ID}" diff --git a/sound_card_init/src/main.rs b/sound_card_init/src/main.rs index addde2df..3f49ed97 100644 --- a/sound_card_init/src/main.rs +++ b/sound_card_init/src/main.rs @@ -25,6 +25,7 @@ use remain::sorted; use sys_util::{error, info, syslog}; use max98390d::run_max98390d; +use utils::run_time; type Result<T> = std::result::Result<T, Error>; const CONF_DIR: &str = "/etc/sound_card_init"; @@ -99,24 +100,35 @@ fn get_config(args: &Args) -> Result<String> { } /// Parses the CONF_DIR/<sound_card_id>.yaml and starts sound card initialization. -fn sound_card_init() -> std::result::Result<(), Box<dyn error::Error>> { - let args = parse_args()?; +fn sound_card_init(args: &Args) -> std::result::Result<(), Box<dyn error::Error>> { info!("sound_card_id: {}", args.sound_card_id); - let conf = get_config(&args)?; + let conf = get_config(args)?; match args.sound_card_id.as_str() { "sofcmlmax98390d" => { run_max98390d(&args.sound_card_id, &conf)?; info!("run_max98390d() finished successfully."); + Ok(()) } - _ => return Err(Error::UnsupportedSoundCard(args.sound_card_id).into()), - }; - Ok(()) + _ => Err(Error::UnsupportedSoundCard(args.sound_card_id.clone()).into()), + } } fn main() { syslog::init().expect("failed to initialize syslog"); - if let Err(e) = sound_card_init() { + let args = match parse_args() { + Ok(args) => args, + Err(e) => { + error!("failed to parse arguments: {}", e); + return; + } + }; + + if let Err(e) = sound_card_init(&args) { error!("sound_card_init: {}", e); } + + if let Err(e) = run_time::now_to_file(&args.sound_card_id) { + error!("failed to create sound_card_init run time file: {}", e); + } } diff --git a/sound_card_init/utils/Cargo.lock b/sound_card_init/utils/Cargo.lock new file mode 100644 index 00000000..fd53fb0f --- /dev/null +++ b/sound_card_init/utils/Cargo.lock @@ -0,0 +1,109 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "dtoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" + +[[package]] +name = "linked-hash-map" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "remain" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ba1e78fa68412cb93ef642fd4d20b9a941be49ee9333875ebaf13112673ea7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_yaml" +version = "0.8.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5" +dependencies = [ + "dtoa", + "linked-hash-map", + "serde", + "yaml-rust", +] + +[[package]] +name = "syn" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "utils" +version = "0.1.0" +dependencies = [ + "remain", + "serde", + "serde_yaml", +] + +[[package]] +name = "yaml-rust" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" +dependencies = [ + "linked-hash-map", +] diff --git a/sound_card_init/utils/Cargo.toml b/sound_card_init/utils/Cargo.toml new file mode 100644 index 00000000..8c688de3 --- /dev/null +++ b/sound_card_init/utils/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "utils" +version = "0.1.0" +authors = ["The Chromium OS Authors"] +edition = "2018" +description = "Utils for sound_card_init" + +[dependencies] +remain = "0.2.1" +serde = { version = "1.0", features = ["derive"]} +serde_yaml = "0.8.11" diff --git a/sound_card_init/utils/src/error.rs b/sound_card_init/utils/src/error.rs new file mode 100644 index 00000000..93d53b9b --- /dev/null +++ b/sound_card_init/utils/src/error.rs @@ -0,0 +1,39 @@ +// Copyright 2020 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//! This mod contains all possible errors that can occur within utils. +use std::fmt; +use std::io; +use std::time; +use std::{error, path::PathBuf}; + +use remain::sorted; + +/// Alias for a `Result` with the error type `utils::Error`. +pub type Result<T> = std::result::Result<T, Error>; + +/// This type represents all possible errors that can occur within utils. +#[sorted] +#[derive(Debug)] +pub enum Error { + /// It wraps file path with the io::Error. + FileIOFailed(PathBuf, io::Error), + /// It wraps file path with the serde_yaml::Error. + SerdeError(PathBuf, serde_yaml::Error), + /// It wraps time::SystemTimeError. + SystemTimeError(time::SystemTimeError), +} + +impl error::Error for Error {} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Error::*; + match self { + FileIOFailed(file, e) => write!(f, "{:?}: {}", file, e), + SerdeError(file, e) => write!(f, "{:?}: {}", file, e), + SystemTimeError(e) => write!(f, "{}", e), + } + } +} diff --git a/sound_card_init/utils/src/lib.rs b/sound_card_init/utils/src/lib.rs new file mode 100644 index 00000000..e8edce77 --- /dev/null +++ b/sound_card_init/utils/src/lib.rs @@ -0,0 +1,87 @@ +// Copyright 2020 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +//! It contains common utils shared within sound_card_init. +#![deny(missing_docs)] + +//! The error definitions for utils. +pub mod error; + +use std::fs::File; +use std::io::{prelude::*, BufReader, BufWriter}; +use std::path::PathBuf; +use std::time::Duration; + +use crate::error::{Error, Result}; + +/// The path of datastore. +pub const DATASTORE_DIR: &str = "/var/lib/sound_card_init"; + +fn duration_from_file(path: &PathBuf) -> Result<Duration> { + let reader = + BufReader::new(File::open(&path).map_err(|e| Error::FileIOFailed(path.clone(), e))?); + serde_yaml::from_reader(reader).map_err(|e| Error::SerdeError(path.clone(), e)) +} + +/// The utils to parse CRAS shutdown time file. +pub mod shutdown_time { + use super::*; + // The path of CRAS shutdown time file. + const SHUTDOWN_TIME_FILE: &str = "/var/lib/cras/stop"; + + /// Reads the unix time from CRAS shutdown time file. + pub fn from_file() -> Result<Duration> { + duration_from_file(&PathBuf::from(SHUTDOWN_TIME_FILE)) + } +} + +/// The utils to create and parse sound_card_init run time file. +pub mod run_time { + use std::time::SystemTime; + + use super::*; + // The filename of sound_card_init run time file. + const RUN_TIME_FILE: &str = "run"; + + /// Returns the sound_card_init run time file existence. + pub fn exists(snd_card: &str) -> bool { + run_time_file(snd_card).exists() + } + + /// Reads the unix time from sound_card_init run time file. + pub fn from_file(snd_card: &str) -> Result<Duration> { + duration_from_file(&run_time_file(snd_card)) + } + + /// Saves the current unix time to sound_card_init run time file. + pub fn now_to_file(snd_card: &str) -> Result<()> { + match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { + Ok(t) => to_file(snd_card, t), + Err(e) => Err(Error::SystemTimeError(e)), + } + } + + /// Saves the unix time to sound_card_init run time file. + pub fn to_file(snd_card: &str, duration: Duration) -> Result<()> { + let path = run_time_file(snd_card); + let mut writer = + BufWriter::new(File::create(&path).map_err(|e| Error::FileIOFailed(path.clone(), e))?); + writer + .write_all( + serde_yaml::to_string(&duration) + .map_err(|e| Error::SerdeError(path.clone(), e))? + .as_bytes(), + ) + .map_err(|e| Error::FileIOFailed(path.clone(), e))?; + writer + .flush() + .map_err(|e| Error::FileIOFailed(path.clone(), e))?; + Ok(()) + } + + fn run_time_file(snd_card: &str) -> PathBuf { + PathBuf::from(DATASTORE_DIR) + .join(snd_card) + .join(RUN_TIME_FILE) + } +} diff --git a/ucm-config/for_all_boards/C505 HD Webcam/C505 HD Webcam.conf b/ucm-config/for_all_boards/C505 HD Webcam/C505 HD Webcam.conf new file mode 100644 index 00000000..126ac331 --- /dev/null +++ b/ucm-config/for_all_boards/C505 HD Webcam/C505 HD Webcam.conf @@ -0,0 +1,6 @@ +Comment "Logitech Webcam C505" + +SectionUseCase."HiFi" { + File "HiFi.conf" + Comment "Default" +} diff --git a/ucm-config/for_all_boards/C505 HD Webcam/HiFi.conf b/ucm-config/for_all_boards/C505 HD Webcam/HiFi.conf new file mode 100644 index 00000000..c7e13426 --- /dev/null +++ b/ucm-config/for_all_boards/C505 HD Webcam/HiFi.conf @@ -0,0 +1,25 @@ +SectionVerb { + Value { + FullySpecifiedUCM "1" + } + + EnableSequence [ + cdev "hw:Webcam" + cset "name='Mic Capture Volume' 0" + ] + + DisableSequence [ + ] +} + +SectionDevice."Webcam" { + Value { + CapturePCM "hw:Webcam,0" + } + + EnableSequence [ + ] + + DisableSequence [ + ] +} diff --git a/ucm-config/for_all_boards/Chat 150 C/HiFi.conf b/ucm-config/for_all_boards/Chat 150 C/HiFi.conf index 5a73702c..368796d7 100644 --- a/ucm-config/for_all_boards/Chat 150 C/HiFi.conf +++ b/ucm-config/for_all_boards/Chat 150 C/HiFi.conf @@ -10,7 +10,7 @@ SectionVerb { ] } -SectionDevice."Dummy".0 { +SectionDevice."Chat 150 C".0 { EnableSequence [ ] diff --git a/ucm-config/for_all_boards/Jabra SPEAK 810/HiFi.conf b/ucm-config/for_all_boards/Jabra SPEAK 810/HiFi.conf index 5a73702c..313bffef 100644 --- a/ucm-config/for_all_boards/Jabra SPEAK 810/HiFi.conf +++ b/ucm-config/for_all_boards/Jabra SPEAK 810/HiFi.conf @@ -10,7 +10,7 @@ SectionVerb { ] } -SectionDevice."Dummy".0 { +SectionDevice."Jabra Speak 810".0 { EnableSequence [ ] diff --git a/unblocked_terms.txt b/unblocked_terms.txt new file mode 100644 index 00000000..674e3d33 --- /dev/null +++ b/unblocked_terms.txt @@ -0,0 +1,11 @@ +# KEEP THIS COMMENT IN YOUR COPY. +# +# Don't delete this file if you want to keep keyword_check enabled even if it's +# empty. +# +# See repohooks/README.md for more details. + +dummy +master +\bnative +white.?list |