Keif

Keif 查看完整档案

上海编辑上海大学  |  信号与信息处理 编辑华硕电脑(上海)  |  行动通讯产品中心实习生 编辑 segmentfault.com/u/keif 编辑
编辑

这里是个人简介。

个人动态

Keif 收藏了文章 · 2020-11-19

一款记忆类App的设计

很多人在背单词时可能都和我一样有这样的经验,就是开始几天还不错,后面却没能坚持下来,即使大部分单词软件都有手机版本,但在手机上每天坚持打开App来背单词,也不是那么容易坚持的一件事。

于是我开发了这一款App - 碎片记忆,为了完成轻松背单词的目标,它有几点不一样的设计:

  • 支持悬浮窗学习。
  • 支持桌面小部件学习。
  • 支持Anki词库,也可以使用Excel自制词库导入。

我逐一道来,首先每个人要学的词库都不一样,为了更多人可以用,碎片记忆没有自带任何词库,它设计为兼容Anki,Anki是一款非常好的记忆类软件,有非常多的共享词库,可以从Anki的各种渠道下载到各种库,英语单词自然不在话下,还包括其他学习库,比如生物、化学甚至是驾考的库,都可以找到,下图是Anki官方的共享库的网站下的一个分类:

image.png

同时也因为没有自带词库,也让碎片记忆这个App变得很小,只有200多k,应该是背单词的App里非常瘦身的一款了。首次启动碎片记忆,会有权限设置的提示,然后就可以看到主界面了。

image.png

主界面上有两个大大的导入选项,分别时导入官网Anki和本地Anki,如果你之前已经在手机上下载好anki文件,导入本地anki就可以了,否则的话可以联网下载官网anki词库,如果这两种方式都不适合,还可以通过右上角菜单选择导入csv词库。不管使用哪种方式,导入成功后,就会弹出待学习的卡片了。看下面这张图:

image.png

每张卡片都有正面和反面,对于英语词库,通常就是一面单词,一面释义,点击卡片可以翻转,卡片下方我们可以看到三个按钮,根据自己对这张卡片的掌握程度来选择忘记、一般或是容易,按钮的下面也会提醒下次复习的时间间隔,比如上面的卡片,如果点击忘记就会在2分钟之后继续复习,而如果选择一般或容易,则分别在10分钟和1天后复习。 当然这个记忆也是基于记忆曲线的,和Anki类似,每次成功复习之后,都会拉长下次的复习时间直到完全记忆,如果暂时不想学习卡片,可以点击卡片左上角的关闭按钮,卡片会临时缩放为一个小图标,以提醒你还有待学习的卡片,直到当前所有卡片完成,卡片才会彻底消失。

由于是悬浮窗的方式,我们再也不需要启动App了,只要复习时间到,无论你是在微信聊天,看视频,还是看新闻,碎片记忆都会立刻弹出。这就是这个App名称-碎片记忆的由来,利用一切碎片时间来完成记忆,这种方式显然需要碎片记忆的后台驻留,但是现在各个厂商的安卓手机都会把后台管理的很严格,因此很容易被系统杀掉,一旦杀掉之后卡片就不能弹出了。所以在手机上需要设置后台保留的相关设置,这在App的帮助中有给出详细的设置方法,好在仅仅需要设置一次就可以了。

回到卡片上,卡片的右下角的三角是可以拖动来改变悬浮窗的大小,上方的标题栏可以拖动以改变悬浮窗的位置,不过在某些时候我们可能想彻底关闭悬浮窗,比如在开车导航的时候,我们可以用安卓7.0支持的通知栏磁贴临时来开关。这个通知栏磁贴开关需要手工添加一下,下图是我的华为手机上,点击小铅笔就可以把碎片记忆的通知栏磁贴开关加进来。

image.png

介绍完悬浮窗,我们再来看另一种学习方式,因为每个人的习惯不一样,有些人可能觉得悬浮窗过于打扰,因此碎片记忆提供了另一种不那么强制的方式 - 桌面小部件,相对而言,桌面小部件只在回到桌面的时候才能看到,这样比较温和一些,并且小部件不需要占用安卓后台。

image.png

碎片记忆也支持Anki库中的音频内容,如果某张卡片上有音频,会在卡片上有一个播放按钮,点击这个按钮就会听到发音了,如果不想每次都点击播放按钮,可以设置自动播放,这样当新打开的卡片包括音频的话,就会自动播放出来,需要提醒一下,大部分Anki库的音频是内置的,但也有小部分Anki库音频是来自网络的,自然也要联网才能听到,播放按钮参考下图(右上角):
image.png

图片也没有问题,一些anki库有大量的图片,图片会随着卡片大小而缩放,比如下面这个识别猫的种类的库:

image.png

为了更好的学习效果,我们可以在设置里,配置每次弹出卡片的批量数量,这样可以根据个人的情况进行调整。还可以控制复习的时间段。这样可以把每天要复习的词平均分布一下,不至于早上的时候记得过多,下午却没有词了。

image.png

碎片可以支持同时学习多个词库,比如四级和六级单词一起学,在App的首页面下,每个词库都有基本的状态可查,包括今日学习的新词数量,剩余新词的数量、复习中、已完成单词的数量等,还有一个最为直观的百分比进度,表示这个词库已经完成了多少,方便随时掌握学习进度。

还有一些高级的功能,比如修改卡片模板、颜色、字体、统计,甚至是修改某个词库的记忆曲线,这里不一一列举,参考下面的图片:

image.png

最后,大家可以去酷安下载碎片记忆来试试看,也可以在酷安给作者反馈意见。

查看原文

Keif 赞了文章 · 2020-11-19

一款记忆类App的设计

很多人在背单词时可能都和我一样有这样的经验,就是开始几天还不错,后面却没能坚持下来,即使大部分单词软件都有手机版本,但在手机上每天坚持打开App来背单词,也不是那么容易坚持的一件事。

于是我开发了这一款App - 碎片记忆,为了完成轻松背单词的目标,它有几点不一样的设计:

  • 支持悬浮窗学习。
  • 支持桌面小部件学习。
  • 支持Anki词库,也可以使用Excel自制词库导入。

我逐一道来,首先每个人要学的词库都不一样,为了更多人可以用,碎片记忆没有自带任何词库,它设计为兼容Anki,Anki是一款非常好的记忆类软件,有非常多的共享词库,可以从Anki的各种渠道下载到各种库,英语单词自然不在话下,还包括其他学习库,比如生物、化学甚至是驾考的库,都可以找到,下图是Anki官方的共享库的网站下的一个分类:

image.png

同时也因为没有自带词库,也让碎片记忆这个App变得很小,只有200多k,应该是背单词的App里非常瘦身的一款了。首次启动碎片记忆,会有权限设置的提示,然后就可以看到主界面了。

image.png

主界面上有两个大大的导入选项,分别时导入官网Anki和本地Anki,如果你之前已经在手机上下载好anki文件,导入本地anki就可以了,否则的话可以联网下载官网anki词库,如果这两种方式都不适合,还可以通过右上角菜单选择导入csv词库。不管使用哪种方式,导入成功后,就会弹出待学习的卡片了。看下面这张图:

image.png

每张卡片都有正面和反面,对于英语词库,通常就是一面单词,一面释义,点击卡片可以翻转,卡片下方我们可以看到三个按钮,根据自己对这张卡片的掌握程度来选择忘记、一般或是容易,按钮的下面也会提醒下次复习的时间间隔,比如上面的卡片,如果点击忘记就会在2分钟之后继续复习,而如果选择一般或容易,则分别在10分钟和1天后复习。 当然这个记忆也是基于记忆曲线的,和Anki类似,每次成功复习之后,都会拉长下次的复习时间直到完全记忆,如果暂时不想学习卡片,可以点击卡片左上角的关闭按钮,卡片会临时缩放为一个小图标,以提醒你还有待学习的卡片,直到当前所有卡片完成,卡片才会彻底消失。

由于是悬浮窗的方式,我们再也不需要启动App了,只要复习时间到,无论你是在微信聊天,看视频,还是看新闻,碎片记忆都会立刻弹出。这就是这个App名称-碎片记忆的由来,利用一切碎片时间来完成记忆,这种方式显然需要碎片记忆的后台驻留,但是现在各个厂商的安卓手机都会把后台管理的很严格,因此很容易被系统杀掉,一旦杀掉之后卡片就不能弹出了。所以在手机上需要设置后台保留的相关设置,这在App的帮助中有给出详细的设置方法,好在仅仅需要设置一次就可以了。

回到卡片上,卡片的右下角的三角是可以拖动来改变悬浮窗的大小,上方的标题栏可以拖动以改变悬浮窗的位置,不过在某些时候我们可能想彻底关闭悬浮窗,比如在开车导航的时候,我们可以用安卓7.0支持的通知栏磁贴临时来开关。这个通知栏磁贴开关需要手工添加一下,下图是我的华为手机上,点击小铅笔就可以把碎片记忆的通知栏磁贴开关加进来。

image.png

介绍完悬浮窗,我们再来看另一种学习方式,因为每个人的习惯不一样,有些人可能觉得悬浮窗过于打扰,因此碎片记忆提供了另一种不那么强制的方式 - 桌面小部件,相对而言,桌面小部件只在回到桌面的时候才能看到,这样比较温和一些,并且小部件不需要占用安卓后台。

image.png

碎片记忆也支持Anki库中的音频内容,如果某张卡片上有音频,会在卡片上有一个播放按钮,点击这个按钮就会听到发音了,如果不想每次都点击播放按钮,可以设置自动播放,这样当新打开的卡片包括音频的话,就会自动播放出来,需要提醒一下,大部分Anki库的音频是内置的,但也有小部分Anki库音频是来自网络的,自然也要联网才能听到,播放按钮参考下图(右上角):
image.png

图片也没有问题,一些anki库有大量的图片,图片会随着卡片大小而缩放,比如下面这个识别猫的种类的库:

image.png

