(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 oscillator = null; // for the instance of OscillatorNode
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) {
// Create the instance of OscillatorNode
oscillator = context.createOscillator();
// Parameter for the instance of OscillatorNode
var type = oscillator.type;
// 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
var 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;
// Flag for starting or stopping sound
var isStop = true;
// Start or Stop sound
var PIANO_88S = [
'A-4', 'A-4h', 'B-4',
'C-3', 'C-3h', 'D-3', 'D-3h', 'E-3', 'F-3', 'F-3h', 'G-3', 'G-3h', 'A-3', 'A-3h', 'B-3',
'C-2', 'C-2h', 'D-2', 'D-2h', 'E-2', 'F-2', 'F-2h', 'G-2', 'G-2h', 'A-2', 'A-2h', 'B-2',
'C-1', 'C-1h', 'D-1', 'D-1h', 'E-1', 'F-1', 'F-1h', 'G-1', 'G-1h', 'A-1', 'A-1h', 'B-1',
'C', 'Ch', 'D', 'Dh', 'E', 'F', 'Fh', 'G', 'Gh', 'A', 'Ah', 'B',
'C1', 'C1h', 'D1', 'D1h', 'E1', 'F1', 'F1h', 'G1', 'G1h', 'A1', 'A1h', 'B1',
'C2', 'C2h', 'D2', 'D2h', 'E2', 'F2', 'F2h', 'G2', 'G2h', 'A2', 'A2h', 'B2',
'C3', 'C3h', 'D3', 'D3h', 'E3', 'F3', 'F3h', 'G3', 'G3h', 'A3', 'A3h', 'B3',
'C4'
];
var pianoKeys = Array.prototype.slice.call(document.querySelectorAll('#piano ul li'), 0);
var convertIndex = function(index) {
//
// The 12 equal temparement
//
// Min -> A 27.5Hz, Max -> C 4186 Hz
//
// Example :
// A * 1.059463 -> A# (half up)
//
var FREQUENCY_RATIO = Math.pow(2, (1 / 12)); // about 1.059463;
var MIN_A = 27.5;
return (MIN_A * Math.pow(FREQUENCY_RATIO, index));
};
pianoKeys.forEach(function(element, index, array) {
// Start sound
element.addEventListener(EventWrapper.START, function(event) {
if (!isStop) {
oscillator.stop(0);
}
var pianoIndex = PIANO_88S.indexOf(this.id);
var frequency = convertIndex(pianoIndex);
// Create the instance of OscillatorNode
oscillator = context.createOscillator();
// for legacy browsers
oscillator.start = oscillator.start || oscillator.noteOn;
oscillator.stop = oscillator.stop || oscillator.noteOff;
var reverbType = parseInt(document.getElementById('select-reverb-type').value);
// GainNode (Master Volume) -> AudioDestinationNode (Output)
gain.connect(context.destination);
if (reverbType === 0) {
// Reverb OFF
// OscillatorNode (Input) -> GainNode (Master Volume) (-> AudioDestinationNode (Output))
oscillator.connect(gain);
} else {
// Reverb ON
// Connect nodes for original sound
// OscillatorNode (Input) -> GainNode (Dry) -> GainNode (Master Volume) (-> AudioDestinationNode (Output))
oscillator.connect(dry);
dry.connect(gain);
// Connect nodes for effect (Reverb) sound
// OscillatorNode (Input) -> ConvolverNode (Reverb) -> GainNode (Wet) -> GainNode (Master Volume) (-> AudioDestinationNode (Output))
oscillator.connect(convolver);
convolver.connect(wet);
wet.connect(gain);
}
// Set parameters
oscillator.type = type;
oscillator.frequency.value = frequency;
oscillator.start(0);
isStop = false;
this.classList.remove('key-off');
this.classList.add('key-on');
}, false);
// Stop sound
element.addEventListener(EventWrapper.END, function() {
if (isStop) {
return;
}
oscillator.stop(0);
isStop = true;
this.classList.remove('key-on');
this.classList.add('key-off');
}, 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);
// Select type
document.getElementById('form-wave-type').addEventListener('change', function() {
for (var i = 0, len = this.elements['radio-wave-type'].length; i < len; i++) {
if (this.elements['radio-wave-type'][i].checked) {
oscillator.type = type = (typeof oscillator.type === 'string') ? this.elements['radio-wave-type'][i].value : i;
break;
}
}
}, false);
// Select Reverb Type
document.getElementById('select-reverb-type').addEventListener('change', function() {
var reverbType = parseInt(this.value);
// Clear connection
oscillator.disconnect(0);
convolver.disconnect(0);
dry.disconnect(0);
wet.disconnect(0);
if (reverbType === 0) {
// Reverb OFF
convolver.buffer = null;
// OscillatorNode (Input) -> GainNode (Master Volume) (-> AudioDestinationNode (Output))
oscillator.connect(gain);
} else {
// Revreb ON
convolver.buffer = reverbs[reverbType - 1];
// Connect nodes for original audio
// OscillatorNode (Input) -> GainNode (Dry) -> GainNode (Master Volume) (-> AudioDestinationNode (Output))
oscillator.connect(dry);
dry.connect(gain);
// Connect nodes for effect (Reverb) sound
// OscillatorNode (Input) -> ConvolverNode (Reverb) -> GainNode (Wet) -> GainNode (Master Volume) (-> AudioDestinationNode (Output))
oscillator.connect(convolver);
convolver.connect(wet);
wet.connect(gain);
}
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;
})();