|
|
var soundsCache = require('./sounds-cache'); |
|
|
var pitchToNoteName = require('./pitch-to-note-name'); |
|
|
var centsToFactor = require("./cents-to-factor"); |
|
|
|
|
|
function placeNote(outputAudioBuffer, sampleRate, sound, startArray, volumeMultiplier, ofsMs, fadeTimeSec, noteEndSec, debugCallback) { |
|
|
|
|
|
|
|
|
|
|
|
var OfflineAC = window.OfflineAudioContext || |
|
|
window.webkitOfflineAudioContext; |
|
|
|
|
|
var len = sound.len * sound.tempoMultiplier; |
|
|
if (ofsMs) |
|
|
len +=ofsMs/1000; |
|
|
len -= noteEndSec; |
|
|
if (len < 0) |
|
|
len = 0.005; |
|
|
var offlineCtx = new OfflineAC(2,Math.floor((len+fadeTimeSec)*sampleRate),sampleRate); |
|
|
var noteName = pitchToNoteName[sound.pitch]; |
|
|
if (!soundsCache[sound.instrument]) { |
|
|
|
|
|
if (debugCallback) |
|
|
debugCallback('placeNote skipped (instrument empty): '+sound.instrument+':'+noteName) |
|
|
return Promise.resolve(); |
|
|
} |
|
|
var noteBufferPromise = soundsCache[sound.instrument][noteName]; |
|
|
|
|
|
if (!noteBufferPromise) { |
|
|
|
|
|
if (debugCallback) |
|
|
debugCallback('placeNote skipped: '+sound.instrument+':'+noteName) |
|
|
return Promise.resolve(); |
|
|
} |
|
|
|
|
|
return noteBufferPromise |
|
|
.then(function (response) { |
|
|
|
|
|
var source = offlineCtx.createBufferSource(); |
|
|
source.buffer = response.audioBuffer; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var volume = (sound.volume / 96) * volumeMultiplier; |
|
|
source.gainNode = offlineCtx.createGain(); |
|
|
|
|
|
|
|
|
if (sound.pan && offlineCtx.createStereoPanner) { |
|
|
source.panNode = offlineCtx.createStereoPanner(); |
|
|
source.panNode.pan.setValueAtTime(sound.pan, 0); |
|
|
} |
|
|
source.gainNode.gain.value = volume; |
|
|
source.gainNode.gain.linearRampToValueAtTime(source.gainNode.gain.value, len); |
|
|
source.gainNode.gain.linearRampToValueAtTime(0.0, len + fadeTimeSec); |
|
|
|
|
|
if (sound.cents) { |
|
|
source.playbackRate.value = centsToFactor(sound.cents); |
|
|
} |
|
|
|
|
|
|
|
|
if (source.panNode) { |
|
|
source.panNode.connect(offlineCtx.destination); |
|
|
source.gainNode.connect(source.panNode); |
|
|
} else { |
|
|
source.gainNode.connect(offlineCtx.destination); |
|
|
} |
|
|
source.connect(source.gainNode); |
|
|
|
|
|
|
|
|
source.start(0); |
|
|
|
|
|
if (source.noteOff) { |
|
|
source.noteOff(len + fadeTimeSec); |
|
|
} else { |
|
|
source.stop(len + fadeTimeSec); |
|
|
} |
|
|
var fnResolve; |
|
|
offlineCtx.oncomplete = function(e) { |
|
|
if (e.renderedBuffer && e.renderedBuffer.getChannelData) { |
|
|
for (var i = 0; i < startArray.length; i++) { |
|
|
|
|
|
var start = startArray[i] * sound.tempoMultiplier; |
|
|
if (ofsMs) |
|
|
start -=ofsMs/1000; |
|
|
if (start < 0) |
|
|
start = 0; |
|
|
start = Math.floor(start*sampleRate); |
|
|
copyToChannel(outputAudioBuffer, e.renderedBuffer, start); |
|
|
} |
|
|
} |
|
|
if (debugCallback) |
|
|
debugCallback('placeNote: '+sound.instrument+':'+noteName) |
|
|
fnResolve(); |
|
|
}; |
|
|
offlineCtx.startRendering(); |
|
|
return new Promise(function(resolve) { |
|
|
fnResolve = resolve; |
|
|
}); |
|
|
}) |
|
|
.catch(function (error) { |
|
|
if (debugCallback) |
|
|
debugCallback('placeNote catch: '+error.message) |
|
|
return Promise.resolve() |
|
|
}); |
|
|
} |
|
|
|
|
|
var copyToChannel = function(toBuffer, fromBuffer, start) { |
|
|
for (var ch = 0; ch < 2; ch++) { |
|
|
var fromData = fromBuffer.getChannelData(ch); |
|
|
var toData = toBuffer.getChannelData(ch); |
|
|
|
|
|
|
|
|
for (var n = 0; n < fromData.length; n++) { |
|
|
toData[n + start] += fromData[n]; |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
module.exports = placeNote; |
|
|
|