var A4 = 440;
var strings = {
dropD: -31,
lowE: -29,
A: -24,
D: -19,
G: -14,
B: -10,
highE: -5
};
var audioContext;
function getHz(semitonesFromA4) {
return A4 * Math.pow( 2, semitonesFromA4 / 12 ) || 0;
}
var chords;
function recalculateChords() {
chords = {
A: [ getHz(strings.A), getHz( strings.D + 2 ), getHz( strings.G + 2 ), getHz( strings.B + 2 ), getHz(strings.highE) ],
A2: [ getHz(strings.A), getHz( strings.D + 2 ), getHz( strings.G + 2 ), getHz(strings.B), getHz(strings.highE) ],
A4: [ getHz(strings.A), getHz( strings.D + 2 ), getHz( strings.G + 2 ), getHz( strings.B + 3 ), getHz(strings.highE) ],
A6: [ getHz(strings.A), getHz( strings.D + 4 ), getHz( strings.G + 2 ), getHz( strings.B + 2 ), getHz(strings.highE) ],
A7: [ getHz(strings.A), getHz( strings.D + 2 ), getHz(strings.G), getHz( strings.B + 2 ), getHz(strings.highE) ],
A9: [ getHz(strings.A), getHz( strings.D + 2 ), getHz( strings.G + 4 ), getHz( strings.B + 2 ), getHz(strings.highE) ],
A11: [ getHz( strings.lowE + 5 ), getHz( strings.A + 5 ), getHz( strings.D + 7 ), getHz( strings.G + 6 ), getHz( strings.B + 5 ), getHz( strings.highE + 5 ) ],
Am: [ getHz(strings.A), getHz( strings.D + 2 ), getHz( strings.G + 2 ), getHz( strings.B + 1 ), getHz(strings.highE) ],
Am7: [ getHz(strings.A), getHz( strings.D + 2 ), getHz(strings.G), getHz( strings.B + 1 ), getHz(strings.highE) ],
As: [ getHz( strings.A + 1 ), getHz( strings.D + 3 ), getHz( strings.G + 3 ), getHz( strings.B + 3 ), getHz( strings.highE + 1 ) ],
As2: [ getHz( strings.A + 1 ), getHz( strings.D + 3 ), getHz( strings.G + 3 ), getHz( strings.B + 1 ), getHz( strings.highE + 1 ) ],
As4: [ getHz( strings.A + 1 ), getHz( strings.D + 3 ), getHz( strings.G + 3 ), getHz( strings.B + 4 ), getHz( strings.highE + 1 ) ],
As6: [ getHz( strings.lowE + 6 ), getHz( strings.A + 5 ), getHz(strings.D), getHz(strings.G), getHz( strings.B + 6 ), getHz( strings.highE + 6 ) ],
As7: [ getHz( strings.A + 1 ), getHz( strings.D + 3 ), getHz( strings.G + 1 ), getHz( strings.B + 3 ), getHz( strings.highE + 1 ) ],
As9: [ getHz( strings.D + 8 ), getHz( strings.G + 7 ), getHz( strings.B + 6 ), getHz( strings.highE + 8 ) ],
As11: [ getHz( strings.lowE + 6 ), getHz( strings.A + 6 ), getHz( strings.D + 8 ), getHz( strings.G + 7 ), getHz( strings.B + 6 ), getHz( strings.highE + 6 ) ],
Asm: [ getHz( strings.A + 1 ), getHz( strings.D + 3 ), getHz( strings.G + 3 ), getHz( strings.B + 2 ), getHz( strings.highE + 1 ) ],
Asm7: [ getHz( strings.A + 1 ), getHz( strings.D + 3 ), getHz( strings.G + 1 ), getHz( strings.B + 2 ), getHz( strings.highE + 1 ) ],
B: [ getHz( strings.A + 2 ), getHz( strings.D + 4 ), getHz( strings.G + 4 ), getHz( strings.B + 4 ), getHz( strings.highE + 2 ) ],
B2: [ getHz( strings.A + 2 ), getHz( strings.D + 4 ), getHz( strings.G + 4 ), getHz( strings.B + 2 ), getHz( strings.highE + 2 ) ],
B4: [ getHz( strings.A + 2 ), getHz( strings.D + 4 ), getHz( strings.G + 4 ), getHz( strings.B + 5 ), getHz( strings.highE + 2 ) ],
B6: [ getHz( strings.A + 2 ), getHz( strings.D + 1 ), getHz( strings.G + 1 ), getHz(strings.B), getHz( strings.highE + 2 ) ],
B7: [ getHz( strings.A + 2 ), getHz( strings.D + 4 ), getHz( strings.G + 2 ), getHz( strings.B + 4 ), getHz( strings.highE + 2 ) ],
B9: [ getHz( strings.D + 9 ), getHz( strings.G + 8 ), getHz( strings.B + 7 ), getHz( strings.highE + 9 ) ],
B11: [ getHz( strings.lowE + 7 ), getHz( strings.A + 7 ), getHz( strings.D + 9 ), getHz( strings.G + 8 ), getHz( strings.B + 7 ), getHz( strings.highE + 7 ) ],
Bm: [ getHz( strings.A + 2 ), getHz( strings.D + 4 ), getHz( strings.G + 4 ), getHz( strings.B + 3 ), getHz( strings.highE + 2 ) ],
Bm7: [ getHz( strings.A + 2 ), getHz( strings.D + 4 ), getHz( strings.G + 2 ), getHz( strings.B + 3 ), getHz( strings.highE + 2 ) ],
C: [ getHz( strings.A + 3 ), getHz( strings.D + 2 ), getHz(strings.G), getHz( strings.B + 1 ), getHz(strings.highE) ],
C2: [ getHz( strings.A + 3 ), getHz(strings.D), getHz(strings.G), getHz( strings.B + 1 ) ],
C4: [ getHz( strings.A + 3 ), getHz( strings.D + 3 ), getHz(strings.G), getHz( strings.B + 1 ), getHz( strings.highE + 1 ) ],
C6: [ getHz( strings.lowE + 8 ), getHz(strings.A), getHz( strings.D + 7 ), getHz(strings.G), getHz( strings.B + 8 ), getHz(strings.highE) ],
C7: [ getHz( strings.A + 3 ), getHz( strings.D + 2 ), getHz( strings.G + 3 ), getHz( strings.B + 1 ), getHz(strings.highE) ],
C9: [ getHz( strings.A + 3 ), getHz( strings.D + 2 ), getHz(strings.G), getHz( strings.B + 3 ), getHz(strings.highE) ],
C11: [ getHz( strings.A + 3 ), getHz( strings.D + 2 ), getHz(strings.G), getHz( strings.B + 3 ), getHz( strings.highE + 1 ) ],
Cm: [ getHz( strings.A + 3 ), getHz( strings.D + 5 ), getHz( strings.G + 5 ), getHz( strings.B + 4 ), getHz( strings.highE + 3 ) ],
Cm7: [ getHz( strings.A + 3 ), getHz( strings.D + 5 ), getHz( strings.G + 3 ), getHz( strings.B + 4 ), getHz( strings.highE + 3 ) ],
Cs: [ getHz( strings.A + 4 ), getHz( strings.D + 6 ), getHz( strings.G + 6 ), getHz( strings.B + 6 ), getHz( strings.highE + 4 ) ],
Cs2: [ getHz( strings.A + 4 ), getHz( strings.D + 6 ), getHz( strings.G + 6 ), getHz( strings.B + 4 ), getHz( strings.highE + 4 ) ],
Cs4: [ getHz( strings.A + 4 ), getHz( strings.D + 6 ), getHz( strings.G + 6 ), getHz( strings.B + 7 ), getHz( strings.highE + 4 ) ],
Cs6: [ getHz( strings.lowE + 9 ), getHz( strings.A + 8 ), getHz( strings.D + 6 ), getHz( strings.G + 6 ), getHz( strings.B + 6 ), getHz( strings.highE + 6 ) ],
Cs7: [ getHz( strings.A + 4 ), getHz( strings.D + 6 ), getHz( strings.G + 4 ), getHz( strings.B + 6 ), getHz( strings.highE + 4 ) ],
Cs9: [ getHz( strings.D + 1 ), getHz( strings.G + 1 ), getHz( strings.B + 2 ), getHz( strings.highE + 1 ) ],
Cs11: [ getHz( strings.lowE + 9 ), getHz( strings.A + 9 ), getHz( strings.D + 11 ), getHz( strings.G + 10 ), getHz( strings.B + 9 ), getHz( strings.highE + 9 ) ],
Csm: [ getHz( strings.A + 4 ), getHz( strings.D + 6 ), getHz( strings.G + 6 ), getHz( strings.B + 5 ), getHz( strings.highE + 4 ) ],
Csm7: [ getHz( strings.A + 4 ), getHz( strings.D + 6 ), getHz( strings.G + 4 ), getHz( strings.B + 5 ), getHz( strings.highE + 4 ) ],
D: [ getHz(strings.D), getHz( strings.G + 2 ), getHz( strings.B + 3 ), getHz( strings.highE + 2 ) ],
D2: [ getHz(strings.D), getHz( strings.G + 2 ), getHz( strings.B + 3 ), getHz(strings.highE) ],
D4: [ getHz(strings.D), getHz( strings.G + 2 ), getHz( strings.B + 3 ), getHz( strings.highE + 3 ) ],
D6: [ getHz(strings.D), getHz( strings.G + 2 ), getHz(strings.B), getHz( strings.highE + 2 ) ],
D7: [ getHz(strings.D), getHz( strings.G + 2 ), getHz( strings.B + 1 ), getHz( strings.highE + 2 ) ],
D9: [ getHz(strings.D), getHz( strings.G + 2 ), getHz( strings.B + 5 ), getHz( strings.highE + 2 ) ],
Dm: [ getHz(strings.D), getHz( strings.G + 2 ), getHz( strings.B + 3 ), getHz( strings.highE + 1 ) ],
Dm7: [ getHz(strings.D), getHz( strings.G + 2 ), getHz( strings.B + 1 ), getHz( strings.highE + 1 ) ],
D11: [ getHz(strings.A), getHz(strings.D), getHz(strings.G), getHz( strings.B + 3 ), getHz( strings.highE + 2 ) ],
Ds: [ getHz( strings.A + 6 ), getHz( strings.D + 8 ), getHz( strings.G + 8 ), getHz( strings.B + 8 ), getHz( strings.highE + 6 ) ],
Ds2: [ getHz( strings.A + 6 ), getHz( strings.D + 8 ), getHz( strings.G + 8 ), getHz( strings.B + 6 ), getHz( strings.highE + 6 ) ],
Ds4: [ getHz( strings.A + 6 ), getHz( strings.D + 8 ), getHz( strings.G + 8 ), getHz( strings.B + 9 ), getHz( strings.highE + 6 ) ],
Ds6: [ getHz( strings.D + 1 ), getHz( strings.G + 3 ), getHz( strings.B + 1 ), getHz( strings.highE + 3 ) ],
Ds7: [ getHz( strings.A + 6 ), getHz( strings.D + 8 ), getHz( strings.G + 6 ), getHz( strings.B + 8 ), getHz( strings.highE + 6 ) ],
Ds11: [ getHz( strings.lowE + 11 ), getHz( strings.A + 11 ), getHz( strings.D + 13 ), getHz( strings.G + 12 ), getHz( strings.B + 11 ), getHz( strings.highE + 11 ) ],
Ds9: [ getHz( strings.A + 6 ), getHz( strings.D + 5 ), getHz(strings.G), getHz( strings.B + 6 ), getHz( strings.highE + 6 ) ],
Dsm: [ getHz( strings.A + 6 ), getHz( strings.D + 8 ), getHz( strings.G + 8 ), getHz( strings.B + 7 ), getHz( strings.highE + 6 ) ],
Dsm7: [ getHz( strings.D + 1 ), getHz( strings.G + 3 ), getHz( strings.B + 2 ), getHz( strings.highE + 2 ) ],
E: [ getHz(strings.lowE), getHz( strings.A + 2 ), getHz( strings.D + 2 ), getHz( strings.G + 1 ), getHz(strings.B), getHz(strings.highE) ],
E2: [ getHz(strings.lowE), getHz( strings.A + 2 ), getHz( strings.D + 4 ), getHz( strings.G + 4 ), getHz(strings.B), getHz(strings.highE) ],
E4: [ getHz(strings.lowE), getHz( strings.A + 2 ), getHz( strings.D + 2 ), getHz( strings.G + 2 ), getHz(strings.B), getHz(strings.highE) ],
E6: [ getHz(strings.lowE), getHz( strings.A + 2 ), getHz( strings.D + 2 ), getHz( strings.G + 1 ), getHz( strings.B + 2 ), getHz(strings.highE) ],
E7: [ getHz(strings.lowE), getHz( strings.A + 2 ), getHz( strings.D + 2 ), getHz( strings.G + 1 ), getHz( strings.B + 3 ), getHz(strings.highE) ],
E9: [ getHz(strings.lowE), getHz( strings.A + 2 ), getHz( strings.D + 2 ), getHz( strings.G + 1 ), getHz(strings.B), getHz( strings.highE + 2 ) ],
E11: [ getHz(strings.lowE), getHz(strings.A), getHz( strings.D + 2 ), getHz( strings.G + 1 ), getHz(strings.B), getHz(strings.highE) ],
Em: [ getHz(strings.lowE), getHz( strings.A + 2 ), getHz( strings.D + 2 ), getHz(strings.G), getHz(strings.B), getHz(strings.highE) ],
Em7: [ getHz(strings.lowE), getHz( strings.A + 2 ), getHz( strings.D + 2 ), getHz(strings.G), getHz( strings.B + 3 ), getHz(strings.highE) ],
F: [ getHz( strings.lowE + 1 ), getHz( strings.A + 3 ), getHz( strings.D + 3 ), getHz( strings.G + 2 ), getHz( strings.B + 1 ), getHz( strings.highE + 1 ) ],
F2: [ getHz( strings.lowE + 1 ), getHz( strings.A + 3 ), getHz( strings.D + 3 ), getHz( strings.G + 0 ), getHz( strings.B + 1 ), getHz( strings.highE + 1 ) ],
F4: [ getHz( strings.lowE + 1 ), getHz( strings.A + 3 ), getHz( strings.D + 3 ), getHz( strings.G + 3 ), getHz( strings.B + 1 ), getHz( strings.highE + 1 ) ],
F6: [ getHz( strings.lowE + 1 ), getHz( strings.A + 3 ), getHz( strings.D + 3 ), getHz( strings.G + 2 ), getHz( strings.B + 3 ), getHz( strings.highE + 1 ) ],
F7: [ getHz( strings.lowE + 1 ), getHz( strings.A + 3 ), getHz( strings.D + 1 ), getHz( strings.G + 2 ), getHz( strings.B + 1 ), getHz( strings.highE + 1 ) ],
F9: [ getHz( strings.D + 3 ), getHz( strings.G + 2 ), getHz( strings.B + 1 ), getHz( strings.highE + 3 ) ],
F11: [ getHz( strings.lowE + 1 ), getHz( strings.A + 1 ), getHz( strings.D + 3 ), getHz( strings.G + 2 ), getHz( strings.B + 1 ), getHz( strings.highE + 1 ) ],
Fm: [ getHz( strings.lowE + 1 ), getHz( strings.A + 3 ), getHz( strings.D + 3 ), getHz( strings.G + 1 ), getHz( strings.B + 1 ), getHz( strings.highE + 1 ) ],
Fm7: [ getHz( strings.lowE + 1 ), getHz( strings.A + 3 ), getHz( strings.D + 1 ), getHz( strings.G + 1 ), getHz( strings.B + 1 ), getHz( strings.highE + 1 ) ],
Fs: [ getHz( strings.lowE + 2 ), getHz( strings.A + 4 ), getHz( strings.D + 4 ), getHz( strings.G + 3 ), getHz( strings.B + 2 ), getHz( strings.highE + 2 ) ],
Fs2: [ getHz( strings.lowE + 2 ), getHz( strings.A + 4 ), getHz( strings.D + 4 ), getHz( strings.G + 3 ), getHz( strings.B + 2 ), getHz( strings.highE + 2 ) ],
Fs4: [ getHz( strings.lowE + 2 ), getHz( strings.A + 4 ), getHz( strings.D + 4 ), getHz( strings.G + 4 ), getHz( strings.B + 2 ), getHz( strings.highE + 2 ) ],
Fs6: [ getHz( strings.lowE + 2 ), getHz( strings.A + 4 ), getHz( strings.D + 4 ), getHz( strings.G + 3 ), getHz( strings.B + 4 ), getHz( strings.highE + 2 ) ],
Fs7: [ getHz( strings.lowE + 2 ), getHz( strings.A + 4 ), getHz( strings.D + 2 ), getHz( strings.G + 3 ), getHz( strings.B + 2 ), getHz( strings.highE + 2 ) ],
Fs9: [ getHz( strings.D + 4 ), getHz( strings.G + 3 ), getHz( strings.B + 2 ), getHz( strings.highE + 4 ) ],
Fs11: [ getHz( strings.lowE + 2 ), getHz( strings.A + 2 ), getHz( strings.D + 4 ), getHz( strings.G + 3 ), getHz( strings.B + 2 ), getHz( strings.highE + 2 ) ],
Fsm: [ getHz( strings.lowE + 2 ), getHz( strings.A + 4 ), getHz( strings.D + 4 ), getHz( strings.G + 2 ), getHz( strings.B + 2 ), getHz( strings.highE + 2 ) ],
Fsm7: [ getHz( strings.lowE + 2 ), getHz( strings.A + 4 ), getHz( strings.D + 2 ), getHz( strings.G + 2 ), getHz( strings.B + 2 ), getHz( strings.highE + 2 ) ],
G: [ getHz( strings.lowE + 3 ), getHz( strings.A + 2 ), getHz(strings.D), getHz(strings.G), getHz(strings.B), getHz( strings.highE + 3 ) ],
G2: [ getHz( strings.lowE + 3 ), getHz( strings.A + 0 ), getHz(strings.D), getHz(strings.G), getHz( strings.B + 3 ), getHz( strings.highE + 3 ) ],
G4: [ getHz( strings.lowE + 3 ), getHz( strings.A + 5 ), getHz( strings.D + 5 ), getHz( strings.G + 5 ), getHz( strings.B + 3 ), getHz( strings.highE + 3 ) ],
G6: [ getHz( strings.lowE + 3 ), getHz( strings.A + 2 ), getHz(strings.D), getHz(strings.G), getHz(strings.B), getHz(strings.highE) ],
G7: [ getHz( strings.lowE + 3 ), getHz( strings.A + 2 ), getHz(strings.D), getHz(strings.G), getHz(strings.B), getHz( strings.highE + 1 ) ],
G9: [ getHz( strings.lowE + 3 ), getHz( strings.A + 0 ), getHz(strings.D), getHz(strings.G), getHz(strings.B), getHz( strings.highE + 3 ) ],
G11: [ getHz( strings.lowE + 3 ), getHz( strings.A + 2 ), getHz(strings.D), getHz(strings.G), getHz( strings.B + 1 ), getHz( strings.highE + 3 ) ],
Gm: [ getHz( strings.lowE + 3 ), getHz( strings.A + 5 ), getHz( strings.D + 5 ), getHz( strings.G + 3 ), getHz( strings.B + 3 ), getHz( strings.highE + 3 ) ],
Gm7: [ getHz( strings.lowE + 3 ), getHz( strings.A + 5 ), getHz( strings.D + 3 ), getHz( strings.G + 3 ), getHz( strings.B + 3 ), getHz( strings.highE + 3 ) ],
Gs: [ getHz( strings.lowE + 4 ), getHz( strings.A + 6 ), getHz( strings.D + 6 ), getHz( strings.G + 5 ), getHz( strings.B + 4 ), getHz( strings.highE + 4 ) ],
Gs2: [ getHz( strings.D + 6 ), getHz( strings.G + 8 ), getHz( strings.B + 9 ), getHz( strings.highE + 6 ) ],
Gs4: [ getHz( strings.lowE + 4 ), getHz( strings.A + 6 ), getHz( strings.D + 6 ), getHz( strings.G + 6 ), getHz( strings.B + 4 ), getHz( strings.highE + 4 ) ],
Gs6: [ getHz( strings.lowE + 4 ), getHz( strings.A + 6 ), getHz( strings.D + 6 ), getHz( strings.G + 5 ), getHz( strings.B + 6 ), getHz( strings.highE + 4 ) ],
Gs7: [ getHz( strings.lowE + 4 ), getHz( strings.A + 6 ), getHz( strings.D + 4 ), getHz( strings.G + 5 ), getHz( strings.B + 4 ), getHz( strings.highE + 4 ) ],
Gs9: [ getHz( strings.D + 6 ), getHz( strings.G + 5 ), getHz( strings.B + 4 ), getHz( strings.highE + 6 ) ],
Gsm: [ getHz( strings.lowE + 4 ), getHz( strings.A + 6 ), getHz( strings.D + 6 ), getHz( strings.G + 4 ), getHz( strings.B + 4 ), getHz( strings.highE + 4 ) ],
Gsm7: [ getHz( strings.lowE + 4 ), getHz( strings.A + 6 ), getHz( strings.D + 4 ), getHz( strings.G + 4 ), getHz( strings.B + 4 ), getHz( strings.highE + 4 ) ],
Gs11: [ getHz( strings.lowE + 4 ), getHz( strings.A + 4 ), getHz( strings.D + 6 ), getHz( strings.G + 5 ), getHz( strings.B + 4 ), getHz( strings.highE + 4 ) ]
};
}
recalculateChords();
function createReverbConfiguration(reverbParams) {
var duration = Math.max( Number(reverbParams.duration) || Number.MIN_VALUE, Number.MIN_VALUE );
var exponentialDecayFactor = Number(reverbParams.exponentialDecayFactor) || 0;
var reverbVolume = ( typeof(reverbParams.reverbVolume) == 'number' ) ? Math.max( Number(reverbParams.reverbVolume) || 0, 0 ) : 1;
var originalVolume = ( typeof(reverbParams.originalVolume) == 'number') ? Math.max( Number(reverbParams.originalVolume) || 0, 0 ) : 1;
if( !audioContext ) {
if( window.AudioContext ) {
audioContext = new AudioContext();
} else if( window.webkitAudioContext ) {
audioContext = new webkitAudioContext();
} else {
return null;
}
}
var audioSampleRate = audioContext.sampleRate;
var totalSamples = Math.ceil( audioSampleRate * duration );
var stereo = 2;
var echoPattern = audioContext.createBuffer( stereo, totalSamples, audioSampleRate );
var channelDataArray;
for( var leftOrRight = 0, i; leftOrRight < stereo; leftOrRight++ ) {
channelDataArray = echoPattern.getChannelData(leftOrRight);
for( i = 0; i < totalSamples; i++ ) {
channelDataArray[i] = ( ( Math.random() * 2 ) - 1 ) * Math.pow( 1 - ( i / totalSamples ), exponentialDecayFactor );
}
}
var reverb = audioContext.createConvolver();
reverb.buffer = echoPattern;
var reverbInput = audioContext.createGain();
var reverbDry = audioContext.createGain();
var reverbWet = audioContext.createGain();
reverbInput.gain.value = 1;
reverbDry.gain.value = originalVolume;
reverbWet.gain.value = reverbVolume;
reverbInput.connect(reverb);
reverb.connect(reverbWet);
reverbInput.connect(reverbDry);
reverbWet.connect(audioContext.destination);
reverbDry.connect(audioContext.destination);
return {
reverbState: 'ready',
input: reverbInput,
disconnect: function (immediate) {
if( !echoPattern ) {
return;
}
var thisObj = this;
if( !immediate ) {
this.reverbState = 'countdown';
setTimeout( function () {
thisObj.disconnect(true);
}, duration * 1000 + 150 );
return;
}
this.input = null;
this.reverbState = 'stopping';
reverbParams = duration = exponentialDecayFactor = reverbVolume = originalVolume = audioSampleRate = totalSamples = stereo = echoPattern = channelDataArray = null;
var now = audioContext.currentTime;
reverbInput.gain.linearRampToValueAtTime( 0, now + 0.05 );
reverbWet.gain.linearRampToValueAtTime( 0, now + 0.05 );
reverbDry.gain.linearRampToValueAtTime( 0, now + 0.05 );
setTimeout( function () {
reverbInput.disconnect();
reverb.disconnect();
reverbWet.disconnect();
reverbDry.disconnect();
reverb = reverbInput = reverbWet = reverbDry = null;
thisObj.reverbState = 'disconnected';
}, 200 );
}
};
}
function getSlide(slideParams) {
if( !slideParams || !slideParams.toTime || !slideParams.steps ) {
return [];
}
var fromTime = Math.min( Math.max( Number(slideParams.fromTime) || 0, 0 ), Number.MAX_SAFE_INTEGER );
var toTime = Math.min( Math.max( Number(slideParams.toTime) || Number.MIN_VALUE, Number.MIN_VALUE ), Number.MAX_SAFE_INTEGER );
var fromHz = Math.max( Number(slideParams.fromHz) || 0, 0 );
var toHz = Math.max( Number(slideParams.toHz) || 0, 0 );
var steps = Math.min( Math.max( Math.floor( Number(slideParams.steps) || 1 ), 1 ), Number.MAX_SAFE_INTEGER );
var timeDiff = toTime - fromTime;
if( timeDiff <= 0 ) {
return [];
}
if( steps == 1 ) {
return [ { time: fromTime, hz: fromHz }, { time: toTime, hz: toHz } ];
}
var hzDiff = toHz - fromHz;
var rampTime = Math.min( 0.005, timeDiff / ( 4 * steps ) );
var remainingTime = timeDiff - ( rampTime * steps );
var slideArray = [];
for( var i = 0; i < steps; i++ ) {
slideArray[slideArray.length] = { time: fromTime + ( remainingTime * i / ( steps - 1 ) ) + ( rampTime * i ), hz: fromHz + i * hzDiff / steps };
if( i == steps - 1 ) {
slideArray[slideArray.length] = { time: toTime, hz: toHz };
} else {
slideArray[slideArray.length] = { time: fromTime + ( remainingTime * i / ( steps - 1 ) ) + ( rampTime * ( i + 1 ) ), hz: fromHz + ( i + 1 ) * hzDiff / steps };
}
}
return slideArray;
}
function playNotes(notesParams) {
function makeClippingCurve( clipThreshold, clipStrength ) {
var interpolationPoints = 5000;
var mapping = new Float32Array(interpolationPoints);
var x;
for( var i = 0; i < interpolationPoints; i++ ) {
x = ( 2 * i / interpolationPoints ) - 1;
if( x > clipThreshold ) {
x = ( x + ( clipThreshold * clipStrength ) ) / ( clipStrength + 1 );
} else if( x < -clipThreshold ) {
x = ( x - ( clipThreshold * clipStrength ) ) / ( clipStrength + 1 );
}
mapping[i] = x;
}
return mapping;
}
function clampFrequency( frequency, note, harmonic, timed, vibrato ) {
var warning, property;
if( frequency > maxFrequencyAllowed ) {
if( window.console && console.warn ) {
warning = '\u26A0 Frequency ' + frequency + ' Hz for playNotes call ' +
playNotes.callCount + ' notes[' + note + ']' +
( ( vibrato >= 0 ) ? ( '.timedVibrato[' + vibrato + ']' ) : '' ) +
( ( timed >= 0 ) ? ( '.timedHz[' + timed + ']' ) : '' ) +
( harmonic ? ( ' tone ' + ( harmonic + 1 ) ) : '' ) +
" is above the computer's maximum of " + maxFrequencyAllowed +
' Hz, so it was clamped to ' + maxFrequencyAllowed + ' Hz. ' +
( ( vibrato < 0 ) ? ( harmonic ? 'Perhaps numTones is set too high.' : 'That note is too high to be heard.' ) : 'This is excessive for a vibrato.' );
if( debugNotes ) {
property = ( timed < 0 ) ? ( vibrato < 0 ) ? 'note' : ( 'vibrato' + vibrato ) : ( 'timed' + timed );
if( !errors[property] ) {
errors[property] = true;
console.groupCollapsed( '%c' + warning, 'color: #000; font-weight: normal;' );
console.log( 'Parameters:', notesParams );
console.trace('Stack trace:');
console.groupEnd();
}
} else if( !playNotes.warnings_shown_once ) {
playNotes.warnings_shown_once = true;
console.log(warning);
console.log('No further frequency warnings will be shown. To see all frequency warnings and stack traces, enable the debugNotes option.');
}
}
frequency = maxFrequencyAllowed;
}
return frequency;
}
if( !notesParams ) {
notesParams = {};
}
var stopPrevious = Array.isArray(notesParams.stopPrevious) ? notesParams.stopPrevious.slice() : [];
var notes = Array.isArray(notesParams.notes) ? notesParams.notes.slice() : [];
var sound = notesParams.sound || 'clean';
var numTones = Math.max( Number(notesParams.numTones) || 10, 1 );
var ringtimeFactor = Math.min( Math.max( Number(notesParams.ringtimeFactor) || 1, 0.21 ), Number.MAX_SAFE_INTEGER );
var masterVolume = ( typeof(notesParams.masterVolume) == 'number' ) ? Math.max( Number(notesParams.masterVolume) || 0, 0 ) : 1;
var balance = Math.min( Math.max( Number(notesParams.balance) || 0, -1 ), 1 );
var delayBetweenNotes = Math.min( Math.max( Number(notesParams.delayBetweenNotes) || 0, Number.MIN_SAFE_INTEGER ), Number.MAX_SAFE_INTEGER );
var imperfection = Math.max( Number(notesParams.imperfection) || 0, 0 );
var reverb = ( notesParams.reverb && window.GainNode && ( notesParams.reverb.input instanceof GainNode ) && typeof(notesParams.reverb.disconnect) == 'function' ) ? notesParams.reverb : window.undefined;
var debugNotes = notesParams.debugNotes;
var callback = ( typeof(notesParams.onstop) == 'function' ) ? notesParams.onstop : null;
playNotes.callCount++;
if( delayBetweenNotes < 0 ) {
notes.reverse();
delayBetweenNotes *= -1;
}
if( !audioContext ) {
if( window.AudioContext ) {
audioContext = new AudioContext();
} else if( window.webkitAudioContext ) {
audioContext = new webkitAudioContext();
} else {
return null;
}
}
var i, j, n;
if( stopPrevious ) {
for( i = 0; i < stopPrevious.length; i++ ) {
if( stopPrevious[i] && typeof(stopPrevious[i].stopPlaying) == 'function' && stopPrevious[i].stopPlaying != playNotes.stopPlaying_replacer ) {
stopPrevious[i].stopPlaying();
}
}
}
var store;
if( !notes.length ) {
store = { playingState: 'disconnected', stopPlaying: playNotes.stopPlaying_replacer };
if( callback ) {
setTimeout( function () {
callback( { type: 'stop', target: store } );
}, 1 );
}
return store;
}
var oscillators = [];
var gains = [];
var disconnectors = [];
store = {
playingState: 'playing',
stopPlaying: function () {
this.stopPlaying = playNotes.stopPlaying_replacer;
this.playingState = 'stopping';
var now = audioContext.currentTime;
var currentValue;
for( var i = 0; i < gains.length; i++ ) {
currentValue = gains[i].gain.value;
gains[i].gain.cancelScheduledValues(now);
gains[i].gain.setValueAtTime( currentValue, now );
gains[i].gain.linearRampToValueAtTime( 0, now + 0.05 );
}
setTimeout( function () {
for( i = 0; i < oscillators.length; i++ ) {
oscillators[i].stop();
oscillators[i].disconnect();
}
for( i = 0; i < gains.length; i++ ) {
gains[i].disconnect();
}
for( i = 0; i < disconnectors.length; i++ ) {
disconnectors[i].disconnect();
}
oscillators.length = 0;
disconnectors.length = 0;
store.playingState = 'disconnected';
if( callback ) {
callback( { type: 'stop', target: store } );
}
}, 200 );
}
};
var now = audioContext.currentTime;
var preamp;
if( sound == 'overdrive' || sound == 'mutedoverdrive' ) {
gains[gains.length] = preamp = audioContext.createGain();
}
var panner = disconnectors[disconnectors.length] = audioContext.createStereoPanner();
panner.pan.value = balance;
panner.connect( reverb ? reverb.input : audioContext.destination );
var offset, ringtime, oscillator, frequencyGain, stopAfter, lastOscillator, startHz, timedHz, timedVibrato, goodVibrato, volume, errors, vibratoOscillator, vibratoGain;
var maxOscillator = 0;
var maxFrequencyAllowed = audioContext.sampleRate / 2;
for( i = 0; i < notes.length; i++ ) {
if( notes[i] && typeof(notes[i]) == 'object' ) {
startHz = Math.max( Number(notes[i].startHz) || 0, 0 );
timedHz = Array.isArray(notes[i].timedHz) ? notes[i].timedHz.slice() : [];
timedVibrato = Array.isArray(notes[i].timedVibrato) ? notes[i].timedVibrato.slice() : [];
volume = Math.max( ( typeof(notes[i].volume) == 'number' ) ? ( notes[i].volume || 0 ) : 1, 0 );
} else {
startHz = Math.max( Number(notes[i]) || 0, 0 );
timedHz = [];
timedVibrato = [];
volume = 1;
}
errors = {};
offset = ( Math.random() - 0.5 ) * imperfection * 200;
if( sound == 'muted' || sound == 'mutedoverdrive' ) {
ringtime = 2;
} else {
ringtime = ringtimeFactor * Math.max( 10, Math.min( 30, 10 + 20 * ( A4 - startHz ) / ( A4 - getHz(strings.lowE) ) ) );
}
stopAfter = ringtime + i * delayBetweenNotes;
for( j = 0; j < timedHz.length; j++ ) {
timedHz[j] = ( timedHz[j] && typeof(timedHz[j]) == 'object' && 'time' in timedHz[j] && 'hz' in timedHz[j] ) ? {
hz: Math.max( Number(timedHz[j].hz) || 0, 0 ),
time: Math.max( Number(timedHz[j].time) || 0, 0 )
} : null;
}
goodVibrato = [];
for( j = 0; j < timedVibrato.length; j++ ) {
if( timedVibrato[j] && typeof(timedVibrato[j]) == 'object' && 'time' in timedVibrato[j] && 'hz' in timedVibrato[j] && 'strength' in timedVibrato[j] ) {
goodVibrato[goodVibrato.length] = {
hz: Math.max( Number(timedVibrato[j].hz) || 0, 0 ),
time: Math.max( Number(timedVibrato[j].time) || 0, 0 ),
strength: Math.max( Number(timedVibrato[j].strength) || 0, 0 )
};
}
}
if( goodVibrato.length ) {
oscillators[oscillators.length] = vibratoOscillator = audioContext.createOscillator();
gains[gains.length] = vibratoGain = audioContext.createGain();
vibratoOscillator.frequency.setValueAtTime( 0, now );
vibratoGain.gain.setValueAtTime( 0, now );
vibratoOscillator.connect(vibratoGain);
for( j = 0; j < goodVibrato.length; j++ ) {
vibratoOscillator.frequency.setValueAtTime( clampFrequency( goodVibrato[j].hz, i, 0, -1, j ), now + goodVibrato[j].time );
vibratoGain.gain.setTargetAtTime( goodVibrato[j].strength * 100, now + goodVibrato[j].time, 0.05 );
}
vibratoOscillator.start();
vibratoOscillator.stop( now + stopAfter );
}
for( j = 0; j < numTones; j++ ) {
oscillators[oscillators.length] = oscillator = audioContext.createOscillator();
gains[gains.length] = frequencyGain = audioContext.createGain();
frequencyGain.gain.setValueAtTime( 0, now );
frequencyGain.gain.setValueAtTime( 0, now + ( i * delayBetweenNotes ) );
if( sound == 'muted' || sound == 'mutedoverdrive' ) {
oscillator.type = 'sine';
frequencyGain.gain.linearRampToValueAtTime( masterVolume * volume * 0.3 / Math.pow( j + 1, 3 ), now + 0.002 + ( i * delayBetweenNotes ) );
frequencyGain.gain.exponentialRampToValueAtTime( masterVolume * 0.0000001 / Math.pow( j + 1, 3 ), now + ringtime + ( i * delayBetweenNotes ) );
} else if( sound == 'harmonic' ) {
oscillator.type = 'sine';
if( j == 1 ) {
frequencyGain.gain.linearRampToValueAtTime( masterVolume * volume * 0.2, now + 0.0005 + ( i * delayBetweenNotes ) );
frequencyGain.gain.exponentialRampToValueAtTime( masterVolume * 0.0000001, now + ringtime + ( i * delayBetweenNotes ) );
} else if( !( ( j - 1 ) % 4 ) ) {
frequencyGain.gain.linearRampToValueAtTime( masterVolume * volume * 0.2 / Math.pow( j + 1, 2.2 ), now + 0.2 + ( i * delayBetweenNotes ) );
frequencyGain.gain.exponentialRampToValueAtTime( masterVolume * 0.0000001 / Math.pow( j + 1, 2.2 ), now + ringtime + ( i * delayBetweenNotes ) );
} else if( !( ( j + 1 ) % 4 ) ) {
frequencyGain.gain.linearRampToValueAtTime( masterVolume * volume * 0.05 / Math.pow( j + 1, 2.2 ), now + 0.2 + ( i * delayBetweenNotes ) );
frequencyGain.gain.exponentialRampToValueAtTime( masterVolume * 0.0000001 / Math.pow( j + 1, 2.2 ), now + ringtime + ( i * delayBetweenNotes ) );
} else {
frequencyGain.gain.linearRampToValueAtTime( masterVolume * volume * 0.01 / Math.pow( j + 1, 2.5 ), now + 2 + ( i * delayBetweenNotes ) );
frequencyGain.gain.exponentialRampToValueAtTime( masterVolume * 0.0000001 / Math.pow( j + 1, 2.5 ), now + ringtime + ( i * delayBetweenNotes ) );
}
} else {
oscillator.type = 'sine';
frequencyGain.gain.linearRampToValueAtTime( masterVolume * volume * 0.22 / Math.pow( j + 1, 1.3 ), now + 0.002 + ( i * delayBetweenNotes ) );
frequencyGain.gain.exponentialRampToValueAtTime( masterVolume * 0.0000001 / Math.pow( j + 1, 1.3 ), now + ringtime + ( i * delayBetweenNotes ) );
}
if( sound == 'overdrive' || sound == 'mutedoverdrive' ) {
oscillator.connect(frequencyGain).connect(preamp);
} else {
oscillator.connect(frequencyGain).connect(panner);
}
oscillator.frequency.setValueAtTime( clampFrequency( ( j + 1 ) * startHz, i, j, -1, -1 ), now );
oscillator.detune.value = offset;
for( n = 0; n < timedHz.length; n++ ) {
if( timedHz[n] ) {
oscillator.frequency.linearRampToValueAtTime( clampFrequency( ( j + 1 ) * timedHz[n].hz, i, j, n, -1 ), now + timedHz[n].time );
}
}
if( stopAfter > maxOscillator ) {
maxOscillator = stopAfter;
lastOscillator = oscillator;
}
oscillator.start();
oscillator.stop( now + stopAfter );
if( goodVibrato.length ) {
vibratoOscillator.connect(vibratoGain).connect(oscillator.detune);
}
}
}
var waveShaper;
if( sound == 'overdrive' || sound == 'mutedoverdrive' ) {
preamp.gain.setValueAtTime( 0.8 / 0.22, now );
disconnectors[disconnectors.length] = waveShaper = audioContext.createWaveShaper();
waveShaper.curve = makeClippingCurve( masterVolume * 0.2, 2.5 );
waveShaper.oversample = '2x';
preamp.connect(waveShaper).connect(panner);
}
lastOscillator.onended = function () {
if( store.stopPlaying != playNotes.stopPlaying_replacer ) {
store.stopPlaying();
}
this.onended = null;
};
return store;
}
playNotes.stopPlaying_replacer = function () {};
playNotes.callCount = 0;