Skip to content

Commit

Permalink
lyrics implementation rework
Browse files Browse the repository at this point in the history
  • Loading branch information
spessasus committed Dec 28, 2024
1 parent 991d66d commit a36b9ec
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 144 deletions.
8 changes: 3 additions & 5 deletions src/spessasynth_lib/sequencer/sequencer.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ export class Sequencer
onSongChange = {};
/**
* Fires on text event
* @type {function}
* @param data {Uint8Array} the data text
* @param type {number} the status byte of the message (the meta status byte)
* @param lyricsIndex {number} if the text is a lyric, the index of the lyric in midiData.lyrics, otherwise -1
*/
onTextEvent;
/**
Expand Down Expand Up @@ -419,13 +421,9 @@ export class Sequencer
break;

case WorkletSequencerReturnMessageType.textEvent:
/**
* @type {[Uint8Array, number]}
*/
let textEventData = messageData;
if (this.onTextEvent)
{
this.onTextEvent(textEventData[0], textEventData[1]);
this.onTextEvent(...(messageData));
}
break;

Expand Down
13 changes: 12 additions & 1 deletion src/spessasynth_lib/sequencer/worklet_sequencer/process_event.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,18 @@ export function _processEvent(event, trackIndex)
case messageTypes.cuePoint:
case messageTypes.instrumentName:
case messageTypes.programName:
this.post(WorkletSequencerReturnMessageType.textEvent, [event.messageData, statusByteData.status]);
let lyricsIndex = -1;
if (statusByteData.status === messageTypes.lyric)
{
lyricsIndex = Math.min(
this.midiData.lyrics.indexOf(event.messageData) + 1,
this.midiData.lyrics.length - 1
);
}
this.post(
WorkletSequencerReturnMessageType.textEvent,
[event.messageData, statusByteData.status, lyricsIndex]
);
break;

case messageTypes.midiPort:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const WorkletSequencerMessageType = {
export const WorkletSequencerReturnMessageType = {
midiEvent: 0, // [...midiEventBytes<number>]
songChange: 1, // [midiData<MidiData>, songIndex<number>, isAutoPlayed<boolean>]
textEvent: 2, // [messageData<number[]>, statusByte<number]
textEvent: 2, // [messageData<number[]>, statusByte<number>, lyricsIndex<number>]
timeChange: 3, // newAbsoluteTime<number>
pause: 4, // no data
getMIDI: 5, // midiData<MIDI>
Expand Down
18 changes: 9 additions & 9 deletions src/spessasynth_lib/synthetizer/worklet_processor.min.js

Large diffs are not rendered by default.

46 changes: 23 additions & 23 deletions src/website/js/sequencer_ui/lyrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { supportedEncodings } from "../utils/encodings.js";
import { messageTypes } from "../../../spessasynth_lib/midi_parser/midi_message.js";
import { AssManager } from "../utils/ass_manager/ass_manager.js";

const ACTUAL_FONT_SIZE = parseFloat(getComputedStyle(document.body).fontSize);

/**
* @this {SequencerUI}
*/
Expand All @@ -17,9 +15,8 @@ export function createLyrics()
* title: HTMLHeadingElement,
* text: {
* main: HTMLParagraphElement
* highlight: HTMLSpanElement,
* gray: HTMLSpanElement,
* other: HTMLDivElement
* other: HTMLDivElement,
* separateLyrics: HTMLSpanElement[]
* },
* selector: HTMLSelectElement
* }}
Expand Down Expand Up @@ -62,14 +59,6 @@ export function createLyrics()
text.classList.add("lyrics_text");
mainLyricsDiv.appendChild(text);

const currentLyrics = document.createElement("span");
currentLyrics.classList.add("lyrics_text_highlight");
text.appendChild(currentLyrics);

const allLyrics = document.createElement("span");
allLyrics.classList.add("lyrics_text_gray");
text.appendChild(allLyrics);

