node 在控制台按时间滚动显示歌词

2019-10-12  本文已影响0人  LilyLaw

先捋一下实现的算法:

  1. 读入歌词
  2. 歌词分行
  3. 获取每一行歌词所出现的时间
  4. 通过setTimeout来延时显示

遇到的问题及解决方案:

  1. 问题:歌词lrc格式的文件一般都是gbk编码,而node自身无法处理此种格式;
    解决方案:引入第三方插件iconv-lite

  2. 问题:按照算法逻辑实现后,发现与原声有细微差别,且歌词越长,差别越明显;
    解决方案:除了往事件队列中放setTimeout,之前的代码执行也需要时间(比如循环遍历),而这些耗时应被歌词延时减去,从而去掉因之前代码执行所消耗的时间

实现

共有三种方法:1. 直接读整个文件;2. 通过流的方式读文件;3. 一行一行地来读文件。

我们先把公共代码提取出来:

// showlrc.js

let begin = new Date().getTime(),
    reg = /\[(\d{2})\:(\d{2})\.(\d{2})\](.+)/; // 正则表达式 使用()分成四组

function showing(item){
    let matches = reg.exec(item)
    if(matches){
        let min = parseFloat(matches[1]);
        let sec = parseFloat(matches[2]);
        let msec = parseFloat(matches[3]);
        let allTime = min*60*1000+sec*1000+msec;

        let offsetTime = new Date().getTime() - begin;
        setTimeout(()=>{
            console.log(matches[4]);
        },allTime-offsetTime)
    }else{
        console.log(item);
    }
}

module.exports = { showing }

方案1 直接读整个文件

const path = require('path');
const fs = require('fs');
const iconv = require('iconv-lite');
const showlrc = require('./showlrc.js');

let fileName = path.join(__dirname,'./files/lake.lrc')

fs.readFile(fileName,(err,data)=>{
    if(err) throw err;
    let lrcs = iconv.decode(data,'gbk'),
        lines = lrcs.split('\r\n');
    lines.forEach((item)=>{
        showlrc.showing(item);
    })
})

方案2 通过流的方式读文件

const path = require('path');
const fs = require('fs');
const iconv = require('iconv-lite');
const showlrc = require('./showlrc.js');

let fileName = path.join(__dirname,'./files/lake.lrc'),
    streamReader = fs.createReadStream(fileName).pipe(iconv.decodeStream('gbk')),
    fileData = '';
// 采用流的方式读取文档
streamReader.on('data',(chunk)=>{ // 开始连续读文档
    fileData += chunk.toString();
});
streamReader.on('end',()=>{ // 读文档结束后触发
    let lines = fileData.split('\r\n');
    lines.forEach((item)=>{
        showlrc.showing(item);
    })
});

方案3 一行一行地来读文件

const path = require('path');
const fs = require('fs');
const iconv = require('iconv-lite');
const readline = require('readline');
const showlrc = require('./showlrc.js');

let fileName = path.join(__dirname,'./files/lake.lrc');

let streamReader = fs.createReadStream(fileName).pipe(iconv.decodeStream('gbk')),
    rl = readline.createInterface({
        input: streamReader
    });

rl.on('line',(line)=>{
    showlrc.showing(line);
})

总结

显然方案2和方案3因为节省内存而比较高效(一块一块地读或者一行一行地读,整个过程占内存较小),尤其是文件较大,行数较多的时候。

上一篇下一篇

猜你喜欢

热点阅读