Editor now has waveform

master
William Toohey 10 years ago
parent eadb5c8795
commit 463cbc0ebc
  1. 135
      src/js/HuesEditor.js

@ -22,6 +22,9 @@
(function(window, document) {
"use strict";
var WAVE_PIXELS_PER_SECOND = 100;
var WAVE_HEIGHT_PIXELS = 20;
function HuesEditor(core) {
this.buildEditSize = 80; // pixels, including header
this.buildEdit = null;
@ -35,6 +38,14 @@ function HuesEditor(core) {
this.undoBuffer = [];
this.redoBuffer = [];
// For rendering the waveform
this.buildWave = null;
this.loopWave = null;
this.buildWaveBuff = null;
this.loopWaveBuff = null;
this.waveContext = null;
this.waveCanvas = null;
// for storing respacks created with "new"
this.respack = null;
// when we're actually following the playing song
@ -119,6 +130,8 @@ HuesEditor.prototype.resize = function(noHilightCalc) {
this.hilightHeight = hilight.clientHeight;
this.editorWidth = this.loopEdit._beatmap.clientWidth;
this.root.removeChild(hilight);
this.waveCanvas.width = this.waveCanvas.clientWidth;
}
}
@ -185,6 +198,7 @@ HuesEditor.prototype.onNewSong = function(song) {
if(song == this.song) {
// Because you can "edit current" before it loads
this.updateInfo();
this.updateWaveform();
} else {
this.linked = false;
// Clear beat hilight
@ -192,6 +206,9 @@ HuesEditor.prototype.onNewSong = function(song) {
this.loopEdit._hilight.innerHTML = "█";
this.buildEdit._hilight.className = "beat-hilight hidden";
this.loopEdit._hilight.className = "beat-hilight hidden";
// Clear waveform
this.buildWave = null;
this.loopWave = null;
}
}
}
@ -277,6 +294,7 @@ HuesEditor.prototype.loadAudio = function(editor) {
this.core.updateBeatLength();
// We may have to go backwards in time
this.core.recalcBeatIndex();
this.updateWaveform();
}).catch(error => {
console.log(error);
alert("Couldn't load song! Is it a LAME encoded MP3?");
@ -293,9 +311,13 @@ HuesEditor.prototype.removeAudio = function(editor) {
this.reflow(editor, "");
// Is the loop playable?
if(this.song.sound) {
this.core.soundManager.playSong(this.song, true, true);
this.core.soundManager.playSong(this.song, true, true)
.then(() => {
this.updateWaveform();
});
} else {
this.core.soundManager.stop();
this.updateWaveform();
}
}
@ -357,6 +379,7 @@ HuesEditor.prototype.newSong = function(song) {
this.linked = true;
this.updateInfo();
this.updateWaveform();
}
HuesEditor.prototype.updateInfo = function() {
@ -937,11 +960,114 @@ HuesEditor.prototype.uiCreateControls = function() {
HuesEditor.prototype.uiCreateVisualiser = function() {
// TODO placeholder
var waveDiv = document.createElement("div");
waveDiv.id = "edit-waveform";
this.root.appendChild(waveDiv);
var wave = document.createElement("canvas");
wave.id = "edit-waveform";
wave.height = WAVE_HEIGHT_PIXELS;
this.root.appendChild(wave);
this.waveCanvas = wave;
this.waveContext = wave.getContext("2d");
this.core.addEventListener("frame", this.drawWave.bind(this));
};
HuesEditor.prototype.updateWaveform = function() {
if(this.buildWaveBuff != this.core.soundManager.buildup) {
this.buildWaveBuff = this.core.soundManager.buildup;
this.buildWave = this.renderWave(this.buildWaveBuff, this.core.soundManager.buildLength);
}
if(this.loopWaveBuff != this.core.soundManager.loop) {
this.loopWaveBuff = this.core.soundManager.loop;
this.loopWave = this.renderWave(this.loopWaveBuff, this.core.soundManager.loopLength);
}
}
HuesEditor.prototype.renderWave = function(buffer, length) {
if(!buffer) {
return null;
}
// The individual wave section
var wave = document.createElement("canvas");
var waveContext = wave.getContext("2d");
wave.height = WAVE_HEIGHT_PIXELS;
wave.width = Math.floor(WAVE_PIXELS_PER_SECOND * length);
var samplesPerPixel = Math.floor(buffer.sampleRate / WAVE_PIXELS_PER_SECOND);
var waveData = [];
for(var i = 0; i < buffer.numberOfChannels; i++) {
waveData.push(buffer.getChannelData(i));
}
// Half pixel offset makes things look crisp
var pixel = 0.5;
waveContext.strokeStyle = "black";
var halfHeight = WAVE_HEIGHT_PIXELS/2;
for(var i = 0; i < buffer.length; i += samplesPerPixel) {
var min = 0, max = 0;
for(var j = 0; j < samplesPerPixel && i + j < buffer.length; j++) {
for(var chan = 0; chan < waveData.length; chan++) {
var sample = waveData[chan][i+j];
if(sample > max) max = sample;
if(sample < min) min = sample;
}
}
var maxPix = Math.floor(halfHeight + max * halfHeight);
// Min is negative, addition is correct
var minPix = Math.floor(halfHeight + min * halfHeight);
waveContext.beginPath();
waveContext.moveTo(pixel, maxPix);
waveContext.lineTo(pixel, minPix);
waveContext.stroke();
pixel+=1;
}
return wave;
}
HuesEditor.prototype.drawWave = function() {
var width = this.waveCanvas.width;
var now = this.core.soundManager.currentTime();
var span = width / WAVE_PIXELS_PER_SECOND / 2;
var minTime = now - span;
var maxTime = now + span;
this.waveContext.clearRect(0, 0, width, WAVE_HEIGHT_PIXELS);
if(this.buildWave && minTime < 0) {
var bLen = this.core.soundManager.buildLength;
var center = Math.floor((now + bLen) / bLen * this.buildWave.width);
this.drawOneWave(this.buildWave, center, width);
}
if(this.loopWave && maxTime > 0) {
var loopLen = this.core.soundManager.loopLength;
var clampedNow = (minTime % loopLen) + span;
var center = Math.floor(clampedNow / loopLen * this.loopWave.width);
this.drawOneWave(this.loopWave, center, width);
var clampedNext = (maxTime % loopLen) - span;
// We've looped and need to draw 2 things
if(clampedNext < clampedNow) {
var center = Math.floor(clampedNext / loopLen * this.loopWave.width);
this.drawOneWave(this.loopWave, center, width);
}
}
// trackbar
this.waveContext.strokeStyle = "red";
this.waveContext.beginPath();
this.waveContext.moveTo(width/2, 0);
this.waveContext.lineTo(width/2, WAVE_HEIGHT_PIXELS);
this.waveContext.stroke();
}
HuesEditor.prototype.drawOneWave = function(wave, center, width) {
this.waveContext.drawImage(wave,
center - width/2, 0, // source x/y
width, WAVE_HEIGHT_PIXELS, // source width/height
0, 0, // dest x/y
width, WAVE_HEIGHT_PIXELS); // dest width/height
}
HuesEditor.prototype.generateXML = function() {
if(!this.song) {
return null;
@ -1019,7 +1145,6 @@ HuesEditor.prototype.copyXML = function() {
} else {
alert("Copy failed! Try saving instead");
}
}
window.HuesEditor = HuesEditor;

Loading…
Cancel
Save