xref: /aosp_15_r20/external/crosvm/e2e_tests/tests/snd.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2023 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker use std::fs;
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use fixture::vhost_user::CmdType;
8*bb4ee6a4SAndroid Build Coastguard Worker use fixture::vhost_user::Config as VuConfig;
9*bb4ee6a4SAndroid Build Coastguard Worker use fixture::vhost_user::VhostUserBackend;
10*bb4ee6a4SAndroid Build Coastguard Worker use fixture::vm::Config;
11*bb4ee6a4SAndroid Build Coastguard Worker use fixture::vm::TestVm;
12*bb4ee6a4SAndroid Build Coastguard Worker use tempfile::NamedTempFile;
13*bb4ee6a4SAndroid Build Coastguard Worker use tempfile::TempDir;
14*bb4ee6a4SAndroid Build Coastguard Worker 
15*bb4ee6a4SAndroid Build Coastguard Worker /// Tests audio playback on virtio-snd with file backend
16*bb4ee6a4SAndroid Build Coastguard Worker ///
17*bb4ee6a4SAndroid Build Coastguard Worker /// 1. Create a temporal directory for the audio file.
18*bb4ee6a4SAndroid Build Coastguard Worker /// 2. Start a VM with a virtiofs device for the temporal directory and a virtio-snd device with
19*bb4ee6a4SAndroid Build Coastguard Worker ///    file backend.
20*bb4ee6a4SAndroid Build Coastguard Worker /// 3. Create a raw audio file in the temporal directory with sox.
21*bb4ee6a4SAndroid Build Coastguard Worker /// 4. Do playback with aplay.
22*bb4ee6a4SAndroid Build Coastguard Worker /// 5. Compare the generated audio file and the output from virtio-snd.
23*bb4ee6a4SAndroid Build Coastguard Worker #[test]
do_playback()24*bb4ee6a4SAndroid Build Coastguard Worker fn do_playback() {
25*bb4ee6a4SAndroid Build Coastguard Worker     let temp_dir = tempfile::tempdir().unwrap();
26*bb4ee6a4SAndroid Build Coastguard Worker     let temp_dir_path_str = temp_dir.path().to_str().unwrap();
27*bb4ee6a4SAndroid Build Coastguard Worker 
28*bb4ee6a4SAndroid Build Coastguard Worker     let config = get_test_vm_config(
29*bb4ee6a4SAndroid Build Coastguard Worker         temp_dir_path_str,
30*bb4ee6a4SAndroid Build Coastguard Worker         vec![
31*bb4ee6a4SAndroid Build Coastguard Worker             "--virtio-snd".to_string(),
32*bb4ee6a4SAndroid Build Coastguard Worker             get_virtio_snd_args(temp_dir_path_str),
33*bb4ee6a4SAndroid Build Coastguard Worker         ],
34*bb4ee6a4SAndroid Build Coastguard Worker     );
35*bb4ee6a4SAndroid Build Coastguard Worker     playback_and_check(config, temp_dir)
36*bb4ee6a4SAndroid Build Coastguard Worker }
37*bb4ee6a4SAndroid Build Coastguard Worker 
38*bb4ee6a4SAndroid Build Coastguard Worker /// Tests audio playback with vhost user.
39*bb4ee6a4SAndroid Build Coastguard Worker #[test]
do_playback_with_vhost_user()40*bb4ee6a4SAndroid Build Coastguard Worker fn do_playback_with_vhost_user() {
41*bb4ee6a4SAndroid Build Coastguard Worker     let temp_dir = tempfile::tempdir().unwrap();
42*bb4ee6a4SAndroid Build Coastguard Worker     let temp_dir_path_str = temp_dir.path().to_str().unwrap();
43*bb4ee6a4SAndroid Build Coastguard Worker 
44*bb4ee6a4SAndroid Build Coastguard Worker     let socket = NamedTempFile::new().unwrap();
45*bb4ee6a4SAndroid Build Coastguard Worker     let socket_path_str = socket.path().to_str().unwrap();
46*bb4ee6a4SAndroid Build Coastguard Worker 
47*bb4ee6a4SAndroid Build Coastguard Worker     let vu_config = VuConfig::new(CmdType::Device, "snd").extra_args(vec![
48*bb4ee6a4SAndroid Build Coastguard Worker         "snd".to_string(),
49*bb4ee6a4SAndroid Build Coastguard Worker         "--config".to_string(),
50*bb4ee6a4SAndroid Build Coastguard Worker         get_virtio_snd_args(temp_dir_path_str),
51*bb4ee6a4SAndroid Build Coastguard Worker         "--socket-path".to_string(),
52*bb4ee6a4SAndroid Build Coastguard Worker         socket_path_str.to_string(),
53*bb4ee6a4SAndroid Build Coastguard Worker     ]);
54*bb4ee6a4SAndroid Build Coastguard Worker     let _vu_device = VhostUserBackend::new(vu_config).unwrap();
55*bb4ee6a4SAndroid Build Coastguard Worker 
56*bb4ee6a4SAndroid Build Coastguard Worker     let config = get_test_vm_config(
57*bb4ee6a4SAndroid Build Coastguard Worker         temp_dir_path_str,
58*bb4ee6a4SAndroid Build Coastguard Worker         vec![
59*bb4ee6a4SAndroid Build Coastguard Worker             "--vhost-user".to_string(),
60*bb4ee6a4SAndroid Build Coastguard Worker             format!("sound,socket={}", socket_path_str),
61*bb4ee6a4SAndroid Build Coastguard Worker         ],
62*bb4ee6a4SAndroid Build Coastguard Worker     );
63*bb4ee6a4SAndroid Build Coastguard Worker     playback_and_check(config, temp_dir)
64*bb4ee6a4SAndroid Build Coastguard Worker }
65*bb4ee6a4SAndroid Build Coastguard Worker 
playback_and_check(config: Config, temp_dir: TempDir)66*bb4ee6a4SAndroid Build Coastguard Worker fn playback_and_check(config: Config, temp_dir: TempDir) {
67*bb4ee6a4SAndroid Build Coastguard Worker     let mut vm = TestVm::new(config).unwrap();
68*bb4ee6a4SAndroid Build Coastguard Worker     vm.exec_in_guest("mount -t virtiofs tmp2 /mnt").unwrap();
69*bb4ee6a4SAndroid Build Coastguard Worker     vm.exec_in_guest("ls /mnt 1>&2").unwrap();
70*bb4ee6a4SAndroid Build Coastguard Worker     vm.exec_in_guest(
71*bb4ee6a4SAndroid Build Coastguard Worker         "sox -n -b 16 -r 48000 -c 2 -e signed -t raw \
72*bb4ee6a4SAndroid Build Coastguard Worker         /mnt/test_440_48000.raw synth 1 sine 440 vol -10dB",
73*bb4ee6a4SAndroid Build Coastguard Worker     )
74*bb4ee6a4SAndroid Build Coastguard Worker     .unwrap();
75*bb4ee6a4SAndroid Build Coastguard Worker     vm.exec_in_guest(
76*bb4ee6a4SAndroid Build Coastguard Worker         "aplay --buffer-size=48000 --period-size=12000 \
77*bb4ee6a4SAndroid Build Coastguard Worker         -d 1 -f dat -Dhw:0,0 /mnt/test_440_48000.raw",
78*bb4ee6a4SAndroid Build Coastguard Worker     )
79*bb4ee6a4SAndroid Build Coastguard Worker     .unwrap();
80*bb4ee6a4SAndroid Build Coastguard Worker 
81*bb4ee6a4SAndroid Build Coastguard Worker     assert!(compare_files(
82*bb4ee6a4SAndroid Build Coastguard Worker         temp_dir,
83*bb4ee6a4SAndroid Build Coastguard Worker         "test_440_48000.raw",
84*bb4ee6a4SAndroid Build Coastguard Worker         "stream-0.out"
85*bb4ee6a4SAndroid Build Coastguard Worker     ));
86*bb4ee6a4SAndroid Build Coastguard Worker }
87*bb4ee6a4SAndroid Build Coastguard Worker 
get_virtio_snd_args(output_file_path_str: &str) -> String88*bb4ee6a4SAndroid Build Coastguard Worker fn get_virtio_snd_args(output_file_path_str: &str) -> String {
89*bb4ee6a4SAndroid Build Coastguard Worker     format!(
90*bb4ee6a4SAndroid Build Coastguard Worker         "backend=file,playback_path={},playback_size=400000",
91*bb4ee6a4SAndroid Build Coastguard Worker         output_file_path_str
92*bb4ee6a4SAndroid Build Coastguard Worker     )
93*bb4ee6a4SAndroid Build Coastguard Worker }
94*bb4ee6a4SAndroid Build Coastguard Worker 
get_test_vm_config(temp_dir_path_str: &str, snd_args: Vec<String>) -> Config95*bb4ee6a4SAndroid Build Coastguard Worker fn get_test_vm_config(temp_dir_path_str: &str, snd_args: Vec<String>) -> Config {
96*bb4ee6a4SAndroid Build Coastguard Worker     let mut args = vec![
97*bb4ee6a4SAndroid Build Coastguard Worker         "--shared-dir".to_string(),
98*bb4ee6a4SAndroid Build Coastguard Worker         format!("{}:tmp2:type=fs:cache=always", temp_dir_path_str),
99*bb4ee6a4SAndroid Build Coastguard Worker     ];
100*bb4ee6a4SAndroid Build Coastguard Worker     args.extend(snd_args);
101*bb4ee6a4SAndroid Build Coastguard Worker     Config::new().extra_args(args)
102*bb4ee6a4SAndroid Build Coastguard Worker }
103*bb4ee6a4SAndroid Build Coastguard Worker 
compare_files(temp_dir: TempDir, golden_file_name: &str, output_file_name: &str) -> bool104*bb4ee6a4SAndroid Build Coastguard Worker fn compare_files(temp_dir: TempDir, golden_file_name: &str, output_file_name: &str) -> bool {
105*bb4ee6a4SAndroid Build Coastguard Worker     // 1 second, 2 channels, 16 bit (2 byte) format, 48000 frame rate.
106*bb4ee6a4SAndroid Build Coastguard Worker     const BYTES_TO_COMPARE: usize = 1 * 2 * 2 * 48000;
107*bb4ee6a4SAndroid Build Coastguard Worker     // Skip the first buffer-size bytes as it's 0 pads.
108*bb4ee6a4SAndroid Build Coastguard Worker     const SKIP_OFFSET: usize = 48000;
109*bb4ee6a4SAndroid Build Coastguard Worker 
110*bb4ee6a4SAndroid Build Coastguard Worker     // Open the second file for reading
111*bb4ee6a4SAndroid Build Coastguard Worker     let buf1 = fs::read(temp_dir.path().join(golden_file_name)).unwrap();
112*bb4ee6a4SAndroid Build Coastguard Worker     let buf2 = fs::read(temp_dir.path().join(output_file_name)).unwrap();
113*bb4ee6a4SAndroid Build Coastguard Worker 
114*bb4ee6a4SAndroid Build Coastguard Worker     if buf1[..BYTES_TO_COMPARE] != buf2[SKIP_OFFSET..(SKIP_OFFSET + BYTES_TO_COMPARE)] {
115*bb4ee6a4SAndroid Build Coastguard Worker         println!("Files differ");
116*bb4ee6a4SAndroid Build Coastguard Worker         return false;
117*bb4ee6a4SAndroid Build Coastguard Worker     }
118*bb4ee6a4SAndroid Build Coastguard Worker 
119*bb4ee6a4SAndroid Build Coastguard Worker     true
120*bb4ee6a4SAndroid Build Coastguard Worker }
121