为了更好的学习效果,我们可以在设置里,配置每次弹出卡片的批量数量,这样可以根据个人的情况进行调整。还可以控制复习的时间段。这样可以把每天要复习的词平均分布一下,不至于早上的时候记得过多,下午却没有词了。

image.png

碎片可以支持同时学习多个词库,比如四级和六级单词一起学,在App的首页面下,每个词库都有基本的状态可查,包括今日学习的新词数量,剩余新词的数量、复习中、已完成单词的数量等,还有一个最为直观的百分比进度,表示这个词库已经完成了多少,方便随时掌握学习进度。

还有一些高级的功能,比如修改卡片模板、颜色、字体、统计,甚至是修改某个词库的记忆曲线,这里不一一列举,参考下面的图片:

image.png

最后,大家可以去酷安下载碎片记忆来试试看,也可以在酷安给作者反馈意见。

查看原文

赞 3 收藏 2 评论 1

Keif 发布了文章 · 2019-08-09

[译]使用iStyle格式化Verilog代码

对博客平台有些失望,转语雀只当留档用了。

原文:Verilogでコード整形

安装

iStyle可以从GitHub上clone、make自行编译出可执行文件,也可以直接下载已编译好的可执行文件。这里都给出来。

Github

https://github.com/thomasruss...

可执行文件

https://github.com/HayasiKei/...

格式化选项

以下是一些格式化时常用的选项及效果示例。

待格式化代码

reg [3:0] cnt;
always @(posedge clk or posedge rst) begin
if(rst) begin
cnt<=4'h0;
end else begin
cnt<=cnt+4'h1;
end
end

--style

ANSI style

./iStyle --style=ansi test.v
reg [3:0] cnt;
always @(posedge clk or posedge rst)
begin
    if(rst)
    begin
        cnt<=4'h0;
    end
    else
    begin
        cnt<=cnt+4'h1;
    end
end

Kernighan&Ritchie style

./iStyle --style=kr test.v
reg [3:0] cnt;
always @(posedge clk or posedge rst) begin
    if(rst) begin
        cnt<=4'h0;
    end
    else begin
        cnt<=cnt+4'h1;
    end
end

GNU style

./iStyle --style=gnu test.v
reg [3:0] cnt;
always @(posedge clk or posedge rst)
  begin
    if(rst)
      begin
        cnt<=4'h0;
      end
    else
      begin
        cnt<=cnt+4'h1;
      end
  end

-s

./iStyle -s2 test.v

该选项指定缩进时的空格数量,-s2表示每次缩进使用2个空格(如果是-s4则表示每次用4个空格缩进)。

reg [3:0] cnt;
always @(posedge clk or posedge rst) begin
  if(rst) begin
    cnt<=4'h0;
  end else begin
    cnt<=cnt+4'h1;
  end
end

-p

-p选项指定在运算符两侧插入空格。

reg [3: 0] cnt;
always @(posedge clk or posedge rst) begin
    if (rst)
    begin
        cnt <= 4'h0;
    end else
    begin
        cnt <= cnt + 4'h1;
    end
end

-P

-P选项指定在运算符和括号周围插入空格。

reg [ 3: 0 ] cnt;
always @( posedge clk or posedge rst ) begin
    if ( rst )
    begin
        cnt <= 4'h0;
    end else
    begin
        cnt <= cnt + 4'h1;
    end
end

小结

虽然文中没有写,module声明的缩进感觉并不是很好。verilog有各种各样的代码风格,因此有一个强大的格式化程序是很有用的。

查看原文

赞 0 收藏 0 评论 0

Keif 赞了文章 · 2019-07-12

命令行录制工具 asciinema

平常出bug求助的时候有时候贴代码或者截图往往不直观,如果能重现给对方看就好了,这里推荐 2 个命令行的录制工具。

asciinema

网站https://asciinema.org/ ,github主页https://github.com/asciinema
直接使用 pip install asciinema 来安装。
执行asciinema rec 开始录制,录制完成后 exit 退出,可以保存到本地或者上传到 https://asciinema.org

[root@VM_0_14_centos ~]# asciinema rec
asciinema: recording asciicast to /tmp/tmp1ua5a2rx-ascii.cast
asciinema: press <ctrl-d> or type "exit" when you're done
[root@VM_0_14_centos ~]# pwd
/root
[root@VM_0_14_centos ~]# cd /usr/share/nginx/html/
[root@VM_0_14_centos html]# pip install asciinema
DEPRECATION: Python 3.4 support has been deprecated. pip 19.1 will be the last one supporting it. Please upgrade your Python as Python 3.4 won't be maintained after March 2019 (cf PEP 429).
Looking in indexes: http://mirrors.tencentyun.com/pypi/simple
Requirement already satisfied: asciinema in /usr/lib/python3.4/site-packages (2.0.2)
You are using pip version 19.0.3, however version 19.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
[root@VM_0_14_centos html]# pip list |grep ascii
DEPRECATION: Python 3.4 support has been deprecated. pip 19.1 will be the last one supporting it. Please upgrade your Python as Python 3.4 won't be maintained after March 2019 (cf PEP 429).
asciinema              2.0.2
You are using pip version 19.0.3, however version 19.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
[root@VM_0_14_centos html]# exit
exit
asciinema: recording finished
asciinema: press <enter> to upload to asciinema.org, <ctrl-c> to save locally

View the recording at:

    https://asciinema.org/a/AdnqMX0QfOg5c7USOtwHZ4Hz1

This installation of asciinema recorder hasn't been linked to any asciinema.org
account. All unclaimed recordings (from unknown installations like this one)
are automatically archived 7 days after upload.

If you want to preserve all recordings made on this machine, connect this
installation with asciinema.org account by opening the following link:

    https://asciinema.org/connect/01fb0f0e-c56a-450f-80ac-4020188dd957

录制过程在https://asciinema.org/a/AdnqM... 可以看到了。

clipboard.png

如果分享给他人可以用邮箱注册,它不需要密码就可以注册。我注册后的主页https://asciinema.org/~susheng
然后打开这个链接 https://asciinema.org/connect... 就会保存到你账号下。

clipboard.png

设置为public 后可生成公开链接,可分享给他人观看,还可以嵌入到自己的网站。

clipboard.png

clipboard.png

视频链接后加 .png 是视频截图 https://asciinema.org/a/25434... ,而链接后加 .js 就可以直接嵌入网站了。

<a href="https://asciinema.org/a/254348" target="_blank"><img data-original="https://asciinema.org/a/254348.svg" /></a>
<script data-original="https://asciinema.org/a/254348.js" id="asciicast-254348" async data-autoplay="true" data-size="big"></script>

TermRecord

TermRecord也是用 pip 安装 pip install TermRecord, 直接开始录制 TermRecord -o termrecord.html 输入 exit 结束录制 。这个 termrecord.html 就是录制生成的文件,可以直接用浏览器打开。

[root@VM_0_14_centos html]# TermRecord -o termrecord.html
Script started, file is /tmp/tmpdekpz_p2
[root@VM_0_14_centos html]# pwd
/usr/share/nginx/html
[root@VM_0_14_centos html]# whoami
root
[root@VM_0_14_centos html]# pip install TermRecord
DEPRECATION: Python 3.4 support has been deprecated. pip 19.1 will be the last one supporting it. Please upgrade your Python as Python 3.4 won't be maintained after March 2019 (cf PEP 429).
Looking in indexes: http://mirrors.tencentyun.com/pypi/simple
Requirement already satisfied: TermRecord in /usr/lib/python3.4/site-packages (1.2.5)
Requirement already satisfied: Jinja2>=2.6 in /usr/lib64/python3.4/site-packages (from TermRecord) (2.10.1)
Requirement already satisfied: MarkupSafe>=0.23 in /usr/lib64/python3.4/site-packages (from Jinja2>=2.6->TermRecord) (1.1.1)
You are using pip version 19.0.3, however version 19.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
[root@VM_0_14_centos html]# pip list|grep Term
DEPRECATION: Python 3.4 support has been deprecated. pip 19.1 will be the last one supporting it. Please upgrade your Python as Python 3.4 won't be maintained after March 2019 (cf PEP 429).
TermRecord             1.2.5
You are using pip version 19.0.3, however version 19.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
[root@VM_0_14_centos html]# exit
exit
Script done, file is /tmp/tmpdekpz_p2

然后打开文件就可以看到录制过程了

clipboard.png

另外还有个基于ruby的showterm和termtosvg就不演示了 http://showterm.io/
https://github.com/nbedos/termtosvg

推荐阅读:

那些你可能不知道的浏览器奇技淫巧

那些你可能不知道的微信奇技淫巧

那些你可能不知道的微博奇技淫巧

那些你可能不知道的网易云音乐奇技淫巧

那些你可能不知道的搜索奇技淫巧

那些你可能不知道的视频下载奇技淫巧

那些你可能不知道的免费观看 VIP 视频奇技淫巧

查看原文

赞 11 收藏 8 评论 1

Keif 发布了文章 · 2019-06-29

使用Python把树莓派改造成一个语音助手

对博客平台有些失望,转语雀只当留档用了。

语音助手已经不是什么新事物了。就在两三年前,语音助手的使用体验还不是那么好,尝尝鲜后也就没用过了。但最近发现不管是微软的Cortana、苹果的Siri,还是一些不怎么有名气的,例如MIUI的小爱同学等,使用体验真的改善了很多,确确实实能带来一些方便了。

随着各种云服务、API的面世,语音方面的云服务可以说是十分健全了。你是否也想过自己动手搭建一个语音助手系统呢?本文将总结使用Python把树莓派(3代b型)改造成一个简易语音助手的基本流程。

概述

这次要做的说白了,就是把各种云服务、API串起来,并不涉及任何核心技术、算法的实现,望知悉。

这次将要使用到的服务包括:

为了实现这个语音助手系统,需要完成的工作每一个都不难,但数量稍多了些。以下是涉及到的一些博客:

后文在介绍各部分的具体实现时,只附上代码和进行一些必要的说明,详细内容还需要参考相应博客。

各部分的实现

