1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Node + 訊飛語音 定時(shí)播放天氣預(yù)報(bào)音頻

        共 27785字,需瀏覽 56分鐘

         ·

        2021-09-01 01:16

        前言

        最近看了幾篇文章,總覺得自己沒發(fā)揮樹莓派的作用,于是就琢磨著,哎,靈光一閃,整一個(gè)早晨叫醒服務(wù),于是便有了本篇水文。

        功能

        每天早上八點(diǎn)鐘,定時(shí)播放音頻(音頻內(nèi)容為當(dāng)天天氣預(yù)報(bào)和空氣質(zhì)量),播放完成之后繼續(xù)等待到明天的八點(diǎn)鐘播放。

        效果如下

        技術(shù)

        開始本來是想加個(gè)客戶端的,但是一想先先直接跑個(gè)服務(wù)就用的node試試,所以本文只需要你會(huì)用js就行

        1. node(服務(wù))
        2. 訊飛語音(轉(zhuǎn)音頻)
        3. play(播放語言)
        4. 聚合api(天氣預(yù)報(bào)接口)
        5. scheduleJob(定時(shí)任務(wù))

        準(zhǔn)備工作

        我們需要文字識(shí)別轉(zhuǎn)成音頻,訊飛是可以白嫖的

        訊飛語音

        請(qǐng)先在訊飛注冊(cè)賬號(hào)及 > 創(chuàng)建應(yīng)用 > 實(shí)名認(rèn)證

        https://passport.xfyun.cn/

        然后去控制臺(tái)找到服務(wù)接口認(rèn)證信息

        https://console.xfyun.cn/services/tts

        找到需要的 key

        聚合數(shù)據(jù)

        天氣預(yù)報(bào)api,如果有你其他的當(dāng)然也可以用其他的,這個(gè)也是白嫖

        就不做重點(diǎn)講了

        官網(wǎng) https://www.juhe.cn/

        天氣預(yù)報(bào) https://www.juhe.cn/docs/api/id/73

        快速上手

        初始化項(xiàng)目

        npm init -y

        然后安裝我們需要的依賴, 下面是依賴的版本

        {
          "name""node-jiaoxing",
          "version""1.0.0",
          "description""",
          "main""src/index.js",
          "scripts": {
            "dev""node src/index.js"
          },
          "author""",
          "license""ISC",
          "dependencies": {
            "node-schedule""^2.0.0",
            "request""^2.88.2",
            "websocket""^1.0.31"
          }
        }

        安裝依賴

        npm insatll

        創(chuàng)建lib文件夾

        創(chuàng)建一個(gè) lib 文件夾,放入別人封裝好的訊飛的資源,其主要作用就是將文本轉(zhuǎn)為音頻

        index.js

        const fs = require("fs");
        const WebSocketClient = require('websocket').client;
        const { getWssInfo, textToJson } = require('./util');

        const xunfeiTTS = (auth, business, text, fileName, cb) => {
          let audioData = [];

          const client = new WebSocketClient();
          client.on('connect', (con) => {
            con.on('error', error => {
              throw (error);
            });

            con.on('close', () => {
              const buffer = Buffer.concat(audioData);
              fs.writeFile(fileName, buffer, (err) => {
                if (err) {
                  throw (err);
                  cb(err);
                } else {
                  cb(null'OK');
                }
              });
            })

            con.on('message', (message) => {
              if (message.type == 'utf8') {
                const ret = JSON.parse(message.utf8Data);
                audioData.push(Buffer.from(ret.data.audio, 'base64'));
                if (ret.data.status == 2) {
                  con.close();
                }
              }
            });

            if (con.connected) {
              const thejson = textToJson(auth, business, text);
              con.sendUTF(thejson);
            }
          });

          client.on('connectFailed', error => {
            throw (error);
          });

          const info = getWssInfo(auth);
          client.connect(info.url);
        }

        module.exports = xunfeiTTS;

        util.js

        function btoa(text{
          return Buffer.from(text, "utf8").toString("base64");
        }

        function getWssInfo(auth, path = "/v2/tts", host = "tts-api.xfyun.cn"{
          const { app_skey, app_akey } = auth;
          const date = new Date(Date.now()).toUTCString();
          const request_line = `GET ${path} HTTP/1.1`;

          const signature_origin = `host: ${host}\ndate: ${date}\n${request_line}`;

          let crypto = require("crypto");

          const signature = crypto
            .createHmac("SHA256", app_skey)
            .update(signature_origin)
            .digest("base64");

          const authorization_origin = `api_key="${app_akey}",algorithm="hmac-sha256",headers="host date request-line",signature="${signature}"`;
          const authorization = btoa(authorization_origin);

          const thepath = `${path}?authorization=${encodeURIComponent(
            authorization
          )}
        &host=${encodeURIComponent(host)}&date=${encodeURIComponent(date)}`
        ;

          const final_url = `wss://${host}${thepath}`;

          return { url: final_url, host: host, path: thepath };
        }

        function textToJson(auth, businessInfo, text{
          const common = { app_id: auth.app_id };

          const business = {};
          business.aue = "raw";
          business.sfl = 1;
          business.auf = "audio/L16;rate=16000";
          business.vcn = "xiaoyan";
          business.tte = "UTF8";
          business.speed = 50;
          Object.assign(business, businessInfo);

          const data = { text: btoa(text), status2 };

          return JSON.stringify({ common, business, data });
        }

        module.exports = {
          btoa,
          getWssInfo,
          textToJson
        };

        創(chuàng)建src文件夾

        主入口文件

        index.js

        // 引入路徑模塊
        const path = require("path");
        const { promisify } = require("util");
        // 訊飛TTS
        const xunfeiTTS = require("../lib/index");
        const tts = promisify(xunfeiTTS);
        // 轉(zhuǎn)換音頻
        const openGreetings = async (app_id, app_skey, app_akey, text) => {
          const auth = { app_id, app_skey, app_akey };
          // 訊飛 api 參數(shù)配置
          // 接口文檔 https://www.xfyun.cn/doc/tts/online_tts/API.html
          const business = {
            aue"lame"// 音頻編碼
            sfl1// 開啟流式返回
            speed50,// 語速
            pitch50// 高音
            volume100,// 音量
            bgs0 // 背景音樂
          };
          // 存儲(chǔ)文件的路徑
          const file = path.resolve('./src/good-morning.wav');
          try {
            // 執(zhí)行請(qǐng)求
            await tts(auth, business, text, file).then(res => {});
          } catch (e) {
            console.log("test exception", e);
          }
        };
        openGreetings('訊飛的APPID''訊飛的APISecret''訊飛的APIKey''早上好,帥氣的嚴(yán)老濕')

        測試一波

        執(zhí)行 npm run dev 可以看到,執(zhí)行完成之后,已經(jīng)在src目錄下面創(chuàng)建了 good-morning.wav 音頻

        戴好耳機(jī),迫不及待的打開音頻,傳來早上好,帥氣的嚴(yán)老濕

        我們到這里就已經(jīng)完成了訊飛語音的接入

        問候語修改

        我們不可能一直是早上好吧

        所以我們需要根據(jù)系統(tǒng)的時(shí)間來

        const greetings = {
          "7, 10": ["早上好""上午"],
          "11,13": ["中午好""中午"],
          "14,17": ["下午好""下午"],
          "18,23": ["晚上好""晚上"],
        }

        const getTimeInfo = () => {
          const TIME = new Date()
          // 年月日
          let year = TIME.getFullYear()
          let month = TIME.getMonth() + 1
          let date = TIME.getDate()
          // 時(shí)分
          let hours = TIME.getHours()
          let minutes = TIME.getMinutes()
          // 生成的問候文本
          let greetingsStr = ""
          // 遍歷定義的問候數(shù)據(jù)
          for (const key in greetings) {
            if(hours >= key.split(",")[0] && hours <= key.split(",")[1]) {
              let greetingsKey = greetings[key]
              greetingsStr = `${greetingsKey[0]},現(xiàn)在是${greetingsKey[1]},${hours}點(diǎn),${minutes}分`
            }
          }
          // 中午好,現(xiàn)在是中午12點(diǎn),12分,今天是2021年,8月,28日
          return `${greetingsStr},今天是${year}年,${month}月,${date}日`
        }

        現(xiàn)在我們拿到的數(shù)據(jù)就是中午好,現(xiàn)在是中午12點(diǎn),12分,今天是2021年,8月,28日

        在執(zhí)行轉(zhuǎn)換音頻的時(shí)候,我們可以動(dòng)態(tài)調(diào)用 getTimeInfo 拿到當(dāng)前的文本傳遞過去轉(zhuǎn)成音頻

        openGreetings('訊飛的APPID''訊飛的APISecret''訊飛的APIKey', getTimeInfo())

        自動(dòng)播放

        當(dāng)我創(chuàng)建好音頻后,我希望立刻播放

        引入play資源

        所以我們需要使用一個(gè)庫 play , 老嚴(yán)也是直接拿資源,然后放入lib文件夾中,叫play.js

        if(typeof exports === 'undefined'){
          var play = {
            soundfunction ( wav {
              debug.log(wav);
              var e = $('#' + wav);
              debug.log(e);
              $('#alarm').remove();
              $(e).attr('autostart',true);
              $('body').append(e);
              return wav;
            }
          };
        }
        else{
          var colors = require('colors'), 
              child_p = require('child_process'),
              exec = child_p.exec,
              spawn = child_p.spawn,
              ee = require('events'),
              util = require('util');
          var Play = exports.Play = function Play({
            var self = this;

            if (!(this instanceof Play)) {
              return new Play();
            }
            ee.EventEmitter.call(this);
            this.playerList = [
              'afplay',
              'mplayer',
              'mpg123',
              'mpg321',
              'play',
            ];
            this.playerName = false;
            this.checked = 0;
            var i = 0, child;
            for (i = 0, l = this.playerList.length; i < l; i++) {
              if (!this.playerName) {
                (function inner (name{
                child = exec(name, function (error, stdout, stderr{
                  self.checked++;
                  if (!self.playerName && (error === null || error.code !== 127 )) {
                    self.playerName = name;
                    self.emit('checked');
                    return;
                  }
                  if (name === self.playerList[self.playerList.length-1]) {
                    self.emit('checked');
                  }
                });
                })(this.playerList[i]);
              } 
              else {
                break;
              }
            }
          };
          util.inherits(Play, ee.EventEmitter);
          Play.prototype.usePlayer = function usePlayer (name{
            this.playerName = name;
          }
          Play.prototype.sound = function sound (file, callback{
            var callback = callback || function ({};
            var self = this;
            if (!this.playerName && this.checked !== this.playerList.length) {
              this.on('checked'function ({
                self.sound.call(self, file, callback);
              });
              return false;
            }
            if (!this.playerName && this.checked === this.playerList.length) {
              console.log('No suitable audio player could be found - exiting.'.red);
              console.log('If you know other cmd line music player than these:'.red, this.playerList);
              console.log('You can tell us, and will add them (or you can add them yourself)'.red);
              this.emit('error'new Error('No Suitable Player Exists'.red, this.playerList));
              return false;
            }
            var command = [file],
                child = this.player = spawn(this.playerName, command);
            console.log('playing'.magenta + '=>'.yellow + file.cyan);
            child.on('exit'function (code, signal{
              if(code == null || signal != null || code === 1) {
                console.log('couldnt play, had an error ' + '[code: '+ code + '] ' + '[signal: ' + signal + '] :' + this.playerName.cyan);
                this.emit('error', code, signal);
              }
              else if ( code == 127 ) {
                console.log( self.playerName.cyan + ' doesn\'t exist!'.red );
                this.emit('error', code, signal);
              }
              else if (code == 2) {
                console.log(file.cyan + '=>'.yellow + 'could not be read by your player:'.red + self.playerName.cyan)
                this.emit('error', code, signal);
              }
              else if (code == 0) {
                console.log( 'completed'.green + '=>'.yellow + file.magenta);
                callback();
              }
              else {
                console.log( self.playerName.cyan + ' has an odd error with '.yellow + file.cyan);
                console.log(arguments);
                emit('error');
              }
            });
            this.emit('play'true);
            return true;
          }
        }

        使用play

        在 src/ index.js 文件中引入

        // 播放器
        const play = require('../lib/play').Play();

        openGreetings 中執(zhí)行轉(zhuǎn)換音頻完成之后播放文件 play.sound(file);

        const openGreetings = async (app_id, app_skey, app_akey, text) => {
            const auth = { app_id, app_skey, app_akey };
            const business = {
                aue"lame",
                sfl1,
                speed50,
                pitch50,
                volume100,
                bgs0
            };
            const file = path.resolve("./src/good-morning.wav");
            try {
                await tts(auth, business, text, file).then(res => {
                  // 執(zhí)行播放
                   play.sound(file);
                });
            } catch (e) {
                console.log("test exception", e);
            }
        };

        測試一下

        執(zhí)行 npm run dev

        執(zhí)行開始,先去轉(zhuǎn)換音頻,然后自動(dòng)開始播放

        加需求

        播放完成之后,我還需要播放一段我喜歡的音樂

        這讓我很為難啊,得加錢!“加個(gè)毛,play.js 有播放完成的 callback”

        play.sound(file, function(){
           // 上一個(gè)播放完成之后,我們開始播放一首《小雞小雞》-王蓉
            play.sound('./src/xiaojixiaoji.m4a')
        });

        這個(gè)音樂資源應(yīng)該不用教學(xué)了吧,隨便去扒拉兩首自己喜歡的歌,常見的格式都可以 m4a,mp3,wav等等

        有條件的同學(xué) 可以找聲音甜美的妹子,錄一個(gè)喊你起床的音頻,放在播放問候語之前,效果更佳

        天氣預(yù)報(bào)

        天氣預(yù)報(bào),我這里是用的聚合數(shù)據(jù)的,當(dāng)然如果你有其他的天氣預(yù)報(bào) api 也可以,老嚴(yán)這里只是做個(gè)簡單的示例

        // 請(qǐng)求
        const request = require('request');

        然后調(diào)用

        // 獲取天氣的城市
        const city = "長沙"
        let text
        request(`http://apis.juhe.cn/simpleWeather/query?city=${encodeURI(city)}&key=聚合數(shù)據(jù)key`,
            (err, response, body) => {
                if (!err && response.statusCode == 200){
                  let res = JSON.parse(body).result.realtime
                  text = `
                    ${getTimeInfo()},
                    接下來為您播報(bào)${city}實(shí)時(shí)天氣預(yù)報(bào),
                    今天,長沙天氣為${res.info}天,
                    室外溫度為${res.temperature}度,
                    室外濕度為百分之${res.humidity},
                    ${res.direct},
                    ${res.power},
                    天氣預(yù)報(bào)播放完畢,
                    接下來播放您喜歡的音樂
                  `

                  openGreetings('訊飛的APPID''訊飛的APISecret''訊飛的APIKey', text)
                } 
            }
        )

        拿到的文本就是這樣的

        中午好,現(xiàn)在是中午,12點(diǎn),27分,今天是2021年,8月,28日,
        接下來為您播報(bào)長沙實(shí)時(shí)天氣預(yù)報(bào),
        今天,長沙天氣為晴天,
        室外溫度為27度,
        室外濕度為百分之76,
        南風(fēng),
        3級(jí),
        天氣預(yù)報(bào)播放完畢,
        接下來播放您喜歡的音樂

        定時(shí)任務(wù)

        為什么要定時(shí)任務(wù),因?yàn)槲覀兊男枨笫?,每天早上八點(diǎn)鐘播放,所以我們用到了 schedule

        schedule 之前也有講過,在幾個(gè)月前的《Node.js之自動(dòng)發(fā)送郵件 | 僅二十行代碼即可》郵件中也提到過

        因?yàn)槲覀冊(cè)谇懊嬉呀?jīng)下載了,我們只需要引入就好了

        引入

        const schedule = require('node-schedule');

        使用

        // 定時(shí)每天8點(diǎn)0分0秒執(zhí)行
        schedule.scheduleJob('0 0 8 * * *', ()=>{
            request(`http://apis.juhe.cn/simpleWeather/query?city=${encodeURI(city)}&key=聚合數(shù)據(jù)key`,
                (err, response, body) => {
                    if (!err && response.statusCode == 200){
                      let res = JSON.parse(body).result.realtime
                      text = `
                        ${getTimeInfo()},
                        接下來為您播報(bào)${city}實(shí)時(shí)天氣預(yù)報(bào),
                        今天,長沙天氣為${res.info}天,
                        室外溫度為${res.temperature}度,
                        室外濕度為百分之${res.humidity},
                        ${res.direct}
                        ${res.power},
                        天氣預(yù)報(bào)播放完畢,
                        接下來播放您喜歡的音樂
                      `

                      openGreetings('訊飛的APPID''訊飛的APISecret''訊飛的APIKey', text)
                    } 
                }
            )
        });

        貼上index.js 所有代碼

        // 引入路徑模塊
        const path = require("path");
        const { promisify } = require("util");
        // 訊飛TTS
        const xunfeiTTS = require("../lib/index");
        const tts = promisify(xunfeiTTS);
        // 播放器
        const play = require("../lib/play").Play();
        // 請(qǐng)求
        const request = require("request");
        // 定時(shí)任務(wù)
        const schedule = require('node-schedule');
        // 問候語及時(shí)間
        const greetings = {
          "7, 10": ["早上好""上午"],
          "11,13": ["中午好""中午"],
          "14,17": ["下午好""下午"],
          "18,23": ["晚上好""晚上"]
        };

        const getTimeInfo = () => {
          const TIME = new Date();
          // 年月日
          let year = TIME.getFullYear();
          let month = TIME.getMonth() + 1;
          let date = TIME.getDate();
          // 時(shí)分
          let hours = TIME.getHours();
          let minutes = TIME.getMinutes();
          // 生成的問候文本
          let greetingsStr = "";
          // 遍歷定義的問候數(shù)據(jù)
          for (const key in greetings) {
            if (hours >= key.split(",")[0] && hours <= key.split(",")[1]) {
              let greetingsKey = greetings[key];
              greetingsStr = `${greetingsKey[0]},現(xiàn)在是${greetingsKey[1]},${hours}點(diǎn),${minutes}分`;
            }
          }
          // 中午好,現(xiàn)在是中午12點(diǎn),12分,今天是2021年,8月,28日
          return `${greetingsStr},今天是${year}年,${month}月,${date}日`;
        };

        const openGreetings = async (app_id, app_skey, app_akey, text) => {
          const auth = { app_id, app_skey, app_akey };
          // 訊飛 api 參數(shù)配置
          const business = {
            aue"lame",
            sfl1,
            speed50,
            pitch50,
            volume100,
            bgs0
          };
          // 存儲(chǔ)文件的路徑
          const file = path.resolve("./src/good-morning.wav");
          try {
            // 執(zhí)行請(qǐng)求
            await tts(auth, business, text, file).then(res => {
              play.sound(file, function({
                play.sound("./src/xiaojixiaoji.m4a");
              });
            });
          } catch (e) {
            console.log("test exception", e);
          }
        };

        const city = "長沙";
        let text;
        schedule.scheduleJob("0 0 8 * * *", () => {
          request(
            `http://apis.juhe.cn/simpleWeather/query?city=${encodeURI(city)}&key=聚合key`,
            (err, response, body) => {
              if (!err && response.statusCode == 200) {
                let res = JSON.parse(body).result.realtime;
                text = `
                ${getTimeInfo()},
                接下來為您播報(bào)${city}實(shí)時(shí)天氣預(yù)報(bào),
                今天,長沙天氣為${res.info}天,
                室外溫度為${res.temperature}度,
                室外濕度為百分之${res.humidity},
                ${res.direct}
                ${res.power},
                天氣預(yù)報(bào)播放完畢,
                接下來播放您喜歡的音樂
              `
        ;
           openGreetings('訊飛的APPID''訊飛的APISecret''訊飛的APIKey', text)
              }
            }
          );
        });

        完結(jié)撒花

        到了這里,執(zhí)行 npm run dev ,就相當(dāng)于你開了一個(gè)鬧鐘,但是前提是你得保證服務(wù)不會(huì)停

        今天早上我就是被這玩意叫醒的,但是推薦大家不要把聲音開太大了

        本來是想裝到樹莓派上去的,但是樹莓派的音頻接口出了點(diǎn)問題,所以沒裝上

        然后我就裝在了自己的電腦上

        全部代碼鏈接: https://pan.baidu.com/s/1aQXQSCB-83P-xlkD35ZndQ  密碼: dao7

        參考文檔

        • https://www.xfyun.cn/doc/tts/online_tts/API.html

        • https://github.com/Marak/play.js

        • https://www.npmjs.com/package/xf-tts-socket

        瀏覽 112
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美性猛交xxxx富婆弯腰 | 亚洲有码在线播放 | 龙的两根好大拔不出去h | 家族轮换对家庭的影响视频 | 65看片黄淫大片 | 天天色天天射天天操 | 国产骚女| 久久国产精品波多野结衣AV | 天天干狠狠| 女生100%无遮挡 |