blob: e2dbb60af81f6643de137c2a65c204e4597995c1 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001# $Id: mod_pesq.py 2417 2009-01-05 15:31:25Z bennylp $
Tristan Matthews0a329cc2013-07-17 13:20:14 -04002
3# Quality test of media calls.
4# - UA1 calls UA2
5# - UA1 plays a file until finished to be streamed to UA2
6# - UA2 records from stream
7# - Apply PESQ to played file (reference) and recorded file (degraded)
8#
9# File should be:
10# - naming: xxxxxx.CLOCK_RATE.wav, e.g: test1.8.wav
11# - clock-rate of those files can only be 8khz or 16khz
12
13import time
14import imp
15import os
16import sys
17import re
18import subprocess
19import wave
20import shutil
21import inc_const as const
22
23from inc_cfg import *
24
25# Load configuration
26cfg_file = imp.load_source("cfg_file", ARGS[1])
27
28# PESQ configs
29PESQ = "tools/pesq" # PESQ executable path
30PESQ_DEFAULT_THRESHOLD = 3.4 # Default minimum acceptable PESQ MOS value
31
32# PESQ params
33pesq_sample_rate_opt = "" # Sample rate option for PESQ
34input_filename = "" # Input/Reference filename
35output_filename = "" # Output/Degraded filename
36
37
38# Test body function
39def test_func(t):
40 global pesq_sample_rate_opt
41 global input_filename
42 global output_filename
43
44 ua1 = t.process[0]
45 ua2 = t.process[1]
46
47 # Get input file name
48 input_filename = re.compile(const.MEDIA_PLAY_FILE).search(ua1.inst_param.arg).group(1)
49
50 # Get output file name
51 output_filename = re.compile(const.MEDIA_REC_FILE).search(ua2.inst_param.arg).group(1)
52
53 # Get WAV input length, in seconds
54 fin = wave.open(input_filename, "r")
55 if fin == None:
56 raise TestError("Failed opening input WAV file")
57 inwavlen = fin.getnframes() * 1.0 / fin.getframerate()
58 inwavlen += 0.2
59 fin.close()
60 print "WAV input len = " + str(inwavlen) + "s"
61
62 # Get clock rate of the output
63 mo_clock_rate = re.compile("\.(\d+)\.wav").search(output_filename)
64 if (mo_clock_rate==None):
65 raise TestError("Cannot compare input & output, incorrect output filename format")
66 clock_rate = mo_clock_rate.group(1)
67
68 # Get channel count of the output
69 channel_count = 1
70 if re.search("--stereo", ua2.inst_param.arg) != None:
71 channel_count = 2
72
73 # Get matched input file from output file
74 # (PESQ evaluates only files whose same clock rate & channel count)
75 if channel_count == 2:
76 if re.search("\.\d+\.\d+\.wav", input_filename) != None:
77 input_filename = re.sub("\.\d+\.\d+\.wav", "." + str(channel_count) + "."+clock_rate+".wav", input_filename)
78 else:
79 input_filename = re.sub("\.\d+\.wav", "." + str(channel_count) + "."+clock_rate+".wav", input_filename)
80
81 if (clock_rate != "8") & (clock_rate != "16"):
82 raise TestError("PESQ only works on clock rate 8kHz or 16kHz, clock rate used = "+clock_rate+ "kHz")
83
84 # Get conference clock rate of UA2 for PESQ sample rate option
85 pesq_sample_rate_opt = "+" + clock_rate + "000"
86
87 # UA1 making call
88 ua1.send("m")
89 ua1.send(t.inst_params[1].uri)
90 ua1.expect(const.STATE_CALLING)
91
92 # UA2 wait until call established
93 ua2.expect(const.STATE_CONFIRMED)
94
95 ua1.sync_stdout()
96 ua2.sync_stdout()
97 time.sleep(2)
98
99 # Disconnect mic -> rec file, to avoid echo recorded when using sound device
100 # Disconnect stream -> spk, make it silent
101 # Connect stream -> rec file, start recording
102 ua2.send("cd 0 1\ncd 4 0\ncc 4 1")
103
104 # Disconnect mic -> stream, make stream purely sending from file
105 # Disconnect stream -> spk, make it silent
106 # Connect file -> stream, start sending
107 ua1.send("cd 0 4\ncd 4 0\ncc 1 4")
108
109 time.sleep(inwavlen)
110
111 # Disconnect files from bridge
112 ua2.send("cd 4 1")
113 ua2.expect(const.MEDIA_DISCONN_PORT_SUCCESS)
114 ua1.send("cd 1 4")
115 ua1.expect(const.MEDIA_DISCONN_PORT_SUCCESS)
116
117
118# Post body function
119def post_func(t):
120 global pesq_sample_rate_opt
121 global input_filename
122 global output_filename
123
124 endpt = t.process[0]
125
126 # Execute PESQ
127 fullcmd = os.path.normpath(PESQ) + " " + pesq_sample_rate_opt + " " + input_filename + " " + output_filename
128 endpt.trace("Popen " + fullcmd)
129 pesq_proc = subprocess.Popen(fullcmd, shell=True, stdout=subprocess.PIPE, universal_newlines=True)
130 pesq_out = pesq_proc.communicate()
131
132 # Parse ouput
133 mo_pesq_out = re.compile("Prediction[^=]+=\s+([\-\d\.]+)\s*").search(pesq_out[0])
134 if (mo_pesq_out == None):
135 raise TestError("Failed to fetch PESQ result")
136
137 # Get threshold
138 if (cfg_file.pesq_threshold != None) | (cfg_file.pesq_threshold > -0.5 ):
139 threshold = cfg_file.pesq_threshold
140 else:
141 threshold = PESQ_DEFAULT_THRESHOLD
142
143 # Evaluate the PESQ MOS value
144 pesq_res = mo_pesq_out.group(1)
145 if (float(pesq_res) >= threshold):
146 endpt.trace("Success, PESQ result = " + pesq_res + " (target=" + str(threshold) + ").")
147 else:
148 endpt.trace("Failed, PESQ result = " + pesq_res + " (target=" + str(threshold) + ").")
149 # Save the wav file
150 wavoutname = ARGS[1]
151 wavoutname = re.sub("[\\\/]", "_", wavoutname)
152 wavoutname = re.sub("\.py$", ".wav", wavoutname)
153 wavoutname = "logs/" + wavoutname
154 try:
155 shutil.copyfile(output_filename, wavoutname)
156 print "Output WAV is copied to " + wavoutname
157 except:
158 print "Couldn't copy output WAV, please check if 'logs' directory exists."
159
160 raise TestError("WAV seems to be degraded badly, PESQ = "+ pesq_res + " (target=" + str(threshold) + ").")
161
162
163# Here where it all comes together
164test = cfg_file.test_param
165test.test_func = test_func
166test.post_func = post_func
167