diff --git a/src/basic/index.mjs b/src/basic/index.mjs index ecaaec9f..db4c3069 100644 --- a/src/basic/index.mjs +++ b/src/basic/index.mjs @@ -284,6 +284,7 @@ let RootDisplay = class extends CustomEventSource { eventPassThru(upThis.device, upThis, "channelmax"); eventPassThru(upThis.device, upThis, "channelreset"); eventPassThru(upThis.device, upThis, "screen"); + eventPassThru(upThis.device, upThis, "metacommit"); eventPassThru(upThis.device, upThis, "efxreverb"); eventPassThru(upThis.device, upThis, "efxchorus"); eventPassThru(upThis.device, upThis, "efxdelay"); diff --git a/src/cambiare/index.mjs b/src/cambiare/index.mjs index 7f0bac27..a38c3e3e 100644 --- a/src/cambiare/index.mjs +++ b/src/cambiare/index.mjs @@ -21,6 +21,44 @@ const modeNames = { "s90es": "Yamaha S90 ES", "motif": "Yamaha Motif ES" }; +const metaNames = { + "Copyrite": "Copyright", + "Cmn.Text": "Text", + "C.Lyrics": "Lyrics", + "C.Marker": "Marker", + "CuePoint": "Cue Point", + "Instrmnt": "Instrument", + "Kar.Info": "Kar Info", + "Kar.Mode": "Karaoke", + "Kar.Lang": "Language", + "KarTitle": "Kar Title", + "KarLyric": "Kar Lyrics", + "Kar.Ver.": "Kar Version", + "SGLyrics": "SG Lyrics", + "TrkTitle": "Title", + "XfSngDte": "XF Date", + "XfSngRgn": "XF Region", + "XfSngCat": "XF Category", + "XfSongBt": "XF Beat", + "XfSngIns": "XF Instr.", + "XfSngVoc": "XF Vocalist", + "XfSngCmp": "XF Composer", + "XfSngLrc": "XF Lyricist", + "XfSngArr": "XF Arranger", + "XfSngPer": "XF Perform.", + "XfSngPrg": "XF Program.", + "XfSngTag": "XF Tags", + "XfKarLng": "XF Lang.", + "XfKarNme": "XF Name", + "XfKarCmp": "XK Composer", + "XfKarLrc": "XK Lyricist", + "XfKarArr": "XK Arranger", + "XfKarPer": "XK Perform.", + "XfKarPrg": "XK Program." +}, metaBlocklist = [ + "XfSongBt", + "XfSngIns" +]; let createElement = function (tag, classes, details = {}) { let target = document.createElement(tag); @@ -32,7 +70,7 @@ let createElement = function (tag, classes, details = {}) { l?.constructor && (target.style.left = `${l}px`); w?.constructor && (target.style.width = `${w}px`); h?.constructor && (target.style.height = `${h}px`); - i?.constructor && (target.innerText = i); + i?.constructor && (target.appendChild(document.createTextNode(i))); a?.constructor && (target.style.textAlign = a); return target; }; @@ -57,6 +95,10 @@ let classOn = function (target, classes) { }; let Cambiare = class extends RootDisplay { + #metaMaxLine = 20; + #metaAmend = false; + #metaType = ""; + #metaLastLine; #maxPoly = 0; #clockSource; #visualizer; @@ -65,7 +107,7 @@ let Cambiare = class extends RootDisplay { #sectInfo = {}; #sectMark = {}; #sectPart = {}; - #sectMeta = {}; + #sectMeta; #resizerSrc() { let aspectRatio = self.innerWidth / self.innerHeight; let targetZoom = 1; @@ -185,13 +227,35 @@ let Cambiare = class extends RootDisplay { ]); // Begin inserting the marker section upThis.#sectMark.root = createElement("div", ["sect-mark"]); + upThis.#sectMark.left = createElement("div", ["sect-mark-left", "boundary"], {t: 0, l: 0}); + upThis.#sectMark.right = createElement("div", ["sect-mark-right", "boundary"], {t: 0, l: 960}); canvasElement.appendChild(upThis.#sectMark.root); + mountElement(upThis.#sectMark.root, [ + upThis.#sectMark.left, + upThis.#sectMark.right + ]); + mountElement(upThis.#sectMark.left, [ + createElement("span", ["field", "field-key"], {t: 0, l: 0, w: 26, h: 33, i: "CH"}), + createElement("span", ["field", "field-key"], {t: 0, l: 30, w: 49, h: 33, i: "Voice"}), + createElement("span", ["field", "field-key", "mark-send-title"], {t: 2, l: 164, w: 25, h: 18, i: "Send"}), + createElement("span", ["field", "field-label", "mark-send-param"], {t: 16, l: 146, w: 58, h: 16, i: "VEMRCDBP12", a: "center"}), + createElement("span", ["field", "field-key"], {t: 0, l: 214, w: 35, h: 33, i: "Pan"}), + createElement("span", ["field", "field-key"], {t: 0, l: 256, w: 45, h: 33, i: "Note"}) + ]); + mountElement(upThis.#sectMark.right, [ + createElement("span", ["field", "field-key"], {t: 0, l: 0, w: 26, h: 33, i: "CH"}), + createElement("span", ["field", "field-key"], {t: 0, l: 30, w: 49, h: 33, i: "Voice"}), + createElement("span", ["field", "field-key", "mark-send-title"], {t: 2, l: 164, w: 25, h: 18, i: "Send"}), + createElement("span", ["field", "field-label", "mark-send-param"], {t: 16, l: 146, w: 58, h: 16, i: "VEMRCDBP12", a: "center"}), + createElement("span", ["field", "field-key"], {t: 0, l: 214, w: 35, h: 33, i: "Pan"}), + createElement("span", ["field", "field-key"], {t: 0, l: 256, w: 45, h: 33, i: "Note"}) + ]); // Begin inserting the channel section upThis.#sectPart.root = createElement("div", ["sect-part"]); canvasElement.appendChild(upThis.#sectPart.root); // Begin inserting the meta section - upThis.#sectMeta.root = createElement("div", ["sect-meta"]); - canvasElement.appendChild(upThis.#sectMeta.root); + upThis.#sectMeta = createElement("div", ["sect-meta"]); + canvasElement.appendChild(upThis.#sectMeta); // Opportunistic value refreshing upThis.addEventListener("mode", (ev) => { upThis.#sectInfo.mode.innerText = `${modeNames[ev.data]}`; @@ -223,6 +287,33 @@ let Cambiare = class extends RootDisplay { upThis.addEventListener("efxinsert0", (ev) => { upThis.#sectInfo.insert.innerText = upThis.getEfx(ev.data); }); + upThis.addEventListener("metacommit", (ev) => { + let meta = ev.data; + //console.debug(meta); + if (upThis.#metaAmend && meta.type == upThis.#metaType && upThis.#metaLastLine) { + // Amend the last line + upThis.#metaLastLine.childNodes[0].data += meta.data; + } else if (meta.data?.length && metaBlocklist.indexOf(meta.type) == -1) { + // Commit a new line + let metaLineRoot = createElement("div", ["meta-line"]), + metaLineType = createElement("span", ["field", "field-key", "meta-type"], {i: metaNames[meta.type] || meta.type}); + if (meta.mask) { + metaLineType.style.display = "none"; + }; + upThis.#metaLastLine = createElement("span", ["field", "meta-data"], {i: meta.data}); + upThis.#sectMeta.appendChild(metaLineRoot); + mountElement(metaLineRoot, [ + metaLineType, + upThis.#metaLastLine + ]); + while (upThis.#sectMeta.children.length > upThis.#metaMaxLine) { + upThis.#sectMeta.children[0].remove(); + }; + }; + upThis.#metaAmend = meta.amend || false; + upThis.#metaType = meta.type || ""; + upThis.#sectMeta.scrollTop = upThis.#sectMeta.scrollHeight - 140; + }); upThis.dispatchEvent("mode", "?"); upThis.dispatchEvent("mastervolume", 100); upThis.dispatchEvent("tempo", 120); @@ -256,6 +347,13 @@ let Cambiare = class extends RootDisplay { }; upThis.addEventListener("reset", () => { upThis.#maxPoly = 0; + upThis.#metaAmend = false; + upThis.#metaType = ""; + upThis.#metaLastLine = null; + let list = upThis.#sectMeta.children; + for (let pointer = list.length - 1; pointer >= 0; pointer --) { + list[pointer].remove(); + }; }); }; }; diff --git a/test/css/cambiare.css b/test/css/cambiare.css index 7994135f..56af5abc 100644 --- a/test/css/cambiare.css +++ b/test/css/cambiare.css @@ -16,7 +16,7 @@ div.cambiare > div.cambiare-container { div.cambiare-canvas { --background-color: #222; --foreground-color: #fff; - --accent-color: #e9e1ff; + --accent-color: #ccc; } div.cambiare-canvas.cambiare-mode-xg, div.cambiare-canvas.cambiare-mode-ns5r, div.cambiare-canvas.cambiare-mode-x5d, div.cambiare-canvas.cambiare-mode-05rw, div.cambiare-canvas.cambiare-mode-k11, div.cambiare-canvas.cambiare-mode-s90es, div.cambiare-canvas.cambiare-mode-motif { --accent-color: #9efaa0; @@ -51,11 +51,11 @@ div.cambiare-canvas.debug > div { div.cambiare-canvas span { display: inline-block; } -div.cambiare-canvas span.field { +div.cambiare-canvas span.field, div.cambiare-canvas div.boundary { position: absolute; cursor: default; } -div.cambiare-canvas.debug span.field { +div.cambiare-canvas.debug span.field, div.cambiare-canvas.debug div.boundary { outline: 1px #fff7 solid; } div.cambiare-canvas span.field-label { @@ -67,23 +67,54 @@ div.cambiare-canvas span.field-key { } div.cambiare-canvas > div.sect-info { - top: 1px; + top: 0px; height: 67px; } div.cambiare-canvas > div.sect-mark { - top: 70px; + top: 69px; height: 33px; } div.cambiare-canvas > div.sect-part { - top: 104px; + top: 103px; height: 832px; } div.cambiare-canvas > div.sect-meta { width: 934px; - top: 938px; - height: 135px; + top: 937px; + height: 137px; + overflow: hidden; } + div.cambiare-canvas > div.sect-mark > div { width: 936px; height: 33px; } +div.cambiare-canvas > div.sect-mark span.mark-send-title { + font-size: 14px; +} +div.cambiare-canvas > div.sect-mark span.mark-send-param { + font-size: 12px; +} +div.cambiare-canvas.cambiare-port1 div.sect-mark-right, div.cambiare-canvas.cambiare-compact div.sect-mark-right { + display: none; +} + +div.cambiare-canvas > div.sect-meta > div.meta-line { + /*position: relative;*/ + margin: 0 0 3px; + height: 25px; + /*overflow: hidden;*/ + line-height: 25px; +} +div.cambiare-canvas > div.sect-meta > div.meta-line > span { + font-size: 20px; +} +div.cambiare-canvas > div.sect-meta span.meta-type { + width: 88px; + text-align: right; +} +div.cambiare-canvas > div.sect-meta span.meta-data { + /*width: 840px;*/ + left: 94px; + white-space: pre; +}