| <!DOCTYPE html> |
| |
| <!-- |
| Tests that AudioBufferSourceNode supports loop-points with .loopStart and .loopEnd. |
| --> |
| |
| <html> |
| <head> |
| <script type="text/javascript" src="resources/audio-testing.js"></script> |
| |
| </head> |
| <body> |
| |
| <script> |
| |
| window.onload = init; |
| |
| var sampleRate = 44100.0; |
| var numberOfNotes = 72; // play over a 6 octave range |
| var noteDuration = 0.100; |
| var noteSpacing = noteDuration + 0.005; // leave 5ms of silence between each "note" |
| var lengthInSeconds = numberOfNotes * noteSpacing; |
| |
| var context = 0; |
| var buffer = 0; |
| |
| function createTestBuffer(frequency, sampleRate) { |
| // Create a buffer containing two periods at this frequency. |
| // The 1st half is a pure sine wave period scaled by a linear ramp from 0 -> 1. |
| // The 2nd half of the buffer corresponds exactly to one pure sine wave period. |
| var onePeriodDuration = 1 / frequency; |
| var sampleFrameLength = 2 * onePeriodDuration * sampleRate; |
| |
| var audioBuffer = context.createBuffer(1, sampleFrameLength, sampleRate); |
| |
| var n = audioBuffer.length; |
| var channelData = audioBuffer.getChannelData(0); |
| |
| for (var i = 0; i < n; ++i) { |
| var sample = Math.sin(frequency * 2.0*Math.PI * i / sampleRate); |
| |
| // Linear ramp from 0 -> 1 for the first period. |
| // Stay at 1 for the 2nd period. |
| var scale = i < n / 2 ? i / (n / 2) : 1; |
| sample *= scale; |
| |
| channelData[i] = sample; |
| } |
| |
| return audioBuffer; |
| } |
| |
| function playNote(time, duration, playbackRate) { |
| var source = context.createBufferSource(); |
| source.buffer = buffer; |
| source.playbackRate.value = playbackRate; |
| |
| var gainNode = context.createGain(); |
| source.connect(gainNode); |
| gainNode.connect(context.destination); |
| |
| // Loop the 2nd half of the buffer. |
| // We should be able to hear any problems as glitches if the looping incorrectly indexes to |
| // anywhere outside of the desired loop-points, since only the 2nd half is a perfect sine-wave cycle, |
| // while the 1st half of the buffer contains a linear ramp of a sine-wave cycle. |
| source.loop = true; |
| source.loopStart = 0.5 * buffer.duration; |
| source.loopEnd = buffer.duration; |
| |
| // Play for the given duration. |
| source.start(time); |
| source.stop(time + duration); |
| |
| // Apply a quick linear fade-out to avoid a click at the end of the note. |
| gainNode.gain.value = 1; |
| gainNode.gain.setValueAtTime(1, time + duration - 0.005); |
| gainNode.gain.linearRampToValueAtTime(0, time + duration); |
| } |
| |
| function init() { |
| if (!window.testRunner) |
| return; |
| |
| // Create offline audio context. |
| context = new webkitOfflineAudioContext(2, sampleRate * lengthInSeconds, sampleRate); |
| |
| // Create the test buffer. |
| // We'll loop this with the loop-points set for the 2nd half of this buffer. |
| buffer = createTestBuffer(440.0, sampleRate); |
| |
| // Play all the notes as a chromatic scale. |
| for (var i = 0; i < numberOfNotes; ++i) { |
| var time = i * noteSpacing; |
| var semitone = i - numberOfNotes/2; // start three octaves down |
| |
| // Convert from semitone to rate. |
| var playbackRate = Math.pow(2, semitone / 12); |
| |
| playNote(time, noteDuration, playbackRate); |
| } |
| |
| context.oncomplete = finishAudioTest; |
| context.startRendering(); |
| |
| testRunner.waitUntilDone(); |
| } |
| |
| </script> |
| |
| </body> |
| </html> |