// display for other texts
const otherTextWrapper = document.createElement("details");
const sum = document.createElement("summary");
Expand Down Expand Up @@ -113,11 +102,10 @@ export function createLyrics()


this.lyricsElement.text = {
highlight: currentLyrics,
gray: allLyrics,
main: text,
other: otherText,
subtitleButton: subtitleUpload
subtitleButton: subtitleUpload,
separateLyrics: []
};
this.lyricsElement.mainDiv = mainLyricsDiv;
this.lyricsElement.selector = encodingSelector;
Expand All @@ -127,15 +115,27 @@ export function createLyrics()

/**
* @this {SequencerUI}
* @param {number} index
*/
export function setLyricsText(text)
export function setLyricsText(index)
{

const highlight = this.lyricsElement.text.highlight;
const gray = this.lyricsElement.text.gray;
gray.innerText = this.currentLyricsString.replace(text, "");
highlight.innerText = text;
this.lyricsElement.text.main.scrollTo(0, highlight.offsetHeight - (ACTUAL_FONT_SIZE * 5));
this.lyricsIndex = index;
for (let i = 0; i < index; i++)
{
this.lyricsElement.text.separateLyrics[i].classList.remove("lyrics_text_gray");
this.lyricsElement.text.separateLyrics[i].classList.add("lyrics_text_highlight");
}
for (let i = index; i < this.lyricsElement.text.separateLyrics.length; i++)
{
this.lyricsElement.text.separateLyrics[i].classList.remove("lyrics_text_highlight");
this.lyricsElement.text.separateLyrics[i].classList.add("lyrics_text_gray");
}
// scroll to the last element
this.lyricsElement.text.separateLyrics[index].scrollIntoView({
behavior: "smooth",
block: "center",
inline: "center"
});
}

/**
Expand Down
97 changes: 76 additions & 21 deletions src/website/js/sequencer_ui/sequencer_ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@ class SequencerUI
this.decoder = new TextDecoder(this.encoding);
this.infoDecoder = new TextDecoder(this.encoding);
this.hasInfoDecoding = false;
// the currently displayed (highlighted) lyrics text
this.text = "";
/**
* the currently displayed (highlighted) lyrics event index
* @type {number}
*/
this.lyricsIndex = 0;
this.requiresTextUpdate = false;
this.rawLyrics = [];
/**
* @type {{type: messageTypes, data: Uint8Array}[]}
*/
Expand All @@ -54,10 +56,14 @@ class SequencerUI
this.locale = locale;
this.currentSongTitle = "";
/**
* @type {Uint8Array}
* @type {Uint8Array[]}
*/
this.currentLyrics = [];
/**
* Current lyrics decoded as strings
* @type {string[]}
*/
this.currentLyrics = new Uint8Array(0);
this.currentLyricsString = "";
this.currentLyricsString = [];
this.musicModeUI = musicMode;
this.renderer = renderer;
}
Expand Down Expand Up @@ -136,20 +142,15 @@ class SequencerUI

