| OLD | NEW |
| 1 <!doctype html> | 1 <!doctype html> |
| 2 <html> | 2 <html> |
| 3 <head> | 3 <head> |
| 4 <title>Test multichannel support.</title> | 4 <title>Test multichannel support for MediaStreamDestination.</title> |
| 5 <style type="text/css"> | 5 <style type="text/css"> |
| 6 body { | 6 body { |
| 7 margin: 2em; | 7 margin: 2em; |
| 8 } | 8 } |
| 9 .manual-test-ui { | 9 .manual-test-ui { |
| 10 font-family: Arial; | 10 font-family: Arial; |
| 11 padding: 1em; | 11 padding: 1em; |
| 12 border: 1px solid #999; | 12 border: 1px solid #999; |
| 13 } | 13 } |
| 14 .manual-test-ui button { | 14 .manual-test-ui button { |
| 15 padding: 1em; | 15 padding: 1em; |
| 16 font-size: 1em; | 16 font-size: 1em; |
| 17 } | 17 } |
| 18 #eError { |
| 19 font-family: Arial; |
| 20 margin: 0.75em; |
| 21 color: red; |
| 22 } |
| 18 </style> | 23 </style> |
| 19 </head> | 24 </head> |
| 20 | 25 |
| 21 <body> | 26 <body> |
| 22 <h1>Test Multichannel Audio Output</h1> | 27 <h1>Test Multichannel Audio Output for MediaStreamDestination</h1> |
| 23 | 28 |
| 24 <p>Tests that multichannel audio output (> 8 channels) is working correctly. | 29 <p><strong>NOTE: This test requires HTTP server.</strong></p> |
| 25 This test cannot be run with an offline audio context because it requires | 30 |
| 26 an actual audio hardware with the multichannel capability.</p> | 31 <p>This test is for multichannel (> 2 channels) support for |
| 32 MediaStreamDestination. This test cannot be run with an offline audio |
| 33 context because the node is not compatible with the offline context.</p> |
| 27 | 34 |
| 28 <p>Press "Start Test Tone" to run the test. You should hear an one-second | 35 <p>Press "Start Test Tone" to run the test. You should hear an one-second |
| 29 sine tone from all the available audio output channels from the channel 1 | 36 sine tone from all the available audio output channels from the channel 1 |
| 30 to the last channel.</p> | 37 to the last channel.</p> |
| 31 | 38 |
| 32 <p>Note that this test only works on OSX because CoreAudio driver supports | 39 <p>CRBUG issue: <a href="https://code.google.com/p/chromium/issues/detail?id
=557185" target="_blank"> |
| 33 the multichannel streams (more than 2) on a single audio device whereas | 40 557185</a></p> |
| 34 other platforms do not.</p> | |
| 35 | |
| 36 <p>CRBUG issue: <a href="https://code.google.com/p/chromium/issues/detail?id
=424795" target="_blank"> | |
| 37 424795</a></p> | |
| 38 | 41 |
| 39 <div class="manual-test-ui"> | 42 <div class="manual-test-ui"> |
| 40 <p>Max Channel Count: <span id="eMaxChannelCount">2</span></p> | 43 <p>Max Channel Count: <span id="eMaxChannelCount">2</span></p> |
| 41 <p>Currently playing: <span id="eChannelIndex">NONE</span></p> | 44 <p>Currently playing: <span id="eChannelIndex">NONE</span></p> |
| 42 <button id="eButton" onclick="startTestTones()">Start Test Tone</button> | 45 <button id="eButton" onclick="startTestTones()">Start Test Tone</button> |
| 43 </div> | 46 </div> |
| 44 | 47 |
| 48 <div id="eError"></div> |
| 49 |
| 45 <script type="text/javascript"> | 50 <script type="text/javascript"> |
| 46 // Silent interval between the test tones. | 51 // Silent interval between the test tones. |
| 47 var testToneInterval = 0.1; | 52 var testToneInterval = 0.1; |
| 48 | 53 |
| 49 // The safe range for the equal loudness of sinusoid is roughly between | 54 // The safe range for the equal loudness of sinusoid is roughly between |
| 50 // 200 ~ 1000Hz, which is A3(57) ~ C6(84). In this test, the starting | 55 // 200 ~ 1000Hz, which is A3(57) ~ C6(84). In this test, the starting |
| 51 // pitch is 220Hz and the interval is the whole tone. (2 MIDI pitch) | 56 // pitch is 220Hz and the interval is the whole tone. (2 MIDI pitch) |
| 52 // With 16 speakers, the last test tone will play the MIDI pitch of | 57 // With 16 speakers, the last test tone will play the MIDI pitch of |
| 53 // F6(89), which is 1396Hz. | 58 // F6(89), which is 1396Hz. |
| 54 var startMIDIPitch = 57; | 59 var startMIDIPitch = 57; |
| 55 | 60 |
| 56 var eMaxChannelCount = document.querySelector('#eMaxChannelCount'); | 61 var eMaxChannelCount = document.querySelector('#eMaxChannelCount'); |
| 57 var eButton = document.querySelector('#eButton'); | 62 var eButton = document.querySelector('#eButton'); |
| 58 var eChannelIndex = document.querySelector('#eChannelIndex'); | 63 var eChannelIndex = document.querySelector('#eChannelIndex'); |
| 64 var eError = document.querySelector('#eError'); |
| 59 | 65 |
| 60 var context = new AudioContext(); | 66 var context = new AudioContext(); |
| 67 var mediaStreamDestination = context.createMediaStreamDestination(); |
| 68 |
| 61 var maxChannelCount = context.destination.maxChannelCount; | 69 var maxChannelCount = context.destination.maxChannelCount; |
| 62 | 70 |
| 63 // Sets the destination properties for multichannel access. | 71 try { |
| 64 context.destination.channelCount = maxChannelCount; | 72 mediaStreamDestination.channelCount = maxChannelCount; |
| 65 context.destination.channelCountMode = 'explicit'; | 73 } catch (error) { |
| 66 context.destination.channelInterpretation = 'discrete'; | 74 eError.textContent = error; |
| 75 } |
| 76 |
| 77 var audioElement = new Audio(); |
| 78 audioElement.src = URL.createObjectURL(mediaStreamDestination.stream); |
| 79 audioElement.play(); |
| 67 | 80 |
| 68 // The ChannelMerger for the individual channel access. | 81 // The ChannelMerger for the individual channel access. |
| 69 var merger = context.createChannelMerger(maxChannelCount); | 82 var merger = context.createChannelMerger(maxChannelCount); |
| 70 merger.channelCountMode = 'explicit'; | 83 merger.channelCountMode = 'explicit'; |
| 71 merger.channelInterpretation = 'discrete'; | 84 merger.channelInterpretation = 'discrete'; |
| 72 merger.connect(context.destination); | 85 merger.connect(mediaStreamDestination); |
| 73 | 86 |
| 74 eMaxChannelCount.textContent = maxChannelCount; | 87 eMaxChannelCount.textContent = maxChannelCount; |
| 75 | 88 |
| 76 // Convert the MIDI pitch to frequency. | 89 // Convert the MIDI pitch to frequency. |
| 77 function midi2freq(midiPitch) { | 90 function midi2freq(midiPitch) { |
| 78 return 440 * Math.pow(2, (midiPitch - 69) / 12); | 91 return 440 * Math.pow(2, (midiPitch - 69) / 12); |
| 79 } | 92 } |
| 80 | 93 |
| 94 // A global storage to keep the OSC reference alive. |
| 95 var oscs = []; |
| 96 |
| 81 // Play a test tone for the specified amount of duration at the channel. | 97 // Play a test tone for the specified amount of duration at the channel. |
| 82 function playTestToneAtChannel(channelIndex, gain, duration) { | 98 function playTestToneAtChannel(channelIndex, gain, duration) { |
| 83 var osc = context.createOscillator(); | 99 var osc = context.createOscillator(); |
| 84 var amp = context.createGain(); | 100 var amp = context.createGain(); |
| 85 osc.connect(amp); | 101 osc.connect(amp); |
| 86 amp.connect(merger, 0, channelIndex); | 102 amp.connect(merger, 0, channelIndex); |
| 87 | 103 |
| 88 osc.onended = function () { | 104 osc.onended = function () { |
| 89 var nextChannelIndex = channelIndex + 1; | 105 var nextChannelIndex = channelIndex + 1; |
| 90 if (nextChannelIndex < maxChannelCount) | 106 if (nextChannelIndex < maxChannelCount) |
| 91 playTestToneAtChannel(nextChannelIndex, gain, duration); | 107 playTestToneAtChannel(nextChannelIndex, gain, duration); |
| 92 else | 108 else |
| 93 endTestTone(); | 109 endTestTone(); |
| 94 }; | 110 }; |
| 95 | 111 |
| 96 // The pitch for each speaker goes up as the channel index increases. | 112 // The pitch for each speaker goes up as the channel index increases. |
| 97 // Note that the interval is 2, whole tone. | 113 // Note that the interval is 2, whole tone. |
| 98 osc.frequency.value = midi2freq(startMIDIPitch + channelIndex * 2); | 114 osc.frequency.value = midi2freq(startMIDIPitch + channelIndex * 2); |
| 99 | 115 |
| 100 // The channel index starts from 1. | 116 // The channel index starts from 1. |
| 101 eChannelIndex.textContent = 'Channel #' + (channelIndex + 1); | 117 eChannelIndex.textContent = 'Channel #' + (channelIndex + 1); |
| 102 | 118 |
| 103 var now = context.currentTime; | 119 var now = context.currentTime; |
| 104 var toneDuration = duration - testToneInterval; | 120 var toneDuration = duration - testToneInterval; |
| 105 | 121 |
| 106 // Add fade in and out to avoid the click noise. | 122 // Add fade in and out to avoid the click noise. |
| 107 amp.gain.setValueAtTime(0.0, now); | 123 amp.gain.setValueAtTime(0.0, now); |
| 108 amp.gain.linearRampToValueAtTime(gain, now + toneDuration * 0.1); | 124 amp.gain.linearRampToValueAtTime(gain, now + toneDuration * 0.1); |
| 109 amp.gain.setValueAtTime(gain, now + toneDuration * 0.9); | 125 amp.gain.setValueAtTime(gain, now + toneDuration * 0.9); |
| 110 amp.gain.linearRampToValueAtTime(0.0, now + toneDuration); | 126 amp.gain.linearRampToValueAtTime(0.0, now + toneDuration); |
| 111 | 127 |
| 112 osc.start(now); | 128 osc.start(now); |
| 113 osc.stop(now + duration); | 129 osc.stop(now + duration); |
| 130 |
| 131 // Push osc to keep the reference alive. Otherwise |osc| will be |
| 132 // collected and the |onended| event won't be fired. |
| 133 oscs.push(osc); |
| 114 } | 134 } |
| 115 | 135 |
| 116 // When the button is clicked the button to produce the test sound, | 136 // When the button is clicked the button to produce the test sound, |
| 117 // the button is grayed out so one cannot press again until the tone is | 137 // the button is grayed out so one cannot press again until the tone is |
| 118 // over. (producing sounds multiple times in a short period time will | 138 // over. (producing sounds multiple times in a short period time will |
| 119 // hurt the speaker). | 139 // hurt the speaker). |
| 120 function startTestTones() { | 140 function startTestTones() { |
| 121 eButton.disabled = true; | 141 eButton.disabled = true; |
| 122 | 142 |
| 123 // Math.SQRT1_2(=0.707..) is -3dB. This is necessary because 1.0 | 143 // Math.SQRT1_2(=0.707..) is -3dB. This is necessary because 1.0 |
| 124 // amplitude can cause overload/distortion on some speakers. | 144 // amplitude can cause overload/distortion on some speakers. |
| 125 playTestToneAtChannel(0, Math.SQRT1_2, 1.0); | 145 playTestToneAtChannel(0, Math.SQRT1_2, 1.0); |
| 126 } | 146 } |
| 127 | 147 |
| 128 // The button needs to be active back again when the test tone is over. | 148 // The button needs to be active back again when the test tone is over. |
| 129 // The index number in DIV should also be reset. | 149 // The index number in DIV should also be reset. |
| 130 function endTestTone() { | 150 function endTestTone() { |
| 131 eButton.disabled = false; | 151 eButton.disabled = false; |
| 132 eChannelIndex.textContent = 'NONE'; | 152 eChannelIndex.textContent = 'NONE'; |
| 133 } | 153 } |
| 134 </script> | 154 </script> |
| 135 </body> | 155 </body> |
| 136 </html> | 156 </html> |
| OLD | NEW |