1#!/usr/bin/env python3 2# 3# Copyright (C) 2017 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); you may not 6# use this file except in compliance with the License. You may obtain a copy of 7# the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14# License for the specific language governing permissions and limitations under 15# the License. 16""" 17Test script to automate the Bluetooth audio testing and analysis. 18 19Quick way to generate necessary audio files: 20sudo apt-get install sox 21sox -b 16 -r 48000 -c 2 -n audio_file_2k1k_10_sec.wav synth 10 sine 2000 sine 3000 22sox -b 16 -r 48000 -c 2 -n audio_file_2k1k_300_sec.wav synth 300 sine 2000 sine 3000 23 24""" 25import os 26import subprocess 27import time 28 29from acts.test_decorators import test_tracker_info 30from acts.test_utils.audio_analysis_lib.check_quality import quality_analysis 31from acts.test_utils.bt.BtFunhausBaseTest import BtFunhausBaseTest 32from acts.test_utils.bt.bt_constants import audio_bits_per_sample_32 33from acts.test_utils.bt.bt_constants import audio_channel_mode_8 34from acts.test_utils.bt.bt_constants import audio_sample_rate_48000 35from acts.test_utils.bt.bt_constants import delay_after_binding_seconds 36from acts.test_utils.bt.bt_constants import delay_before_record_seconds 37from acts.test_utils.bt.bt_constants import fpga_linein_bus_endpoint 38from acts.test_utils.bt.bt_constants import headphone_bus_endpoint 39from acts.test_utils.bt.bt_constants import silence_wait_seconds 40 41 42class BtChameleonTest(BtFunhausBaseTest): 43 44 audio_file_2k1k_10_sec = "audio_file_2k1k_10_sec.wav" 45 audio_file_2k1k_300_sec = "audio_file_2k1k_300_sec.wav" 46 android_sdcard_music_path = "/sdcard/Music" 47 48 def setup_class(self): 49 super().setup_class() 50 self.chameleon = self.chameleon_devices[0] 51 self.dut = self.android_devices[0] 52 self.raw_audio_dest = "{}/{}".format(self.android_devices[0].log_path, 53 "Chameleon_audio") 54 os.makedirs(self.raw_audio_dest, exist_ok=True) 55 self.chameleon.audio_board_connect(1, headphone_bus_endpoint) 56 self.chameleon.audio_board_connect(1, fpga_linein_bus_endpoint) 57 time.sleep(delay_after_binding_seconds) 58 59 def _orchestrate_audio_quality_test(self, output_file_prefix_name, 60 bits_per_sample, rate, record_seconds, 61 channel, audio_to_play): 62 audio_analysis_filename = "{}_audio_analysis.txt".format( 63 output_file_prefix_name) 64 bluetooth_bind_time_seconds = 5 65 port_id = 6 66 has_file = True 67 # Additional sleep to allow full connection of Bluetooth device 68 # from test setup. 69 time.sleep(bluetooth_bind_time_seconds) 70 self.chameleon.start_capturing_audio(port_id, has_file) 71 time.sleep(delay_before_record_seconds) 72 self.dut.droid.mediaPlayOpen("file://{}".format(audio_to_play)) 73 time.sleep(record_seconds + silence_wait_seconds) 74 raw_audio_info = self.chameleon_devices[0].stop_capturing_audio( 75 port_id) 76 self.ad.droid.mediaPlayStopAll() 77 raw_audio_path = raw_audio_info[0] 78 dest_file_path = "{}/{}_recording.raw".format(self.raw_audio_dest, 79 output_file_prefix_name) 80 self.chameleon.scp(raw_audio_path, dest_file_path) 81 self._collect_bluetooth_manager_dumpsys_logs(self.android_devices) 82 self.collect_bluetooth_manager_metrics_logs(self.android_devices) 83 analysis_path = "{}/{}".format(self.raw_audio_dest, 84 audio_analysis_filename) 85 try: 86 quality_analysis( 87 filename=dest_file_path, 88 output_file=analysis_path, 89 bit_width=bits_per_sample, 90 rate=rate, 91 channel=channel, 92 spectral_only=False) 93 except Exception as err: 94 self.log.exception("Failed to analyze raw audio: {}".format(err)) 95 return False 96 # TODO: Log results to proto 97 return True 98 99 @test_tracker_info(uuid='b808fed6-5cb0-4e40-9522-c0f410cd77e8') 100 def test_run_bt_audio_quality_2k1k_10_sec_sine_wave(self): 101 """Measure audio quality over Bluetooth by playing a 1k2k sine wave. 102 103 Play a sine wave and measure the analysis of 1kHz and 2kHz on two 104 different channels for 10 seconds: 105 1. Delays during playback. 106 2. Noise before playback. 107 3. Noise after playback. 108 4. Bursts during playback. 109 5. Volume changes. 110 111 Steps: 112 1. Connect Chameleon headphone audio bus endpoint. 113 2. Connect FPGA line-in bus endpoint. 114 3. Clear audio routes on the Chameleon device. 115 4. Start capturing audio on the Chameleon device. 116 5. Start playing the sine wave on the Android device. 117 6. Record for record_seconds + silence_wait_seconds. 118 7. Stop recording audio on the Chameleon device. 119 8. Stop playing audio on the Android Device. 120 9. Pull raw recorded audio from the Chameleon device. 121 10. Analyze raw audio and log results. 122 123 124 Expected Result: 125 Audio is recorded and processed successfully. 126 127 Returns: 128 True if Pass 129 False if Fail 130 131 TAGS: Classic, A2DP, Chameleon 132 Priority: 2 133 """ 134 sox_call = "{}{}".format("sox -b 16 -r 48000 -c 2 -n {}".format( 135 self.audio_file_2k1k_10_sec), " synth 10 sine 2000 sine 3000") 136 subprocess.call(sox_call, shell=True) 137 sox_audio_path = "{}/{}".format( 138 os.path.dirname(os.path.realpath(self.audio_file_2k1k_10_sec)), 139 self.audio_file_2k1k_10_sec) 140 sox_audio_path = os.path.join( 141 os.path.dirname(os.path.realpath(self.audio_file_2k1k_10_sec)), 142 self.audio_file_2k1k_10_sec) 143 self.dut.adb.push("{} {}".format(sox_audio_path, 144 self.android_sdcard_music_path)) 145 output_file_prefix_name = "{}_{}".format("test_2k1k_10_sec", 146 time.time()) 147 bits_per_sample = audio_bits_per_sample_32 148 rate = audio_sample_rate_48000 149 record_seconds = 10 # The length in seconds for how long to record 150 channel = audio_channel_mode_8 151 audio_to_play = "{}/{}".format(self.android_sdcard_music_path, 152 self.audio_file_2k1k_10_sec) 153 audio_to_play = os.path.join(self.android_sdcard_music_path, 154 self.audio_file_2k1k_10_sec) 155 return self._orchestrate_audio_quality_test( 156 output_file_prefix_name=output_file_prefix_name, 157 bits_per_sample=bits_per_sample, 158 rate=rate, 159 record_seconds=record_seconds, 160 channel=channel, 161 audio_to_play=audio_to_play) 162 163 @test_tracker_info(uuid='7e971cef-6637-4198-929a-7ecc712121d7') 164 def test_run_bt_audio_quality_2k1k_300_sec_sine_wave(self): 165 """Measure audio quality over Bluetooth by playing a 1k2k sine wave. 166 167 Play a sine wave and measure the analysis of 1kHz and 2kHz on two 168 different channels for 300 seconds: 169 1. Delays during playback. 170 2. Noise before playback. 171 3. Noise after playback. 172 4. Bursts during playback. 173 5. Volume changes. 174 175 Steps: 176 1. Connect Chameleon headphone audio bus endpoint. 177 2. Connect FPGA line-in bus endpoint. 178 3. Clear audio routes on the Chameleon device. 179 4. Start capturing audio on the Chameleon device. 180 5. Start playing the sine wave on the Android device. 181 6. Record for record_seconds + silence_wait_seconds. 182 7. Stop recording audio on the Chameleon device. 183 8. Stop playing audio on the Android Device. 184 9. Pull raw recorded audio from the Chameleon device. 185 10. Analyze raw audio and log results. 186 187 188 Expected Result: 189 Audio is recorded and processed successfully. 190 191 Returns: 192 True if Pass 193 False if Fail 194 195 TAGS: Classic, A2DP, Chameleon 196 Priority: 2 197 """ 198 sox_call = "{}{}".format("sox -b 16 -r 48000 -c 2 -n {}".format( 199 self.audio_file_2k1k_300_sec), " synth 300 sine 2000 sine 3000") 200 subprocess.call(sox_call, shell=True) 201 sox_audio_path = os.path.join( 202 os.path.dirname(os.path.realpath(self.audio_file_2k1k_300_sec)), 203 self.audio_file_2k1k_300_sec) 204 self.dut.adb.push("{} {}".format(sox_audio_path, 205 self.android_sdcard_music_path)) 206 output_file_prefix_name = "{}_{}".format("test_2k1k_300_sec.wav", 207 time.time()) 208 bits_per_sample = audio_bits_per_sample_32 209 rate = audio_sample_rate_48000 210 record_seconds = 300 # The length in seconds for how long to record 211 channel = audio_channel_mode_8 212 audio_to_play = os.path.join(self.android_sdcard_music_path, 213 self.audio_file_2k1k_300_sec) 214 215 return self._orchestrate_audio_quality_test( 216 output_file_prefix_name=output_file_prefix_name, 217 bits_per_sample=bits_per_sample, 218 rate=rate, 219 record_seconds=record_seconds, 220 channel=channel, 221 audio_to_play=audio_to_play) 222