马黑黑 发表于 2022-9-23 19:06

花潮LRC在线增强版

<style>
/*
        名称 :花潮LRC在线增强版
        制作 :马黑黑
        更新 :2022年9月23日
        简述 :用于制作加餐格式的花潮LRC,兼容以往帖子模板
        声明 :转载与转用请注明作者与出处
*/
.outer { margin: auto; width: fit-content; height: fit-content; }
.outer h2 { margin: 10px; padding: 0; font: bold 1.5em sans-serif; text-align: center; }
.outer p { margin: 10px; }
.outer input { font: normal 16px sans-serif; outline: none; }
.menu { margin: 0 0 -1px 8px; width: fit-content; height: fit-content; display: flex; position: relative; }
.item { width: 60px; height: 30px; font: normal 1em / 30px sans-serif; text-align: center; background: #fff; border: 1px solid; border-bottom: none; cursor: pointer; }
.item1 { border: none; border-bottom: 1px solid; background: none; }
#slip { width: 50px; text-align: center; }
#lrcText { padding: 10px; width: 720px; height: 400px; font: normal 16px / 26px sans-serif; resize: both; border: 1px solid; outline: none; }
#mUrl { padding: 4px; width: 460px; font-size: 14px; }
#aud, #up { display: none; outline: none; }
#audMsg { display: inline-block; margin-left: 12px; height: 54px; font: normal 1em / 54px sans-serif; }
</style>

<div class="outer">
        <h2>花潮LRC在线增强版</h2>
        <div class="menu">
                <div class="item">制作</div>
                <div class="item item1">转换</div>
                <div class="item item1">微调</div>
        </div>
        <textarea id="lrcText" rows ="18" cols="60" placeholder="原始歌词"></textarea>
        <p>
                <input id="begin" type="button" value=" 制作 " />
                <input id="up" type="button" value=" ↑↑ " />
                <input id="copy" type="button" value=" 复制 " disabled />
                <input id="mUrl" type="text" placeholder="音频地址" value="" />
                <label>误差值 : </label>
                <input id="slip" type="number" placeholder="0" value="0.2" min="0" max="1" step="0.1" />
        </p>
        <p style="display:flex">
                <audio id="aud" controls="controls"></audio>
                <span id="audMsg"></span>
        </p>
</div>

<script>
let items = document.querySelectorAll('.item');
let lrcArr = [], slipNum = , btnName = ['制作','转换','微调'], modeName = ['原始歌词','mm:(.)ss.(:)ms → ss.ms','花潮LRC歌词'], mode = 0, yp, currentIdx, mkidx;
Array.from(items).forEach((ele,key) => {
        ele.onclick = () => {
                ele.className = 'item';
                slip.value = slipNum;
                mode = key;
                begin.value = btnName;
                lrcText.placeholder = modeName;
                lrcText.focus();
                begin.style.display = 'inline-block';
                up.style.display = 'none';
                aud.style.display = audMsg.style.display = 'none';
                if(mode === 0) {
                        lrcText.value = '';
                        items.className = items.className = 'item item1';
                } else if(mode === 1){
                        lrcText.value = '';
                        items.className = items.className = 'item item1';
                } else {
                        items.className = items.className = 'item item1';
                }
        }
});
begin.onclick =() => {
        if(lrcText.value === '' || mUrl.value === '') return false;
        getUrl(mUrl.value);
        aud.style.display = audMsg.style.display = 'inline-block';
        if(mode === 0) {
                lrcArr.length = 0;
                lrcArr = (lrcText.value).trim().split('\n');
                currentIdx = mkidx = 0;
                lrcText.value = 'let lrcAr = [\n';
                mUrl.value = lrcArr;
                begin.style.display = 'none';
                up.style.display = 'inline-block';
                up.disabled = true;
        } else if(mode === 1) {
                if(lrcText.value === '' || mUrl.value === '') return false;
                getUrl(mUrl.value);
                let str = 'let lrcAr = [\n';
                lrcArr.length = 0;
                lrcArr = getLrcMsg(lrcText.value);
                for(x of lrcArr) str += "\t[" + x + ",'" + x + "'],\n";
                str = str.substring(0, str.lastIndexOf(','));
                lrcText.value = str + '\n];';
                lrcText.value = getAr(lrcText.value);
                begin.style.display = 'none';
        } else {
                lrcText.value = getAr(lrcText.value);
                currentIdx = 0;
                lrcText.scrollTop = 0;
                begin.style.display = 'none';
                up.style.display = 'inline-block';
        }
}
up.onclick = () => {
        if(mode === 0) {
                if(currentIdx === lrcArr.length - 1) {
                        lrcText.value += '\t' + mUrl.value.slice(0,-1) + '\n];';
                        lrcText.value = getAr(lrcText.value);
                        up.disabled = true;
                        mUrl.value = yp;
                } else {
                        lrcText.value += '\t' + mUrl.value + '\n';
                        lrcText.scrollTop = lrcText.scrollHeight;
                        if(currentIdx < lrcArr.length - 1) currentIdx ++;
                        mUrl.value = lrcArr;
                }
                mkidx ++;
                audMsg.innerText = audMsg.innerText.replace(/:\s\d+/, ': ' + mkidx);
        } else {
                if(lrcText.value === '' || mUrl.value === '') return false;
                let txtAr = mUrl.value.split('"');
                lrcAr = txtAr.trim();
                lrcAr = txtAr.trim();
                replaceText(currentIdx + 1, lrcText);
        }
        up.disabled = true;
}
copy.onclick = () => {
        lrcText.select();
        document.execCommand('copy');
        mUrl.value = '内容已复制到剪切板';
}
lrcText.onchange = () => copy.disabled = lrcText.value === '' ? true : false;
slip.onchange = () => {
        slipNum = slip.value;
        if(slipNum > 1) slipNum = 1;
        if(slipNum < -1) slipNum = -1;
}
aud.addEventListener('pause', () =>{
        if(mode === 0) {
                let time = (aud.currentTime - slipNum).toFixed(2);
                mUrl.value = `[${time},"${lrcArr}"],`;
        }
        up.disabled = mode > 0 ? false : (mkidx >= lrcArr.length ? true : false);
});
aud.addEventListener('timeupdate', () =>{
        let done = mode === 0 ? (' [ 完成量: ' + mkidx + ' / ' + lrcArr.length + ' ]') : (mode === 2 ? (' [ 当前操作: ' + (currentIdx + 1) + ' / ' + lrcAr.length + ' ] ') : '');
        audMsg.innerText = aud.currentTime.toFixed(2) + ' | ' + (aud.duration || 0).toFixed(2) +done ;
        if(mode === 2){
                for(j=0; j<lrcAr.length; j++){
                        if(aud.currentTime >= lrcAr - slipNum){
                                currentIdx = j;
                                let time = (aud.currentTime - lrcAr).toFixed(1);
                                mUrl.value = lrcAr + ' ' + lrcAr + ' ' + time;
                        }
                }
        }
});
let getLrcMsg = (text) => {
        let lrcAr = [];
        let calcRule = ;
        for(x of text.split('\n')) {
                let ar = [];
                let re = /\d+[\.:]\d+([\.:]\d+)?/g;
                let geci = x.replace(re,'');
                if(geci) {
                        geci = geci.replace(/[\[\]\'\"\t,]s?/g,'');
                        let time = x.match(re);
                        if(time != null) {
                                for(y of time) {
                                        let tmp = y.match(/\d+/g);
                                        let sec = 0;
                                        for(z in tmp) sec += tmp * calcRule;
                                        sec -= slipNum;
                                        if(sec <0) sec = 0.00;
                                        ar = ;
                                        lrcAr.push(ar);
                                }
                        }
                }
        }
        lrcAr.sort((a,b)=> a - b);
        return lrcAr;
}
let getUrl = (url) => {
        let reg = /\.?:wav|mp3|wma|ogg|aac|ape|flac$/;
        if(reg.test(url.toLowerCase())) yp = url.trim();
        aud.src = yp;
}
let getAr = (text) => {
        let re = /.*lrcAr/;
        let jsStr = text.replace(re, 'lrcAr');
        (new Function(jsStr))();
        if(typeof(lrcAr) === 'undefined') {
                alert('数据非法!请确保 lrcAr 数组完整出现在文本框中');
                throw new Error('lrcAr数组加载失败');
        }
        for(key in lrcAr) {
                let gap = parseInt(key) === lrcAr.length - 1 ? 6 : (lrcAr - lrcAr).toFixed(1);
                lrcAr = '"' + lrcAr+'"';
                lrcAr = gap;
        }
        return 'let lrcAr = [' + lrcAr.map((item) => '\n\t[' + item + ']') + '\n];';
}
let replaceText = (line,ele) => {
        ele.value ='let lrcAr = [' + lrcAr.map((item) => `\n\t[${item},${item},${item || 'x'}]`) + '\n];';
        let arr = ele.value.split('\n');
        let pos1 = 0, pos2 = 0;
        for(j = 0; j < line; j ++) {
                pos1 += arr.length + 1;
        }
        pos1 += 1;
        pos2 = pos1 + arr.length;
        ele.setSelectionRange(pos1,pos2);
        ele.scrollTop = line * 26 - 26;
        ele.focus();
}
</script>

马黑黑 发表于 2022-9-23 19:06

本帖最后由 马黑黑 于 2022-9-23 19:22 编辑

花潮LRC在线增强版使用说明



与之前的花潮LRC在线相比,增强版集成了支持模拟逐字同步的制作模块。现简要说明使用方法:

一、工作模式

程序分三个工作模式:① 制作、② 转换与 ③ 微调。

重要声明:三个模式顺向切换时仅有模式 ③ 保留前面模式的工作成果,其他顺向、逆向切换均被视为将进入新的工作模式,大文本框里的内容将会被自动清空!

二、操作说明

① 制作

制作模式需要粘贴原始即纯歌词到大文本框、音频URL地址到小文本框,完成后单击小文本框左边的“制作”按钮,开始进入制作环节,此时请点击下方audio播放器的播放按钮进行音频播放,并通过播放器的暂停功能获得当前歌词的起唱时间,时间会先存放在小文本框内,如果不合适可以回退重新获取或手动在小文本框里修改,接着单击双向上箭头按钮,歌词上屏,然后依此方法操作余下歌词,直至所有歌词操作完毕。当最后一句歌词上屏,程序会自动给每句歌词加入另外一个时间节点,它是拿下一句的起始时间减去上一句的起始时间所得的结果,并不准确,需要后续处理。

上述工作完成后,可单击“复制”按钮进行复制,大文本框的内容会复制到剪切板。

此模式工作过程中,修改大、小文本框均有效。

② 转换

转换模式可以将合法的常规lrc歌词转为花潮格式的lrc歌词(与“制作”模式的结果一致)。切换到该模式后,粘贴待转换lrc歌词信息到大文本框,同时将歌曲URL粘贴到小文本框,单击小文本框左边的“制作”按钮。如果没有意外,程序会瞬间完成转换工作,并且每句歌词都加入了模拟逐字同步所需的时间信息。同样的,并不准确。

③ 微调

微调模式主要针对模拟逐字同步效果而设计,但也可以微调每句歌词的起唱时间,两者都是修改小文本框里的数字。起唱时间放在歌词的前面,只能通过手工修改;用于模拟逐字同步的时间信息放在歌词的后面,播放器播放时会自动修改、直至按下暂停按钮,如果不理想,可回退重来,或手动修改。觉得当句歌词的时间信息满意后,单击双向上箭头按钮,小文本框的信息会覆盖大文本框同句歌词的信息。

微调模式下,如果手动修改数字,请确保小文本框的基本格式不受破坏,歌词两头的小角双引号一定要保留!另外,微调模式下,在程序中修改歌词内容是不被接受的,在大文本框中修改任何信息也将是无效的。

微调工作可以不按顺序调整歌词时间信息,当前唱到哪一句,程序会自动去操作那一句。

最后提一下:增强版制作的歌词数组适用于以前的无逐字模拟功能的音画帖模板。

其他未尽事宜请回复讨论,更多相关说明的将在楼下以 Q&A 的方式延伸介绍。

马黑黑 发表于 2022-9-23 19:07

本帖最后由 马黑黑 于 2022-9-23 20:24 编辑

答疑汇总

一、之前没有逐字同步信息的花潮LRC歌词数组可以通过此程序转换成“加餐”数组吗?

可以。切换到“微调”工作模式,将旧格式的花潮lrc歌词数组完整粘贴到大文本框,将音频URL地址粘贴到小文本框,然后单击小文本框左边的“微调”按钮,在大文本框里即可看到数组已经转换成了“加餐”模式。

二、有时候点击工作按钮后程序没有任何响应,这是什么原因?

大、小文本框没有内容时,单击小文本框左边的工作按钮无效。另外,个别按钮在特定工作模式下设置了一些保护模式,例如微调模式工作环节里,双向上箭头会出现暂时处于禁用的状态,需要通过点击播放器的暂停按钮才得以激活、重新回到工作状态,而在制作模式下,制作完毕后双向上箭头就会处于不可用状态,直至开启一曲的制作工作;又如“复制”按钮,大文本框 内容为空时不可用。

红影 发表于 2022-9-23 20:02

黑黑辛苦了,现在有制作,有转换,还有微调,对歌词的操作算是完美无缺了{:4_199:}

小辣椒 发表于 2022-9-23 20:19

黑黑辛苦,小辣椒还得好好学习一下

马黑黑 发表于 2022-9-23 20:22

小辣椒 发表于 2022-9-23 20:19
黑黑辛苦,小辣椒还得好好学习一下

晚上好。这个,用起来并不难,但建议读一下使用说明。

小辣椒 发表于 2022-9-23 20:26

马黑黑 发表于 2022-9-23 20:22
晚上好。这个,用起来并不难,但建议读一下使用说明。

好的,我先完成昨天的作业{:4_173:}

寒冬残荷 发表于 2022-9-23 20:29

呵呵,太深奥了,没有操作成功。
纯歌词:

唱出歌儿嗨起来
作词 李秉笙
作曲 高音敏子
演唱 高音敏子/许志刚
你正在赶路 我在爬坡
人生就是一首歌一呀一首歌
起起落落悲喜 悲喜都有
曲曲折折快乐 快乐度过
你正在翻山 我在趟河
人生就是一首歌一呀一首歌
坎坎坷坷共同 共同闯过
风风雨雨一起 一起坚守
人生就是一首歌一呀一首歌
酸甜苦辣唱着过唱呀唱着过
月圆月缺 人有离合
唱出快乐好生活好呀好生活
人生就是一首歌一呀一首歌
得失成败唱着过唱呀唱着过
天有阴晴,潮有涨落
唱着歌儿好幸福好呀好幸福
风雨过后 就有彩虹
寒冬尽头有春色有呀有春色
只要你执着 执着朝前走
酸甜苦辣都是歌 都是歌
艰难困苦 都是财富
一张白纸绘蓝图绘呀绘蓝图
只要你坚强 坚强不退缩
青春年华莫错过 莫错过
人生就是一首歌一呀一首歌
酸甜苦辣唱着过唱呀唱着过
月圆月缺 人有离合
唱出快乐好生活好呀好生活
人生就是一首歌一呀一首歌
得失成败唱着过唱呀唱着过
天有阴晴,潮有涨落
唱着歌儿好幸福好呀好幸福
天有阴晴,潮有涨落
唱着歌儿好幸福好呀好幸福
唱着歌儿好幸福好呀好幸福

音乐地址:http://url.amp3a.com/kuwo.php/54238887.mp3

按说明输入完后,点制作,没有结果。

马黑黑 发表于 2022-9-23 20:59

寒冬残荷 发表于 2022-9-23 20:29
呵呵,太深奥了,没有操作成功。
纯歌词:



点制作仅是开启了制作环境,要一句一句听、暂停、上屏等系列操作。制作歌词,不可能一键完成,除非找到原始lrc歌词,用转换完成(然后微调还得一句一句来)

马黑黑 发表于 2022-9-23 20:59

小辣椒 发表于 2022-9-23 20:26
好的,我先完成昨天的作业

没问题

寒冬残荷 发表于 2022-9-23 21:17

马黑黑 发表于 2022-9-23 20:59
点制作仅是开启了制作环境,要一句一句听、暂停、上屏等系列操作。制作歌词,不可能一键完成,除非找到原 ...

哦,我有LRC歌词呀。成功了,成功转得您的播放器的格式了。

let lrcAr = [
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
        ,
       
];

马黑黑 发表于 2022-9-23 21:43

寒冬残荷 发表于 2022-9-23 21:17
哦,我有LRC歌词呀。成功了,成功转得您的播放器的格式了。

let lrcAr = [


不错的

醉美水芙蓉 发表于 2022-9-23 22:07

马黑黑 发表于 2022-9-23 22:08

醉美水芙蓉 发表于 2022-9-23 22:07
又来了个音画高手了!

何许人也?

醉美水芙蓉 发表于 2022-9-23 22:09

马黑黑 发表于 2022-9-23 22:25

醉美水芙蓉 发表于 2022-9-23 22:09
寒冬残荷

这个我知道,我问的不是这个网名

马黑黑 发表于 2022-10-3 09:15

小鱼儿 发表于 2022-10-3 08:42
厉害的,国庆节快乐

小鱼儿是哪方神圣呢
{:4_190:}

马黑黑 发表于 2022-10-3 09:29

小鱼儿 发表于 2022-10-3 09:27
我是天线宝宝

啊?酱紫呀?迪西还是拉拉?小波还是丁丁?

马黑黑 发表于 2022-10-3 09:30

小鱼儿 发表于 2022-10-3 09:30
最漂亮的那个

不同人眼里,漂亮是不同的审美。有人觉得拉拉漂亮,也有人认为拉拉长的不咋的

马黑黑 发表于 2022-10-3 10:15

小鱼儿 发表于 2022-10-3 09:36
反正是最好看的那个

这就不好说了,四选一就随便吧
页: [1] 2 3
查看完整版本: 花潮LRC在线增强版