901 lines
23 KiB
JavaScript
901 lines
23 KiB
JavaScript
'use strict';
|
|
|
|
var TrackEditor = function() {
|
|
|
|
};
|
|
|
|
TrackEditor.prototype.classes = {
|
|
"cursor": [
|
|
"state-select"
|
|
],
|
|
|
|
"select": [
|
|
"state-select"
|
|
],
|
|
|
|
"fadein": [
|
|
"state-select"
|
|
],
|
|
|
|
"fadeout": [
|
|
"state-select"
|
|
],
|
|
|
|
"shift": [
|
|
"state-shift"
|
|
],
|
|
|
|
"active": [
|
|
"active"
|
|
],
|
|
|
|
"disabled": [
|
|
"disabled"
|
|
]
|
|
};
|
|
|
|
TrackEditor.prototype.events = {
|
|
"cursor": {
|
|
"mousedown": "selectCursorPos"
|
|
},
|
|
|
|
"select": {
|
|
"mousedown": "selectStart"
|
|
},
|
|
|
|
"fadein": {
|
|
"mousedown": "selectFadeIn"
|
|
},
|
|
|
|
"fadeout": {
|
|
"mousedown": "selectFadeOut"
|
|
},
|
|
|
|
"shift": {
|
|
"mousedown": "timeShift"
|
|
}
|
|
};
|
|
|
|
TrackEditor.prototype.setConfig = function(config) {
|
|
this.config = config;
|
|
};
|
|
|
|
TrackEditor.prototype.setWidth = function(width) {
|
|
this.width = width;
|
|
};
|
|
|
|
TrackEditor.prototype.init = function(src, start, end, fades, cues, stateConfig) {
|
|
|
|
var statesEnabled = {
|
|
'cursor': true,
|
|
'fadein': true,
|
|
'fadeout': true,
|
|
'select': true,
|
|
'shift': true
|
|
};
|
|
|
|
//extend enabled states config.
|
|
Object.keys(statesEnabled).forEach(function (key) {
|
|
statesEnabled[key] = (key in stateConfig) ? stateConfig[key] : statesEnabled[key];
|
|
});
|
|
|
|
this.enabledStates = statesEnabled;
|
|
|
|
makePublisher(this);
|
|
|
|
this.container = document.createElement("div");
|
|
|
|
this.drawer = new WaveformDrawer();
|
|
this.drawer.init(this.container, this.config);
|
|
|
|
this.playout = new AudioPlayout();
|
|
this.playout.init(this.config);
|
|
|
|
this.sampleRate = this.config.getSampleRate();
|
|
this.resolution = this.config.getResolution();
|
|
|
|
//value is a float in seconds
|
|
this.startTime = start || 0;
|
|
//value is a float in seconds
|
|
this.endTime = end || 0; //set properly in onTrackLoad.
|
|
|
|
this.leftOffset = this.secondsToSamples(this.startTime); //value is measured in samples.
|
|
|
|
this.prevStateEvents = {};
|
|
this.setState(this.config.getState());
|
|
|
|
this.fades = {};
|
|
if (fades !== undefined && fades.length > 0) {
|
|
|
|
for (var i = 0; i < fades.length; i++) {
|
|
this.fades[this.getFadeId()] = fades[i];
|
|
}
|
|
}
|
|
|
|
if (cues.cuein !== undefined) {
|
|
this.setCuePoints(this.secondsToSamples(cues.cuein), this.secondsToSamples(cues.cueout));
|
|
}
|
|
|
|
this.active = false;
|
|
this.selectedArea = undefined; //selected area of track stored as inclusive buffer indices to the audio buffer.
|
|
|
|
this.container.classList.add("channel-wrapper");
|
|
this.container.style.left = this.leftOffset;
|
|
|
|
this.drawer.drawLoading();
|
|
|
|
return this.container;
|
|
};
|
|
|
|
TrackEditor.prototype.getFadeId = function() {
|
|
var id = ""+Math.random();
|
|
|
|
return id.replace(".", "");
|
|
};
|
|
|
|
TrackEditor.prototype.getBuffer = function() {
|
|
return this.playout.getBuffer();
|
|
};
|
|
|
|
TrackEditor.prototype.setBuffer = function(buffer) {
|
|
this.playout.setBuffer(buffer);
|
|
};
|
|
|
|
|
|
TrackEditor.prototype.loadTrack = function(track) {
|
|
var el;
|
|
|
|
el = this.init(
|
|
track.src,
|
|
track.start,
|
|
track.end,
|
|
track.fades,
|
|
{
|
|
cuein: track.cuein,
|
|
cueout: track.cueout
|
|
},
|
|
track.states || {}
|
|
);
|
|
|
|
if (track.selected !== undefined) {
|
|
this.selectedArea = {
|
|
start: this.secondsToSamples(track.selected.start),
|
|
end: this.secondsToSamples(track.selected.end)
|
|
};
|
|
}
|
|
|
|
this.loadBuffer(track.src);
|
|
|
|
return el;
|
|
};
|
|
|
|
/**
|
|
* Loads an audio file via XHR.
|
|
*/
|
|
TrackEditor.prototype.loadBuffer = function(src) {
|
|
var that = this,
|
|
xhr = new XMLHttpRequest();
|
|
|
|
xhr.open('GET', src, true);
|
|
xhr.responseType = 'arraybuffer';
|
|
|
|
xhr.addEventListener('progress', function(e) {
|
|
var percentComplete;
|
|
|
|
if (e.lengthComputable) {
|
|
percentComplete = e.loaded / e.total * 100;
|
|
that.drawer.updateLoader(percentComplete);
|
|
}
|
|
|
|
}, false);
|
|
|
|
xhr.addEventListener('load', function(e) {
|
|
that.src = src;
|
|
that.drawer.setLoaderState("decoding");
|
|
|
|
that.playout.loadData(
|
|
e.target.response,
|
|
that.onTrackLoad.bind(that)
|
|
);
|
|
}, false);
|
|
|
|
xhr.send();
|
|
};
|
|
|
|
TrackEditor.prototype.drawTrack = function(buffer) {
|
|
|
|
this.drawer.drawBuffer(buffer, this.getPixelOffset(this.leftOffset), this.cues);
|
|
this.drawer.drawFades(this.fades);
|
|
};
|
|
|
|
TrackEditor.prototype.onTrackLoad = function(buffer, err) {
|
|
var res,
|
|
startTime,
|
|
endTime;
|
|
|
|
if (err !== undefined) {
|
|
this.container.innerHTML = "";
|
|
this.container.classList.add("error");
|
|
|
|
this.fire('unregister');
|
|
|
|
return;
|
|
}
|
|
|
|
if (this.cues === undefined) {
|
|
this.setCuePoints(0, buffer.length - 1);
|
|
}
|
|
//adjust if the length was inaccurate and cueout is set to a higher sample than we actually have.
|
|
else if (this.cues.cueout > (buffer.length - 1)) {
|
|
this.cues.cueout = buffer.length - 1;
|
|
}
|
|
|
|
if (this.width !== undefined) {
|
|
res = Math.ceil(buffer.length / this.width);
|
|
|
|
this.config.setResolution(res);
|
|
this.resolution = res;
|
|
}
|
|
|
|
this.drawTrack(buffer);
|
|
|
|
if (this.selectedArea !== undefined) {
|
|
startTime = this.samplesToSeconds(this.selectedArea.start);
|
|
endTime = this.samplesToSeconds(this.selectedArea.end);
|
|
|
|
this.config.setCursorPos(startTime);
|
|
this.notifySelectUpdate(startTime, endTime);
|
|
}
|
|
};
|
|
|
|
TrackEditor.prototype.samplesToSeconds = function(samples) {
|
|
return samples / this.sampleRate;
|
|
};
|
|
|
|
TrackEditor.prototype.secondsToSamples = function(seconds) {
|
|
return Math.ceil(seconds * this.sampleRate);
|
|
};
|
|
|
|
TrackEditor.prototype.samplesToPixels = function(samples) {
|
|
return ~~(samples / this.resolution);
|
|
};
|
|
|
|
TrackEditor.prototype.pixelsToSamples = function(pixels) {
|
|
return ~~(pixels * this.resolution);
|
|
};
|
|
|
|
TrackEditor.prototype.pixelsToSeconds = function(pixels) {
|
|
return pixels * this.resolution / this.sampleRate;
|
|
};
|
|
|
|
TrackEditor.prototype.secondsToPixels = function(seconds) {
|
|
return ~~(seconds * this.sampleRate / this.resolution);
|
|
};
|
|
|
|
TrackEditor.prototype.getPixelOffset = function() {
|
|
return this.leftOffset / this.resolution;
|
|
};
|
|
|
|
TrackEditor.prototype.activate = function() {
|
|
this.active = true;
|
|
this.container.classList.add("active");
|
|
};
|
|
|
|
TrackEditor.prototype.deactivate = function() {
|
|
this.active = false;
|
|
this.selectedArea = undefined;
|
|
this.container.classList.remove("active");
|
|
this.updateEditor(-1, undefined, undefined, true);
|
|
};
|
|
|
|
/* start of state methods */
|
|
|
|
TrackEditor.prototype.timeShift = function(e) {
|
|
e.preventDefault();
|
|
|
|
var el = e.currentTarget, //want the events placed on the channel wrapper.
|
|
startX = e.pageX,
|
|
diffX = 0,
|
|
origX = 0,
|
|
updatedX = 0,
|
|
editor = this,
|
|
res = editor.resolution,
|
|
scroll = this.config.getTrackScroll(),
|
|
scrollX = scroll.left;
|
|
|
|
origX = editor.leftOffset / res;
|
|
|
|
//dynamically put an event on the element.
|
|
el.onmousemove = function(e) {
|
|
e.preventDefault();
|
|
|
|
var endX = e.pageX;
|
|
|
|
diffX = endX - startX;
|
|
updatedX = origX + diffX;
|
|
editor.drawer.setTimeShift(updatedX);
|
|
editor.leftOffset = editor.pixelsToSamples(updatedX);
|
|
};
|
|
el.onmouseup = function(e) {
|
|
e.preventDefault();
|
|
|
|
var delta;
|
|
|
|
el.onmousemove = el.onmouseup = null;
|
|
editor.leftOffset = editor.pixelsToSamples(updatedX);
|
|
delta = editor.pixelsToSeconds(diffX);
|
|
|
|
//update track's start and end time relative to the playlist.
|
|
editor.startTime = editor.startTime + delta;
|
|
editor.endTime = editor.endTime + delta;
|
|
};
|
|
};
|
|
|
|
/*
|
|
startTime, endTime in seconds.
|
|
*/
|
|
TrackEditor.prototype.notifySelectUpdate = function(startTime, endTime) {
|
|
|
|
this.updateEditor(-1, undefined, undefined, true);
|
|
|
|
this.fire('changecursor', {
|
|
start: startTime,
|
|
end: endTime,
|
|
editor: this
|
|
});
|
|
};
|
|
|
|
|
|
TrackEditor.prototype.getSelectedPlayTime = function() {
|
|
var selected = this.selectedArea,
|
|
offset = this.leftOffset,
|
|
start = this.samplesToSeconds(offset + selected.start),
|
|
end = this.samplesToSeconds(offset + selected.end);
|
|
|
|
return {
|
|
startTime: start,
|
|
endTime: end
|
|
}
|
|
};
|
|
|
|
|
|
TrackEditor.prototype.getSelectedArea = function() {
|
|
return this.selectedArea;
|
|
};
|
|
|
|
/*
|
|
start, end in samples. (relative to cuein/cueout)
|
|
*/
|
|
TrackEditor.prototype.adjustSelectedArea = function(start, end) {
|
|
var buffer = this.getBuffer(),
|
|
cues = this.cues;
|
|
|
|
if (start === undefined || start < 0) {
|
|
start = 0;
|
|
}
|
|
|
|
if (end === undefined) {
|
|
end = cues.cueout - cues.cuein;
|
|
}
|
|
|
|
if (end > buffer.length - 1) {
|
|
end = buffer.length - 1;
|
|
}
|
|
|
|
return {
|
|
start: start,
|
|
end: end
|
|
};
|
|
};
|
|
|
|
/*
|
|
start, end in pixels
|
|
*/
|
|
TrackEditor.prototype.setSelectedArea = function(start, end, shiftKey) {
|
|
var left,
|
|
right,
|
|
currentStart,
|
|
currentEnd,
|
|
sampLeft,
|
|
sampRight,
|
|
buffer = this.getBuffer();
|
|
|
|
//extending selected area since shift is pressed.
|
|
if (shiftKey && (end - start === 0) && (this.prevSelectedArea !== undefined)) {
|
|
|
|
currentStart = this.samplesToPixels(this.prevSelectedArea.start);
|
|
currentEnd = this.samplesToPixels(this.prevSelectedArea.end);
|
|
|
|
if (start < currentStart) {
|
|
left = start;
|
|
right = currentEnd;
|
|
}
|
|
else if (end > currentEnd) {
|
|
left = currentStart;
|
|
right = end;
|
|
}
|
|
//it's ambigous otherwise, cut off the smaller duration.
|
|
else {
|
|
if ((start - currentStart) < (currentEnd - start)) {
|
|
left = start;
|
|
right = currentEnd;
|
|
}
|
|
else {
|
|
left = currentStart;
|
|
right = end;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
left = start;
|
|
right = end;
|
|
}
|
|
|
|
sampLeft = left === undefined ? undefined : this.pixelsToSamples(left);
|
|
sampRight = right === undefined ? undefined : this.pixelsToSamples(right);
|
|
|
|
this.prevSelectedArea = this.selectedArea;
|
|
this.selectedArea = this.adjustSelectedArea(sampLeft, sampRight);
|
|
};
|
|
|
|
TrackEditor.prototype.activateAudioSelection = function() {
|
|
|
|
this.fire("activateSelection");
|
|
};
|
|
|
|
TrackEditor.prototype.deactivateAudioSelection = function() {
|
|
|
|
this.fire("deactivateSelection");
|
|
};
|
|
|
|
TrackEditor.prototype.findLayerOffset = function(e) {
|
|
var layerOffset = 0,
|
|
parent;
|
|
|
|
if (e.target.tagName !== "CANVAS") {
|
|
layerOffset = -1;
|
|
}
|
|
else {
|
|
//have to check if a fade canvas was selected. (Must add left offset)
|
|
parent = e.target.parentNode;
|
|
|
|
if (parent.classList.contains('playlist-fade')) {
|
|
layerOffset = parent.offsetLeft;
|
|
}
|
|
}
|
|
|
|
return layerOffset;
|
|
};
|
|
|
|
TrackEditor.prototype.selectStart = function(e) {
|
|
e.preventDefault();
|
|
|
|
var el = e.currentTarget, //want the events placed on the channel wrapper.
|
|
editor = this,
|
|
startX = e.layerX || e.offsetX, //relative to e.target (want the canvas).
|
|
prevX = e.layerX || e.offsetX,
|
|
offset = this.leftOffset,
|
|
startTime,
|
|
layerOffset;
|
|
|
|
layerOffset = this.findLayerOffset(e);
|
|
if (layerOffset < 0) {
|
|
return;
|
|
}
|
|
startX = startX + layerOffset;
|
|
prevX = prevX + layerOffset;
|
|
|
|
editor.setSelectedArea(startX, startX);
|
|
startTime = editor.samplesToSeconds(offset + editor.selectedArea.start);
|
|
|
|
editor.notifySelectUpdate(startTime, startTime);
|
|
|
|
//dynamically put an event on the element.
|
|
el.onmousemove = function(e) {
|
|
e.preventDefault();
|
|
|
|
var currentX = layerOffset + (e.layerX || e.offsetX),
|
|
delta = currentX - prevX,
|
|
minX = Math.min(prevX, currentX, startX),
|
|
maxX = Math.max(prevX, currentX, startX),
|
|
selectStart,
|
|
selectEnd,
|
|
startTime, endTime;
|
|
|
|
if (currentX > startX) {
|
|
selectStart = startX;
|
|
selectEnd = currentX;
|
|
}
|
|
else {
|
|
selectStart = currentX;
|
|
selectEnd = startX;
|
|
}
|
|
|
|
startTime = editor.samplesToSeconds(offset + editor.selectedArea.start);
|
|
endTime = editor.samplesToSeconds(offset + editor.selectedArea.end);
|
|
|
|
editor.setSelectedArea(selectStart, selectEnd);
|
|
editor.notifySelectUpdate(startTime, endTime);
|
|
prevX = currentX;
|
|
};
|
|
el.onmouseup = function(e) {
|
|
e.preventDefault();
|
|
|
|
var endX = layerOffset + (e.layerX || e.offsetX),
|
|
minX, maxX,
|
|
startTime, endTime;
|
|
|
|
minX = Math.min(startX, endX);
|
|
maxX = Math.max(startX, endX);
|
|
|
|
editor.setSelectedArea(minX, maxX, e.shiftKey);
|
|
|
|
minX = editor.samplesToPixels(offset + editor.selectedArea.start);
|
|
maxX = editor.samplesToPixels(offset + editor.selectedArea.end);
|
|
|
|
el.onmousemove = el.onmouseup = null;
|
|
|
|
//if more than one pixel is selected, listen to possible fade events.
|
|
if (Math.abs(minX - maxX)) {
|
|
editor.activateAudioSelection();
|
|
}
|
|
else {
|
|
editor.deactivateAudioSelection();
|
|
}
|
|
|
|
startTime = editor.samplesToSeconds(offset + editor.selectedArea.start);
|
|
endTime = editor.samplesToSeconds(offset + editor.selectedArea.end);
|
|
|
|
editor.config.setCursorPos(startTime);
|
|
editor.notifySelectUpdate(startTime, endTime);
|
|
};
|
|
};
|
|
|
|
TrackEditor.prototype.selectCursorPos = function(e) {
|
|
var editor = this,
|
|
startX = e.layerX || e.offsetX, //relative to e.target (want the canvas).
|
|
offset = this.leftOffset,
|
|
startTime,
|
|
endTime,
|
|
layerOffset;
|
|
|
|
layerOffset = this.findLayerOffset(e);
|
|
if (layerOffset < 0) {
|
|
return;
|
|
}
|
|
startX = startX + layerOffset;
|
|
|
|
editor.setSelectedArea(startX, startX);
|
|
startTime = editor.samplesToSeconds(offset + editor.selectedArea.start);
|
|
endTime = editor.samplesToSeconds(offset + editor.selectedArea.end);
|
|
|
|
editor.config.setCursorPos(startTime);
|
|
editor.notifySelectUpdate(startTime, endTime);
|
|
|
|
editor.deactivateAudioSelection();
|
|
};
|
|
|
|
TrackEditor.prototype.selectFadeIn = function(e) {
|
|
e.preventDefault();
|
|
|
|
var startX = e.layerX || e.offsetX, //relative to e.target (want the canvas).
|
|
layerOffset,
|
|
FADETYPE = "FadeIn",
|
|
shape = this.config.getFadeType();
|
|
|
|
layerOffset = this.findLayerOffset(e);
|
|
if (layerOffset < 0) {
|
|
return;
|
|
}
|
|
startX = startX + layerOffset;
|
|
|
|
this.setSelectedArea(undefined, startX);
|
|
this.removeFadeType(FADETYPE);
|
|
this.createFade(FADETYPE, shape);
|
|
};
|
|
|
|
TrackEditor.prototype.selectFadeOut = function(e) {
|
|
e.preventDefault();
|
|
|
|
var startX = e.layerX || e.offsetX, //relative to e.target (want the canvas).
|
|
layerOffset,
|
|
FADETYPE = "FadeOut",
|
|
shape = this.config.getFadeType();
|
|
|
|
layerOffset = this.findLayerOffset(e);
|
|
if (layerOffset < 0) {
|
|
return;
|
|
}
|
|
startX = startX + layerOffset;
|
|
|
|
this.setSelectedArea(startX, undefined);
|
|
this.removeFadeType(FADETYPE);
|
|
this.createFade(FADETYPE, shape);
|
|
};
|
|
|
|
/* end of state methods */
|
|
|
|
TrackEditor.prototype.saveFade = function(id, type, shape, start, end) {
|
|
|
|
this.fades[id] = {
|
|
type: type,
|
|
shape: shape,
|
|
start: start,
|
|
end: end
|
|
};
|
|
|
|
return id;
|
|
};
|
|
|
|
TrackEditor.prototype.removeFade = function(id) {
|
|
|
|
delete this.fades[id];
|
|
this.drawer.removeFade(id);
|
|
};
|
|
|
|
TrackEditor.prototype.removeFadeType = function(type) {
|
|
var id,
|
|
fades = this.fades,
|
|
fade;
|
|
|
|
for (id in fades) {
|
|
fade = fades[id];
|
|
|
|
if (fade.type === type) {
|
|
this.removeFade(id);
|
|
}
|
|
}
|
|
};
|
|
|
|
/*
|
|
Cue points are stored internally in the editor as sample indices for highest precision.
|
|
|
|
sample at index cueout is not included.
|
|
*/
|
|
TrackEditor.prototype.setCuePoints = function(cuein, cueout) {
|
|
var offset = this.cues ? this.cues.cuein : 0;
|
|
|
|
this.cues = {
|
|
cuein: offset + cuein,
|
|
cueout: offset + cueout
|
|
};
|
|
|
|
this.duration = (cueout - cuein) / this.sampleRate;
|
|
this.endTime = this.duration + this.startTime;
|
|
};
|
|
|
|
/*
|
|
Will remove all audio samples from the track's buffer except for the currently selected area.
|
|
Used to set cuein / cueout points in the audio.
|
|
|
|
start, end are indices into the audio buffer and are inclusive.
|
|
*/
|
|
TrackEditor.prototype.trim = function(start, end) {
|
|
|
|
this.setCuePoints(start, end+1);
|
|
this.resetCursor();
|
|
this.drawTrack(this.getBuffer());
|
|
};
|
|
|
|
|
|
/*
|
|
Will remove all audio samples from the track's buffer in the currently selected area.
|
|
|
|
start, end are indices into the audio buffer and are inclusive.
|
|
*/
|
|
TrackEditor.prototype.removeAudio = function(start, end) {
|
|
|
|
};
|
|
|
|
TrackEditor.prototype.onTrackEdit = function(event) {
|
|
var type = event.type,
|
|
method = "on" + type.charAt(0).toUpperCase() + type.slice(1);
|
|
|
|
if (this.active === true) {
|
|
this[method].call(this, event.args);
|
|
}
|
|
};
|
|
|
|
TrackEditor.prototype.createFade = function(type, shape) {
|
|
var selected = this.selectedArea,
|
|
start = this.samplesToPixels(selected.start),
|
|
end = this.samplesToPixels(selected.end),
|
|
startTime = this.samplesToSeconds(selected.start),
|
|
endTime = this.samplesToSeconds(selected.end),
|
|
id = this.getFadeId();
|
|
|
|
this.resetCursor();
|
|
this.saveFade(id, type, shape, startTime, endTime);
|
|
this.updateEditor(-1, undefined, undefined, true);
|
|
this.drawer.drawFade(id, type, shape, start, end);
|
|
};
|
|
|
|
TrackEditor.prototype.onCreateFade = function(args) {
|
|
this.createFade(args.type, args.shape);
|
|
this.deactivateAudioSelection();
|
|
};
|
|
|
|
TrackEditor.prototype.onZeroCrossing = function() {
|
|
var selected = this.getSelectedArea(),
|
|
startTime,
|
|
endTime,
|
|
offset = this.leftOffset;
|
|
|
|
this.selectedArea = this.findNearestZeroCrossing(selected.start, selected.end);
|
|
|
|
startTime = this.samplesToSeconds(offset + this.selectedArea.start);
|
|
endTime = this.samplesToSeconds(offset + this.selectedArea.end);
|
|
this.notifySelectUpdate(startTime, endTime);
|
|
this.updateEditor(-1, undefined, undefined, true);
|
|
};
|
|
|
|
TrackEditor.prototype.onTrimAudio = function() {
|
|
var selected = this.getSelectedArea();
|
|
|
|
this.trim(selected.start, selected.end);
|
|
this.deactivateAudioSelection();
|
|
};
|
|
|
|
TrackEditor.prototype.onRemoveAudio = function() {
|
|
var selected = this.getSelectedArea();
|
|
|
|
this.removeAudio(selected.start, selected.end);
|
|
this.deactivateAudioSelection();
|
|
};
|
|
|
|
TrackEditor.prototype.setState = function(state) {
|
|
var that = this,
|
|
stateEvents = this.events[state],
|
|
stateClasses = this.classes[state],
|
|
disabledClasses = this.classes['disabled'],
|
|
enabledStates = this.enabledStates,
|
|
container = this.container,
|
|
prevState = this.currentState,
|
|
prevStateClasses,
|
|
prevStateEvents = this.prevStateEvents,
|
|
func, event, cl,
|
|
i, len;
|
|
|
|
if (prevState) {
|
|
prevStateClasses = this.classes[prevState];
|
|
|
|
if (enabledStates[prevState] === true) {
|
|
for (event in prevStateEvents) {
|
|
container.removeEventListener(event, prevStateEvents[event]);
|
|
}
|
|
this.prevStateEvents = {};
|
|
|
|
for (i = 0, len = prevStateClasses.length; i < len; i++) {
|
|
container.classList.remove(prevStateClasses[i]);
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0, len = disabledClasses.length; i < len; i++) {
|
|
container.classList.remove(disabledClasses[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (enabledStates[state] === true) {
|
|
for (event in stateEvents) {
|
|
func = that[stateEvents[event]].bind(that);
|
|
//need to keep track of the added events for later removal since a new function is returned after using "bind"
|
|
this.prevStateEvents[event] = func;
|
|
container.addEventListener(event, func);
|
|
}
|
|
for (i = 0, len = stateClasses.length; i < len; i++) {
|
|
container.classList.add(stateClasses[i]);
|
|
}
|
|
}
|
|
else {
|
|
for (i = 0, len = disabledClasses.length; i < len; i++) {
|
|
container.classList.add(disabledClasses[i]);
|
|
}
|
|
}
|
|
|
|
this.currentState = state;
|
|
};
|
|
|
|
TrackEditor.prototype.onResolutionChange = function(res) {
|
|
var selected = this.selectedArea;
|
|
|
|
this.resolution = res;
|
|
this.drawTrack(this.getBuffer());
|
|
|
|
if (this.active === true && this.selectedArea !== undefined) {
|
|
|
|
this.updateEditor(-1, this.samplesToPixels(selected.start), this.samplesToPixels(selected.end), true);
|
|
}
|
|
};
|
|
|
|
TrackEditor.prototype.isPlaying = function() {
|
|
return this.playout.isScheduled() || this.playout.isPlaying();
|
|
};
|
|
|
|
/*
|
|
startTime, endTime in seconds (float).
|
|
*/
|
|
TrackEditor.prototype.schedulePlay = function(now, delay, startTime, endTime) {
|
|
var start,
|
|
duration,
|
|
relPos,
|
|
when = now + delay,
|
|
window = (endTime) ? (endTime - startTime) : undefined,
|
|
cueOffset = this.cues.cuein / this.sampleRate;
|
|
|
|
//track has no content to play.
|
|
if (this.endTime <= startTime) return;
|
|
|
|
//track does not start in this selection.
|
|
if (window && (startTime + window) < this.startTime) return;
|
|
|
|
|
|
//track should have something to play if it gets here.
|
|
|
|
//the track starts in the future of the cursor position
|
|
if (this.startTime >= startTime) {
|
|
start = 0;
|
|
when = when + this.startTime - startTime; //schedule additional delay for this audio node.
|
|
window = window - (this.startTime - startTime);
|
|
duration = (endTime) ? Math.min(window, this.duration) : this.duration;
|
|
}
|
|
else {
|
|
start = startTime - this.startTime;
|
|
duration = (endTime) ? Math.min(window, this.duration - start) : this.duration - start;
|
|
}
|
|
|
|
start = start + cueOffset;
|
|
|
|
relPos = startTime - this.startTime;
|
|
this.playout.applyFades(this.fades, relPos, now, delay);
|
|
this.playout.play(when, start, duration);
|
|
};
|
|
|
|
TrackEditor.prototype.scheduleStop = function(when) {
|
|
|
|
this.playout.stop(when);
|
|
};
|
|
|
|
TrackEditor.prototype.resetCursor = function() {
|
|
this.selectedArea = undefined;
|
|
this.config.setCursorPos(0);
|
|
this.notifySelectUpdate(0, 0);
|
|
};
|
|
|
|
TrackEditor.prototype.updateEditor = function(cursorPos, start, end, highlighted) {
|
|
var pixelOffset = this.getPixelOffset(),
|
|
selected;
|
|
|
|
if (this.selectedArea) {
|
|
//must pass selected area in pixels.
|
|
selected = {
|
|
start: this.samplesToPixels(this.selectedArea.start),
|
|
end: this.samplesToPixels(this.selectedArea.end)
|
|
};
|
|
}
|
|
|
|
this.drawer.updateEditor(cursorPos, pixelOffset, start, end, highlighted, selected);
|
|
};
|
|
|
|
TrackEditor.prototype.getTrackDetails = function() {
|
|
var d,
|
|
cues = this.cues,
|
|
fades = [],
|
|
id;
|
|
|
|
for (id in this.fades) {
|
|
fades.push(this.fades[id]);
|
|
}
|
|
|
|
d = {
|
|
start: this.startTime,
|
|
end: this.endTime,
|
|
fades: fades,
|
|
src: this.src,
|
|
cuein: this.samplesToSeconds(cues.cuein),
|
|
cueout: this.samplesToSeconds(cues.cueout)
|
|
};
|
|
|
|
return d;
|
|
};
|