由于整个项目用到的服务比较多,而且各部分的分工很明显,所以选择各部分分别用一个python程序来实现,最后再用一个程序整合在一起的方式。

录音

参考:树莓派学习手记——使用Python录音

笔者采用了“按住按钮进行录音”的操作方式,如下图所示接线。如果你手头上没有按钮或觉得这么做不方便,可以修改代码改成“按回车键开始/结束录音”之类的操作方式。

另外,树莓派的板载3.5mm耳机接口是不带语音输入功能的,所以你需要另外购买USB声卡。

* 文件 rec.py

import RPi.GPIO as GPIO
import pyaudio
import wave
import os
import sys

def rec_fun():
    # 隐藏错误消息,因为会有一堆ALSA和JACK错误消息,但其实能正常录音
    os.close(sys.stderr.fileno())
    
    BUTT = 26    # 开始录音的按钮:一边接GPIO26,一边接地
    GPIO.setmode(GPIO.BCM)
    # 设GPIO26脚为输入脚,电平拉高,也就是说26脚一旦读到低电平,说明按了按钮
    GPIO.setup(BUTT, GPIO.IN, pull_up_down = GPIO.PUD_UP)

    # wav文件是由若干个CHUNK组成的,CHUNK我们就理解成数据包或者数据片段。
    CHUNK = 512 
    FORMAT = pyaudio.paInt16  # pyaudio.paInt16表示我们使用量化位数 16位来进行录音
    RATE = 44100  # 采样率 44.1k,每秒采样44100个点。
    WAVE_OUTPUT_FILENAME = "/home/pi/chat/command.wav"
    print('请按住按钮开始录音...')
    GPIO.wait_for_edge(BUTT, GPIO.FALLING)

    # To use PyAudio, first instantiate PyAudio using pyaudio.PyAudio(), which sets up the portaudio system.
    p = pyaudio.PyAudio()
    stream = p.open(format = FORMAT,
                    channels = 1,    # cloud speecAPI只支持单声道
                    rate = RATE,
                    input = True,
                    frames_per_buffer = CHUNK)
    print("录音中...")

    frames = []
    # 按住按钮录音,放开时结束
    while GPIO.input(BUTT) == 0:
        data = stream.read(CHUNK)
        frames.append(data)
    print("录音完成,输出文件:" + WAVE_OUTPUT_FILENAME + '\n')
    stream.stop_stream()
    stream.close()
    p.terminate()

    wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
    wf.setnchannels(1)
    wf.setsampwidth(p.get_sample_size(FORMAT))    # Returns the size (in bytes) for the specified sample format.
    wf.setframerate(RATE)
    wf.writeframes(b''.join(frames))
    wf.close()
    
    return

# 可以直接运行rec.py进行测试,同时保证该文件import时不会自动运行
if __name__ == '__main__':
    rec_fun()

语音识别

参考:

使用Google云计算引擎实现科学上网

在Windows命令行、Linux终端使用代理

在Python中使用谷歌Cloud Speech API将语音转换为文字(另一种方案)

由于某些原因,笔者选择了使用谷歌Cloud Speech API进行语音识别。既然要用谷歌的服务,自然就涉及到了科学上网、代理、谷歌云平台的使用,如果不想这么折腾,完全可以用国内的讯飞、百度来实现。

另外,API KEY之类的字符串在这里删除了,还请麻烦修改代码加上你自己申请的API KEY。

* 文件 speech_api.py

import json
import urllib.request
import base64

def wav_to_text():
    api_url = "https://speech.googleapis.com/v1beta1/speech:syncrecognize?key=替换成你的API密钥"
    print('语音文件编码中...')
    audio_file = open('/home/pi/chat/command.wav', 'rb')
    audio_b64str = (base64.b64encode(audio_file.read())).decode()
    audio_file.close()

    voice = {
      "config":
      {
        "languageCode": "cmn-Hans-CN"
      },

      "audio":
      {
        "content": audio_b64str
      }
    }
    voice = json.dumps(voice).encode('utf8')
    print('编码完成。正在上传语音...')
    req = urllib.request.Request(api_url, data=voice, headers={'content-type': 'application/json'})
    response = urllib.request.urlopen(req)
    response_str = response.read().decode('utf8')
    response_dic = json.loads(response_str)

    if ('results' not in response_dic.keys()):
        print('您录制的文件似乎没有声音,请检查麦克风。')
        return
    
    transcript = response_dic['results'][0]['alternatives'][0]['transcript']
    confidence = response_dic['results'][0]['alternatives'][0]['confidence']
    result_dic = {'text':transcript ,'confidence':confidence}
    print('识别完成。以字典格式输出:')
    print(result_dic)
    
    return result_dic

if __name__ == '__main__':
    wav_to_text()

获取文字回答

参考:使用Python与图灵机器人聊天

这个获取回答的程序有些粗糙,只能获得普通的文字回答。实际上图灵机器人回复的内容中包括了文字、问题类型甚至情感等信息,还有很多修改的空间。

* 文件 turing.py

import json
import urllib.request

def chat(question):
    api_url = "http://openapi.tuling123.com/openapi/api/v2"
    text_input = question['text']
    req = {
        "perception":
        {
            "inputText":
            {
                "text": text_input
            },

            "selfInfo":
            {
                "location":
                {
                    "city": "上海",
                    "province": "上海",
                    "street": "文汇路"
                }
            }
        },

        "userInfo": 
        {
            "apiKey": "替换成你的APIKEY",
            "userId": "用户参数"
        }
    }
    # 将字典格式的req转为utf8编码的字符串
    req = json.dumps(req).encode('utf8')
    
    print('\n' + '正在调用图灵机器人API...')
    http_post = urllib.request.Request(api_url, data=req, headers={'content-type': 'application/json'})
    response = urllib.request.urlopen(http_post)
    
    print('得到回答,输出为字典格式:')
    response_str = response.read().decode('utf8')
    response_dic = json.loads(response_str)
    intent_code = response_dic['intent']['code']
    
    # 返回网页类的输出方式
    if(intent_code == 10023):
        results_url = response_dic['results'][0]['values']['url']
        results_text = response_dic['results'][1]['values']['text']
        answer = {"code": intent_code, "text": results_text, "url":results_url}
        print(answer)
        return(answer)
    # 一般的输出方式
    else:
        results_text = response_dic['results'][0]['values']['text']
        answer = {"code": intent_code, "text": results_text}
        print(answer)
        return(answer)
    
if __name__ == '__main__':
    eg_question = {'text': '今天是几号', 'confidence': 0.9}
    chat(eg_question)

读出回答(语音合成)

参考:在Python中使用科大讯飞Web API进行语音合成

笔者在使用讯飞Web API时,该服务才开放不到一周,难免以后该API会有所变动,如有问题建议查阅官方文档。

* 文件 tts.py

import base64
import json
import time
import hashlib
import urllib.request
import urllib.parse
import os

def speak(text_content):
    # API请求地址、API KEY、APP ID等参数,提前填好备用
    api_url = "http://api.xfyun.cn/v1/service/v1/tts"
    API_KEY = "替换成你的APIKEY"
    APP_ID = "替换成你的APPID"
    AUE = "lame"

    # 构造输出音频配置参数
    Param = {
        "auf": "audio/L16;rate=16000",    #音频采样率
        "aue": AUE,    #音频编码,raw(生成wav)或lame(生成mp3)
        "voice_name": "xiaoyan",
        "speed": "50",    #语速[0,100]
        "volume": "10",    #音量[0,100]
        "pitch": "50",    #音高[0,100]
        "engine_type": "aisound"    #引擎类型。aisound(普通效果),intp65(中文),intp65_en(英文)
    }
    # 配置参数编码为base64字符串,过程:字典→明文字符串→utf8编码→base64(bytes)→base64字符串
    Param_str = json.dumps(Param)    #得到明文字符串
    Param_utf8 = Param_str.encode('utf8')    #得到utf8编码(bytes类型)
    Param_b64 = base64.b64encode(Param_utf8)    #得到base64编码(bytes类型)
    Param_b64str = Param_b64.decode('utf8')    #得到base64字符串

    # 构造HTTP请求的头部
    time_now = str(int(time.time()))
    checksum = (API_KEY + time_now + Param_b64str).encode('utf8')
    checksum_md5 = hashlib.md5(checksum).hexdigest()
    header = {
        "X-Appid": APP_ID,
        "X-CurTime": time_now,
        "X-Param": Param_b64str,
        "X-CheckSum": checksum_md5
    }

    # 构造HTTP请求Body
    body = {
        "text": text_content
    }
    body_urlencode = urllib.parse.urlencode(body)
    body_utf8 = body_urlencode.encode('utf8')

    # 发送HTTP POST请求
    print('\n' + "正在调用科大讯飞语音合成API...")
    req = urllib.request.Request(api_url, data=body_utf8, headers=header)
    response = urllib.request.urlopen(req)

    # 读取结果
    response_head = response.headers['Content-Type']
    if(response_head == "audio/mpeg"):
        out_file = open('/home/pi/chat/answer.mp3', 'wb')
        data = response.read() # a `bytes` object
        out_file.write(data)
        out_file.close()
        print('得到结果,输出文件: /home/pi/chat/answer.mp3')
    else:
        print(response.read().decode('utf8'))

    # 播放音频
    print("播放音频中...")
    print("以下均为mplayer的输出内容\n")
    os.system("mplayer -ao alsa:device=hw=1.0 /home/pi/chat/answer.mp3")
    
    return
    
if __name__ == '__main__':
    eg_text_content = "苟利国家生死以,岂因祸福避趋之"
    speak(eg_text_content)

整合&测试

现在,你的项目文件夹中应该有这些python代码文件:

接下来我们只需要将他们整合在一起运行。

* 文件 combine.py

# 这些import进来的模块是同目录下的py文件
import rec    # rec.py负责录制wav音频
import speech_api    # speech_api.py负责wav转文字
import turing    # turing.py负责获得图灵机器人的文字回答
import tts    # tts.py负责读出回答

