ultrastar2ass
Version:
Convert Ultrastar karaoke files to ASS files
200 lines • 7.54 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class UltrastarParser {
config;
meta;
track;
track_duet;
first_pass;
constructor(config) {
this.config = config;
this.meta = {
title: null,
artist: null,
mp3: null,
bpm: null,
gap: 0,
duet: false
};
this.track = [];
this.track_duet = [];
this.first_pass = true;
}
parse(file) {
const lines = file.replace(/\r+/g, '').split('\n');
let sentenceID = 1;
let trackID = 1;
let relative = false;
let beatsCount = 0;
let beatDuration = null;
let currentStart = null;
let previousEnd = null;
let syllables = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i].replace(/^\s*/, '');
if (line.replace(/\s*/g, '').length == 0) {
continue;
}
if (line[0] == '#') {
let matches = line.match(/(\w+):(.+)/);
if (matches == null || matches.length == 0) {
continue;
}
matches = matches[0].split(':');
const keyword = matches[0].toLowerCase();
let value = matches[1];
if (keyword == 'bpm' || keyword == 'gap') {
value = parseFloat(value.replace(',', '.'));
}
if (keyword == 'bpm') {
beatDuration = (60000) / (value * 4);
}
if (keyword == 'gap') {
if (this.config.offset) {
value += this.config.offset;
}
currentStart = value;
}
if (keyword == 'relative' && value.toLowerCase() == 'yes') {
relative = true;
}
if (value != '') {
this.meta[keyword] = value;
}
continue;
}
if ((line == 'P2' || line == 'P 2' || line[0] == 'E') && syllables.length > 0) {
const sentence = this.makeSentence(sentenceID, syllables, currentStart, null, previousEnd);
if (currentStart != null) {
currentStart = null;
}
if (trackID == 1) {
this.track.push(sentence);
}
else {
this.track_duet.push(sentence);
}
sentenceID++;
syllables = [];
}
if (line == 'P2' || line == 'P 2') {
trackID = 2;
this.meta.duet = true;
continue;
}
if ([':', '*', 'F', 'R', 'G'].indexOf(line[0]) > -1) {
let matches = line.match(/^[:*FRG] (-?\d+) (\d+) (-?\d+) (.+)/);
if (matches == null || matches.length == 0) {
continue;
}
const syllable = {
type: 'normal'
};
syllable.text = matches[0].split(' ').splice(4).join(' ');
matches = matches[0].split(' ').splice(1, 3);
if (!relative) {
syllable.start = Math.floor(this.meta.gap + parseInt(matches[0]) * beatDuration);
}
else {
syllable.start = Math.floor(this.meta.gap + (beatsCount + parseInt(matches[0])) * beatDuration);
}
const matchesAfter = lines[i + 1].replace(/^\s*/, '').match(/^[:*FRG-] (-?\d+)/);
syllable.duration = Math.floor((matchesAfter ? (parseInt(matchesAfter[1]) - parseInt(matches[0])) : parseInt(matches[1])) * beatDuration);
syllable.end = syllable.start + syllable.duration;
syllable.pitch = parseInt(matches[2]);
if (this.meta.gap < 0 && syllable.start < 0) {
syllable.start = 0;
syllable.duration = syllable.end;
}
if (line[0] == '*' || line[0] == 'G') {
syllable.type = 'golden';
}
else if (line[0] == 'F') {
syllable.type = 'freestyle';
}
else if (line[0] == 'R') {
syllable.type = 'rap';
}
beatsCount += parseInt(matches[1]);
syllables.push(syllable);
}
if (line[0].indexOf('-') > -1 && syllables.length > 0) {
let matches = line.match(/^- ?(\d+)\s?(\d+)?/);
if (matches == null || matches.length == 0) {
continue;
}
matches = matches[0].split(' ');
if (matches[0].length > 1) {
matches[0] = matches[0].substr(1, matches[0].length);
}
else {
matches = matches.splice(1);
}
let currentEnd = null;
if (!relative) {
currentEnd = Math.floor(this.meta.gap + parseInt(matches[0]) * beatDuration);
}
else {
currentEnd = Math.floor(this.meta.gap + (beatsCount + parseInt(matches[0])) * beatDuration);
}
if (this.meta.gap < 0 && currentStart < 0) {
currentStart = 0;
}
else if (null !== currentStart && (0 < syllables.length) && this.first_pass) {
currentStart = Math.max(currentStart, syllables[0].start);
}
const sentence = this.makeSentence(sentenceID, syllables, currentStart, currentEnd, previousEnd);
if (currentStart != null) {
currentStart = null;
}
previousEnd = sentence.end;
this.first_pass = false;
if (trackID == 1) {
this.track.push(sentence);
}
else {
this.track_duet.push(sentence);
}
sentenceID++;
syllables = [];
}
if (line[0] == 'E') {
break;
}
}
return {
meta: this.meta,
track: this.track,
track_duet: (this.track_duet.length > 0) ? this.track_duet : null
};
}
makeSentence(id, syllables, start, end, previousEnd) {
const sentence = {
id: id,
start: syllables[0].start,
end: syllables[syllables.length - 1].end
};
if (this.config.syllable_precision) {
sentence.syllables = syllables;
}
else {
sentence.text = '';
for (let j = 0; j < syllables.length; j++) {
sentence.text += syllables[j].text;
}
}
if (start != null) {
sentence.start = start;
}
else if (previousEnd != null && this.config.start_as_previous_end) {
sentence.start = previousEnd;
}
if (end != null) {
sentence.end = end;
}
sentence.duration = sentence.end - sentence.start;
return sentence;
}
}
exports.default = UltrastarParser;
//# sourceMappingURL=ultrastar.js.map