| #!/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) |