rec.rec_fun()    # 录制音频
recognize_result = speech_api.wav_to_text()    # 识别语音,返回值是字典格式,包含文字结果和信心
turing_answer = turing.chat(recognize_result)    # 得到图灵的回答,返回值仍是字典格式
tts.speak(turing_answer['text'])

如果一切顺利的话,实际运行效果如下:
树莓派_语音助手_youku

小结

语音助手这边的工作算是告一段落了,结果小结却不知道怎么写了。不管怎么说,很开心最后能得到实际的结果,做的过程中也有一些脑洞想要继续扩展,过段时间应该还会继续!

做这个项目的过程中,项目外的收获或许比这个项目本身还要多。这段时间从很多博客、论坛得到了数不尽的帮助,国内的、国外的、中文的、英文的、日文的都有,深深地感受到了互联网共享精神的力量,这也是促使我开始写这些文章的原因。那么,最后还是说一句:感谢你阅读文章!

查看原文

赞 10 收藏 4 评论 5

Keif 发布了文章 · 2019-06-29

使用Python把树莓派改造成一个语音助手

对博客平台有些失望,转语雀只当留档用了。

语音助手已经不是什么新事物了。就在两三年前,语音助手的使用体验还不是那么好,尝尝鲜后也就没用过了。但最近发现不管是微软的Cortana、苹果的Siri,还是一些不怎么有名气的,例如MIUI的小爱同学等,使用体验真的改善了很多,确确实实能带来一些方便了。

随着各种云服务、API的面世,语音方面的云服务可以说是十分健全了。你是否也想过自己动手搭建一个语音助手系统呢?本文将总结使用Python把树莓派(3代b型)改造成一个简易语音助手的基本流程。

概述

这次要做的说白了,就是把各种云服务、API串起来,并不涉及任何核心技术、算法的实现,望知悉。

这次将要使用到的服务包括:

为了实现这个语音助手系统,需要完成的工作每一个都不难,但数量稍多了些。以下是涉及到的一些博客:

后文在介绍各部分的具体实现时,只附上代码和进行一些必要的说明,详细内容还需要参考相应博客。

各部分的实现

由于整个项目用到的服务比较多,而且各部分的分工很明显,所以选择各部分分别用一个python程序来实现,最后再用一个程序整合在一起的方式。

录音

参考:树莓派学习手记——使用Python录音

笔者采用了“按住按钮进行录音”的操作方式,如下图所示接线。如果你手头上没有按钮或觉得这么做不方便,可以修改代码改成“按回车键开始/结束录音”之类的操作方式。

另外,树莓派的板载3.5mm耳机接口是不带语音输入功能的,所以你需要另外购买USB声卡。

* 文件 rec.py

import RPi.GPIO as GPIO
import pyaudio
import wave
import os
import sys

def rec_fun():
    # 隐藏错误消息,因为会有一堆ALSA和JACK错误消息,但其实能正常录音
    os.close(sys.stderr.fileno())
    
    BUTT = 26    # 开始录音的按钮:一边接GPIO26,一边接地
    GPIO.setmode(GPIO.BCM)
    # 设GPIO26脚为输入脚,电平拉高,也就是说26脚一旦读到低电平,说明按了按钮
    GPIO.setup(BUTT, GPIO.IN, pull_up_down = GPIO.PUD_UP)

    # wav文件是由若干个CHUNK组成的,CHUNK我们就理解成数据包或者数据片段。
    CHUNK = 512 
    FORMAT = pyaudio.paInt16  # pyaudio.paInt16表示我们使用量化位数 16位来进行录音
    RATE = 44100  # 采样率 44.1k,每秒采样44100个点。
    WAVE_OUTPUT_FILENAME = "/home/pi/chat/command.wav"
    print('请按住按钮开始录音...')
    GPIO.wait_for_edge(BUTT, GPIO.FALLING)

    # To use PyAudio, first instantiate PyAudio using pyaudio.PyAudio(), which sets up the portaudio system.
    p = pyaudio.PyAudio()
    stream = p.open(format = FORMAT,
                    channels = 1,    # cloud speecAPI只支持单声道
                    rate = RATE,
                    input = True,
                    frames_per_buffer = CHUNK)
    print("录音中...")

    frames = []
    # 按住按钮录音,放开时结束
    while GPIO.input(BUTT) == 0:
        data = stream.read(CHUNK)
        frames.append(data)
    print("录音完成,输出文件:" + WAVE_OUTPUT_FILENAME + '\n')
    stream.stop_stream()
    stream.close()
    p.terminate()

    wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
    wf.setnchannels(1)
    wf.setsampwidth(p.get_sample_size(FORMAT))    # Returns the size (in bytes) for the specified sample format.
    wf.setframerate(RATE)
    wf.writeframes(b''.join(frames))
    wf.close()
    
    return

# 可以直接运行rec.py进行测试,同时保证该文件import时不会自动运行
if __name__ == '__main__':
    rec_fun()

语音识别

参考:

使用Google云计算引擎实现科学上网

在Windows命令行、Linux终端使用代理

在Python中使用谷歌Cloud Speech API将语音转换为文字(另一种方案)

由于某些原因,笔者选择了使用谷歌Cloud Speech API进行语音识别。既然要用谷歌的服务,自然就涉及到了科学上网、代理、谷歌云平台的使用,如果不想这么折腾,完全可以用国内的讯飞、百度来实现。

另外,API KEY之类的字符串在这里删除了,还请麻烦修改代码加上你自己申请的API KEY。

* 文件 speech_api.py

import json
import urllib.request
import base64

def wav_to_text():
    api_url = "https://speech.googleapis.com/v1beta1/speech:syncrecognize?key=替换成你的API密钥"
    print('语音文件编码中...')
    audio_file = open('/home/pi/chat/command.wav', 'rb')
    audio_b64str = (base64.b64encode(audio_file.read())).decode()
    audio_file.close()

    voice = {
      "config":
      {
        "languageCode": "cmn-Hans-CN"
      },

      "audio":
      {
        "content": audio_b64str
      }
    }
    voice = json.dumps(voice).encode('utf8')
    print('编码完成。正在上传语音...')
    req = urllib.request.Request(api_url, data=voice, headers={'content-type': 'application/json'})
    response = urllib.request.urlopen(req)
    response_str = response.read().decode('utf8')
    response_dic = json.loads(response_str)

    if ('results' not in response_dic.keys()):
        print('您录制的文件似乎没有声音,请检查麦克风。')
        return
    
    transcript = response_dic['results'][0]['alternatives'][0]['transcript']
    confidence = response_dic['results'][0]['alternatives'][0]['confidence']
    result_dic = {'text':transcript ,'confidence':confidence}
    print('识别完成。以字典格式输出:')
    print(result_dic)
    
    return result_dic

if __name__ == '__main__':
    wav_to_text()

获取文字回答

参考:使用Python与图灵机器人聊天

这个获取回答的程序有些粗糙,只能获得普通的文字回答。实际上图灵机器人回复的内容中包括了文字、问题类型甚至情感等信息,还有很多修改的空间。

* 文件 turing.py

import json
import urllib.request

def chat(question):
    api_url = "http://openapi.tuling123.com/openapi/api/v2"
    text_input = question['text']
    req = {
        "perception":
        {
            "inputText":
            {
                "text": text_input
            },

            "selfInfo":
            {
                "location":
                {
                    "city": "上海",
                    "province": "上海",
                    "street": "文汇路"
                }
            }
        },

        "userInfo": 
        {
            "apiKey": "替换成你的APIKEY",
            "userId": "用户参数"
        }
    }
    # 将字典格式的req转为utf8编码的字符串
    req = json.dumps(req).encode('utf8')
    
    print('\n' + '正在调用图灵机器人API...')
    http_post = urllib.request.Request(api_url, data=req, headers={'content-type': 'application/json'})
    response = urllib.request.urlopen(http_post)
    
    print('得到回答,输出为字典格式:')
    response_str = response.read().decode('utf8')
    response_dic = json.loads(response_str)
    intent_code = response_dic['intent']['code']
    
    # 返回网页类的输出方式
    if(intent_code == 10023):
        results_url = response_dic['results'][0]['values']['url']
        results_text = response_dic['results'][1]['values']['text']
        answer = {"code": intent_code, "text": results_text, "url":results_url}
        print(answer)
        return(answer)
    # 一般的输出方式
    else:
        results_text = response_dic['results'][0]['values']['text']
        answer = {"code": intent_code, "text": results_text}
        print(answer)
        return(answer)
    
if __name__ == '__main__':
    eg_question = {'text': '今天是几号', 'confidence': 0.9}
    chat(eg_question)

读出回答(语音合成)

参考:在Python中使用科大讯飞Web API进行语音合成

笔者在使用讯飞Web API时,该服务才开放不到一周,难免以后该API会有所变动,如有问题建议查阅官方文档。

* 文件 tts.py

import base64
import json
import time
import hashlib
import urllib.request
import urllib.parse
import os

