diff options
author | Profpatsch <mail@profpatsch.de> | 2024-10-01T01·24+0200 |
---|---|---|
committer | Profpatsch <mail@profpatsch.de> | 2024-10-05T13·49+0000 |
commit | 102c9b30a7ca3b41b0cfcd48f52bdf6c06f259ff (patch) | |
tree | 0af8ed3730dd5ada3c0fb544df93df41d64ba9b1 /users | |
parent | c014e39dfd35915fc19ba28e4c170774d1aad5c0 (diff) |
feat(users/Profpatsch/lyric/ext): add bpm on quantization r/8765
If the bpm header already exists, overwrite it with the new value. Also use an existing header as suggestion. Change-Id: If6431e8056504db437c31313d885b5ba0d0e55d5 Reviewed-on: https://cl.tvl.fyi/c/depot/+/12553 Tested-by: BuildkiteCI Reviewed-by: Profpatsch <mail@profpatsch.de>
Diffstat (limited to 'users')
-rw-r--r-- | users/Profpatsch/lyric/extension/src/extension.ts | 110 |
1 files changed, 102 insertions, 8 deletions
diff --git a/users/Profpatsch/lyric/extension/src/extension.ts b/users/Profpatsch/lyric/extension/src/extension.ts index f09be13d04bd..1272b3c1b80d 100644 --- a/users/Profpatsch/lyric/extension/src/extension.ts +++ b/users/Profpatsch/lyric/extension/src/extension.ts @@ -164,12 +164,6 @@ async function shiftLyricsUp() { /** first ask the user for the BPM of the track, then quantize the timestamps in the active text editor to the closest eighth note based on the given BPM */ async function quantizeLrc() { - const bpm = await timeInputBpm(); - - if (bpm === undefined) { - return; - } - const editor = vscode.window.activeTextEditor; if (!editor) { @@ -179,6 +173,22 @@ async function quantizeLrc() { const ext = new Ext(editor.document); + const startBpmStr = ext.findHeader('bpm')?.value; + let startBpm; + if (startBpmStr !== undefined) { + startBpm = parseInt(startBpmStr, 10); + if (isNaN(startBpm)) { + startBpm = undefined; + } + } + const bpm = await timeInputBpm(startBpm); + + if (bpm === undefined) { + return; + } + + await ext.writeHeader('bpm', bpm.toString()); + const getLine = (line: number) => ({ number: line, range: editor.document.lineAt(line), @@ -214,9 +224,21 @@ async function quantizeLrc() { }); } +// convert the given bpm to miliseconds +function bpmToMs(bpm: number) { + return Math.floor((60 / bpm) * 1000); +} + // Show input boxes in a loop, and record the time between each input, averaging the last 5 inputs over a sliding window, then calculate the BPM of the average -async function timeInputBpm() { - const timeDifferences: number[] = [500, 500, 500, 500, 500]; +async function timeInputBpm(startBpm?: number) { + const startBpmMs = bpmToMs(startBpm ?? 120); + const timeDifferences: number[] = [ + startBpmMs, + startBpmMs, + startBpmMs, + startBpmMs, + startBpmMs, + ]; // assign a weight to the time differences, so that the most recent time differences have more weight const weights = [0.1, 0.1, 0.2, 0.3, 0.3]; @@ -235,6 +257,7 @@ async function timeInputBpm() { const res = await vscode.window.showInputBox({ prompt: `Press enter to record BPM (current BPM: ${calculateBPM()}), enter the final BPM once you know, or press esc to finish`, placeHolder: 'BPM', + value: startBpm !== undefined ? startBpm.toString() : undefined, }); if (res === undefined) { return undefined; @@ -398,6 +421,77 @@ class Ext { const seconds = milliseconds / 1000; return { milliseconds, seconds, text }; } + + // Find a header line of the format + // [header:value] + // at the beginning of the lrc file (before the first empty line) + findHeader(headerName: string) { + for (let line = 0; line < this.document.lineCount; line++) { + const text = this.document.lineAt(line).text; + if (text.trim() === '') { + return; + } + const match = text.match(/^\[(\w+):(.*)\]$/); + if (match && match[1] === headerName) { + return { key: match[1], value: match[2], line: line }; + } + } + } + + // check if the given line is a header line + isHeaderLine(line: string) { + return ( + line.trim() !== '' && + line.match(/^\[(\w+):(.*)\]$/) !== null && + line.match(/^\[\d\d:\d\d.\d+\]/) === null + ); + } + + // write the given header to the lrc file, if the header already exists, update the value + async writeHeader(headerName: string, value: string) { + const header = this.findHeader(headerName); + const editor = findActiveEditor(this.document); + if (!editor) { + return; + } + if (header) { + const lineRange = this.document.lineAt(header.line).range; + await editor.edit(editBuilder => { + editBuilder.replace(lineRange, `[${headerName}: ${value}]`); + }); + } else { + // insert before the first timestamp line if no header is found, or after the last header if there are multiple headers + let insertLine = 0; + let extraNewline = ''; + for (let line = 0; line < this.document.lineCount; line++) { + const text = this.document.lineAt(line).text; + // check if header + if (this.isHeaderLine(text)) { + insertLine = line + 1; + } else if (text.trim() === '') { + insertLine = line; + break; + } else { + insertLine = line; + if (line == 0) { + extraNewline = '\n'; + } + break; + } + } + await editor.edit(editBuilder => { + editBuilder.insert( + new vscode.Position(insertLine, 0), + `[${headerName}: ${value}]\n${extraNewline}`, + ); + }); + } + } +} + +// find an active editor that has the given document opened +function findActiveEditor(document: vscode.TextDocument) { + return vscode.window.visibleTextEditors.find(editor => editor.document === document); } function parseTimestamp(timestamp: string): number { |