<!doctype html>
<html>
<head>
<title>Test multichannel support for MediaStreamDestination.</title>
<style type="text/css">
body {
margin: 2em;
}
.manual-test-ui {
font-family: Arial;
padding: 1em;
border: 1px solid #999;
}
.manual-test-ui button {
padding: 1em;
font-size: 1em;
}
#eError {
font-family: Arial;
margin: 0.75em;
color: red;
}
</style>
</head>
<body>
<h1>Test Multichannel Audio Output for MediaStreamDestination</h1>
<p><strong>NOTE: This test requires HTTP server.</strong></p>
<p>This test is for multichannel (> 2 channels) support for
MediaStreamDestination. This test cannot be run with an offline audio
context because the node is not compatible with the offline context.</p>
<p>Press "Start Test Tone" to run the test. You should hear an one-second
sine tone from all the available audio output channels from the channel 1
to the last channel.</p>
<p>CRBUG issue: <a href="https://code.google.com/p/chromium/issues/detail?id=557185" target="_blank">
557185</a></p>
<div class="manual-test-ui">
<p>Max Channel Count: <span id="eMaxChannelCount">2</span></p>
<p>Currently playing: <span id="eChannelIndex">NONE</span></p>
<button id="eButton" onclick="startTestTones()">Start Test Tone</button>
</div>
<div id="eError"></div>
<script type="text/javascript">
// Silent interval between the test tones.
var testToneInterval = 0.1;
// The safe range for the equal loudness of sinusoid is roughly between
// 200 ~ 1000Hz, which is A3(57) ~ C6(84). In this test, the starting
// pitch is 220Hz and the interval is the whole tone. (2 MIDI pitch)
// With 16 speakers, the last test tone will play the MIDI pitch of
// F6(89), which is 1396Hz.
var startMIDIPitch = 57;
var eMaxChannelCount = document.querySelector('#eMaxChannelCount');
var eButton = document.querySelector('#eButton');
var eChannelIndex = document.querySelector('#eChannelIndex');
var eError = document.querySelector('#eError');
var context = new AudioContext();
var mediaStreamDestination = context.createMediaStreamDestination();
var maxChannelCount = context.destination.maxChannelCount;
try {
mediaStreamDestination.channelCount = maxChannelCount;
} catch (error) {
eError.textContent = error;
}
var audioElement = new Audio();
audioElement.srcObject = mediaStreamDestination.stream;
audioElement.play();
// The ChannelMerger for the individual channel access.
var merger = context.createChannelMerger(maxChannelCount);
merger.channelCountMode = 'explicit';
merger.channelInterpretation = 'discrete';
merger.connect(mediaStreamDestination);
eMaxChannelCount.textContent = maxChannelCount;
// Convert the MIDI pitch to frequency.
function midi2freq(midiPitch) {
return 440 * Math.pow(2, (midiPitch - 69) / 12);
}
// A global storage to keep the OSC reference alive.
var oscs = [];
// Play a test tone for the specified amount of duration at the channel.
function playTestToneAtChannel(channelIndex, gain, duration) {
var osc = context.createOscillator();
var amp = context.createGain();
osc.connect(amp);
amp.connect(merger, 0, channelIndex);
osc.onended = function () {
var nextChannelIndex = channelIndex + 1;
if (nextChannelIndex < maxChannelCount)
playTestToneAtChannel(nextChannelIndex, gain, duration);
else
endTestTone();
};
// The pitch for each speaker goes up as the channel index increases.
// Note that the interval is 2, whole tone.
osc.frequency.value = midi2freq(startMIDIPitch + channelIndex * 2);
// The channel index starts from 1.
eChannelIndex.textContent = 'Channel #' + (channelIndex + 1);
var now = context.currentTime;
var toneDuration = duration - testToneInterval;
// Add fade in and out to avoid the click noise.
amp.gain.setValueAtTime(0.0, now);
amp.gain.linearRampToValueAtTime(gain, now + toneDuration * 0.1);
amp.gain.setValueAtTime(gain, now + toneDuration * 0.9);
amp.gain.linearRampToValueAtTime(0.0, now + toneDuration);
osc.start(now);
osc.stop(now + duration);
// Push osc to keep the reference alive. Otherwise |osc| will be
// collected and the |onended| event won't be fired.
oscs.push(osc);
}
// When the button is clicked the button to produce the test sound,
// the button is grayed out so one cannot press again until the tone is
// over. (producing sounds multiple times in a short period time will
// hurt the speaker).
function startTestTones() {
eButton.disabled = true;
// Math.SQRT1_2(=0.707..) is -3dB. This is necessary because 1.0
// amplitude can cause overload/distortion on some speakers.
playTestToneAtChannel(0, Math.SQRT1_2, 1.0);
}
// The button needs to be active back again when the test tone is over.
// The index number in DIV should also be reset.
function endTestTone() {
eButton.disabled = false;
eChannelIndex.textContent = 'NONE';
}
</script>
</body>
</html>