def speak(text_content):
    # API请求地址、API KEY、APP ID等参数,提前填好备用
    api_url = "http://api.xfyun.cn/v1/service/v1/tts"
    API_KEY = "替换成你的APIKEY"
    APP_ID = "替换成你的APPID"
    AUE = "lame"

    # 构造输出音频配置参数
    Param = {
        "auf": "audio/L16;rate=16000",    #音频采样率
        "aue": AUE,    #音频编码,raw(生成wav)或lame(生成mp3)
        "voice_name": "xiaoyan",
        "speed": "50",    #语速[0,100]
        "volume": "10",    #音量[0,100]
        "pitch": "50",    #音高[0,100]
        "engine_type": "aisound"    #引擎类型。aisound(普通效果),intp65(中文),intp65_en(英文)
    }
    # 配置参数编码为base64字符串,过程:字典→明文字符串→utf8编码→base64(bytes)→base64字符串
    Param_str = json.dumps(Param)    #得到明文字符串
    Param_utf8 = Param_str.encode('utf8')    #得到utf8编码(bytes类型)
    Param_b64 = base64.b64encode(Param_utf8)    #得到base64编码(bytes类型)
    Param_b64str = Param_b64.decode('utf8')    #得到base64字符串

    # 构造HTTP请求的头部
    time_now = str(int(time.time()))
    checksum = (API_KEY + time_now + Param_b64str).encode('utf8')
    checksum_md5 = hashlib.md5(checksum).hexdigest()
    header = {
        "X-Appid": APP_ID,
        "X-CurTime": time_now,
        "X-Param": Param_b64str,
        "X-CheckSum": checksum_md5
    }

    # 构造HTTP请求Body
    body = {
        "text": text_content
    }
    body_urlencode = urllib.parse.urlencode(body)
    body_utf8 = body_urlencode.encode('utf8')

    # 发送HTTP POST请求
    print('\n' + "正在调用科大讯飞语音合成API...")
    req = urllib.request.Request(api_url, data=body_utf8, headers=header)
    response = urllib.request.urlopen(req)

    # 读取结果
    response_head = response.headers['Content-Type']
    if(response_head == "audio/mpeg"):
        out_file = open('/home/pi/chat/answer.mp3', 'wb')
        data = response.read() # a `bytes` object
        out_file.write(data)
        out_file.close()
        print('得到结果,输出文件: /home/pi/chat/answer.mp3')
    else:
        print(response.read().decode('utf8'))

    # 播放音频
    print("播放音频中...")
    print("以下均为mplayer的输出内容\n")
    os.system("mplayer -ao alsa:device=hw=1.0 /home/pi/chat/answer.mp3")
    
    return
    
if __name__ == '__main__':
    eg_text_content = "苟利国家生死以,岂因祸福避趋之"
    speak(eg_text_content)

整合&测试

现在,你的项目文件夹中应该有这些python代码文件:

接下来我们只需要将他们整合在一起运行。

* 文件 combine.py

# 这些import进来的模块是同目录下的py文件
import rec    # rec.py负责录制wav音频
import speech_api    # speech_api.py负责wav转文字
import turing    # turing.py负责获得图灵机器人的文字回答
import tts    # tts.py负责读出回答

rec.rec_fun()    # 录制音频
recognize_result = speech_api.wav_to_text()    # 识别语音,返回值是字典格式,包含文字结果和信心
turing_answer = turing.chat(recognize_result)    # 得到图灵的回答,返回值仍是字典格式
tts.speak(turing_answer['text'])

如果一切顺利的话,实际运行效果如下:
树莓派_语音助手_youku

小结

语音助手这边的工作算是告一段落了,结果小结却不知道怎么写了。不管怎么说,很开心最后能得到实际的结果,做的过程中也有一些脑洞想要继续扩展,过段时间应该还会继续!

做这个项目的过程中,项目外的收获或许比这个项目本身还要多。这段时间从很多博客、论坛得到了数不尽的帮助,国内的、国外的、中文的、英文的、日文的都有,深深地感受到了互联网共享精神的力量,这也是促使我开始写这些文章的原因。那么,最后还是说一句:感谢你阅读文章!

查看原文

赞 10 收藏 4 评论 5

Keif 发布了文章 · 2019-06-28

【日更】今天的日语单词

2019年2月12日
おとしだま【お年玉】
压岁钱;新年礼物.

  • お年玉(としだま)をもらう。/得到压岁钱。
  • お年玉(としだま)をあげる。/给压岁钱。
  • お年(とし)玉(だま)付(つ)き年(ねん)賀(が)はがき。/带抽奖的贺年卡。

【説明(せつめい)】新年(しんねん)の祝(いわ)いに子供(こども)など目下(めした)のものに贈(おく)るお金(かね)やプレゼントのこと。親(おや)や祖父母(そふぼ)、親戚(しんせき)の叔父(おじ)・叔母(おば)などからもらうお年玉(としだま)を、子供(こども)たちは非常(ひじょう)に楽(たの)しみにしている。
【中訳(ちゅうやく)】为祝贺新年,由长辈给孩子、晚辈的钱或者礼物。临近新年时,孩子们都盼望着能从父母、祖父母和其他长辈亲戚那里得到压岁钱。


2019年2月13日
かぜ【風邪】
感冒;伤风;流感.

  • 風邪(かぜ)を引(ひ)く。/患感冒;伤风;着凉。
  • 風邪(かぜ)が治(なお)る。/感冒好了。
  • 風邪(かぜ)がなかなか抜(ぬ)けない。/感冒老也不好。
  • 風邪(かぜ)で会社(かいしゃ)を休(やす)む。/因感冒不去公司上班。
  • 風邪(かぜ)がはやっている。/感冒很流行。
  • 弟(おとうと)の風邪(かぜ)がうつったようだ。/弟弟的感冒好像传染我了。

【慣用句(かんようく)】
風邪(かぜ)は万病(まんびょう)の元(もと)。/伤风感冒是万病之源。


2019年2月14日
じゃぐち【蛇口】
水龙头.

  • 蛇口(じゃぐち)を締(し)める。/关上水龙头。
  • 蛇口(じゃぐち)をあける。/打开水龙头。

【大约10年前的N1真题,选择汉字的读法】
蛇口から直接水(ちょくせつみず)を飲(の)む。
①じゃぐち ②びぐち ③びこう ④じゃこう
(日本的自来水可以直接饮用。)


2019年2月15日
ナシ【梨】
梨;梨子;梨树.

  • このナシは甘(あま)くて美味(おい)しい。/这种梨味道香甜。
  • 鳥取産(とっとりさん)のナシ(なし)。/鸟取县出产的梨子。

(鳥取県(とっとりけん):日本本州西部的一个县,是日本所有都道府県(とどうふけん)中人口最少的一个,盛产梨。)

【慣用句(かんようく)】
ナシのつぶて。/杳无音信;石沉大海。
「梨(なし)」和「無(な)し」读音相同;
礫(つぶて):小石子;
「ナシのつぶて」字面意思是“扔出去的小石子(找不回来)”,玩了个同音的文字游戏,写成「梨(なし)の礫(つぶて)」。
【例】何遍(なんべん)か催促(さいそく)したが、梨(なし)の礫(つぶて)だ。/我一再催促,但都杳无音信。


2019年2月16日
けんきゅうせい【研究生】
无中文释义,类似考研预科班.
【説明】
留学生(りゅうがくせい)の場合(ばあい)、最初(さいしょ)の半年(はんとし)から一年程度(いちねんていど)を研究生(けんきゅうせい)として指導教員(しどうきょういん)の指導(しどう)の下(もと)で研究(けんきゅう)を行(おこな)ったり、日本語(にほんご)を勉強(べんきょう)をしたりして過(す)ごし、大学院(だいがくいん)への入学(にゅうがく)の準備(じゅんび)をするという事例(じれい)がよく見(み)られます。/留学生最初的半年到一年时间是在指导员的指导下作为研究生(けんきゅうせい),一边进行研究一边学习日语度过的,以准备研究生院的入学考试。

いんせい【院生】
研究生.

  • 教授(きょうじゅ)の指導(しどう)の下(もと)で院生(いんせい)が論文(ろんぶん)を執筆(しっぴつ)している。/在教授的指导下研究生执笔写论文。

しゅうし【修士】
硕士.

  • 大学院(だいがくいん)の修士課程(しゅうしかてい)を終(お)えて博士(はかせ)へ進(すす)む。/读完硕士继续读博士。

2019年2月17日
ごい【語彙】
词汇.

  • 語彙(ごい)が豊(ゆた)かだ〔乏(とぼ)しい〕。/词汇很丰富〔贫乏〕。

ぶんぽう【文法】
语法.

  • 英文法(えいぶんぽう)の授業(じゅぎょう)をサボった。/旷了英语语法课。
  • 彼(かれ)の書(か)く英語(えいご)は文法(ぶんぽう)がでたらめだ。/他写的英语,语法乱七八糟。

どっかい【読解】
阅读理解.

  • 多(おお)くの本(ほん)を読(よ)み、読解力(どっかいりょく)をつける。/多读书,从而提高阅读理解能力。

ちょうかい【聴解】
听力.


2019年2月18日
なさけ【情け】
慈悲;同情;人情.

  • 情(なさ)けのある人(ひと)。/有同情心的人;乐善好施的人。
  • 人(ひと)の情(なさ)けが身(み)にしみる。/感受到了人情温暖。
  • あいつに情(なさ)けをかけてはいけない。/对那个人可不能怜悯。

【慣用句(かんようく)】

  • 悪女(あくじょ)の深情(ふかなさ)け。/丑女多情〔美女薄情〕;令人为难的好意。
  • 旅(たび)は道連(みちづ)れ、世(よ)は情(なさ)け。/行要好伴,住要好邻;行旅要伴侣,处世要互助。

2019年2月19日
うしなう【失う】
失去;错过;迷失.

  • 自信(じしん)を失(うしな)う。/失去信心。
  • バランスを失(うしな)う。/失去平衡。
  • 最後(さいご)のチャンスを失(うしな)った。/错过了最后一次机会。
  • 山(やま)の中(なか)で方角(ほうがく)を失(うしな)った。/在山里迷失了方向。
  • 毎(まい)年(とし)交(こう)通(つう)事(じ)故(こ)で命(いのち)を失(うしな)う人(ひと)は一万人(いちまんにん)を超(こ)える。/每年因交通事故丧命的人超过一万人。
  • 色(いろ)を失(うしな)う。/〔受到惊吓而〕失色。
  • 客(きゃく)車(しゃ)が暴(ぼう)走(そう)し、誰(だれ)もが色(いろ)を失(うしな)った。/客车失控了,每个人都大惊失色。

2019年2月20日
やくそく【約束】
约定;协定;说好.

  • 約(やく)束(そく)を守(まも)る。/守约。
  • 約(やく)束(そく)を破(やぶ)る。/失约。
  • 口(くち)約(やく)束(そく)では危(あぶ)ない、契(けい)約(やく)書(しょ)を交(か)わしたほうがいい。/口头承诺是靠不住的,还是签个合同比较好。
  • 約(やく)束(そく)した以(い)上(じょう)、必(かなら)ず来(く)るだろう。/既然约好了,肯定会来。
  • 無(む)言(ごん)の約(やく)束(そく)を取(と)り交(か)わす。/(无形之中)达成了默契。

