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 41from acts import utils 42 43 44class BtChameleonTest(BtFunhausBaseTest): 45 46 audio_file_2k1k_10_sec = "audio_file_2k1k_10_sec.wav" 47 audio_file_2k1k_300_sec = "audio_file_2k1k_300_sec.wav" 48 android_sdcard_music_path = "/sdcard/Music" 49 50 def __init__(self, controllers): 51 BtFunhausBaseTest.__init__(self, controllers) 52 self.chameleon = self.chameleon_devices[0] 53 self.dut = self.android_devices[0] 54 self.raw_audio_dest = "{}/{}".format(self.android_devices[0].log_path, 55 "Chameleon_audio") 56 utils.create_dir(self.raw_audio_dest) 57 self.chameleon.audio_board_connect(1, headphone_bus_endpoint) 58 self.chameleon.audio_board_connect(1, fpga_linein_bus_endpoint) 59 time.sleep(delay_after_binding_seconds) 60 61 def _orchestrate_audio_quality_test(self, output_file_prefix_name, 62 bits_per_sample, rate, record_seconds, 63 channel, audio_to_play): 64 audio_analysis_filename = "{}_audio_analysis.txt".format( 65 output_file_prefix_name) 66 bluetooth_bind_time_seconds = 5 67 port_id = 6 68 has_file = True 69 # Additional sleep to allow full connection of Bluetooth device 70 # from test setup. 71 time.sleep(bluetooth_bind_time_seconds) 72 self.chameleon.start_capturing_audio(port_id, has_file) 73 time.sleep(delay_before_record_seconds) 74 self.dut.droid.mediaPlayOpen("file://{}".format(audio_to_play)) 75 time.sleep(record_seconds + silence_wait_seconds) 76 raw_audio_info = self.chameleon_devices[0].stop_capturing_audio( 77 port_id) 78 self.ad.droid.mediaPlayStopAll() 79 raw_audio_path = raw_audio_info[0] 80 dest_file_path = "{}/{}_recording.raw".format(self.raw_audio_dest, 81 output_file_prefix_name) 82 self.chameleon.scp(raw_audio_path, dest_file_path) 83 self._collect_bluetooth_manager_dumpsys_logs(self.android_devices) 84 self.collect_bluetooth_manager_metrics_logs(self.android_devices) 85 analysis_path = "{}/{}".format(self.raw_audio_dest, 86 audio_analysis_filename) 87 try: 88 quality_analysis( 89 filename=dest_file_path, 90 output_file=analysis_path, 91 bit_width=bits_per_sample, 92 rate=rate, 93 channel=channel, 94 spectral_only=False) 95 except Exception as err: 96 self.log.exception("Failed to analyze raw audio: {}".format(err)) 97 return False 98 # TODO: Log results to proto 99 return True 100 101 @test_tracker_info(uuid='b808fed6-5cb0-4e40-9522-c0f410cd77e8') 102 def test_run_bt_audio_quality_2k1k_10_sec_sine_wave(self): 103 """Measure audio quality over Bluetooth by playing a 1k2k sine wave. 104 105 Play a sine wave and measure the analysis of 1kHz and 2kHz on two 106 different channels for 10 seconds: 107 1. Delays during playback. 108 2. Noise before playback. 109 3. Noise after playback. 110 4. Bursts during playback. 111 5. Volume changes. 112 113 Steps: 114 1. Connect Chameleon headphone audio bus endpoint. 115 2. Connect FPGA line-in bus endpoint. 116 3. Clear audio routes on the Chameleon device. 117 4. Start capturing audio on the Chameleon device. 118 5. Start playing the sine wave on the Android device. 119 6. Record for record_seconds + silence_wait_seconds. 120 7. Stop recording audio on the Chameleon device. 121 8. Stop playing audio on the Android Device. 122 9. Pull raw recorded audio from the Chameleon device. 123 10. Analyze raw audio and log results. 124 125 126 Expected Result: 127 Audio is recorded and processed successfully. 128 129 Returns: 130 True if Pass 131 False if Fail 132 133 TAGS: Classic, A2DP, Chameleon 134 Priority: 2 135 """ 136 sox_call = "{}{}".format("sox -b 16 -r 48000 -c 2 -n {}".format( 137 self.audio_file_2k1k_10_sec), " synth 10 sine 2000 sine 3000") 138 subprocess.call(sox_call, shell=True) 139 sox_audio_path = "{}/{}".format( 140 os.path.dirname(os.path.realpath(self.audio_file_2k1k_10_sec)), 141 self.audio_file_2k1k_10_sec) 142 sox_audio_path = os.path.join( 143 os.path.dirname(os.path.realpath(self.audio_file_2k1k_10_sec)), 144 self.audio_file_2k1k_10_sec) 145 self.dut.adb.push("{} {}".format(sox_audio_path, 146 self.android_sdcard_music_path)) 147 output_file_prefix_name = "{}_{}".format("test_2k1k_10_sec", 148 time.time()) 149 bits_per_sample = audio_bits_per_sample_32 150 rate = audio_sample_rate_48000 151 record_seconds = 10 # The length in seconds for how long to record 152 channel = audio_channel_mode_8 153 audio_to_play = "{}/{}".format(self.android_sdcard_music_path, 154 self.audio_file_2k1k_10_sec) 155 audio_to_play = os.path.join(self.android_sdcard_music_path, 156 self.audio_file_2k1k_10_sec) 157 return self._orchestrate_audio_quality_test( 158 output_file_prefix_name=output_file_prefix_name, 159 bits_per_sample=bits_per_sample, 160 rate=rate, 161 record_seconds=record_seconds, 162 channel=channel, 163 audio_to_play=audio_to_play) 164 165 @test_tracker_info(uuid='7e971cef-6637-4198-929a-7ecc712121d7') 166 def test_run_bt_audio_quality_2k1k_300_sec_sine_wave(self): 167 """Measure audio quality over Bluetooth by playing a 1k2k sine wave. 168 169 Play a sine wave and measure the analysis of 1kHz and 2kHz on two 170 different channels for 300 seconds: 171 1. Delays during playback. 172 2. Noise before playback. 173 3. Noise after playback. 174 4. Bursts during playback. 175 5. Volume changes. 176 177 Steps: 178 1. Connect Chameleon headphone audio bus endpoint. 179 2. Connect FPGA line-in bus endpoint. 180 3. Clear audio routes on the Chameleon device. 181 4. Start capturing audio on the Chameleon device. 182 5. Start playing the sine wave on the Android device. 183 6. Record for record_seconds + silence_wait_seconds. 184 7. Stop recording audio on the Chameleon device. 185 8. Stop playing audio on the Android Device. 186 9. Pull raw recorded audio from the Chameleon device. 187 10. Analyze raw audio and log results. 188 189 190 Expected Result: 191 Audio is recorded and processed successfully. 192 193 Returns: 194 True if Pass 195 False if Fail 196 197 TAGS: Classic, A2DP, Chameleon 198 Priority: 2 199 """ 200 sox_call = "{}{}".format("sox -b 16 -r 48000 -c 2 -n {}".format( 201 self.audio_file_2k1k_300_sec), " synth 300 sine 2000 sine 3000") 202 subprocess.call(sox_call, shell=True) 203 sox_audio_path = os.path.join( 204 os.path.dirname(os.path.realpath(self.audio_file_2k1k_300_sec)), 205 self.audio_file_2k1k_300_sec) 206 self.dut.adb.push("{} {}".format(sox_audio_path, 207 self.android_sdcard_music_path)) 208 output_file_prefix_name = "{}_{}".format("test_2k1k_300_sec.wav", 209 time.time()) 210 bits_per_sample = audio_bits_per_sample_32 211 rate = audio_sample_rate_48000 212 record_seconds = 300 # The length in seconds for how long to record 213 channel = audio_channel_mode_8 214 audio_to_play = os.path.join(self.android_sdcard_music_path, 215 self.audio_file_2k1k_300_sec) 216 217 return self._orchestrate_audio_quality_test( 218 output_file_prefix_name=output_file_prefix_name, 219 bits_per_sample=bits_per_sample, 220 rate=rate, 221 record_seconds=record_seconds, 222 channel=channel, 223 audio_to_play=audio_to_play) 224