(function() {
var onDOMContentLoaded = function() {
window.AudioContext = window.AudioContext || window.webkitAudioContext;
try {
// Create the instance of AudioContext
var context = new AudioContext();
} catch (error) {
window.alert(error.message + ' : Please use Chrome or Safari.');
return;
}
var displayProperties = function(node, tableid, caption) {
var html = '<caption>' + caption + '</caption>';
html += '<thead>';
html += '<tr>';
html += '<th scope="col">Property</th>';
html += '<th scope="col">Value</th>';
html += '<th scope="col">hasOwnProperty</th>';
html += '</tr>';
html += '</thead>';
html += '<tbody>';
for (var key in node) {
html += '<tr>';
html += '<td>' + key + '</td>';
html += '<td>' + node[key] + '</td>';
html += '<td>' + node.hasOwnProperty(key) + '</td>';
html += '</tr>';
}
html += '</tbody>';
document.getElementById(tableid).innerHTML = html;
document.getElementById(tableid).parentNode.previousElementSibling.style.display = 'block';
};
var source = null; // for the instance of AudioBufferSourceNode
var gain = null; // for the instance of GainNode (Master Volume)
var convolver = null; // for the instance of ConvolverNode (Reverb)
var dry = null; // for the instance of ConvolverNode (Dry)
var wet = null; // for the instance of ConvolverNode (Wet)
// This function is executed after loading all of the impluse responses
var onloadCallback = function(reverbs) {
// for legacy browsers
context.createGain = context.createGain || context.createGainNode;
// Create the instance of GainNode (for Master Volume)
gain = context.createGain();
// Reverb
// Create the instance of ConvolverNode
convolver = context.createConvolver();
// Create the instance of GainNode (for Reverb)
dry = context.createGain();
wet = context.createGain();
// Initialize parameters for Reverb
convolver.buffer = null;
dry.gain.value = document.getElementById('range-reverb-dry').valueAsNumber;
wet.gain.value = document.getElementById('range-reverb-wet').valueAsNumber;
// Trigger 'ended' event
var trigger = function() {
var event = document.createEvent('Event');
event.initEvent('ended', true, true);
if (source instanceof AudioBufferSourceNode) {
source.dispatchEvent(event);
}
};
// This funciton is executed after getting ArrayBuffer of audio data
var startAudio = function(arrayBuffer) {
// The 2nd argument for decodeAudioData
var successCallback = function(audioBuffer) {
// The 1st argument (audioBuffer) is the instance of AudioBuffer
// If there is previous AudioBufferSourceNode, program stops previous audio
if ((source instanceof AudioBufferSourceNode) && (source.buffer instanceof AudioBuffer)) {
// Execute onended event handler
trigger();
source = null;
}
// Create the instance of AudioBufferSourceNode
source = context.createBufferSource();
// for legacy browsers
source.start = source.start || source.noteOn;
source.stop = source.stop || source.noteOff;
// Set the instance of AudioBuffer
source.buffer = audioBuffer;
// Set parameters
source.playbackRate.value = document.getElementById('range-playback-rate').valueAsNumber;
source.loop = document.querySelector('[type="checkbox"]').checked;
// GainNode (Master Volume) -> AudioDestinationNode (Output);
gain.connect(context.destination);
// Connect nodes for original audio
// AudioBufferSourceNode (Input) -> GainNode (Dry) -> GainNode (Master Volume) (-> AudioDestinationNode (Output))
source.connect(dry);
dry.connect(gain);
// Connect nodes for effect (Reverb) sound
// AudioBufferSourceNode (Input) -> ConvolverNode (Reverb) -> GainNode (Wet) -> GainNode (Master Volume) (-> AudioDestinationNode (Output))
source.connect(convolver);
convolver.connect(wet);
wet.connect(gain);
// Start audio
source.start(0);
// Set Callback
source.onended = function(event) {
// Remove event handler
source.onended = null;
document.onkeydown = null;
// Stop audio
source.stop(0);
console.log('STOP by "on' + event.type + '" event handler !!');
// Audio is not started !!
// It is necessary to create the instance of AudioBufferSourceNode again
// Cannot replay
// source.start(0);
};
// Stop audio
document.onkeydown = function(event) {
// Space ?
if (event.keyCode !== 32) {
return;
}
// Execute onended event handler
trigger();
return false;
};
};
// The 3rd argument for decodeAudioData
var errorCallback = function(error) {
if (error instanceof Error) {
window.alert(error.message);
} else {
window.alert('Error : "decodeAudioData" method.');
}
};
// Create the instance of AudioBuffer (Asynchronously)
context.decodeAudioData(arrayBuffer, successCallback, errorCallback);
};
/*
* File Uploader
*/
document.getElementById('file-upload-audio').addEventListener('change', function(event) {
var uploader = this;
var progressArea = document.getElementById('progress-file-upload-audio');
// Get the instance of File (extends Blob)
var file = event.target.files[0];
if (!(file instanceof File)) {
window.alert('Please upload file.');
} else if (file.type.indexOf('audio') === -1) {
window.alert('Please upload audio file.');
} else {
// Create the instance of FileReader
var reader = new FileReader();
reader.onprogress = function(event) {
if (event.lengthComputable && (event.total > 0)) {
var rate = Math.floor((event.loaded / event.total) * 100);
progressArea.textContent = rate + ' %';
}
};
reader.onerror = function() {
window.alert('FileReader Error : Error code is ' + reader.error.code);
uploader.value = '';
};
// Success read
reader.onload = function() {
var arrayBuffer = reader.result; // Get ArrayBuffer
startAudio(arrayBuffer);
uploader.value = '';
progressArea.textContent = file.name;
};
// Read the instance of File
reader.readAsArrayBuffer(file);
}
}, false);
// Control Master Volume
document.getElementById('range-volume').addEventListener('input', function() {
var min = gain.gain.minValue || 0;
var max = gain.gain.maxValue || 1;
if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) {
gain.gain.value = this.valueAsNumber;
document.getElementById('output-volume').textContent = this.value;
}
}, false);
// Control playbackRate
document.getElementById('range-playback-rate').addEventListener('input', function() {
if (source instanceof AudioBufferSourceNode) {
var min = source.playbackRate.minValue || 0;
var max = source.playbackRate.maxValue || 1024;
if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) {
source.playbackRate.value = this.valueAsNumber;
}
}
document.getElementById('output-playback-rate').textContent = this.value;
}, false);
// Toggle loop
document.querySelector('[type="checkbox"]').addEventListener(EventWrapper.CLICK, function() {
if (source instanceof AudioBufferSourceNode) {
source.loop = this.checked;
}
}, false);
// Select Reverb type
document.getElementById('select-reverb-type').addEventListener('change', function() {
var reverbType = parseInt(this.value);
if (source instanceof AudioBufferSourceNode) {
// Clear connection
source.disconnect(0);
convolver.disconnect(0);
dry.disconnect(0);
wet.disconnect(0);
if (reverbType === 0) {
// Reverb OFF
convolver.buffer = null;
// AudioBufferSourceNode (Input) -> GainNode (Master Volume) (-> AudioDestinationNode (Output))
source.connect(gain);
} else {
// Revreb ON
convolver.buffer = reverbs[reverbType - 1];
// Connect nodes for original audio
// AudioBufferSourceNode (Input) -> GainNode (Dry) -> GainNode (Master Volume) (-> AudioDestinationNode (Output))
source.connect(dry);
dry.connect(gain);
// Connect nodes for effect (Reverb) sound
// AudioBufferSourceNode (Input) -> ConvolverNode (Reverb) -> GainNode (Wet) -> GainNode (Master Volume) (-> AudioDestinationNode (Output))
source.connect(convolver);
convolver.connect(wet);
wet.connect(gain);
}
} else {
if (reverbType === 0) {
// Reverb OFF
convolver.buffer = null;
} else {
// Revreb ON
convolver.buffer = reverbs[reverbType - 1];
}
}
displayProperties(convolver, 'convolvernode-properties', 'ConvolverNode');
}, false);
// Control Reverb Dry
document.getElementById('range-reverb-dry').addEventListener('input', function() {
var min = dry.gain.minValue || 0;
var max = dry.gain.maxValue || 1;
if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) {
dry.gain.value = this.valueAsNumber;
document.getElementById('output-reverb-dry').textContent = this.value;
}
}, false);
// Control Reverb Wet
document.getElementById('range-reverb-wet').addEventListener('input', function() {
var min = wet.gain.minValue || 0;
var max = wet.gain.maxValue || 1;
if ((this.valueAsNumber >= min) && (this.valueAsNumber <= max)) {
wet.gain.value = this.valueAsNumber;
document.getElementById('output-reverb-wet').textContent = this.value;
}
}, false);
/*
* Display properties
*/
displayProperties(convolver, 'convolvernode-properties', 'ConvolverNode');
};
// Load impulse responses
var base = './impulse-responses/';
var urls = [
(base + 's1_r1_b.wav'),
(base + 's1_r2_b.wav'),
(base + 's1_r3_b.wav'),
(base + 's1_r4_b.wav'),
(base + 's2_r1_b.wav'),
(base + 's2_r2_b.wav'),
(base + 's2_r3_b.wav'),
(base + 's2_r4_b.wav'),
(base + 's3_r1_b.wav'),
(base + 's3_r2_b.wav'),
(base + 's3_r3_b.wav'),
(base + 's3_r4_b.wav'),
(base + 's1_r1_bd.wav'),
(base + 's1_r2_bd.wav'),
(base + 's1_r3_bd.wav'),
(base + 's1_r4_bd.wav'),
(base + 's2_r1_bd.wav'),
(base + 's2_r2_bd.wav'),
(base + 's2_r3_bd.wav'),
(base + 's2_r4_bd.wav'),
(base + 's3_r1_bd.wav'),
(base + 's3_r2_bd.wav'),
(base + 's3_r3_bd.wav'),
(base + 's3_r4_bd.wav'),
(base + 's1_r1_c.wav'),
(base + 's1_r2_c.wav'),
(base + 's1_r3_c.wav'),
(base + 's1_r4_c.wav'),
(base + 's2_r1_c.wav'),
(base + 's2_r2_c.wav'),
(base + 's2_r3_c.wav'),
(base + 's2_r4_c.wav'),
(base + 's3_r1_c.wav'),
(base + 's3_r2_c.wav'),
(base + 's3_r3_c.wav'),
(base + 's3_r4_c.wav'),
(base + 's1_r1_o.wav'),
(base + 's1_r2_o.wav'),
(base + 's1_r3_o.wav'),
(base + 's1_r4_o.wav'),
(base + 's2_r1_o.wav'),
(base + 's2_r2_o.wav'),
(base + 's2_r3_o.wav'),
(base + 's2_r4_o.wav'),
(base + 's3_r1_o.wav'),
(base + 's3_r2_o.wav'),
(base + 's3_r3_o.wav'),
(base + 's3_r4_o.wav'),
(base + 's1_p1_o.wav'),
(base + 's1_p2_o.wav'),
(base + 's1_p3_o.wav'),
(base + 's2_p1_o.wav'),
(base + 's2_p2_o.wav'),
(base + 's2_p3_o.wav'),
(base + 's3_p1_o.wav'),
(base + 's3_p2_o.wav'),
(base + 's3_p3_o.wav')
];
// for the instances of AudioBuffer
var rirs = new Array(urls.length);
// Get ArrayBuffer of impulse response by Ajax
var load = function(url, index) {
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
// Timeout (1 minutes)
xhr.timeout = 60000;
xhr.ontimeout = function() {
window.alert('Timeout.');
};
xhr.onerror = function() {
window.alert('Ajax Error.');
};
xhr.onload = function() {
if (xhr.status === 200) {
var arrayBuffer = xhr.response; // Get ArrayBuffer
if (arrayBuffer instanceof ArrayBuffer) {
// The 2nd argument for decodeAudioData
var successCallback = function(audioBuffer) {
// Get the instance of AudioBuffer
rirs[index] = audioBuffer;
// The loading instances of AudioBuffer has completed ?
for (var i = 0, len = rirs.length; i < len; i++) {
if (rirs[i] === undefined) {
return;
}
}
window.alert('The loading impulse responses has completed !!');
onloadCallback(rirs);
};
// The 3rd argument for decodeAudioData
var errorCallback = function(error) {
if (error instanceof Error) {
window.alert(error.message);
} else {
window.alert('Error : "decodeAudioData" method.');
}
};
// Create the instance of AudioBuffer (Asynchronously)
context.decodeAudioData(arrayBuffer, successCallback, errorCallback);
}
}
};
xhr.open('GET', url, true);
xhr.send(null);
};
urls.forEach(function(url, index) {
load(url, index);
});
};
if ((document.readyState === 'interactive') || (document.readyState === 'complete')) {
onDOMContentLoaded();
} else {
document.addEventListener('DOMContentLoaded', onDOMContentLoaded, true);
}
})();
function EventWrapper(){
}
(function(){
var click = '';
var start = '';
var move = '';
var end = '';
// Touch Panel ?
if (/iPhone|iPad|iPod|Android/.test(navigator.userAgent)) {
click = 'click';
start = 'touchstart';
move = 'touchmove';
end = 'touchend';
} else {
click = 'click';
start = 'mousedown';
move = 'mousemove';
end = 'mouseup';
}
EventWrapper.CLICK = click;
EventWrapper.START = start;
EventWrapper.MOVE = move;
EventWrapper.END = end;
})();