chromium/third_party/blink/manual_tests/webaudio/audiocontext-setsinkid.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Test output device selection in AudioContext.</title>
  <style type="text/css">
    body {
      font-family: sans-serif;
      padding: 1em;
    }

    .row {
      margin-top: 1em;
    }

    #appView {
      margin-top: 1em;
      font-family: monospace;
    }

    #log {
      line-height: 2em;
      font-size: 1.0em;
      padding: 0.5em;
      border: 1px solid #ccc;
      color: #666;
      background-color: #eee;
    }

    #device-dropdown {
      font-size: 1.0em;
      padding: 0.5em;
      border-radius: 0.25em;
    }

    #device-change {
      font-size: 1.0em;
      padding: 0.6em 1.0em;
      border: 0;
      border-radius: 0.25em;
      color: #fff;
      background-color: #2962ff;
    }

    #device-change:disabled {
      background-color: #bbdefb;
    }
  </style>
</head>
<body>
  <h1>AudioContext.setSinkId() Manual Test</h1>
    <div id="appView">
      <div id="log"></div>
      <div class="row">
        <div id="inspector"></div>
      </div>
      <div class="row">
        <select id="device-dropdown" disabled>
          <option value="">Getting device information...</option>
        </select>
        <button id="device-change" disabled>CHANGE</button>
      </div>
    </div>
  <script type="text/javascript">
    // Handles the button click to activate `setSinkId()` method.
    const onDeviceChange = async (event, audioContext, appView) => {
      const deviceId = appView.dropdown.value;
      if (deviceId === 'default') {
        audioContext.setSinkId('');
      } else if (deviceId === 'silent') {
        audioContext.setSinkId({type: 'none'});
      } else {
        audioContext.setSinkId(deviceId);
      }

      const sinkId = `AudioContext.sinkId = ` + typeof audioContext.sinkId === 'object'
          ? JSON.stringify(audioContext.sinkId) : audioContext.sinkId;
      const maxChan = audioContext.destination.maxChannelCount;
      appView.inspector.textContent = `${sinkId} (${maxChan} channels max)`;
    };

    // The manual test body
    const startManuaTestApp = async () => {
      const appView = {
        log: document.getElementById('log'),
        inspector: document.getElementById('inspector'),
        dropdown: document.getElementById('device-dropdown'),
        changeButton: document.getElementById('device-change')
      };

      const audioContext = new AudioContext();
      const osc = new OscillatorNode(audioContext);
      const amp = new GainNode(audioContext, {gain: 0.25});
      osc.connect(amp).connect(audioContext.destination);
      osc.start();

      if (typeof audioContext.setSinkId !== 'function' ||
          typeof audioContext.sinkId != 'string') {
        appView.log.textContent =
            'This browser does not support AudioContext.setSinkId()';
        return;
      }

      appView.inspector.textContent =
          `AudioContext.sinkId = ${audioContext.sinkId}`+
          `(${audioContext.destination.maxChannelCount} channels max)`;

      // Get a permission, enumerate devices, and set up the UI.
      try {
        const stream =
            await navigator.mediaDevices.getUserMedia({ audio: true });
        if (stream) {
          const deviceList = await navigator.mediaDevices.enumerateDevices();
          let deviceListString = '';
          deviceList.forEach((device) => {
            if (device.kind === 'audiooutput') {
              deviceListString +=
                  `<option value="${device.deviceId}">${device.label}</option>`;
            }
          });
          deviceListString +=
              `<option value="silent">none (silent output)</option>`;
          appView.dropdown.innerHTML = deviceListString;
          appView.changeButton.addEventListener('click', (event) => {
            audioContext.resume();
            onDeviceChange(event, audioContext, appView);
          });
          appView.dropdown.disabled = false;
          appView.changeButton.disabled = false;
          appView.log.textContent =
              'Device information successfully retrieved. The test is ready.';
        } else {
          appView.log.textContent =
              'navigator.mediaDevices.getUserMedia() failed.';
        }
      } catch (error) {
        // The permission dialog was dismissed, or acquiring a stream from
        // getUserMedia() failed.
        console.error(error);
        appView.log.textContent = `The initialization failed: ${error}`;
      }
    };

    // Entry point
    window.addEventListener('load', startManuaTestApp);
  </script>
</body>
</html>