good progress

This commit is contained in:
2021-05-23 16:55:57 +02:00
parent 90ea2174db
commit fb7ade79cb
12 changed files with 671 additions and 189 deletions

View File

@ -1,8 +1,7 @@
main { display: flex; flex-wrap: nowrap; height: 100vh; height: -webkit-fill-available; max-height: 100vh; overflow-x: auto; overflow-y: hidden; }
.btn-group-xs > .btn, .btn-xs { padding: .25rem .4rem; font-size: .875rem; line-height: .5; border-radius: .2rem; }
.playlist { margin: 2em 0; }
.playlist .playlist-time-scale { height: 30px; }
.playlist .playlist-tracks { background: #e0eff1; }
@ -31,10 +30,12 @@
.playlist .controls { background: white; text-align: center; border: 1px solid black; border-radius: 0.2rem; }
.playlist .controls .track-header { overflow: hidden; color: black; height: 26px; display: flex; align-items: center; justify-content: space-between; padding: 0 0.2rem; font-size: 0.65rem; margin-bottom: -10px; }
.playlist .controls .track-header { overflow: hidden; color: black; height: 18px; display: flex; align-items: center; justify-content: space-between; padding: 0 0.2rem; font-size: 0.65rem; margin-bottom: -10px; }
.playlist .controls .track-header button { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; }
.playlist .controls .track-header span { margin-top: -8px; }
.playlist .controls input[type="range"] { display: inline-block; width: 90%; }
.playlist .controls .user-info { word-break: break-word; color: black; align-items: center; justify-content: space-between; padding: 0 0.2rem; font-size: 0.65rem; margin-top: -5px; }
.playlist .controls .info { word-break: break-word; color: black; align-items: center; justify-content: space-between; padding: 0 0.2rem; font-size: 0.65rem; margin-top: -10px; }

View File

@ -1,7 +1,7 @@
var playlist = WaveformPlaylist.init({
container: document.getElementById("playlist"),
timescale: true,
state: 'cursor',
state: 'select',
samplesPerPixel: 16384,
zoomLevels: [2048, 4096, 8192, 16384],
@ -12,23 +12,58 @@ var playlist = WaveformPlaylist.init({
stereoPan: false,
}
},
waveHeight: 96,
waveHeight: 80,
});
function onFinishedLoading() {
//initialize the WAV exporter.
playlist.initExporter();
const tracks = playlist.tracks;
for (var i = 0; i < tracks.length; i++) {
playlist.collapseTrack(tracks[i], {collapsed: true});
}
const highlight = window.location.hash.split('#').filter(Boolean);
for(var i = 0; i < highlight.length; i++) {
const guid = highlight[i];
for (var j = 0; j < tracks.length; j++) {
if (tracks[j].name == guid) {
tracks[j].setWaveOutlineColor('#d1e7dd');
}
}
for (var j = 0; j < g_chats.length; j++) {
if (g_chats[j].player_guid == guid) {
chatRows[j].classList.add("table-active");
}
}
}
playlist.drawRequest();
}
function collapseTrack(guid, collapse) {
const tracks = playlist.tracks;
for (var i = 0; i < tracks.length; i++) {
if (guid === null || tracks[i].name == guid) {
playlist.collapseTrack(tracks[i], {collapsed: collapse});
if (guid != null) {
break;
}
}
}
}
function updateTrackInfo(guid, info) {
var tracks = $(".playlist-tracks").children();
const tracks = playlist.tracks;
for (var i = 0; i < tracks.length; i++) {
var track = tracks[i].firstChild;
var trackGuid = track.firstChild.getElementsByTagName('span')[0].innerText;
var userInfoElem = track.lastChild;
if (userInfoElem.className != 'user-info') {
userInfoElem = document.createElement('label');
userInfoElem.className = 'user-info';
track.appendChild(userInfoElem);
}
if (guid === null || trackGuid == guid) {
userInfoElem.innerText = info;
if (guid === null || tracks[i].name == guid) {
tracks[i].setInfo(info);
if (guid != null) {
break;
}
}
}
}
@ -56,25 +91,144 @@ function clockFormat(seconds, decimals) {
return result;
}
var lastChunkIdx = 0;
var lastUpdateTime = 0;
function updateTime(time) {
$time.html(clockFormat(time, 3));
audioPos = time;
if (time < lastUpdateTime) {
lastChunkIdx = 0;
}
var tick = time / g_session.tickinterval;
var silenceTicks = 0;
for (var i = 0; i < g_session.silence_chunks.length; i++) {
var chunk = g_session.silence_chunks[i];
for (; lastChunkIdx < g_session.silence_chunks.length; lastChunkIdx++) {
var chunk = g_session.silence_chunks[lastChunkIdx];
if (tick > chunk[0]) {
silenceTicks += chunk[1];
silenceTicks = chunk[1];
} else {
break;
}
}
var tickedTime = (tick + silenceTicks) * g_session.tickinterval;
tick += silenceTicks;
var tickedTime = tick * g_session.tickinterval;
$time_.html(clockFormat(tickedTime, 3));
lastUpdateTime = time;
if (lastChunkIdx > 0) {
lastChunkIdx -= 1;
}
onTick(tick, tickedTime);
}
updateTime(audioPos);
function onEvent(idx, event) {
var update = 0;
if (event.event == "player_connect") {
collapseTrack(event.player_guid, false);
updateTrackInfo(event.player_guid, event.data.name);
update += 1;
}
else if (event.event == "player_disconnect") {
collapseTrack(event.player_guid, true);
update += 1;
}
else if (event.event == "player_changename") {
updateTrackInfo(event.player_guid, event.data.newname);
update += 1;
}
return update;
}
updateTime(audioPos);
var chatBox = $("div#chat");
var chatRows = $("div#chat>table>tbody").children();
var lastPrimaryRow = undefined;
function onChat(idx, chat) {
if (idx == lastPrimaryRow) {
return 0;
}
if (lastPrimaryRow != undefined) {
chatRows[lastPrimaryRow].classList.remove("table-primary");
}
chatRows[idx].classList.add("table-primary");
if (autoScrollChat) {
chatRows[idx].scrollIntoViewIfNeeded();
}
lastPrimaryRow = idx;
return 1;
}
var lastTick = undefined;
var lastChatIdx = 0;
var lastEventIdx = 0;
function onTick(tick, time) {
var update = 0;
if (tick == lastTick) {
return;
}
if (tick < lastTick) {
lastChatIdx = 0;
lastEventIdx = 0;
}
for (; lastEventIdx < g_events.length; lastEventIdx++) {
const event = g_events[lastEventIdx];
if (tick > event.tick) {
update += onEvent(lastEventIdx, event);
} else {
break;
}
}
if (lastEventIdx > 0) {
lastEventIdx -= 1;
}
for (; lastChatIdx < g_chats.length; lastChatIdx++) {
const chat = g_chats[lastChatIdx];
if (tick < chat.tick) {
if (lastChatIdx > 0) {
lastChatIdx -= 1;
}
update += onChat(lastChatIdx, chat);
break;
}
}
lastTick = tick;
if (update) {
playlist.drawRequest();
}
}
function gameTimeToAudio(tick) {
tick += 1;
var silenceTicks = 0;
for (var i = 0; i < g_session.silence_chunks.length; i++) {
const chunk = g_session.silence_chunks[i];
if ((tick - chunk[1]) > chunk[0]) {
silenceTicks = chunk[1];
} else {
break;
}
}
return (tick - silenceTicks) * g_session.tickinterval;
}
function jumpToGameTick(tick) {
var audioTime = gameTimeToAudio(tick);
playlist.seek(audioTime);
playlist.drawRequest();
updateTime(audioTime);
}
$container.on("click", ".btn-play", function() {
ee.emit("play");
@ -100,7 +254,7 @@ $container.on("click", ".btn-fast-forward", function() {
ee.emit("fastforward");
});
//zoom buttons
// zoom buttons
$container.on("click", ".btn-zoom-in", function() {
ee.emit("zoomin");
});
@ -109,21 +263,102 @@ $container.on("click", ".btn-zoom-out", function() {
ee.emit("zoomout");
});
// download
var downloadUrl = undefined;
var downloadName = undefined;
$container.on("click", ".btn-download", function () {
if (downloadName) {
return;
}
downloadName = g_session.demoname;
if (playlist.isSegmentSelection()) {
const segment = playlist.getTimeSelection();
downloadName += "-" + clockFormat(segment.start).replaceAll(':', '-') + "_" + clockFormat(segment.end).replaceAll(':', '-');
}
downloadName += ".wav";
ee.emit('startaudiorendering', 'wav');
});
ee.on('audiorenderingfinished', function (type, data) {
if (type != 'wav') {
return;
}
if (downloadUrl) {
window.URL.revokeObjectURL(downloadUrl);
}
downloadUrl = window.URL.createObjectURL(data);
const tempLink = document.createElement('a');
tempLink.style.display = 'none';
tempLink.href = downloadUrl;
tempLink.setAttribute('download', downloadName);
document.body.appendChild(tempLink);
tempLink.click();
document.body.removeChild(tempLink);
downloadName = undefined;
});
$container.on("input change", ".master-gain", function(e){
ee.emit("mastervolumechange", e.target.value);
});
$container.find(".master-gain").change();
$container.on("change", ".automatic-scroll", function(e){
ee.emit("automaticscroll", $(e.target).is(':checked'));
var autoScrollVoice = false;
$container.on("change", "#autoscroll_voice", function(e){
autoScrollVoice = $(e.target).is(':checked');
ee.emit("automaticscroll", autoScrollVoice);
});
$container.find(".automatic-scroll").change();
$container.find("#autoscroll_voice").change();
var autoScrollChat = false;
$container.on("change", "#autoscroll_chat", function(e){
autoScrollChat = $(e.target).is(':checked');
});
$container.find("#autoscroll_chat").change();
ee.on("timeupdate", updateTime);
function onFinishedLoading() {
updateTrackInfo(null, "name");
function getParent(el) {
var parent = el.parentNode;
if (parent === document) {
return document;
} else if (parent.offsetHeight < parent.scrollHeight || parent.offsetWidth < parent.scrollWidth) {
return parent;
} else {
return getParent(parent);
}
}
if (!Element.prototype.scrollIntoViewIfNeeded) {
Element.prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) {
centerIfNeeded = arguments.length === 0 ? true : !!centerIfNeeded;
var parent = getParent(this),
parentComputedStyle = window.getComputedStyle(parent, null),
parentBorderTopWidth = parseInt(parentComputedStyle.getPropertyValue('border-top-width')),
parentBorderLeftWidth = parseInt(parentComputedStyle.getPropertyValue('border-left-width')),
overTop = this.offsetTop - parent.offsetTop < parent.scrollTop,
overBottom = (this.offsetTop - parent.offsetTop + this.clientHeight - parentBorderTopWidth) > (parent.scrollTop + parent.clientHeight),
overLeft = this.offsetLeft - parent.offsetLeft < parent.scrollLeft,
overRight = (this.offsetLeft - parent.offsetLeft + this.clientWidth - parentBorderLeftWidth) > (parent.scrollLeft + parent.clientWidth),
alignWithTop = overTop && !overBottom;
if ((overTop || overBottom) && centerIfNeeded) {
parent.scrollTop = this.offsetTop - parent.offsetTop - parent.clientHeight / 2 - parentBorderTopWidth + this.clientHeight / 2;
}
if ((overLeft || overRight) && centerIfNeeded) {
parent.scrollLeft = this.offsetLeft - parent.offsetLeft - parent.clientWidth / 2 - parentBorderLeftWidth + this.clientWidth / 2;
}
if ((overTop || overBottom || overLeft || overRight) && !centerIfNeeded) {
this.scrollIntoView(alignWithTop);
}
};
}

View File

@ -106,7 +106,8 @@ var WaveformPlaylist =
volume: true,
stereoPan: true,
collapse: true,
remove: true
remove: true,
info: true
}
},
colors: {
@ -3465,6 +3466,7 @@ var WaveformPlaylist =
var tracks = audioBuffers.map(function (audioBuffer, index) {
var info = trackList[index];
var name = info.name || 'Untitled';
var infostr = info.info || undefined;
var start = info.start || 0;
var states = info.states || {};
var fadeIn = info.fadeIn;
@ -3487,6 +3489,7 @@ var WaveformPlaylist =
track.src = info.src;
track.setBuffer(audioBuffer);
track.setName(name);
track.setInfo(infostr);
track.setEventEmitter(_this3.ee);
track.setEnabledStates(states);
track.setCues(cueIn, cueOut);
@ -3586,15 +3589,24 @@ var WaveformPlaylist =
if (this.isRendering) {
return;
}
this.isRendering = true;
this.offlineAudioContext = new OfflineAudioContext(2, 44100 * this.duration, 44100);
var duration = this.duration;
var startTime = 0;
var endTime = 0;
if (this.isSegmentSelection()) {
var segment = this.getTimeSelection();
startTime = segment.start;
endTime = segment.end;
duration = endTime - startTime;
}
this.offlineAudioContext = new OfflineAudioContext(1, 44100 * duration, 44100);
var currentTime = this.offlineAudioContext.currentTime;
this.tracks.forEach(function (track) {
track.setOfflinePlayout(new _Playout2.default(_this4.offlineAudioContext, track.buffer));
track.schedulePlay(currentTime, 0, 0, {
track.schedulePlay(currentTime, startTime, endTime, {
shouldPlay: _this4.shouldTrackPlay(track),
masterGain: 1,
isOffline: true
@ -3615,7 +3627,8 @@ var WaveformPlaylist =
_this4.exportWorker.postMessage({
command: 'init',
config: {
sampleRate: 44100
sampleRate: 44100,
stereo: false
}
});
@ -3633,7 +3646,7 @@ var WaveformPlaylist =
// send the channel data from our buffer to the worker
_this4.exportWorker.postMessage({
command: 'record',
buffer: [audioBuffer.getChannelData(0), audioBuffer.getChannelData(1)]
buffer: [audioBuffer.getChannelData(0)]
});
// ask the worker for a WAV
@ -5905,7 +5918,7 @@ var WaveformPlaylist =
if (audioData.byteLength > 16) {
var view = new DataView(audioData);
var wanted = "DEMOPUSHEADER_V1";
var wanted = "DEMOPUSHEADER_V2";
var success = true;
for (var i = 0, n = 16; i < n; i++) {
var c = view.getUint8(i);
@ -5942,74 +5955,54 @@ var WaveformPlaylist =
var _this2 = this;
this.setStateChange(STATE_DECODING);
var parsed = [];
var sampleRate = 0;
var numSamples = 0;
var channels = 1;
var promises = [];
var view = new DataView(demopusData);
var ofs = 16; // skip header
var channels = 1;
var sampleRate = view.getUint32(ofs, true);
ofs += 4;
var numSamples = Number(view.getBigUint64(ofs, true));
ofs += 8;
// output sample rate != input sample rate
numSamples *= this.ac.sampleRate / sampleRate;
var audioBuffer = this.ac.createBuffer(channels, numSamples, this.ac.sampleRate);
while (ofs < demopusData.byteLength) {
var header = view.getUint8(ofs);
ofs += 1;
var samplesOfs = Number(view.getBigUint64(ofs, true));
ofs += 8;
samplesOfs *= this.ac.sampleRate / sampleRate;
if (header == 0x02) {
// opus
var dataLen = Number(view.getBigUint64(ofs, true));
ofs += 8;
var opusData = demopusData.slice(ofs, ofs + dataLen);
ofs += dataLen;
var promise = this.ac.decodeAudioData(opusData, function (audioBuffer) {
return audioBuffer;
}, function (err) {
if (err === null) {
// Safari issues with null error
return Error('MediaDecodeAudioDataUnknownContentType');
} else {
return err;
}
});
parsed.push(promise);
} else if (header == 0x03) {
// silence
var samples = Number(view.getBigUint64(ofs, true));
ofs += 8;
parsed.push(samples);
} else if (header == 0x01) {
// info
sampleRate = view.getUint32(ofs, true);
ofs += 4;
numSamples = Number(view.getBigUint64(ofs, true));
ofs += 8;
} else if (header == 0x04) {
// done
if (ofs >= demopusData.byteLength) {
break;
}
var dataLen = view.getUint32(ofs, true);
ofs += 4;
var opusData = demopusData.slice(ofs, ofs + dataLen);
ofs += dataLen;
var promise = this.ac.decodeAudioData(opusData, function (decoded) {
var buf = decoded.getChannelData(0);
audioBuffer.copyToChannel(buf, 0, this);
return decoded.length;
}.bind(samplesOfs), function (err) {
if (err === null) {
// Safari issues with null error
return Error('MediaDecodeAudioDataUnknownContentType');
} else {
return err;
}
});
promises.push(promise);
}
return new Promise(function (resolve, reject) {
// output sample rate != input sample rate
numSamples *= _this2.ac.sampleRate / sampleRate;
var audioBuffer = _this2.ac.createBuffer(channels, numSamples, _this2.ac.sampleRate);
return Promise.all(parsed).then(function (result) {
var curSamples = 0;
for (var i = 0; i < result.length; i++) {
var elem = result[i];
if (typeof elem == "number") {
curSamples += elem * (_this2.ac.sampleRate / sampleRate);
} else {
var buf = elem.getChannelData(0);
audioBuffer.copyToChannel(buf, 0, curSamples);
curSamples += elem.length;
}
}
Promise.all(promises).then(function (result) {
_this2.setStateChange(STATE_FINISHED);
resolve(audioBuffer);
});
@ -6505,6 +6498,7 @@ var WaveformPlaylist =
_classCallCheck(this, _class);
this.name = 'Untitled';
this.info = undefined;
this.customClass = undefined;
this.waveOutlineColor = undefined;
this.gain = 1;
@ -6532,6 +6526,11 @@ var WaveformPlaylist =
value: function setName(name) {
this.name = name;
}
}, {
key: 'setInfo',
value: function setInfo(info) {
this.info = info;
}
}, {
key: 'setCustomClass',
value: function setCustomClass(className) {
@ -6980,6 +6979,10 @@ var WaveformPlaylist =
}
})]));
}
if (widgets.info) {
controls.push((0, _h2.default)('label.info', [this.info]));
}
}
return (0, _h2.default)('div.controls', {
@ -7134,6 +7137,7 @@ var WaveformPlaylist =
start: this.startTime,
end: this.endTime,
name: this.name,
info: this.info,
customClass: this.customClass,
cuein: this.cueIn,
cueout: this.cueOut,
@ -10408,14 +10412,18 @@ var WaveformPlaylist =
var recBuffersL = [];
var recBuffersR = [];
var sampleRate = void 0;
var stereo = void 0;
function init(config) {
sampleRate = config.sampleRate;
stereo = config.stereo;
}
function record(inputBuffer) {
recBuffersL.push(inputBuffer[0]);
recBuffersR.push(inputBuffer[1]);
if (stereo) {
recBuffersR.push(inputBuffer[1]);
}
recLength += inputBuffer[0].length;
}
@ -10434,15 +10442,14 @@ var WaveformPlaylist =
}
function encodeWAV(samples) {
var mono = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
var numChannels = stereo ? 2 : 1;
var buffer = new ArrayBuffer(44 + samples.length * 2);
var view = new DataView(buffer);
/* RIFF identifier */
writeString(view, 0, 'RIFF');
/* file length */
view.setUint32(4, 32 + samples.length * 2, true);
view.setUint32(4, 36 + samples.length * 2, true);
/* RIFF type */
writeString(view, 8, 'WAVE');
/* format chunk identifier */
@ -10452,13 +10459,13 @@ var WaveformPlaylist =
/* sample format (raw) */
view.setUint16(20, 1, true);
/* channel count */
view.setUint16(22, mono ? 1 : 2, true);
view.setUint16(22, numChannels, true);
/* sample rate */
view.setUint32(24, sampleRate, true);
/* byte rate (sample rate * block align) */
view.setUint32(28, sampleRate * 4, true);
/* byte rate (sample rate * channel count * bytes per sample) */
view.setUint32(28, sampleRate * numChannels * 2, true);
/* block align (channel count * bytes per sample) */
view.setUint16(32, 4, true);
view.setUint16(32, numChannels * 2, true);
/* bits per sample */
view.setUint16(34, 16, true);
/* data chunk identifier */
@ -10500,8 +10507,11 @@ var WaveformPlaylist =
function exportWAV(type) {
var bufferL = mergeBuffers(recBuffersL, recLength);
var bufferR = mergeBuffers(recBuffersR, recLength);
var interleaved = interleave(bufferL, bufferR);
var interleaved = bufferL;
if (stereo) {
var bufferR = mergeBuffers(recBuffersR, recLength);
interleaved = interleave(bufferL, bufferR);
}
var dataview = encodeWAV(interleaved);
var audioBlob = new Blob([dataview], { type: type });

File diff suppressed because one or more lines are too long