/**
* @param text {ArrayBuffer}
* @param useInfoEncoding {boolean}
* @returns {string}
*/
decodeTextFix(text, useInfoEncoding = false)
decodeTextFix(text)
{
let encodingIndex = 0;
while (true)
{
try
{
if (useInfoEncoding)
{

}
return this.decoder.decode(text);
}
catch (e)
Expand All @@ -173,9 +174,8 @@ class SequencerUI
this.createNavigatorHandler();
this.updateTitleAndMediaStatus();

this.seq.onTextEvent = (data, type) =>
this.seq.onTextEvent = (data, type, lyricsIndex) =>
{
const text = this.decodeTextFix(data.buffer);
switch (type)
{
default:
Expand All @@ -193,17 +193,13 @@ class SequencerUI
return;

case messageTypes.lyric:
this.text += text;
this.rawLyrics.push(...data);
this.setLyricsText(this.text);
this.setLyricsText(lyricsIndex);
break;
}
};

this.seq.addOnTimeChangeEvent(() =>
{
this.text = "";
this.rawLyrics = [];
this.seqPlay(false);
}, "sequi-time-change");

Expand Down Expand Up @@ -271,10 +267,14 @@ class SequencerUI
this.infoDecoder = new TextDecoder(encoding);
}
this.updateOtherTextEvents();
this.text = this.decodeTextFix(new Uint8Array(this.rawLyrics).buffer);
// update all spans with the new encoding
this.lyricsElement.text.separateLyrics.forEach((span, index) =>
{
span.innerText = this.decodeTextFix(this.currentLyrics[index].buffer);
});
this.lyricsElement.selector.value = encoding;
this.updateTitleAndMediaStatus(false);
this.setLyricsText(this.text);
this.setLyricsText(this.lyricsIndex);
}

createControls()
Expand Down Expand Up @@ -494,6 +494,61 @@ class SequencerUI
{
setInterval(this._updateInterval.bind(this), 100);
}

loadLyricData()
{
// load lyrics
this.currentLyrics = this.seq.midiData.lyrics;
this.currentLyricsString = this.currentLyrics.map(l => this.decodeTextFix(l.buffer));
if (this.currentLyrics.length === 0)
{
this.currentLyricsString = [this.locale.getLocaleString("locale.sequencerController.lyrics.noLyrics")];
}
else
{
// perform a check for double lyrics:
// for example in some midi's, every lyric event is duplicated:
// "He's " turns into two "He's " and another "He's " event
// if that's the case for all events in the current lyrics, set duplicates to "" to avoid index errors
let isDoubleLyrics = true;
// note: the first lyrics is usually a control character
for (let i = 1; i < this.currentLyricsString.length - 1; i += 2)
{
const first = this.currentLyricsString[i];
const second = this.currentLyricsString[i + 1];
// note: newline should be skipped
if (first.trim() === "" || second.trim() === "")
{
i -= 1;
continue;
}
if (first.trim() !== second.trim())
{
isDoubleLyrics = false;
break;
}
}
if (isDoubleLyrics)
{
this.currentLyricsString = this.currentLyricsString.map((_, i) => (i % 2 === 0 ? "" : this.currentLyricsString[i]));
}

}
// create lyrics as separate spans
// clear previous lyrics
this.lyricsElement.text.main.innerHTML = "";
this.lyricsElement.text.separateLyrics = [];
for (const lyric of this.currentLyricsString)
{
const span = document.createElement("span");
span.innerText = lyric;
// gray (not highlighted) text
span.classList.add("lyrics_text_gray");
this.lyricsElement.text.main.appendChild(span);
this.lyricsElement.text.separateLyrics.push(span);
}

}
}

SequencerUI.prototype.createNavigatorHandler = createNavigatorHandler;
Expand Down
13 changes: 1 addition & 12 deletions src/website/js/sequencer_ui/title_and_media_status.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,7 @@ export function updateTitleAndMediaStatus(cleanOtherTextEvents = true)
}
if (this.seq.midiData)
{
// combine lyrics into one binary array
const lyricsArray = this.seq.midiData.lyrics;
this.currentLyrics = new Uint8Array(lyricsArray.reduce((sum, cur) => sum + cur.length, 0));
let offset = 0;
for (const lyr of lyricsArray)
{
this.currentLyrics.set(lyr, offset);
offset += lyr.length;
}
this.currentLyricsString = this.decodeTextFix(this.currentLyrics.buffer) || this.locale.getLocaleString(
"locale.sequencerController.lyrics.noLyrics");
this.setLyricsText("");
this.loadLyricData();
if (cleanOtherTextEvents)
{
this.rawOtherTextEvents = [];
Expand Down
Loading

0 comments on commit a36b9ec

Please sign in to comment.