Skip to content

Commit

Permalink
Use board.ini to tell whether beamforming is supported
Browse files Browse the repository at this point in the history
FIXED=b:339785214
TEST=tast run dut14 audio.CrasEffects.beamforming_\*
TEST=audio_diagnostics

Cq-Depend: chromium:6040873
Change-Id: I1bd1feb291c8735276781834735e49edee94fd4e
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/adhd/+/6041034
Commit-Queue: Li-Yu Yu <aaronyu@google.com>
Reviewed-by: Hung-Hsien Chen <hunghsienchen@chromium.org>
Tested-by: Li-Yu Yu <aaronyu@google.com>
  • Loading branch information
afq984 authored and Chromeos LUCI committed Nov 22, 2024
1 parent c6ef276 commit e548e3d
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 56 deletions.
2 changes: 1 addition & 1 deletion Cargo.Bazel.lock
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"checksum": "ebbade571a19bd8a249f549bcec79328578f1001529441856d301b861d5eb5ad",
"checksum": "007a6f993637dd2a9588e36625c054d01a11ddca0e94f8fb0410c7b32cc02e32",
"crates": {
"addr2line 0.20.0": {
"name": "addr2line",
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions cras/server/processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use audio_processor::ProcessorVec;
use cras_common::types_internal::CrasDlcId;
use cras_common::types_internal::CrasProcessorEffect;
use cras_dlc::get_dlc_state_cached;
use cras_s2::BEAMFORMING_CONFIG_PATH;
use cras_s2::global::cras_s2_get_beamforming_config_path;

mod processor_override;
mod proto;
Expand Down Expand Up @@ -126,7 +126,10 @@ fn get_style_transfer_pipeline_decl(context: &dyn ResolverContext) -> anyhow::Re
}

fn get_beamforming_pipeline_decl(context: &dyn ResolverContext) -> anyhow::Result<Processor> {
cdcfg::parse(context, Path::new(BEAMFORMING_CONFIG_PATH))
cdcfg::parse(
context,
&cras_s2_get_beamforming_config_path().context("beamforming config path unknown")?,
)
}

impl CrasProcessor {
Expand Down
1 change: 1 addition & 0 deletions cras/server/s2/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ cras_rust_library(
deps = all_crate_deps(normal = True) + [
"//audio_processor",
"//cras/common:rust_common",
"//cras/server/ini",
],
)

Expand Down
1 change: 1 addition & 0 deletions cras/server/s2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition = "2021"
[dependencies]
audio_processor = { workspace = true }
cras_common = { workspace = true }
cras_ini = { workspace = true }

anyhow = { workspace = true }
libc = { workspace = true }
Expand Down
21 changes: 15 additions & 6 deletions cras/server/s2/src/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::collections::HashMap;
use std::ffi::c_char;
use std::ffi::CString;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::Mutex;
use std::sync::MutexGuard;
use std::sync::OnceLock;
Expand All @@ -16,6 +17,8 @@ use cras_common::types_internal::CrasProcessorEffect;
use cras_common::types_internal::CRAS_NC_PROVIDER;
use cras_common::types_internal::EFFECT_TYPE;

use crate::processing::BeamformingConfig;

fn state() -> MutexGuard<'static, crate::S2> {
static CELL: OnceLock<Mutex<crate::S2>> = OnceLock::new();
CELL.get_or_init(|| Mutex::new(crate::S2::new()))
Expand Down Expand Up @@ -133,16 +136,13 @@ pub extern "C" fn cras_s2_get_voice_isolation_ui_enabled() -> bool {
pub extern "C" fn cras_s2_init() {
match std::fs::read_to_string("/run/chromeos-config/v1/audio/main/cras-config-dir") {
Ok(str) => {
state().set_cras_config_dir(&str);
state().read_cras_config(&str);
}
Err(err) => {
state().set_cras_config_dir("");
log::info!("Failed to read cras-config-dir: {err}");
state().read_cras_config("");
log::error!("Failed to read cras-config-dir: {err:#}");
}
}
if let Err(err) = state().read_beamforming_config() {
log::error!("{err}");
}
}

#[no_mangle]
Expand Down Expand Up @@ -285,3 +285,12 @@ pub extern "C" fn cras_s2_set_spatial_audio_supported(supported: bool) {
pub extern "C" fn cras_s2_get_spatial_audio_supported() -> bool {
state().input.spatial_audio_supported
}

pub fn cras_s2_get_beamforming_config_path() -> Option<PathBuf> {
match &state().input.beamforming_config {
BeamformingConfig::Supported(beamforming_properties) => {
Some(beamforming_properties.pipeline_path.clone())
}
BeamformingConfig::Unsupported { .. } => None,
}
}
95 changes: 48 additions & 47 deletions cras/server/s2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,20 @@
// found in the LICENSE file.

use std::collections::HashMap;
use std::collections::HashSet;
use std::path::Path;

use anyhow::Context;
use audio_processor::cdcfg;
use cras_common::types_internal::CrasDlcId;
use cras_common::types_internal::CrasEffectUIAppearance;
use cras_common::types_internal::CRAS_NC_PROVIDER;
use cras_common::types_internal::CRAS_NC_PROVIDER_PREFERENCE_ORDER;
use cras_common::types_internal::EFFECT_TYPE;
use global::ResetIodevListForVoiceIsolation;
use processing::BeamformingConfig;
use serde::Serialize;

use crate::global::NotifyAudioEffectUIAppearanceChanged;

pub mod global;

pub const BEAMFORMING_CONFIG_PATH: &str = "/etc/cras/processor/beamforming.txtpb";
pub mod processing;

#[derive(Default, Serialize)]
struct Input {
Expand All @@ -34,9 +30,7 @@ struct Input {
style_transfer_featured_allowed: bool,
// cros_config /audio/main cras-config-dir.
cras_config_dir: String,
// List of DLCs to provide the beamforming feature.
// None means beamforming is not supported.
beamforming_required_dlcs: Option<HashSet<String>>,
beamforming_config: BeamformingConfig,
active_input_node_compatible_nc_providers: CRAS_NC_PROVIDER,
voice_isolation_ui_enabled: bool,
voice_isolation_ui_preferred_effect: EFFECT_TYPE,
Expand Down Expand Up @@ -94,10 +88,10 @@ struct Output {

fn resolve(input: &Input) -> Output {
// TODO(b/339785214): Decide this based on config file content.
let beamforming_supported = input.cras_config_dir.ends_with(".3mic");
let beamforming_allowed = match &input.beamforming_required_dlcs {
None => false,
Some(dlcs) => dlcs.iter().all(|dlc| {
let beamforming_supported = matches!(input.beamforming_config, BeamformingConfig::Supported(_));
let beamforming_allowed = match &input.beamforming_config {
BeamformingConfig::Unsupported { .. } => false,
BeamformingConfig::Supported(properties) => properties.required_dlcs.iter().all(|dlc| {
input
.dlc_installed
.as_ref()
Expand Down Expand Up @@ -379,13 +373,15 @@ impl S2 {
self.update();
}

fn set_cras_config_dir(&mut self, cras_config_dir: &str) {
fn read_cras_config(&mut self, cras_config_dir: &str) {
self.input.cras_config_dir = cras_config_dir.into();
self.input.beamforming_config = BeamformingConfig::probe(&self.input.cras_config_dir);
self.update();
}

fn set_beamforming_required_dlcs(&mut self, dlcs: HashSet<String>) {
self.input.beamforming_required_dlcs = Some(dlcs);
#[cfg(test)]
fn set_beamforming_config(&mut self, beamforming_config: BeamformingConfig) {
self.input.beamforming_config = beamforming_config;
self.update();
}

Expand Down Expand Up @@ -458,14 +454,6 @@ impl S2 {
&CRAS_NC_PROVIDER::NONE
}

fn read_beamforming_config(&mut self) -> anyhow::Result<()> {
self.set_beamforming_required_dlcs(
cdcfg::get_required_dlcs(Path::new(BEAMFORMING_CONFIG_PATH))
.context("get_required_dlcs")?,
);
Ok(())
}

fn set_spatial_audio_enabled(&mut self, enabled: bool) {
self.input.spatial_audio_enabled = enabled;
self.update();
Expand Down Expand Up @@ -531,6 +519,8 @@ mod tests {
use cras_common::types_internal::CRAS_NC_PROVIDER;
use cras_common::types_internal::EFFECT_TYPE;

use crate::processing::BeamformingConfig;
use crate::processing::BeamformingProperties;
use crate::resolve_effect_toggle_type;
use crate::AudioEffectStatus;
use crate::S2;
Expand Down Expand Up @@ -606,29 +596,34 @@ mod tests {
let mut s = S2::new();
s.set_ap_nc_segmentation_allowed(true);
s.set_style_transfer_featured_allowed(true);
s.set_beamforming_required_dlcs(HashSet::from([CrasDlcId::CrasDlcIntelligoBeamforming
.as_str()
.to_string()]));
assert!(!s.output.get_bf_status().supported);
s.set_beamforming_config(BeamformingConfig::Supported(BeamformingProperties {
required_dlcs: HashSet::from([CrasDlcId::CrasDlcIntelligoBeamforming
.as_str()
.to_string()]),
..Default::default()
}));
assert!(s.output.get_bf_status().supported);
assert!(s.output.get_ast_status().supported);

s.set_cras_config_dir("omniknight.3mic");
s.input.cras_config_dir = "does not end with .3mic____".to_string(); // The config dir should not matter.
s.update();
assert!(s.output.get_bf_status().supported);

assert!(!s.output.get_bf_status().allowed);
s.set_dlc_installed(CrasDlcId::CrasDlcIntelligoBeamforming, true);
assert!(s.output.get_bf_status().allowed);

let dlcs = HashSet::from(["does-not-exist".to_string()]);
s.set_beamforming_required_dlcs(dlcs);
s.set_beamforming_config(BeamformingConfig::Supported(BeamformingProperties {
required_dlcs: dlcs,
..Default::default()
}));
assert!(!s.output.get_bf_status().allowed);
s.input.beamforming_required_dlcs = None;
s.set_beamforming_config(BeamformingConfig::Unsupported {
reason: "testing".to_string(),
});
s.update();
assert!(!s.output.get_bf_status().allowed);

s.set_cras_config_dir("omniknight");
assert!(!s.output.get_bf_status().supported);
assert!(s.output.get_ast_status().supported);
}

#[test]
Expand Down Expand Up @@ -920,11 +915,13 @@ mod tests {
expected |= CRAS_NC_PROVIDER::AST;
assert_eq!(s.output.system_valid_nc_providers, expected);

s.set_beamforming_required_dlcs(HashSet::from([CrasDlcId::CrasDlcIntelligoBeamforming
.as_str()
.to_string()]));
s.set_beamforming_config(BeamformingConfig::Supported(BeamformingProperties {
required_dlcs: HashSet::from([CrasDlcId::CrasDlcIntelligoBeamforming
.as_str()
.to_string()]),
..Default::default()
}));
s.set_dlc_installed(CrasDlcId::CrasDlcIntelligoBeamforming, true);
s.set_cras_config_dir("omniknight.3mic");

s.set_voice_isolation_ui_preferred_effect(EFFECT_TYPE::STYLE_TRANSFER);
assert_eq!(s.output.system_valid_nc_providers, expected);
Expand Down Expand Up @@ -994,11 +991,13 @@ mod tests {
);

// Test for UI preferred effect
s.set_beamforming_required_dlcs(HashSet::from([CrasDlcId::CrasDlcIntelligoBeamforming
.as_str()
.to_string()]));
s.set_beamforming_config(BeamformingConfig::Supported(BeamformingProperties {
required_dlcs: HashSet::from([CrasDlcId::CrasDlcIntelligoBeamforming
.as_str()
.to_string()]),
..Default::default()
}));
s.set_dlc_installed(CrasDlcId::CrasDlcIntelligoBeamforming, true);
s.set_cras_config_dir("omniknight.3mic");
s.set_voice_isolation_ui_preferred_effect(EFFECT_TYPE::STYLE_TRANSFER);
assert_eq!(
s.resolve_nc_provider(CRAS_NC_PROVIDER::all(), true),
Expand Down Expand Up @@ -1034,11 +1033,13 @@ mod tests {
CALLED.store(false, Ordering::SeqCst);
s.set_voice_isolation_ui_preferred_effect(EFFECT_TYPE::STYLE_TRANSFER);
// Set BF as supported and allowed.
s.set_beamforming_required_dlcs(HashSet::from([CrasDlcId::CrasDlcIntelligoBeamforming
.as_str()
.to_string()]));
s.set_beamforming_config(BeamformingConfig::Supported(BeamformingProperties {
required_dlcs: HashSet::from([CrasDlcId::CrasDlcIntelligoBeamforming
.as_str()
.to_string()]),
..Default::default()
}));
s.set_dlc_installed(CrasDlcId::CrasDlcIntelligoBeamforming, true);
s.set_cras_config_dir("omniknight.3mic");
// No need to reset iodev list because the preferred effect is AST.
assert!(!CALLED.load(Ordering::SeqCst));

Expand Down
69 changes: 69 additions & 0 deletions cras/server/s2/src/processing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::collections::HashSet;
use std::path::Path;
use std::path::PathBuf;

use anyhow::Context;
use audio_processor::cdcfg;
use serde::Serialize;

#[derive(Serialize)]
pub enum BeamformingConfig {
Supported(BeamformingProperties),
Unsupported { reason: String },
}

impl Default for BeamformingConfig {
fn default() -> Self {
BeamformingConfig::Unsupported {
reason: "not probed yet".to_string(),
}
}
}

impl BeamformingConfig {
pub fn probe(cras_config_dir: &str) -> Self {
match BeamformingProperties::probe(cras_config_dir) {
Ok(properties) => Self::Supported(properties),
Err(err) => Self::Unsupported {
reason: format!("{err:#}"),
},
}
}
}

#[derive(Default, Serialize)]
pub struct BeamformingProperties {
/// The pipeline path to use for beamforming.
pub pipeline_path: PathBuf,
/// The set of DLCs required for beamforming to function.
pub required_dlcs: HashSet<String>,
}

impl BeamformingProperties {
fn probe(cras_config_dir: &str) -> anyhow::Result<Self> {
let board_ini = cras_ini::parse_file(
&Path::new("/etc/cras")
.join(cras_config_dir)
.join("board.ini"),
)
.context("cannot parse board.ini")?;
let pipeline_file = board_ini
.get("processing")
.context("processing section not found in board.ini")?
.get("beamforming_pipeline_file")
.context("beamforming_pipeline_file not found in [processing] in board.ini")?
.as_str()
.to_string();
let pipeline_path = Path::new("/etc/cras/processor").join(&pipeline_file);
let required_dlcs = cdcfg::get_required_dlcs(&pipeline_path)
.context("cannot get required DLCs from pipeline file")?;
Ok(BeamformingProperties {
pipeline_path,
required_dlcs,
})
}
}

0 comments on commit e548e3d

Please sign in to comment.