まどろみの約束https://music.163.com/#/song?...


2019年2月21日
凪【なぎ】
风平浪静〔体言(也就是名词)〕.

  • 朝(あさ)凪(なぎ)。/早晨海上风平浪静。
  • 凪(なぎ)になってから船(ふね)を出(だ)す。/等到风平浪静后再出航。

凪ぐ【なぐ】
风平浪静;(风)停〔用言(也就是动词)〕.

  • 海(うみ)が凪(な)いできた。/大海平静了。
  • 昼(ひる)ごろになると風(かぜ)が急(きゅう)にないだ。/到了午间风突然平息了。

2019年2月23日
きのこ ・ キノコ
蘑菇(一般不写汉字,汉字写出来是 木(き)の子(こ) ・ 茸(きのこ) ).

  • キノコ狩(が)りに行(い)こう。/去采蘑菇吧。
  • きのこが生(は)えた。/长蘑菇了。
  • きのこ雲(ぐも)。/蘑菇云。

2019年2月25日
さくらぜんせん【桜前線】
樱花(由南向北)开放的前锋.
【説明(せつめい)】気象庁(きしょうちょう)による桜(さくら)の開花予想図(かいかよそうず)に見(み)られるもの。ソメイヨシノを対象(たいしょう)としており、同(おな)じ日(ひ)に開花(かいか)する地点(ちてん)を線(せん)で結(むす)ぶ。日本(にほん)は南北(なんぼく)に長(なが)いため、三月末(さんがつすえ)から九州(きゅうしゅう)より北上(ほくじょう)を始(はじ)め、五月初(ごがつはじ)めに北海道(ほっかいどう)に至(いた)る。開花予想(かいかよそう)は花見(はなみ)や旅行(りょこう)をする人(ひと)たちに特(とく)に注目(ちゅうもく)される。
【中(ちゅう)訳(やく)】这个词出自气象厅绘制的樱花开花时间预报图。开花预报以“染井吉野樱花”为代表,在地图上把同时开花的地点用线条连接起来,从而形成由南向北推移的“樱花前锋”。日本国土南北跨度较大,每年三月底,樱花前锋从九州开始北上,一直要到五月初才能到达北海道。开花预报尤其受到赏花人和旅行者的关注。

ばいうぜんせん【梅雨前線】
梅雨前锋.
おんだんぜんせん【温暖前線】
暖锋.
かんれいぜんせん【寒冷前線】
冷锋.


2019年2月27日
いなずま【稲妻】
闪电.
(古代(こだい)、稲(いね)の結実時期(けつじつじき)に雷(かみなり)が多(おお)いことから、雷光(らいこう)が稲(いね)を実(みの)らせるという信仰(しんこう)があった。/在古代,因为稻子结果的时期经常打雷,所以人们认为雷光与稻子交配使其结果,故称闪电为稲(いな)妻(ずま)。)

  • 稲妻(いなずま)が走(はし)り、雷鳴(らいめい)がとどろいた。/电光闪闪,雷声隆隆。
  • 稲妻(いなずま)のような速(はや)さで身(み)を隠(かく)した。/飞快地躲了起来。
  • 稲妻形(いなずまかたち)。/闪电形;之字形。

2019年2月28日
みのる【実る】
结果;有成果.

  • 今(ことし)年はイネがよく実(みの)った。/今年稻子收成好。
  • ブドウがたわわに実(みの)っている。/串串葡萄压弯了树枝。
  • 長(なが)年(ねん)の努(ど)力(りょく)がやっと実(みの)った。/多年的努力终于有了成果。
  • 実(みの)るほど頭(あたま)の下(さ)がる稲(いな)穂(ほ)かな。/粒满穗垂,知博益谦;越是成熟越谦虚。

2019年3月2日
きしべ【岸辺】
岸边.

  • 岸(きし)辺(べ)を散(さん)策(さく)する。/在岸边散步。
  • 岸(きし)辺(べ)にボートを着(つ)ける。/把小船靠在岸边。
  • 波(なみ)が次(つぎ)から次(つぎ)へと岸(きし)辺(べ)に打(う)ち返(かえ)す。/波浪一波接一波地冲向岸边。

2019年3月4日
やきもち【焼き餅】
烤年糕;吃醋;嫉妒.

  • つまらないことにもすぐやきもちを焼(や)く。/一点点小事也马上忌妒起来。
  • やたらにやきもちを焼(や)く。/爱吃醋。
  • 妻(つま)の男(おとこ)友(とも)達(だち)にやきもちを焼(や)く。/嫉妒妻子的男性朋友。
  • 母(はは)親(おや)をとられた気(き)がして兄(あに)は赤(あか)ん坊(ぼう)の弟(おとうと)にやきもちを焼(や)いた。/哥哥觉得母亲被抢走了,嫉妒起还在襁褓中的弟弟来。

2019年3月4日
とうとい【尊い】
宝贵;高贵.

  • 尊(とうと)い経験(けいけん)。/宝贵的经验。
  • 地震(じしん)は多(おお)くの尊(とうと)い人命(じんめい)を奪(うば)った。/地震夺去了许多宝贵的生命。
  • 病気(びょうき)になって健康(けんこう)の尊(とうと)さを知(し)った。/得病以后才知道健康的宝贵。
  • 尊(とうと)い身分(みぶん)。/高贵的身份。

2019年3月8日
しかる【叱る】
责备;训斥;忠告.

  • 先生(せんせい)に叱(しか)られる。/被老师批评了。
  • 私(わたし)は子供(こども)の時(とき)いたずらっ子(こ)だったので、よく叱(しか)られた。/我小时候因为是个小淘气,所以时常挨骂。
  • 彼(かれ)は部下(ぶか)を叱(しか)るとき、少(すこ)しも容赦(ようしゃ)しない。/他批评起部下来毫不留情。
  • 何(なん)べんも叱(しか)ったのに、彼(かれ)はどうしても改(あらた)めようとしない。/说过他好几次,他就是不改。

2019年3月9日
たどる【辿る】
沿着(道路);探索;趋于.

  • 家(いえ)路(じ)を辿(たど)る。/走上回家的路。
  • 記(き)憶(おく)を辿(たど)りながら思(おも)い出(で)の場(ば)所(しょ)を探(さが)した。/凭着记忆寻找印象中的故地。
  • 中(ちゅう)国(ごく)文(ぶん)明(めい)の源(げん)流(りゅう)をたどる。/追溯中国文明的起源。
  • 売(う)り上(あ)げが下(か)降(こう)線(せん)を辿(たど)る。/销售额下降。

2019年3月11日
ちゅうこ【中古】
二手,半旧.

  • 中(ちゅう)古(こ)品(ひん)。/二手货。
  • 中(ちゅう)古(こ)のレコードを買(か)う。/买旧唱片。
  • あの中(ちゅう)古(こ)車(しゃ)はいい値(あたい)で売(う)れた。/那辆二手车卖了个好价钱。
  • やっぱり中(ちゅう)古(こ)ではなく新(しん)品(ぴん)のギターが欲(ほ)しい。/我还是想要新的吉他,不想要旧的。

2019年3月12日
おとなしい【大人しい】
老实;规规矩矩;温顺;雅致.

  • おとなしい子供(こども)。/老实的孩子。
  • このイヌはおとなしい。/这只狗很温顺。
  • 敵(てき)はおとなしく銃(じゅう)を捨(す)てて降(こう)参(さん)した。/敌人乖乖地缴枪投降了。
  • この柄(がら)はおとなしい。/这种纹样很素雅。

2019年3月16日
はれる【晴れる】
晴;(云雾)消散;(疑虑)消散;(心情)舒畅.

  • 明(あ)日(す)は晴(は)れでしょう。/明天该放晴了吧。
  • 空(そら)が晴(は)れて星(ほし)がよく見(み)える。/天空晴朗,星星清晰可见。
  • 晴(は)れた日(ひ)にはここから富(ふ)士(じ)の全(ぜん)景(けい)が一(いち)望(ぼう)できる。/天晴的时候从这儿可以望见富士山的全景。
  • 真(しん)犯(はん)人(にん)逮(たい)捕(ほ)で、やっと私(わたし)の疑(ぎ)惑(わく)が晴(は)れた。/真正的犯人被捕后,对我的嫌疑才消除了。
  • 心(こころ)のもやもやがまだ晴(は)れない。/心里的疙瘩还没解开。

2019年3月18日
さび【錆】
铁锈;恶果.

  • ナイフの錆(さび)を落(お)とす。/把小刀上的绣去掉。
  • この包丁(ほうちょう)は長(なが)い間使(あいだつか)ってなかったから錆(さび)だらけだ。/这把菜刀由于长期没有使用,刀上尽是锈。
  • 身(み)から出(で)た錆(さび)。/自食其果;咎由自取。
  • 自慢(じまん)の腕(うで)もすっかり錆(さび)ついてしまった。/拿手的技艺也完全荒废了。

2019年3月19日
はじける【弾ける】
裂开;爆开;迸发.

  • クリのいがが弾(はじ)ける。/栗子的毛壳绷开了。
  • バブルが弾(はじ)ける。/泡沫经济崩溃。
  • 彼(かれ)はとても太(ふと)っていて、洋(よう)服(ふく)が弾(はじ)けそうだ。/他太胖了,西服都快要绽开了。
  • 笑(わら)いが弾(はじ)ける。/迸发出笑声。
  • 弾(はじ)けそうな若(わか)さ。/迸发的青春活力。

2019年4月8日
ざわめく【騒めく】
人声嘈杂;(树叶等)沙沙作响

  • 町(まち)中(なか)がその知(し)らせ(せ)でざわめいていた。/全城因为这个消息轰动了起来。
  • 新婦(しんぷ)のお色直(いろなお)しに会場(かいじょう)がざわめいた。/新娘换了衣服走进来,会场立刻一片嘁嘁喳喳声。
  • 質問者(しつもんしゃ)の発言(はつげん)に一瞬会場(いっしゅんかいじょう)がざわめいた。/提问者的发言使会场喧哗了一阵。

