美文网首页
node 在控制台按时间滚动显示歌词

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

作者: LilyLaw | 来源:发表于2019-10-12 10:16 被阅读0次

    先捋一下实现的算法:

    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因为节省内存而比较高效(一块一块地读或者一行一行地读,整个过程占内存较小),尤其是文件较大,行数较多的时候。

    相关文章

      网友评论

          本文标题:node 在控制台按时间滚动显示歌词

          本文链接:https://www.haomeiwen.com/subject/axzfmctx.html