18e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org#!/usr/bin/env python 28e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org# 38e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 48e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org# 58e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org# Use of this source code is governed by a BSD-style license 68e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org# that can be found in the LICENSE file in the root of the source 78e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org# tree. An additional intellectual property rights grant can be found 88e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org# in the file PATENTS. All contributing project authors may 98e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org# be found in the AUTHORS file in the root of the source tree. 108e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 118e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org"""Runs an end-to-end audio quality test on Linux. 128e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 138e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgExpects the presence of PulseAudio virtual devices (null sinks). These are 148e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgconfigured as default devices for a VoiceEngine audio call. A PulseAudio 158e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgutility (pacat) is used to play to and record from the virtual devices. 168e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 178e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgThe input reference file is then compared to the output file. 188e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org""" 198e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 208e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgimport optparse 218e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgimport os 228e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgimport re 238e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgimport shlex 248e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgimport subprocess 258e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgimport sys 268e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgimport time 278e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 288e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgimport perf.perf_utils 298e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 308e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgdef main(argv): 318e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser = optparse.OptionParser() 328e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org usage = 'Usage: %prog [options]' 338e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser.set_usage(usage) 348e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser.add_option('--input', default='input.pcm', help='input PCM file') 358e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser.add_option('--output', default='output.pcm', help='output PCM file') 368e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser.add_option('--codec', default='ISAC', help='codec name') 378e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser.add_option('--rate', default='16000', help='sample rate in Hz') 388e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser.add_option('--channels', default='1', help='number of channels') 398e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser.add_option('--play_sink', default='capture', 408e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org help='name of PulseAudio sink to which to play audio') 418e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser.add_option('--rec_sink', default='render', 428e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org help='name of PulseAudio sink whose monitor will be recorded') 438e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser.add_option('--harness', 448e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org default=os.path.abspath(os.path.dirname(sys.argv[0]) + 458e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org '/../../../out/Debug/audio_e2e_harness'), 468e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org help='path to audio harness executable') 478e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser.add_option('--compare', 488e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org help='command-line arguments for comparison tool') 498e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org parser.add_option('--regexp', 508e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org help='regular expression to extract the comparison metric') 518e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org options, _ = parser.parse_args(argv[1:]) 528e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 538e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # Get the initial default capture device, to restore later. 548e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org command = ['pacmd', 'list-sources'] 558e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org print ' '.join(command) 568e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org proc = subprocess.Popen(command, stdout=subprocess.PIPE) 578e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org output = proc.communicate()[0] 588e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org if proc.returncode != 0: 598e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org return proc.returncode 608e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org default_source = re.search(r'(^ \* index: )([0-9]+$)', output, 618e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org re.MULTILINE).group(2) 628e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 638e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # Set the default capture device to be used by VoiceEngine. We unfortunately 648e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # need to do this rather than select the devices directly through the harness 658e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # because monitor sources don't appear in VoiceEngine except as defaults. 668e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # 678e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # We pass the render device for VoiceEngine to select because (for unknown 688e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # reasons) the virtual device is sometimes not used when the default. 698e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org command = ['pacmd', 'set-default-source', options.play_sink + '.monitor'] 708e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org print ' '.join(command) 718e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org retcode = subprocess.call(command, stdout=subprocess.PIPE) 728e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org if retcode != 0: 738e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org return retcode 748e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 758e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org command = [options.harness, '--render=' + options.rec_sink, 768e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org '--codec=' + options.codec, '--rate=' + options.rate] 778e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org print ' '.join(command) 788e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org voe_proc = subprocess.Popen(command) 798e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 808e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # If recording starts before there is data available, pacat sometimes 818e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # inexplicably adds a large delay to the start of the file. We wait here in 828e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # an attempt to prevent that, because VoE often takes some time to startup a 838e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # call. 848e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org time.sleep(5) 858e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 868e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org format_args = ['--format=s16le', '--rate=' + options.rate, 878e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org '--channels=' + options.channels, '--raw'] 888e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org command = (['pacat', '-p', '-d', options.play_sink] + format_args + 898e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org [options.input]) 908e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org print ' '.join(command) 918e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org play_proc = subprocess.Popen(command) 928e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 938e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org command = (['pacat', '-r', '-d', options.rec_sink + '.monitor'] + 948e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org format_args + [options.output]) 958e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org print ' '.join(command) 968e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org record_proc = subprocess.Popen(command) 978e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 988e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org retcode = play_proc.wait() 998e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # If these ended early, an exception will be thrown here. 1008e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org record_proc.kill() 1018e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org voe_proc.kill() 1028e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org if retcode != 0: 1038e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org return retcode 1048e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 1058e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # Restore the initial default capture device. 1068e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org command = ['pacmd', 'set-default-source', default_source] 1078e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org print ' '.join(command) 1088e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org retcode = subprocess.call(command, stdout=subprocess.PIPE) 1098e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org if retcode != 0: 1108e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org return retcode 1118e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 1128e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org if options.compare and options.regexp: 1138e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org command = shlex.split(options.compare) + [options.input, options.output] 1148e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org print ' '.join(command) 1158e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org proc = subprocess.Popen(command, stdout=subprocess.PIPE) 1168e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org output = proc.communicate()[0] 1178e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org if proc.returncode != 0: 1188e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org return proc.returncode 1198e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 1208e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org # The list should only contain one item. 1218e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org value = ''.join(re.findall(options.regexp, output)) 1228e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 1238e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org perf.perf_utils.PrintPerfResult(graph_name='audio_e2e_score', 1248e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org series_name='e2e_score', 1258e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org data_point=value, 1268e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org units='MOS') # Assuming we run PESQ. 1278e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 1288e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org return 0 1298e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org 1308e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.orgif __name__ == '__main__': 1318e701084207e4bfccf28a0088d41310d6af06410kjellander@webrtc.org sys.exit(main(sys.argv)) 132