2019年4月16日
あらそう【争う】
争夺;对抗;争辩;

  • 一(いっ)刻(こく)を争(あらそ)うときだ。/是分秒必争的时候。
  • 住(じゅう)民(みん)たちは日(にっ)照(しょう)権(けん)のことで役(やく)所(しょ)と激(はげ)しく争(あらそ)った。/因日照权的问题,居民们同有关部门进行了激烈的争论。
  • 年(とし)は争(あらそ)えない。/岁月不饶人;要服老。
  • 血(ち)は争(あらそ)えない。/虎父无犬子;血浓于水。
  • 顔(かお)も声(こえ)もそっくりで、流石(さすが)に血(ち)は争(あらそ)えない。/长相、声音都如出一辙,到底是一家人。

2019年4月22日
はかない【儚い・果敢ない・果敢無い】
短暂;虚幻无常;

  • 人(ひと)の一生(いっしょう)ははかないものだ。/人的一生是短暂无常的;人生如梦。
  • はかない生(しょう)涯(がい)を閉(と)じる。/结束了短暂的一生。
  • 儚(はかな)い最(さい)期(ご)を遂(と)げる。/死得可怜。

2019年4月26日
てどり【手取り】
(扣除税金保险等费用后的)净收入.

  • 税(ぜい)引(び)き後(ご)の手(て)取(ど)り金(きん)額(がく)。/扣除税款后的纯收入。
  • 私(わたし)の給(きゅう)料(りょう)はいろいろな手(て)当(あて)まで含(ふく)めて月(つき)に手(て)取(ど)り30(さんじゅう)万(まん)円(えん)です。/我的工资加上各种补贴最后到手的差不多每个月30万日元。

てとりあしとり【手取り足取り】
手把手;耳提面命.

  • じっくりと手(て)取(と)り足(あし)取(と)り指(し)導(どう)する。/手把手地耐心指导。
  • 彼(かれ)の技(ぎ)術(じゅつ)は私(わたし)が手(て)取(と)り足(あし)取(と)り教(おし)えてやったのだ。/他的技术是我手把手教的。

2019年4月27日
すきとおる【透き通る】
透明;清澈.

  • この池(いけ)の水(みず)は透(す)き通(とお)っている。/这个池子的水清澈见底。
  • この生(き)地(じ)は薄(うす)くて透(す)き通(とお)って見(み)える。/这块布薄得透明。
  • 北(きた)国(ぐに)の人(ひと)は透(す)き通(とお)るような白(しろ)い肌(はだ)をしている。/北方人皮肤白皙透明。
  • あの歌(か)手(しゅ)は透(す)き通(とお)った声(こえ)が魅(み)力(りょく)だ。/那个歌手清脆的歌声很有魅力。

2019年4月28日
こよみ【暦】
日历;历法.

  • 暦(こよみ)の上(うえ)では春(はる)だが、まだ随(ずい)分(ぶん)寒(さむ)い。/论节气已经到了春天,但还是很冷。
  • 掛(か)け暦(こよみ)/挂历
  • 卓(たく)上(じょう)暦(こよみ)/台历

2019年5月7日
わく【湧く】
涌出;涌现;产生.

  • 温泉(おんせん)が湧(わ)く。/温泉涌出。
  • あなたのお話(はなし)を聞(き)いて希望(きぼう)が湧(わ)いてきた。/听了你的话,我产生了希望。
  • いいアイデアが突(とつ)然(ぜん)湧(わ)いた。/突然想出个好主意。
  • 歴史(れきし)に興味(きょうみ)がわく。/对历史产生兴趣。
  • 東(ひがし)の空(そら)に雷雲(らいうん)がわく。/东方的天空中涌现出雷云。

2019年5月8日
カバー
外皮;罩子;弥补;填补.

  • 本(ほん)にカバーを掛(か)ける。/给书包一个书皮。
  • 他部門(ほかぶもん)の収益(しゅうえき)で赤字(あかじ)をカバーする。/拿其他部门的收益来抵消赤字。
  • お互(たが)いの欠点(けってん)をカバーしあう。/互相弥补短处。
  • 新人(しんじん)を選抜(せんばつ)して卒業(そつぎょう)したの欠員(けついん)をカバーする。/遴选新人填补毕业运动员的空缺。

2020年2月25日
しゅっきん【出勤】
上班.

  • 毎朝9時に出勤する。/每天早上9点上班。
  • わが社はフレックスタイム制で、出勤時間は決まっていない。/我们公司是弹性工作制,上班时间不固定。
  • 混雑解消(こんざつかいしょう)のため時差出勤(じさしゅっきん)をお願いします。/为解除拥挤,请错开时间上班。
  • 今度の日曜は休日出勤だ。/这个星期天要加班。
  • 彼の課は10時までに社に行けばいいんだから重役出勤(じゅうやくしゅっきん)だよ。/他们科只要10点以前能到公司就行,属于干部待遇。
  • 重役出勤/像大人物一样姗姗来迟

2020年2月26日
だそく【蛇足】
蛇足,多余.

  • 蛇足を加える。/画蛇添足。
  • ちょっと蛇足を申し上げますと…/我稍微说句多余的…
  • 蛇足とは思いますが一言つけ加えさせていただきます。/我知道我是画蛇添足,但是我还要说一句。
查看原文

赞 0 收藏 0 评论 0

Keif 发布了文章 · 2019-06-25

记一篇日语短文

今年(2019)7月的JLPT也快要开始了,我最重要的一个...emmm...朋友,参加这次的N2考试,祝愿拿到好成绩。??

两年前,大三的暑假,似乎比今年的夏天更加闷热一些,我第一次参加JLPT,也是N2。糊里糊涂刷了不到半本红蓝宝书,外加几套真题,就这样跑去另一个校区考试了。(考试前一天有点感冒,突然发烧头痛,好在睡了一下午就好了很多,想想自己当时也是心大?)

那次的N2似乎比往年真题简单一些,接近3小时的答题时间过得还算舒适,本科3年各种突击应付式的考试都快让我忘记了这种——沉浸在试题里的紧张、兴奋和满足感。

那年的最后一篇长阅读(除去海报内容理解那篇)答得很开心,难度适中,文笔优美(按我的审美来说?)。尽管是篇鸡汤文,但也强烈地让我想去拜读一下原作,可惜当时没能把出处记下来,也没办法Google到。最近不知为何又突然想起这篇文章,遂找出了当年的真题卷重温一遍,顺手在这做一下记录。


​ 人生はいつも旅になぞらえられる。

​ 人は人生という旅路を、地図もなく歩いている。誰しもそうだし、それが人間としては自然な姿である。人生に地図などあるわけがない。なのに人は、人生の地図を持とうとするのが常だ。暗闇の中を歩くのが不安で仕様がないのだ。迷ってしまった時の恐怖を想像したくないからだ。

​ そして自分の地図には、人生の設計図としてわがままな道程が記されている。三十歳までには結婚し、三十五歳頃には二人の子どもをもつ。四十歳には課長になり、五十歳までには何とか部長に昇進する。

​ (中略)

​ 人生の地図に描かれた道を、その通りに歩むことができるなら、そんなに楽なことはない。一度も脇道にそれずに、ただまっすぐに歩くことができるのなら、人は何も悩まなくても済むだろう。そんな人生を送る人間は、おそらくこの世に一人もいない。もしそういう人間がいるのだとしたら、それはその人間の人生ではない。その人生は他人から与えられたものに過ぎない。

​ 五十歳の時には部長になっている。これは今という現在地から見た目標であろう。目標を持つことはもちろん大切なことだ。しかし、その目標へ辿り着く道は決して一本ではない。五十歳という現在地に立った時、もし部長になっていなければどうするのか。一枚の地図しか持っていない人、あるいは決して地図を書き変えようとしない人は、そこで人生の現在地を見失ってしまうだろう。「今、自分はこの場所にいるはずなのに、全く違う所に来てしまった」と、そんな思いに囚われてしまい、行くべき道も見失ってしまうのである。

​ 地図を持たない人生が不安であるならば、地図を持てばいいだけのことだ。しかし、その一枚の地図にこだわってはならない。常に現在地を確認しながら、どんどん地図を変えていくことだ。

​ 少し脇道に入ってしまったのなら、その脇道を歩いてみればいい。無理をして元の道に戻ろうとしても、余計に迷うだけだ。脇道を歩いているうちに、いつの間にか元の道に戻ることもあるだろうし、また別の大通りに出会うこともあるだろう。人生には数え切れないほどの道があることを知っておいたほうがいい。今いる場所さえしっかりと認識できていれば、人はどんな道だって歩いていくことができるものだ。

(立松平和『人生の現在地---まだまだ迷っているぞ、私は。』による)

查看原文

赞 0 收藏 0 评论 0

Keif 评论了文章 · 2019-04-02

在Python中使用科大讯飞Web API进行语音合成

前几日讯飞开放平台推出了WebAPI接口,恰好最近需要实现一个文字转语音的功能,于是就尝试着用了起来。但不知什么原因,官方文档的调用示例一直报错,最后自己照着示例的思路用python3重写了一遍。所以这次总结一下在Python中使用讯飞Web API进行语音合成的过程。

注册讯飞开放平台

首先注册讯飞开放平台:http://passport.xfyun.cn/regi...

注册完成后进入控制台,在控制台创建一个新应用 ,填写一些基本信息,注意 应用平台 选择 WebAPI

创建完成后,记录下 APPIDAPIKey ,将在程序中用到。

另外,请在 IP白名单 中添加自己的外网IP,可以在http://www.ip138.com/ 查看。(一般来说外网IP会常常发生变化,请注意)

在Python3中使用讯飞Web API

先上代码,后面进行必要的说明:

可能提示缺库:pip3 install requests

* 使用python3执行

import base64
import json
import time
import hashlib
import requests

