webamp/js/winamp.js
Jordan Eldredge 1b8ab66845 Use URL.createObjectURL for reading local files
Somehow I overlooked this method of extracting data from a local file. Now that
we have a real URL for a local file that does not break Cross Origin Request
rules, we can go back to using an `<audio>` tag for playing our media which
greatly simplifies much of the media functionality.
2015-09-30 16:42:22 -07:00

299 lines
8.5 KiB
JavaScript
Executable file

// UI and App logic
define([
'main-window',
'window-manager',
'skin',
'media',
'my-file'
], function(
MainWindow,
WindowManager,
Skin,
Media,
MyFile
) {
return {
init: function(options) {
this.fileInput = document.createElement('input');
this.fileInput.type = 'file';
this.fileInput.style.display = 'none';
this.objectURL = null;
this.windowManager = WindowManager;
this.media = new Media();
this.skin = Skin.init(document.getElementById('visualizer'), this.media._analyser);
this.state = '';
this.mainWindow = MainWindow.init(this);
this.events = {
timeUpdated: new Event('timeUpdated'),
startWaiting: new Event('startWaiting'),
stopWaiting: new Event('stopWaiting'),
startLoading: new Event('startLoading'),
stopLoading: new Event('stopLoading'),
toggleTimeMode: new Event('toggleTimeMode'),
changeState: new Event('changeState'),
titleUpdated: new Event('titleUpdated'),
channelCountUpdated: new Event('channelCountUpdated'),
volumeChanged: new Event('volumeChanged'),
balanceChanged: new Event('balanceChanged'),
doubledModeToggled: new Event('doubledModeToggled'),
repeatToggled: new Event('repeatToggled'),
llamaToggled: new Event('llamaToggled'),
close: new Event('close')
};
this.setVolume(options.volume);
this.setBalance(options.balance);
this.loadFromUrl(options.mediaFile.url, options.mediaFile.name);
var skinFile = new MyFile();
skinFile.setUrl(options.skinUrl);
this.setSkin(skinFile);
this._registerListeners();
return this;
},
_registerListeners: function() {
var self = this;
this.windowManager.registerWindow(this.mainWindow);
this.media.addEventListener('timeupdate', function() {
window.dispatchEvent(self.events.timeUpdated);
});
this.media.addEventListener('visualizerupdate', function(analyser) {
self.skin.visualizer.paintFrame(self.visualizerStyle, analyser);
});
this.media.addEventListener('ended', function() {
self.skin.visualizer.clear();
self.setState('stop');
});
this.media.addEventListener('waiting', function() {
window.dispatchEvent(self.events.startWaiting);
});
this.media.addEventListener('canplay', function() {
window.dispatchEvent(self.events.stopWaiting);
});
this.media.addEventListener('play', function() {
self.setState('play');
});
this.media.addEventListener('loadedmetadata', function() {
self.loadMetaData();
});
this.fileInput.onchange = function(e){
self.loadFromFileReference(e.target.files[0]);
};
},
/* Functions */
setState: function(state) {
this.state = state;
window.dispatchEvent(this.events.changeState);
},
getState: function() {
return this.state;
},
getDuration: function() {
return this.media.duration();
},
getTimeRemaining: function() {
return this.media.timeRemaining();
},
getTimeElapsed: function() {
return this.media.timeElapsed();
},
getPercentComplete: function() {
return this.media.percentComplete();
},
getChannelCount: function() {
return this.media.channels();
},
getVolume: function() {
return Math.round(this.media.getVolume() * 100);
},
seekToPercentComplete: function(percent) {
this.media.seekToPercentComplete(percent);
},
toggleTimeMode: function() {
window.dispatchEvent(this.events.toggleTimeMode);
},
previous: function(num) {
// Jump back num tracks
// Not yet supported
},
play: function() {
if(this.getState() === 'play'){
this.media.stop();
}
this.media.play();
this.setState('play');
},
pause: function() {
if(this.getState() === 'pause'){
this.media.play();
}
else if(this.getState() === 'play')
{
this.media.pause();
this.setState('pause');
}
},
stop: function() {
this.media.stop();
this.setState('stop');
},
next: function(num) {
// Jump back num tracks
// Not yet supported
},
// From 0-100
setVolume: function(volume) {
// Ensure volume does not go out of bounds
volume = Math.max(volume, 0);
volume = Math.min(volume, 100);
var percent = volume / 100;
this.media.setVolume(percent);
window.dispatchEvent(this.events.volumeChanged);
},
incrementVolumeBy: function(ammount) {
this.setVolume((this.media.getVolume() * 100) + ammount);
},
toggleDoubledMode: function() {
window.dispatchEvent(this.events.doubledModeToggled);
},
// From -100 to 100
setBalance: function(balance) {
this.media.setBalance(balance);
window.dispatchEvent(this.events.balanceChanged);
},
getBalance: function() {
return this.media.getBalance();
},
seekForwardBy: function(seconds) {
this.media.seekToTime(this.media.timeElapsed() + seconds);
window.dispatchEvent(self.events.timeUpdated);
},
toggleRepeat: function() {
this.media.toggleRepeat();
window.dispatchEvent(this.events.repeatToggled);
},
toggleShuffle: function() {
this.media.toggleShuffle();
this.mainWindow.toggleShuffle();
},
toggleLlama: function() {
window.dispatchEvent(this.events.llamaToggled);
},
close: function() {
window.dispatchEvent(this.events.close);
this.media.stop();
this.setState('stop'); // Currently unneeded
},
openFileDialog: function() {
this.fileInput.click();
},
loadFromFileReference: function(fileReference) {
if(new RegExp("(wsz|zip)$", 'i').test(fileReference.name)) {
var file = new MyFile();
file.setFileReference(fileReference);
this.skin.setSkinByFile(file);
} else {
this.media.setAutoPlay(true);
this.fileName = fileReference.name;
if(this.objectURL) {
URL.revokeObjectURL(this.objectURL);
}
this.objectURL = URL.createObjectURL(fileReference);
this.loadFromUrl(this.objectURL, this.fileName);
}
},
// Used only for the initial load, since it must have a CORS header
loadFromUrl: function(url, fileName) {
this.fileName = fileName ? fileName : url.split("/").pop();
this.media.loadUrl(url);
},
setSkin: function(file) {
this.setLoadingState();
this.skin.setSkinByFile(file, this.unsetLoadingState.bind(this));
},
setLoadingState: function() {
window.dispatchEvent(this.events.startLoading);
},
unsetLoadingState: function() {
window.dispatchEvent(this.events.stopLoading);
},
toggleVisualizer: function() {
if(this.skin.visualizer.style === this.skin.visualizer.NONE) {
this.skin.visualizer.setStyle(this.skin.visualizer.BAR);
} else if(this.skin.visualizer.style === this.skin.visualizer.BAR) {
this.skin.visualizer.setStyle(this.skin.visualizer.OSCILLOSCOPE);
} else if(this.skin.visualizer.style === this.skin.visualizer.OSCILLOSCOPE) {
this.skin.visualizer.setStyle(this.skin.visualizer.NONE);
}
this.skin.visualizer.clear();
},
loadMetaData: function() {
var kbps = "128";
var khz = Math.round(this.media.sampleRate() / 1000).toString();
this.skin.font.setNodeToString(document.getElementById('kbps'), kbps);
this.skin.font.setNodeToString(document.getElementById('khz'), khz);
window.dispatchEvent(this.events.channelCountUpdated);
window.dispatchEvent(this.events.titleUpdated);
window.dispatchEvent(this.events.timeUpdated);
},
/* Helpers */
_timeObject: function(time) {
var minutes = Math.floor(time / 60);
var seconds = time - (minutes * 60);
return [
Math.floor(minutes / 10),
Math.floor(minutes % 10),
Math.floor(seconds / 10),
Math.floor(seconds % 10)
];
},
};
});