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