# API请求地址、API KEY、APP ID等参数,提前填好备用
api_url = "http://api.xfyun.cn/v1/service/v1/tts"
API_KEY = "替换成你的APIKEY"
APP_ID = "替换成你的APPID"
OUTPUT_FILE = "C://output.mp3"    # 输出音频的保存路径,请根据自己的情况替换
TEXT = "苟利国家生死以,岂因祸福避趋之"

# 构造输出音频配置参数
Param = {
    "auf": "audio/L16;rate=16000",    #音频采样率
    "aue": "lame",    #音频编码,raw(生成wav)或lame(生成mp3)
    "voice_name": "xiaoyan",
    "speed": "50",    #语速[0,100]
    "volume": "77",    #音量[0,100]
    "pitch": "50",    #音高[0,100]
    "engine_type": "aisound"    #引擎类型。aisound(普通效果),intp65(中文),intp65_en(英文)
}
# 配置参数编码为base64字符串,过程:字典→明文字符串→utf8编码→base64(bytes)→base64字符串
Param_str = json.dumps(Param)    #得到明文字符串
Param_utf8 = Param_str.encode('utf8')    #得到utf8编码(bytes类型)
Param_b64 = base64.b64encode(Param_utf8)    #得到base64编码(bytes类型)
Param_b64str = Param_b64.decode('utf8')    #得到base64字符串

# 构造HTTP请求的头部
time_now = str(int(time.time()))
checksum = (API_KEY + time_now + Param_b64str).encode('utf8')
checksum_md5 = hashlib.md5(checksum).hexdigest()
header = {
    "X-Appid": APP_ID,
    "X-CurTime": time_now,
    "X-Param": Param_b64str,
    "X-CheckSum": checksum_md5
}

# 发送HTTP POST请求
def getBody(text):
    data = {'text':text}
    return data
response = requests.post(api_url, data=getBody(TEXT), headers=header)

# 读取结果
response_head = response.headers['Content-Type']
if(response_head == "audio/mpeg"):
    out_file = open(OUTPUT_FILE, 'wb')
    data = response.content # a 'bytes' object
    out_file.write(data)
    out_file.close()
    print('输出文件: ' + OUTPUT_FILE)
else:
    print(response.read().decode('utf8'))

下面按照代码顺序进行各部分的说明。

APIKey等参数

在代码开头填好各项参数,方面代码中使用。

API_KEY和APP_ID请替换为上一步创建应用后得到的内容。请不要删除双引号

OUTPUT_FILE是最终输出音频的保存路径,根据自己的情况替换。

TEXT是将要输出为语音的文本。

音频配置参数

Param 是字典格式的音频配置参数,其中 "aue" 可选 raw (生成wav)或 lame (生成mp3),如果修改成raw请记得同时修改输出文件的扩展名。

最后需要将配置参数编码为Base64字符串:字典类型→明文字符串→utf8编码→Base64(bytes)→Base64字符串,具体实现可以参考代码。

音频配置参数的详细说明可以参考请求参数 | 语音合成

HTTP请求头部

根据 授权认证 | 科大讯飞RESET_API开发指南 ,在调用所有业务接口时,都需要在HTTP请求头部中配置以下参数用于授权认证:

参数格式说明
X-Appidstring讯飞开放平台注册申请应用的应用ID(appid)
X-CurTimestring当前UTC时间戳,从1970年1月1日0点0 分0 秒开始到现在的秒数
X-Paramstring音频配置参数JSON串经Base64编码后的字符串
X-CheckSumstring令牌,计算方法:MD5(apiKey + curTime + param)。三个值拼接的字符串,进行MD5哈希计算(32位小写)。

具体实现参考代码中字典 header

发送请求&读取结果

最后使用requests库发送HTTP POST请求,得到结果。根据响应的 header 可以判断是否合成成功。

若响应头部包含Content-type: audio/mpeg,则响应Body为音频数据,可写入文件保存。

若合成出现错误,响应头部包含Content-type: text/plain,响应Body为记载了错误类型的json字符串。

返回值的具体说明请参考 返回值 | 语音合成

运行结果

使用几次后,感觉合成语音的断句做得不是很优秀,但响应速度很快,还是比较满意的。

output.mp3

小结

最近使用了几种Web API,对这类API的使用方法也算是有些经验了。最后,现在语音识别、图灵机器人、语音合成都试着做了一遍,下一篇博客将把他们组合起来,实现一个简单的语音助手。

感谢你阅读文章!

查看原文

Keif 评论了文章 · 2019-03-20

在Python中使用科大讯飞Web API进行语音合成

前几日讯飞开放平台推出了WebAPI接口,恰好最近需要实现一个文字转语音的功能,于是就尝试着用了起来。但不知什么原因,官方文档的调用示例一直报错,最后自己照着示例的思路用python3重写了一遍。所以这次总结一下在Python中使用讯飞Web API进行语音合成的过程。

注册讯飞开放平台

首先注册讯飞开放平台:http://passport.xfyun.cn/regi...

注册完成后进入控制台,在控制台创建一个新应用 ,填写一些基本信息,注意 应用平台 选择 WebAPI

创建完成后,记录下 APPIDAPIKey ,将在程序中用到。

另外,请在 IP白名单 中添加自己的外网IP,可以在http://www.ip138.com/ 查看。(一般来说外网IP会常常发生变化,请注意)

在Python3中使用讯飞Web API

先上代码,后面进行必要的说明:

可能提示缺库:pip3 install requests

* 使用python3执行

import base64
import json
import time
import hashlib
import requests

# API请求地址、API KEY、APP ID等参数,提前填好备用
api_url = "http://api.xfyun.cn/v1/service/v1/tts"
API_KEY = "替换成你的APIKEY"
APP_ID = "替换成你的APPID"
OUTPUT_FILE = "C://output.mp3"    # 输出音频的保存路径,请根据自己的情况替换
TEXT = "苟利国家生死以,岂因祸福避趋之"

# 构造输出音频配置参数
Param = {
    "auf": "audio/L16;rate=16000",    #音频采样率
    "aue": "lame",    #音频编码,raw(生成wav)或lame(生成mp3)
    "voice_name": "xiaoyan",
    "speed": "50",    #语速[0,100]
    "volume": "77",    #音量[0,100]
    "pitch": "50",    #音高[0,100]
    "engine_type": "aisound"    #引擎类型。aisound(普通效果),intp65(中文),intp65_en(英文)
}
# 配置参数编码为base64字符串,过程:字典→明文字符串→utf8编码→base64(bytes)→base64字符串
Param_str = json.dumps(Param)    #得到明文字符串
Param_utf8 = Param_str.encode('utf8')    #得到utf8编码(bytes类型)
Param_b64 = base64.b64encode(Param_utf8)    #得到base64编码(bytes类型)
Param_b64str = Param_b64.decode('utf8')    #得到base64字符串

# 构造HTTP请求的头部
time_now = str(int(time.time()))
checksum = (API_KEY + time_now + Param_b64str).encode('utf8')
checksum_md5 = hashlib.md5(checksum).hexdigest()
header = {
    "X-Appid": APP_ID,
    "X-CurTime": time_now,
    "X-Param": Param_b64str,
    "X-CheckSum": checksum_md5
}

# 发送HTTP POST请求
def getBody(text):
    data = {'text':text}
    return data
response = requests.post(api_url, data=getBody(TEXT), headers=header)

# 读取结果
response_head = response.headers['Content-Type']
if(response_head == "audio/mpeg"):
    out_file = open(OUTPUT_FILE, 'wb')
    data = response.content # a 'bytes' object
    out_file.write(data)
    out_file.close()
    print('输出文件: ' + OUTPUT_FILE)
else:
    print(response.read().decode('utf8'))

下面按照代码顺序进行各部分的说明。

APIKey等参数

在代码开头填好各项参数,方面代码中使用。

API_KEY和APP_ID请替换为上一步创建应用后得到的内容。请不要删除双引号

OUTPUT_FILE是最终输出音频的保存路径,根据自己的情况替换。

TEXT是将要输出为语音的文本。

音频配置参数

Param 是字典格式的音频配置参数,其中 "aue" 可选 raw (生成wav)或 lame (生成mp3),如果修改成raw请记得同时修改输出文件的扩展名。

最后需要将配置参数编码为Base64字符串:字典类型→明文字符串→utf8编码→Base64(bytes)→Base64字符串,具体实现可以参考代码。

音频配置参数的详细说明可以参考请求参数 | 语音合成

HTTP请求头部

根据 授权认证 | 科大讯飞RESET_API开发指南 ,在调用所有业务接口时,都需要在HTTP请求头部中配置以下参数用于授权认证:

参数格式说明
X-Appidstring讯飞开放平台注册申请应用的应用ID(appid)
X-CurTimestring当前UTC时间戳,从1970年1月1日0点0 分0 秒开始到现在的秒数
X-Paramstring音频配置参数JSON串经Base64编码后的字符串
X-CheckSumstring令牌,计算方法:MD5(apiKey + curTime + param)。三个值拼接的字符串,进行MD5哈希计算(32位小写)。

具体实现参考代码中字典 header

发送请求&读取结果

最后使用requests库发送HTTP POST请求,得到结果。根据响应的 header 可以判断是否合成成功。

若响应头部包含Content-type: audio/mpeg,则响应Body为音频数据,可写入文件保存。

若合成出现错误,响应头部包含Content-type: text/plain,响应Body为记载了错误类型的json字符串。

返回值的具体说明请参考 返回值 | 语音合成

运行结果

使用几次后,感觉合成语音的断句做得不是很优秀,但响应速度很快,还是比较满意的。

output.mp3

小结

最近使用了几种Web API,对这类API的使用方法也算是有些经验了。最后,现在语音识别、图灵机器人、语音合成都试着做了一遍,下一篇博客将把他们组合起来,实现一个简单的语音助手。

感谢你阅读文章!

查看原文

认证与成就

  • 获得 63 次点赞
  • 获得 4 枚徽章 获得 0 枚金徽章, 获得 1 枚银徽章, 获得 3 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

注册于 2018-01-06
个人主页被 2.2k 人浏览