普通文本  |  267行  |  9.62 KB

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