blob: 6ee401be888dfaf2f185aa8eefd31b2de9b89cc5 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Script functions as a web app and wrapper for the cras_router program."""
import re
import subprocess
import logging
import cherrypy
# Node Format: [Stable_Id, ID, Vol, Plugged, L/R_swapped, Time, Type, Name]
ID_INDEX = 1
PLUGGED_INDEX = 3
TYPE_INDEX = 6
NAME_INDEX = 7
def get_plugged_nodes(plugged_nodes, lines, is_input):
start_str = 'Input Nodes:' if is_input else 'Output Nodes:'
end_str = 'Attached clients:' if is_input else 'Input Devices:'
for i in range(lines.index(start_str) + 2,
lines.index(end_str)):
node = filter(None, re.split(r'\s+|\*+', lines[i]))
# check for nodes that are plugged nodes and loopback
if node[PLUGGED_INDEX] == 'yes' and node[TYPE_INDEX][:4] != 'POST':
key = node[TYPE_INDEX] + ' ' + node[NAME_INDEX]
plugged_nodes[key] = node[ID_INDEX]
class CrasRouterTest(object):
"""Cherrypy class that builds and runs the HTML for audio testing tool."""
@cherrypy.expose
def index(self):
"""Builds up and displays the html for the audio testing tool.
Returns:
html that was built up based on plugged audio devices.
"""
# Stop program if currently being run.
if 'process' in cherrypy.session:
print 'Existing process'
# If return code is None process is still running
if cherrypy.session['process'].poll() is None:
print 'Killing existing process'
cherrypy.session['process'].kill()
else:
print 'Process already finished'
html = """<html>
<head>
<title>Audio Test</title>
</head>
<body style="background-color:lightgrey;">
<font color="red">
<h1>Audio Closed Loop Test</h1>
<font style="color:rgb(100, 149, 237)">
<h3>
<form name="routerOptions" method="get"
onsubmit="return validateForm()" action="start_test">
<h2>Input Type</h2>
"""
dump = subprocess.check_output(['cras_test_client', '--dump_s'])
if not dump:
return 'Could not connect to server'
dump_lines = dump.split('\n')
input_plugged_nodes = {}
get_plugged_nodes(input_plugged_nodes, dump_lines, True)
for name, node_id in input_plugged_nodes.items():
line = '<input type ="radio" name="input_type" value="'
line += node_id + '">' +name + '<br>\n'
html += line
html += """<input type ="radio" id="i0" name="input_type"
value="file">File<br>
<div id="input_file" style="display:none;">
Filename <input type="text" name="input_file"><br><br>
</div>
<h2>Output Type</h2>"""
output_plugged_nodes = {}
get_plugged_nodes(output_plugged_nodes, dump_lines, False)
for name, node_id in output_plugged_nodes.items():
line = '<input type ="radio" name="output_type" value="'
line = line + node_id + '">' +name + '<br>\n'
html += line
html += """<input type ="radio" name="output_type"
value="file">File<br>
<div id="output_file" style="display:none;">
Filename <input type="text" name="output_file">
</div><br>
<h2>Sample Rate</h2>
<input type="radio" name="rate" id="sample_rate1" value=48000
checked>48,000 Hz<br>
<input type="radio" name="rate" id="sample_rate0" value=44100>
44,100 Hz<br><br>
<button type="submit" onclick="onOff(this)">Test!</button>
</h3>
</form>
</font>
</body>
</html>
"""
javascript = """
<script>
/* Does basic error checking to make sure user doesn't
* give bad options to the router.
*/
function validateForm(){
var input_type =
document.forms['routerOptions']['input_type'].value;
var output_type =
document.forms['routerOptions']['output_type'].value;
if (input_type == '' || output_type == '') {
alert('Please select an input and output type.');
return false;
}
if (input_type == 'file' && output_type == 'file') {
alert('Input and Output Types cannot both be files!');
return false;
}
//check if filename is valid
if (input_type == 'file') {
var input_file =
document.forms['routerOptions']['input_file'].value;
if (input_file == '') {
alert('Please enter a file name');
return false;
}
}
if (output_type == 'file') {
var output_file =
document.forms['routerOptions']['output_file'].value;
if (output_file == '') {
alert('Please enter a file name');
return false;
}
}
}
function show_filename(radio, file_elem) {
for(var i =0; i < radio.length; i++){
radio[i].onclick = function(){
if (this.value == 'file') {
file_elem.style.display = 'block';
} else {
file_elem.style.display = 'none';
}
}
}
}
/* Loops determine if filename field should be shown */
var input_type_rad =
document.forms['routerOptions']['input_type'];
var input_file_elem =
document.getElementById('input_file');
var output_type_rad =
document.forms['routerOptions']['output_type'];
var output_file_elem =
document.getElementById('output_file');
show_filename(input_type_rad, input_file_elem);
show_filename(output_type_rad, output_file_elem);
</script>"""
html += javascript
return html
@cherrypy.expose
def start_test(self, input_type, output_type, input_file='',
output_file='', rate=48000):
"""Capture audio from the input_type and plays it back to the output_type.
Args:
input_type: Node id for the selected input or 'file' for files
output_type: Node id for the selected output or 'file' for files
input_file: Path of the input if 'file' is input type
output_file: Path of the output if 'file' is output type
rate: Sample rate for the test.
Returns:
html for the tesing in progress page.
"""
print 'Beginning test'
if input_type == 'file' or output_type == 'file':
command = ['cras_test_client']
else:
command = ['cras_router']
if input_type == 'file':
command.append('--playback_file')
command.append(str(input_file))
else:
set_input = ['cras_test_client', '--select_input', str(input_type)]
if subprocess.check_call(set_input):
print 'Error setting input'
if output_type == 'file':
command.append('--capture_file')
command.append(str(output_file))
else:
set_output = ['cras_test_client', '--select_output', str(output_type)]
if subprocess.check_call(set_output):
print 'Error setting output'
command.append('--rate')
command.append(str(rate))
print 'Running commmand: ' + str(command)
p = subprocess.Popen(command)
cherrypy.session['process'] = p
return """
<html>
<head>
<style type="text/css">
body {
background-color: #DC143C;
}
#test {
color: white;
text-align: center;
}
</style>
<title>Running test</title>
</head>
<body>
<div id="test">
<h1>Test in progress</h1>
<form action="index"><!--Go back to orginal page-->
<button type="submit" id="stop">Click to stop</button>
</form>
<h2>Time Elapsed<br>
<time id="elapsed_time">00:00</time>
</h2>
</div>
</body>
</html>
<script type="text/javascript">
var seconds = 0;
var minutes = 0;
var elapsed_time;
var start_time = new Date().getTime();
function secondPassed(){
var time = new Date().getTime() - start_time;
elapsed_time = Math.floor(time / 100) / 10;
minutes = Math.floor(elapsed_time / 60);
seconds = Math.floor(elapsed_time % 60);
var seconds_str = (seconds < 10 ? '0' + seconds: '' + seconds);
var minutes_str = (minutes < 10 ? '0' + minutes: '' + minutes);
var time_passed = minutes_str + ':' + seconds_str;
document.getElementById("elapsed_time").textContent = time_passed;
}
//have time tic every second
var timer = setInterval(secondPassed, 1000);
var stop = document.getElementById("stop");
stop.onclick = function(){
seconds = 0;
minutes = 0;
clearInterval(timer);
}
</script>"""
if __name__ == '__main__':
conf = {
'/': {
'tools.sessions.on': True
}
}
cherrypy.quickstart(CrasRouterTest(), '/', conf)