花潮LRC在线(6.25)测试版
本帖最后由 马黑黑 于 2022-6-25 12:06 编辑 <br /><br /><style>.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; }
.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: 736px; height: 400px; font: normal 16px / 26px sans-serif; resize: both; border: 1px solid; outline: none; }
#mUrl { padding: 4px; width: 440px; font-size: 14px; }
#aud { display: none; outline: none; }
#audMsg { display: inline-block; margin-left: 12px; height: 54px; font: normal 1em / 54px sans-serif; }
#up, #copy { display: none; }
#check, #invert { width: 100px; }
</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=" 复制 " />
<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" />
<input id="check" type="button" value="lrc效果测试" disabled />
<input id="invert" type="button" value="lrc转换" style="display: none" />
</p>
<p style="display:flex">
<audio id="aud" controls="controls"></audio>
<span id="audMsg"></span>
</p>
</div>
<script>
//更新时间 2022.6.24
let begin = document.querySelector('#begin'), copy = document.querySelector('#copy'),
up = document.querySelector('#up'), check = document.querySelector('#check'),
invert = document.querySelector('#invert'), lrcText = document.querySelector('#lrcText'),
slip = document.querySelector('#slip'), audMsg = document.querySelector('#audMsg'),
mUrl = document.querySelector('#mUrl'), items = document.querySelectorAll('.item');
let lrcArr = [], slipNum = , mode = 0, yp, idx, mkidx;
//模式切换
Array.from(items).forEach((ele,key) => {
ele.onclick = () => {
slip.value = slipNum;
ele.className = 'item';
mode = key;
if(mode == 0) { //制作模式
lrcText.placeholder = '原始歌词';
items.className = items.className = 'item item1';
check.disabled = true;
begin.disabled = false;
check.style.display = 'inline-block';
invert.style.display = 'none';
} else if(mode == 1){ //转换模式
lrcText.placeholder = 'mm:(.)ss.(:)ms → ss.ms';
items.className = items.className = 'item item1';
check.disabled = true;
check.style.display = 'none';
begin.disabled = true;
invert.style.display = 'inline-block';
} else { //检验模式
lrcText.placeholder = '花潮lrc歌词同步测试';
items.className = items.className = 'item item1';
check.disabled = false;
check.style.display = 'inline-block';
begin.disabled = true;
invert.style.display = 'none';
}
}
});
//开始制作
begin.onclick =() => {
if(lrcText.value == '' || mUrl.value == '') return false;
getUrl(mUrl.value);
lrcArr.length = 0;//清空数组
idx = mkidx = 0;
lrcArr = (lrcText.value).trim().split('\n');
lrcText.value = 'let lrcAr = [\n';
mUrl.value = lrcArr;
begin.style.display = 'none';
up.style.display = 'inline-block';
aud.style.display = 'inline-block';
}
//lrc上屏
up.onclick = () => {
if(idx == lrcArr.length - 1) {//最后一句
lrcText.value += '\t' + mUrl.value.slice(0,-1) + '\n];';
up.style.display = 'none';
copy.style.display = 'inline-block';
mUrl.value = aud.src;
} else {
lrcText.value += '\t' + mUrl.value + '\n';
}
lrcText.scrollTop = lrcText.scrollHeight;
if(idx < lrcArr.length - 1) idx ++;
mkidx ++;
audMsg.innerText = audMsg.innerText.replace(/\/\s\d+/,'/ ' + mkidx);
mUrl.value = lrcArr;
}
//复制
copy.onclick = () => {
lrcText.select();
document.execCommand('copy');
mUrl.value = '已复制到剪切板';
copy.style.display = 'none';
begin.style.display = 'inline-block';
}
//lrc效果测试
check.onclick = () => {
if(lrcText.value == '' || mUrl.value == '') return false;
getUrl(mUrl.value);
lrcArr.length = 0; //清空数组
idx = 0; //重置数组标识
let ar = lrcText.value.split('\n');
for(j=0; j<ar.length; j++) {
let str = ar.replace(/\['|'],|']|\["|"],|"]|\t|\s/g,'');
let tmpar = str.split("','");
if(tmpar.length > 1) lrcArr.push(tmpar);
}
aud.style.display = 'block';
aud.src = mUrl.value;
aud.play();
}
//转换
invert.onclick = () => {
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];';
}
//误差值改变
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}','${rep(lrcArr)}'],`;
}
});
//进度监听
aud.addEventListener('timeupdate', () =>{
audMsg.innerText = aud.duration + ' | ' + aud.currentTime + (mode == 0 ? ' [ 完成量: ' + lrcArr.length + ' / ' + mkidx + ' ]' : '');
if(mode == 2){ //检测模式
let tt = aud.currentTime ;
for(j=0; j<lrcArr.length; j++){
if(tt >= lrcArr - slipNum){
mUrl.value = lrcArr;
selectText(lrcArr,lrcText);
if(lrcText.scrollTop < lrcText.scrollHeight) lrcText.scrollTop = j * lrcText.scrollHeight / (lrcArr.length + 2);
}
}
}
});
//播放结束监听
aud.addEventListener('ended', () => mUrl.value = yp);
//处理小角引号
let rep = (str) =>str.replace(/\'/g,"\'");
//转换函数 mm:(.)ss:(.)ms → ss.ms
function getLrcMsg(text) {
let lrcAr = []; //返回的数组
let calcRule = ; //不同数位的分钟运算规则
for(x of text.split('\n')) {
let ar = [];
let re = /\d+[\.:]\d+([\.:]\d+)?/g; //匹配 mm:(.)ss:(.)ms
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 selectText = (txt,ele) =>{
let all = ele.value,
start = all.search(txt),
end = start + txt.length;
ele.setSelectionRange(start,end);
ele.focus();
}
function getUrl(url) {
let reg = /\.?:wav|mp3|wma|ogg|aac|ape|flac$/;
if(reg.test(url.toLowerCase())) yp = url.trim();
aud.src = yp;
}
</script>
本帖最后由 马黑黑 于 2022-6-25 12:05 编辑
当前为测试之用,发布的主要目的是想通过测试收集存在问题。
主要更新:
一、改进程序一些细节逻辑性问题,比如工作模式切换后音频地址的继承或变更等等;
二、提升程序识别lrc歌词中可能存在的变种时间格式的能力;
三、制作进度的信息呈现方式得到些微改善,表现更为友好;
四、修复业已发现的个别问题。
欣赏支持! 岩新新 发表于 2022-6-25 12:00
欣赏支持!
请{:4_190:} 等会我就来试一下。 加林森 发表于 2022-6-25 12:15
等会我就来试一下。
好的,发现问题吱一声
{:4_190:} 马黑黑 发表于 2022-6-25 12:16
好的,发现问题吱一声
好的。 马黑黑 发表于 2022-6-25 11:57
当前为测试之用,发布的主要目的是想通过测试收集存在问题。
主要更新:
黑黑我又躺床上了{:5_149:} 小辣椒 发表于 2022-6-25 12:28
黑黑我又躺床上了
咋的啦 马黑黑 发表于 2022-6-25 13:16
咋的啦
发烧了,空调害的 小辣椒 发表于 2022-6-25 13:19
发烧了,空调害的
额。这没关系,很快恢复。空调还是开到合适的温度的好,比如家里可以考虑28°C,或自己穿件长袖。 马黑黑 发表于 2022-6-25 12:16
好的,发现问题吱一声
已经制作出来一个了。《绿叶对根的情意》,你去看看吧。 小辣椒 发表于 2022-6-25 13:19
发烧了,空调害的
又感冒了啊?多喝水排毒的。真是的,空调平均温度是根据你们那里的天气你就给着调整哦。最好26度左右就行了。 加林森 发表于 2022-6-25 13:54
已经制作出来一个了。《绿叶对根的情意》,你去看看吧。
看到了,非常漂亮 马黑黑 发表于 2022-6-25 15:30
看到了,非常漂亮
嗯嗯。这个版本应该是很成功的。 黑黑辛苦了,每一次的更新,知道你要花大量的功夫,太不容易了{:4_199:} 红影 发表于 2022-6-25 15:58
黑黑辛苦了,每一次的更新,知道你要花大量的功夫,太不容易了
这次更新,写代码的时间极短的,思考的时间可能多了一些 美妙的花潮lrc在线,伟大的黑黑{:4_178:} 红影 发表于 2022-6-25 16:01
美妙的花潮lrc在线,伟大的黑黑
伟大说过了{:5_117:} 加林森 发表于 2022-6-25 15:34
嗯嗯。这个版本应该是很成功的。
做完我自己测试过两次,感觉目前的样纸还是可以接受的