tp5.1.x 使用队列扩展(think-queue)入门用法

说明

队列(think-queue)是tp5.1.x的一个扩展,需要先安装才能使用

以下是基础用法,本文是按照数据库驱动模式进行

一、安装队列(think-queue)扩展

注:think-queue 最新的版本需要 tp6.x 的支持,所以本文的安装版本为 2.0.4

composer require topthink/think-queue 2.0.4

二、创建数据表

CREATE TABLE `prefix_jobs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `queue` varchar(255) NOT NULL,
  `payload` longtext NOT NULL,
  `attempts` tinyint(3) unsigned NOT NULL,
  `reserved` tinyint(3) unsigned NOT NULL,
  `reserved_at` int(10) unsigned DEFAULT NULL,
  `available_at` int(10) unsigned NOT NULL,
  `created_at` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码

三、修改配置文件,添加对应的驱动配置

文件位置:根目录/config/queue.php

return [
    'connector' => 'Database',   // 数据库驱动
    'expire'    => 60,           // 任务的过期时间,默认为60秒; 若要禁用,则设置为 null
    'default'   => 'default',    // 默认的队列名称
    'table'     => 'jobs',       // 存储消息的表名,不带前缀
    'dsn'       => [],
]
复制代码

配置文件中的 expire 参数说明
expire 参数指的是任务的过期时间, 单位为秒。
过期的任务,其准确的定义是:
   1.任务的状态为执行中
   2.任务的开始执行的时刻 + expire > 当前时刻
expire 不为null 时 ,thinkphp-queue 会在每次获取下一个任务之前检查并重发过期(执行超时)的任务。
expire 为null 时,thinkphp-queue 不会检查过期的任务,性能相对较高一点。但是需要注意:
这些执行超时的任务会一直留在消息队列中,需要开发者另行处理(删除或者重发)!

四、消息的创建与推送

我们在业务控制器中创建一个新的消息,并推送到队列中
1.创建一个新的控制器: \application\index\controller\JobTest.php
2.在控制器中新增一个方法:test()
3.代码如下

<?php
namespace app\admin\controller;
use think\Queue;
use think\Controller;
class JobTest extends Controller
{
    public function test()
    {
        // 1.当前任务将由哪个类来负责处理。
        // 当轮到该任务时,系统将生成一个该类的实例,并调用其 fire 方法
        $jobHandlerClassName = 'app\jobs\JobTest';
        
        // 2.当前任务归属的队列名称,如果为新队列,会自动创建
        $jobQueueName = "helloJobQueue";
        
        // 3.当前任务所需的业务数据, 不能为 resource 类型,其他类型最终将转化为json形式的字符串
        // (jobData 为对象时,存储其public属性的键值对 )
        $jobData = ['ts' => time(), 'bizId' => uniqid(), 'a' => 1];
        
        // 4.将该任务推送到消息队列,等待对应的消费者去执行
        $isPushed = Queue::push($jobHandlerClassName, $jobData, $jobQueueName);
        
        // database 驱动时,返回值为 1|false;redis 驱动时,返回值为 随机字符串|false
        if( $isPushed !== false ){
            echo date('Y-m-d H:i:s') . " a new Hello Job is Pushed to the MQ" . "<br>";
        }else{
            echo 'Oops, something went wrong.';
        }
    }
}
复制代码

五、消息的消费与删除

我们创建一个消费者类,用于处理队列中的任务
1.新增一个消费者类:\application\jobs\JobTest.php

<?php
namespace app\jobs;
use think\queue\Job;
use app\common\model\JobsTest as JobsTestModel;
class JobTest
{
    /**
     * fire方法是消息队列默认调用的方法
     * @param Job            $job      当前的任务对象
     * @param array|mixed    $data     发布任务时自定义的数据
     */
    public function fire(Job $job, $data)
    {
        // 此处做一些 check,提前判断是否需要执行
        $isJobStillNeedToBeDone = $this->checkDatabaseToSeeIfJobNeedToBeDone($data);
        if(! $isJobStillNeedToBeDone){
            $job->delete();
            return;
        }
        // 执行逻辑处理(即:你需要该消息队列做什么)
        $isJobDone = $this->doHelloJob($data);
        if ($isJobDone) {
            // 如果任务执行成功,记得删除任务
            $job->delete();
        } else {
            // 通过这个方法可以检查这个任务已经重试了几次了
            if ($job->attempts() > 3) {
                $job->delete();
                // 也可以重新发布这个任务
                //$job->release(2); // $delay为延迟时间,表示该任务延迟2秒后再执行
            }
        }
    }
    
    /**
     * 有些消息在到达消费者时,可能已经不再需要执行了
     * @param $data 发布任务时自定义的数据
     * @return bool 任务执行的结果
     */
    private function checkDatabaseToSeeIfJobNeedToBeDone($data){
        return true;
    }
    
    /**
     * 根据消息中的数据进行实际的业务处理...
     * @param $data
     * @return bool
     */
    private function doHelloJob($data)
    {
        // TODO 该处为实际业务逻辑,即:对消息中的数据进行处理
        $model = new JobsTestModel();
        $inData = [
            'uniqId' => $data['uniqId'],
            'time' => $data['ts'],
            'content' => '队列成功的插入数据'
        ];
        $res = $model->save($inData);
        if (! $res) {
            return false;
        }
        return true;
    }
}
复制代码

至此,我们的相关代码已经完成

六、调试/测试

1.我们创建一张数据表 jobs_test

CREATE TABLE `st_jobs_test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `uniqId` varchar(255) DEFAULT NULL,
  `time` varchar(255) DEFAULT NULL,
  `content` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
复制代码

2.发布任务:执行消息创建方法 http://xxx/admin/job_test/test

如果运行成功,可以看到页面返回显示:”2020-08-07 10:53:00 a new Hello Job is Pushed to the MQ”
此时我们查看数据表 jobs 中的数据:

idqueuepayloadattemptsreservedreserved_atavailable_atcreated_at
6helloJobQueue{“job”:”app\jobs\JobTest”,”data”:{“ts”:1596769329,”uniqId”:”5f2cc4317c6a3″}}0015967693291596769329
7helloJobQueue{“job”:”app\jobs\JobTest”,”data”:{“ts”:1596769332,”uniqId”:”5f2cc4349c42d”}}0015967693321596769332
8helloJobQueue{“job”:”app\jobs\JobTest”,”data”:{“ts”:1596769334,”uniqId”:”5f2cc4367f8f1″}}0015967693341596769334
9helloJobQueue{“job”:”app\jobs\JobTest”,”data”:{“ts”:1596769335,”uniqId”:”5f2cc437cfcaf”}}0015967693351596769335
10helloJobQueue{“job”:”app\jobs\JobTest”,”data”:{“ts”:1596769337,”uniqId”:”5f2cc439086dd”}}0015967693371596769337

3.处理任务
切换到终端窗口,执行命令:php think queue:work --queue helloJobQueue
如果执行成功,可以看到窗口返回 “Processed: app\jobs\JobTest”
此时我们查看数据库
表 jobs 中的消息已经被消费了一条,而 jobs_test 中新增了一条消费后的数据

iduniqIdtimecontent
65f2cc4317c6a31596769329队列成功的插入数据

至此,我们成功地经历了一个消息的 创建 -> 推送 -> 消费 -> 删除 的基本流程

【附录】

tp5.1.x 队列的使用文档
github.com/coolseven/n…

作者:joker丶牧羊人
链接:https://juejin.cn/post/6858171582511054855
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

tp5.1 + think-queue + supervisor队列使用

最近学习使用了tp官方的的消息队列,我的项目用的是 TP5.1 框架,消息队列用的think-queue消息队列,结合 supervisor 进程管理使队列进程常驻。在这里记录一下顺便分享给大家,下面逻辑是加入队列、消费队列和写入数据库。

一、tp5.1的安装方法
用 composer 安装最新稳定版本

composer create-project topthink/think 5.1.*
1
二、添加think-queue扩展
composer安装

composer require topthink/think-queue
1
think-queue 的官方链接

配置

配置文件位于项目根目录下的 config/queue.php,添加如下内容:

return [
    'connector'  => 'Redis',            // 可选驱动类型:sync(默认)、Redis、database、topthink等其他自定义类型
    'expire'     => 60,                    // 任务的过期时间,默认为60秒; 若要禁用,则设置为 null
    'default'    => 'default',            // 默认的队列名称
    'host'       => '127.0.0.1',        // redis 主机ip
    'port'       => 6379,                // redis 端口
    'password'   => '',                 // redis 连接密码
    'select'     => 0,                    // 使用哪一个 db,默认为 db0
    'timeout'    => 0,                    // redis连接的超时时间
    'persistent' => false,                // 是否是长连接
]; 

1
2
3
4
5
6
7
8
9
10
11
我配置了redis驱动,大家可以根据自己的情况配置参数。

创建数据库表

创建一张表,用于展示消费队列写入数据库的操作。

CREATE TABLE test (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
data text,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
1
2
3
4
5
创建消息队列任务

以index模块为例,创建一个 /app/index/job/Task.php 文件(job目录也是手动创建,一定要注意的是:TP5.1里面链接数据库一定要用model,否则将会不定期的出现MYSQL链接超时,将会导致supervisor的子进程卡死,自动重启也无效;原因是没能释放连接,也没有找到Db手动释放的方法),代码如下:

namespace app\index\job;

use think\queue\Job;

class Task{

public function fire(Job $job,$data){
    //任务失败(重复)3次以上,删除该任务
    if($job->attempts() > 3){
        //TODO
        ....
        //删除
        $job->delete();
    }else{
         //写入数据库
        $res = db('test')->insert(['data'=>json_encode($data)]);
        if(!$res){
            $delay = 1;//$delay为延迟时间
            //重新发布该任务
            $job->release($delay);
        }else{
            //成功之后
            $job->delete();
        }
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
入队列(生产任务)

1). 有push()和later()两种方法,前者是立即执行,后者是延迟$delay秒后执行。

$job = ‘app\index\job\Task’;//调用的任务名
$data = [];//传入的数据
$queue = ‘group1’;//队列名,可以理解为组名

//push()方法是立即执行
Queue::push($job, $data, $queue);

//later()方法是延迟 $delay 秒之后再执行
$delay = 10;//延迟时间
Queue::later($delay, $job, $data, $queue);
1
2
3
4
5
6
7
8
9
10
2). 调用later()方法,将该任务分配到group1队列里,延迟10秒执行

use think\Queue;

class Index{
public function index(){
Queue::later(10,’app\index\job\Task’,[‘arg1’=>1,’arg2’=>2],’group1’);
}
}
1
2
3
4
5
6
7
3). 调用之后,我们通过redis的远程管理工具会发现指定db库的队列里有对应的数据,说明完成了入队列

1) {“job”:”app\index\job\Task”,”data”:{“name”:”group1″,”data”:[]},”id”:”90BWJfkfgIISzK67cWg99mDxB428GV3A”,”attempts”:1}
1
出队列(消费任务)

在项目根目录执行命令

php think queue:work –queue group1
1
之后(其中group1为队列名),数据库成功写入一条数据。接下来开始安装和配置supervisor来守护该进程不断的执行任务。

三、supervisor的安装和配置
yum安装supervisor

yum install epel-release

yum install supervisor

//设置成开机自动启动

systemctl enable supervisord

1
2
3
4
5
配置

在这里我创建了一个命名为supervisor的目录用于存放supervisor和队列的日志文件以及include的配置文件,其目录结构为:
/var/supervisor/log/ #可以自定义
/run/ #可以自定义
/conf/ #可以自定义
1
2
3
然后找到/etc/supervisord.conf配置文件,编辑如下信息:
; 将supervisor.sock 的路径换成如下

[unix_http_server]

file=/var/supervisor/run/supervisor.sock ; (the path to the socket file)

; 将supervisord.log 和 supervisord.pid 的路径换成如下

[supervisord]

logfile=/var/supervisor/log/supervisord.log ; (main log file;default $CWD/supervisord.log)
pidfile=/var/supervisor/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)

; 将supervisor.sock 的路径换成如下

[supervisorctl]

serverurl=unix:///var/supervisor/run/supervisor.sock ; use a unix:// URL for a unix socket

; 将最底部的files路径换成如下

[include]

files = /var/supervisor/conf/*.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
在/var/supervisor/conf目录里创建一个.conf文件,这里命名为queue_work.conf,内容如下:

[program:queue_worker]

;项目名称
directory = /opt/www/tp5.1 ; 程序的启动目录,项目根目录的上一级
command = php think queue:work –queue queueName –daemon ; 启动命令 queueName就是队列名
process_name=%(program_name)s_%(process_num)02d
numprocs = 3 ; 开启的进程数量
autostart = true ; 在 supervisord 启动的时候也自动启动
startsecs = 5 ; 启动 5 秒后没有异常退出,就当作已经正常启动了
autorestart = true ; 程序异常退出后自动重启
startretries = 3 ; 启动失败自动重试次数,默认是 3
user = root ; 用哪个用户启动
redirect_stderr = true ; 把 stderr 重定向到 stdout,默认 false
stdout_logfile_maxbytes = 50MB ; stdout 日志文件大小,默认 50MB
stdout_logfile_backups = 20 ; stdout 日志文件备份数
; stdout 日志文件,需要手动创建目录(supervisord 会自动创建日志文件)
stdout_logfile = /var/supervisor/log/queue_worker.log
loglevel=info
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
对于index这个单模块而言,不同的业务逻辑为了区分可能会存在多个队列名,这种情况将多个队列名用逗号拼接起来:
command = php think queue:work –queue queueName1,queueName2 –daemon ;
1
重启

systemctl stop supervisord

systemctl start supervisord

1
2

systemctl restart supervisord

1
调用方法,成功写入数据库。
————————————————
版权声明:本文为CSDN博主「牛转乾坤~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34856247/article/details/86741533

Linux命令之ls(文件与目录的查看)

本文将会学到的命令
ls
ls(文件与目录的查看)
ls这个命令官方提供的可选参数太多,本文就列举了一些平时比较常用的几个参数进行讲解与说明,可以在文末查看该命令的全部参数。

比较常用参数有[-aAdfFhilnrRSt]
-a :全部的文件,连同隐藏文件(开头为.的文件)一起列出来
-A :全部的文件,连同隐藏文件,但是不包括.和…这两个目录
-d :仅列出目录本身,而不是列出目录内的文件数据
-f :直接列出结果,而不进行排序(ls默认会以文件名排序)
-F:根据文件、目录等信息,给予附件数据结构,例如:*:代表可执行文件;/:代表目录;=:代表socket文件;|:代表FIFO文件
-h:将文件容量以人类较易读的方式(如GB,KB等)列出来
-i:列出inode号码
-l:详细信息显示,包含文件的属性和权限等数据
-n:列出UID和GID而非使用者与用户组的名称
-r:将排序结果反向输出,例如:原本文件名由小到大,反向则由大到小
-R:连同子目录内容一起列出来,等于该目录下的所有文件都会显示出来
-S:以文件容量大小排序,而不是用文件名排序
-t:依时间排序,而不是用文件名排序
这里需要注意的是,当在一次查询中使用多个-S -t 等排序方式时,哪个参数写在后面那个参数生效

最后还有两个特殊的参数–color与–full-time

–color:颜色显示

never:不要依据文件特性给予颜色显示
always:显示颜色
auto:让系统自行依据设置来判断是否给予颜色

–full-time:以完整时间模式输出

最后来看一下官方文档

man ls
LS(1) General Commands Manual LS(1)
NAME
ls, dir, vdir – 列目录内容
提要
ls [选项] [文件名…]
POSIX 标准选项: [-CFRacdilqrtu1]
GNU 选项 (短格式):
[-1abcdfgiklmnopqrstuxABCDFGLNQRSUX] [-w cols] [-T cols] [-I pattern] [–full-time] [–format={long,verbose,commas,across,vertical,single-column}] [–sort={none,time,size,extension}] [–time={atime,access,use,ctime,status}] [–color[={none,auto,always}]] [–help]
[–version] [–]
描述( DESCRIPTION )
程序ls先列出非目录的文件项,然后是每一个目录中的“可显示”文件。如果 没有选项之外的参数【译注:即文件名部分为空】出现,缺省为 “.” (当前目录)。 选项“ -d ”使得目录与非目录项同样对待。除非“ -a ” 选项出现,文 件名以“.”开始的文件不属“可显示”文件。
以当前目录为准,每一组文件(包括非目录文件项,以及每一内含文件的目录)分 别按文件名比较顺序排序。如果“ -l ”选项存在,每组文件前显示一摘要行: 给出该组文件长度之和(以 512 字节为单位)。
输出是到标准输出( stdout )。除非以“ -C ”选项要求按多列输出,输出 将是一行一个。然而,输出到终端时,单列输出或多列输出是不确定的。可以分别 用选项“ -1 ” 或“ -C ”来强制按单列或多列输出。
-C 多列输出,纵向排序。
-F 每个目录名加“ / ”后缀,每个 FIFO 名加“ | ”后缀, 每个可运行名加“ * ”后缀。
-R 递归列出遇到的子目录。
-a 列出所有文件,包括以 “.” 开头的隐含文件。
-c 使用“状态改变时间”代替“文件修改时间”为依据来排序 (使用“ -t ”选项时)或列出(使用“ -l ”选项时)。
-d 将目录名象其它文件一样列出,而不是列出它们的内容。
-i 输出文件前先输出文件系列号(即 i 节点号: i-node number)。 -l 列出(以单列格式)文件模式( file mode ),文件的链 接数,所有者名,组名,文件大小(以字节为单位),时间信 息,及文件名。缺省时,时间信息显示最近修改时间;可以以 选项“ -c ”和“ -u
”选择显示其它两种时间信息。对于设 备文件,原先显示文件大小的区域通常显示的是主要和次要的 号(majorand minor device numbers)。
-q 将文件名中的非打印字符输出为问号。(对于到终端的输出这是缺省的。)
-r 逆序排列。
-t 按时间信息排序。
-u 使用最近访问时间代替最近修改时间为依据来排序(使用 “ -t ”选项时)或列出(使用“ -l ”选项时)。
-1 单列输出。
GNU 细节
如果标准输出是终端,将多列输出(纵向排序)。
dir ( 也被安装为命令 d ) 等同于“ ls -C ”;即,文件
缺省是多列输出,纵向排序。vdir ( 也被安装为命令 v ) 等同于“ ls -l ”; 即,文件缺省是按长格式输出。
GNU 选项
-1, –format=single-column 一行输出一个文件(单列输出)。如标准输出不是到终端, 此选项就是缺省选项。
-a, –all
列出目录中所有文件,包括以“.”开头的文件。
-b, –escape
把文件名中不可输出的字符用反斜杠加字符编号(就象在 C 语言里一样)的形式列出。
-c, –time=ctime, –time=status
按文件状态改变时间(i节点中的ctime)排序并输出目录内 容。如采用长格式输出(选项“-l”),使用文件的状态改 变时间取代文件修改时间。【译注:所谓文件状态改变(i节 点中以ctime标志),既包括文件被修改,又包括文件属性( 如所有者、组、链接数等等)的变化】
-d, –directory
将目录名象其它文件一样列出,而不是列出它们的内容。
-f 不排序目录内容;按它们在磁盘上存储的顺序列出。同时启 动“ -a ”选项,如果在“ -f ”之前存在“ -l ”、“ – -color ”或“ -s ”,则禁止它们。
-g 忽略,为兼容UNIX用。
-i, –inode
在每个文件左边打印 i 节点号(也叫文件序列号和索引号: file serial number and index number)。i节点号在每个特定的文件系统中是唯一的。
-k, –kilobytes
如列出文件大小,则以千字节KB为单位。
-l, –format=long, –format=verbose
除每个文件名外,增加显示文件类型、权限、硬链接数、所 有者名、组名、大小( byte )、及时间信息(如未指明是 其它时间即指修改时间)。对于6个月以上的文件或超出未来 1 小时的文件,时间信息中的时分将被年代取代。
每个目录列出前,有一行“总块数”显示目录下全部文件所 占的磁盘空间。块默认是 1024 字节;如果设置了 POSIXLY_CORRECT 的环境变量,除非用“ -k ”选项,则默认块大小是 512 字 节。每一个硬链接都计入总块数(因此可能重复计数),这无 疑是个缺点。
列出的权限类似于以符号表示(文件)模式的规范。但是 ls
在每套权限的第三个字符中结合了多位( multiple bits ) 的信息,如下: s 如果设置了 setuid 位或 setgid 位,而且也设置了相应的可执行位。 S 如果设置了 setuid 位或 setgid 位,但是没有设置相应的可执行位。 t 如果设置了 sticky 位,而且也设置了相应的可执行位。 T
如果设置了 sticky 位,但是没有设置相应的可执行位。 x 如果仅仅设置了可执行位而非以上四种情况。 – 其它情况(即可执行位未设置)。
-m, –format=commas
水平列出文件,每行尽可能多,相互用逗号和一个空格分隔。
-n, –numeric-uid-gid
列出数字化的 UID 和 GID 而不是用户名和组名。
-o 以长格式列出目录内容,但是不显示组信息。等于使用“ –format=long –no-group ”选项。提供此选项是为了与其它版本的 ls 兼容。
-p 在每个文件名后附上一个字符以说明该文件的类型。类似“ -F ”选项但是不 标示可执行文件。
-q, –hide-control-chars
用问号代替文件名中非打印的字符。这是缺省选项。
-r, –reverse
逆序排列目录内容。
-s, –size
在每个文件名左侧输出该文件的大小,以 1024 字节的块为单位。如果设置了 POSIXLY_CORRECT 的环境变量,除非用“ -k ”选项,块大小是 512 字节。
-t, –sort=time
按文件最近修改时间( i 节点中的 mtime )而不是按文件名字典序排序,新文件 靠前。
-u, –time=atime, –time=access, –time=use
类似选项“ -t ”,但是用文件最近访问时间( i 节点中的 atime )取代文件修 改时间。如果使用长格式列出,打印的时间是最近访问时间。
-w, –width cols
假定屏幕宽度是 cols ( cols 以实际数字取代)列。如未用此选项,缺省值是这 样获得的:如可能先尝试取自终端驱动,否则尝试取自环境变量 COLUMNS (如果设 置了的话),都不行则取 80 。
-x, –format=across, –format=horizontal
多列输出,横向排序。
-A, –almost-all
显示除 “.” 和 “..” 外的所有文件。
-B, –ignore-backups
不输出以“ ~ ”结尾的备份文件,除非已经在命令行中给出。
-C, –format=vertical
多列输出,纵向排序。当标准输出是终端时这是缺省项。使用命令名 dir 和 d 时, 则总是缺省的。
-D, –dired
当采用长格式(“ -l ”选项)输出时,在主要输出后,额外打印一行: //DIRED// BEG1 END1 BEG2 END2 …
BEGn 和 ENDn 是无符号整数,记录每个文件名的起始、结束位置在输出中的位置(
字节偏移量)。这使得 Emacs 易于找到文件名,即使文件名包含空格或换行等非正 常字符也无需特异的搜索。
如果目录是递归列出的(“ -R ”选项),每个子目录后列出类似一行:
//SUBDIRED// BEG1 END1 … 【译注:我测试了 TurboLinux4.0 和 RedHat6.1 ,发现它们都是在 “ //DIRED// BEG1… ”之后列出“ //SUBDIRED// BEG1 … ”,也即只有一个 而不是在每个子目录后都有。而且“ //SUBDIRED// BEG1 … ”列出的是各个子目 录名的偏移。】
-F, –classify, –file-type
在每个文件名后附上一个字符以说明该文件的类型。“ * ”表示普通的可执行文件; “ / ”表示目录;“ @ ”表示符号链接;“ | ”表示FIFOs;“ = ”表示套接字 (sockets) ;什么也没有则表示普通文件。
-G, –no-group
以长格式列目录时不显示组信息。
-I, –ignorepattern
除非在命令行中给定,不要列出匹配 shell 文件名匹配式( pattern ,不是指一般 表达式)的文件。在 shell 中,文件名以 “.” 起始的不与在文件名匹配式 (pattern) 开头的通配符匹配。
-L, –dereference
列出符号链接指向的文件的信息,而不是符号链接本身。
-N, –literal
不要用引号引起文件名。
-Q, –quote-name
用双引号引起文件名,非打印字符以 C 语言的方法表示。
-R, –recursive
递归列出全部目录的内容。
-S, –sort=size
按文件大小而不是字典序排序目录内容,大文件靠前。
-T, –tabsize cols
假定每个制表符宽度是 cols 。缺省为 8。为求效率, ls 可能在输出中使用制表符。 若 cols 为 0,则不使用制表符。
-U, –sort=none
不排序目录内容;按它们在磁盘上存储的顺序列出。(选项“ -U ”和“ -f ”的不 同是前者不启动或禁止相关的选项。)这在列很大的目录时特别有用,因为不加排序 能显著的加快速度。
-X, –sort=extension
按文件扩展名(由最后的 “.” 之后的字符组成)的字典序排序。没有扩展名的先列 出。
–color[=when]
指定是否使用颜色区别文件类别。环境变量 LS_COLORS 指定使用的颜色。如何设置 这个变量见 dircolors(1) 。 when 可以被省略,或是以下几项之一:
none 不使用颜色,这是缺省项。
auto 仅当标准输出是终端时使用。 always 总是使用颜色。指定 –color 而且省略 when 时就等同于 –color=always 。
–full-time
列出完整的时间,而不是使用标准的缩写。格式如同 date(1) 的缺省格式;此格式 是不能改变的,但是你可以用 cut(1) 取出其中的日期字串并将结果送至命令 “ date -d ”。
输出的时间包括秒是非常有用的。( Unix 文件系统储存文件的时间信息精确到秒,
因此这个选项已经给出了系统所知的全部信息。)例如,当你有一个 Makefile 文件 不能恰当的生成文件时,这个选项会提供帮助。

————————————————
版权声明:本文为CSDN博主「April_FGR」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39016934/article/details/89305385

解决eclipse中git插件中的cannot open git-upload-pack问题

原本用得好好的eclipse在对git.oschina.net提交代码时,突然出现“cannot open git-upload-pack”错误,百度了下,

打开eclipse中的windows–>Preferences–>Team–>Git–>Configuration–>User Settings.然后点Add Entry新建一个键值对,输入http.sslVerify=false。
问题得到解决,但是不明白为何原来正常的,突然却不正常了。
————————————————
版权声明:本文为CSDN博主「Pompeii」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Pompeii/article/details/49657949

Android语音播报的两种简单实现

a、两种方案

1、Android自带的语音播报(老版本手机Android6.0以下,不支持中文)

2、讯飞语音播报封装(直接用)

b、具体实现

一、Android自带的语音播报

查看手机是否支持中文语音播报,在测试的设备中打开‘设置’ –>找到 ‘语言和输入法’–>查看语音选项,是否支持中文,默认仅支持英文。

public class AndroidTTSActivity extends AppCompatActivity implements View.OnClickListener {

private TextToSpeech textToSpeech = null;//创建自带语音对象

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.android_tts_layout);
    findViewById(R.id.btn0).setOnClickListener(this);
    initTTS();
}

private void initTTS() {
    //实例化自带语音对象
    textToSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
        @Override
        public void onInit(int status) {
            if (status == textToSpeech.SUCCESS) {
                // Toast.makeText(MainActivity.this,"成功输出语音",
                // Toast.LENGTH_SHORT).show();
                // Locale loc1=new Locale("us");
                // Locale loc2=new Locale("china");

                textToSpeech.setPitch(1.0f);//方法用来控制音调
                textToSpeech.setSpeechRate(1.0f);//用来控制语速

                //判断是否支持下面两种语言
                int result1 = textToSpeech.setLanguage(Locale.US);
                int result2 = textToSpeech.setLanguage(Locale.
                        SIMPLIFIED_CHINESE);
                boolean a = (result1 == TextToSpeech.LANG_MISSING_DATA || result1 == TextToSpeech.LANG_NOT_SUPPORTED);
                boolean b = (result2 == TextToSpeech.LANG_MISSING_DATA || result2 == TextToSpeech.LANG_NOT_SUPPORTED);

                Log.i("zhh_tts", "US支持否?--》" + a +
                        "\nzh-CN支持否》--》" + b);

            } else {
                Toast.makeText(AndroidTTSActivity.this, "数据丢失或不支持", Toast.LENGTH_SHORT).show();
            }

        }
    });
}

@Override
public void onClick(View v) {
    if (v.getId() == R.id.btn0) {
        startAuto("big sea");
    }

}

private void startAuto(String data) {
    // 设置音调,值越大声音越尖(女生),值越小则变成男声,1.0是常规
    textToSpeech.setPitch(1.0f);
    // 设置语速
    textToSpeech.setSpeechRate(0.3f);
    textToSpeech.speak(data,//输入中文,若不支持的设备则不会读出来
            TextToSpeech.QUEUE_FLUSH, null);

}

@Override
protected void onStop() {
    super.onStop();
    textToSpeech.stop(); // 不管是否正在朗读TTS都被打断
    textToSpeech.shutdown(); // 关闭,释放资源
}

}
补充工具类

/**

  • Created by RongGuang on 2014-11-21.
  • 中文朗读
    */
    public class ChineseToSpeech {
    private TextToSpeech textToSpeech; public ChineseToSpeech() {
    this.textToSpeech = new TextToSpeech(Application.getContext(), new TextToSpeech.OnInitListener() {
    @Override
    public void onInit(int status) {
    if (status == TextToSpeech.SUCCESS) {
    int result = textToSpeech.setLanguage(Locale.CHINA);
    if (result == TextToSpeech.LANG_MISSING_DATA
    || result == TextToSpeech.LANG_NOT_SUPPORTED) {
    new CoolToast(Application.getContext()).show(“不支持朗读功能”);
    }
    }
    }
    });
    } public void speech(String text) {
    textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null);
    } public void destroy() {
    if (textToSpeech != null) {
    textToSpeech.stop();
    textToSpeech.shutdown();
    }
    }
    }
    二、讯飞语音播报封装(直接用)

1.接入项目前准备:

1.申请APPID(步骤):

I.登录讯飞官网–>创建应用–>创建完成在”我的应用”中即可看见自己新建的项目&APPID–>

II.添加需要开通的服务:这里选择在线语音合成+sdk下载(so+jar文件),注意:so文件必须用你对应的项目的,用别人so文件,会导致与你的APPID不匹配,

2.使用说明+接入高频易发问题:

语音次数是有限制的,提高次数需要实名认证+上传项目
引入的so文件必须是你项目所对应的
不可多次初始化合成对象
3.接入项目(AndroidStudio):

 I.相关sdk文件引入,如图(再次说明:so文件用的是你新建项目的so文件,不要用他人so):

II.初始化语音播报(API>=23需要授权,所以先授权,再初始化,如下:)

public class StartActivity extends AppCompatActivity {

private List<String> permissionList = null;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    SystemClock.sleep(1000);//延时加载
    requestPermissions();
}

private void openActivity(Class<? extends AppCompatActivity> clazz) {
    initTTS();
    startActivity(new Intent(this, clazz));
    finish();
}

//权限申请
private void requestPermissions() {
    // 版本判断。当手机系统大于 23 时,才有必要去判断权限是否获取
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        addListPermission();
        boolean isGranted = false;//是否全部授权
        // 权限是否已经 授权 GRANTED---授权  DINIED---拒绝
        Iterator<String> iterator = permissionList.iterator();
        while (iterator.hasNext()) {
            // 检查该权限是否已经获取
            int granted = ContextCompat.checkSelfPermission(this, iterator.next());
            if (granted == PackageManager.PERMISSION_GRANTED) {
                iterator.remove();//已授权则remove
            }
        }
        if (permissionList.size() > 0) {
            // 如果没有授予该权限,就去提示用户请求
            //将List转为数组
            String[] permissions = permissionList.toArray(new String[permissionList.size()]);
            // 开始提交请求权限
            ActivityCompat.requestPermissions(this, permissions, 0x10);
        } else {
            Log.i("zhh", "权限已申请");
            openActivity(MainActivity.class);
        }

    } else {
        openActivity(MainActivity.class);
    }
}

//初始化语音合成
private void initTTS() {
    //讯飞语音播报平台
    SpeechUtility.createUtility(this, "appid=");//=号后面写自己应用的APPID
    Setting.setShowLog(true); //设置日志开关(默认为true),设置成false时关闭语音云SDK日志打印
    TTSUtils.getInstance().init(); //初始化工具类
}


/**
 * 权限申请返回结果
 *
 * @param requestCode  请求码
 * @param permissions  权限数组
 * @param grantResults 申请结果数组,里面都是int类型的数
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case 0x10:
            if(grantResults.length>0&&ifGrantResult(grantResults)){
                Toast.makeText(this, "同意权限申请", Toast.LENGTH_SHORT).show();
                openActivity(MainActivity.class);
            }else{
                Toast.makeText(this, "权限被拒绝了", Toast.LENGTH_SHORT).show();
                finish();
            }
            break;
        default:
            break;
    }

}

private boolean ifGrantResult(int[] grants) {
    boolean isGrant = true;
    for (int grant : grants) {
        if (grant == PackageManager.PERMISSION_DENIED) {
            isGrant = false;
            break;
        }
    }
    return isGrant;
}


//敏感权限添加
private void addListPermission() {
    if (null == permissionList) {
        permissionList = new ArrayList<>();
        permissionList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        permissionList.add(Manifest.permission.READ_PHONE_STATE);
        permissionList.add(Manifest.permission.RECORD_AUDIO);
    }
}

}
 

III.语音播报封装(部分代码)

public class TTSUtils implements InitListener, SynthesizerListener {

private static volatile TTSUtils instance = null;
private boolean isInitSuccess = false;
private SpeechSynthesizer mTts;

//单例模式
public static TTSUtils getInstance() {
    if (instance == null) {
        synchronized (TTSUtils.class) {
            if (instance == null) {
                instance = new TTSUtils();
            }
        }
    }
    return instance;
}

public TTSUtils() {

}

// 初始化合成对象
public void init() {
    //判断进程是否已启动,初始化多次会报错
    //个人遇到问题:极光推送引入后,不加该条件回报错
    if (CourseUtils.resultProcess("com.zhanghai.ttsapp")) {

        mTts = SpeechSynthesizer.createSynthesizer(App.getContext(), this);
        // 清空参数
        mTts.setParameter(SpeechConstant.PARAMS, null);
        // 设置在线云端
        mTts.setParameter(SpeechConstant.ENGINE_TYPE,
                SpeechConstant.TYPE_CLOUD);

        // 设置发音人--发音人选择--具体见values-string
        mTts.setParameter(SpeechConstant.VOICE_NAME, "xiaoqi");

        // 设置发音语速
        mTts.setParameter(SpeechConstant.SPEED, "50");
        // 设置音调
        mTts.setParameter(SpeechConstant.PITCH, "50");
        // 设置合成音量
        mTts.setParameter(SpeechConstant.VOLUME, "100");
        // 设置播放器音频流类型
        mTts.setParameter(SpeechConstant.STREAM_TYPE, "3");
        // 设置播放合成音频打断音乐播放,默认为true
        mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");
        // 设置音频保存路径,需要申请WRITE_EXTERNAL_STORAGE权限,如不需保存注释该行代码

// mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH,”./sdcard/iflytek.pcm”);
Log.i(“zhh”, “–初始化成完成-“);
}

}

//开始合成
public void speak(String msg) {
    if (isInitSuccess) {
        if (mTts.isSpeaking()) {
            stop();
        }
        mTts.startSpeaking(msg, this);
    } else {
        init();
    }
}

}
IV:调用实例

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private static final String TAG = MainActivity.class.getSimpleName();
private EditText et = null;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    et = findViewById(R.id.et);
    findViewById(R.id.btn0).setOnClickListener(this);
    findViewById(R.id.btn1).setOnClickListener(this);
    findViewById(R.id.btn2).setOnClickListener(this);
}


@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.btn0:
            TTSUtils.getInstance().speak("bigsea是大海");
            break;
        case R.id.btn1:
            String msg = et.getText().toString();
            TTSUtils.getInstance().speak(TextUtils.isEmpty(msg) ? "输入信息为空" : msg);
            break;
        case R.id.btn2:
            startActivity(new Intent(this, AndroidTTSActivity.class));
            break;
        default:
            break;
    }
}

@Override
protected void onResume() {
    //移动数据统计分析--不用可不用加入
    FlowerCollector.onResume(MainActivity.this);
    FlowerCollector.onPageStart(TAG);
    super.onResume();
}

@Override
protected void onPause() {
    //移动数据统计分析
    FlowerCollector.onPageEnd(TAG);
    FlowerCollector.onPause(MainActivity.this);
    super.onPause();
}


@Override
protected void onDestroy() {
    super.onDestroy();
    TTSUtils.getInstance().release();//释放资源
}

}
完!!!

原文地址:http://www.cnblogs.com/xxdh/p/9303377.html

下载地址:https://github.com/seastoneard/TTSApp
————————————————
版权声明:本文为CSDN博主「沧海龙腾LV」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xialong_927/article/details/84256354

Android 轻松实现语音识别

苹果的iphone 有语音识别用的是Google 的技术,做为Google 力推的Android 自然会将其核心技术往Android 系统里面植入,并结合google 的云端技术将其发扬光大。 
所以Google Voice Recognition在Android 的实现就变得极其轻松。 
语音识别,借助于云端技术可以识别用户的语音输入,包括语音控制等技术,下面我们将利用Google 提供的Api 实现这一功能。 
功能点为:通过用户语音将用户输入的语音识别出来,并打印在列表上。 
用户通过点击speak按钮显示界面: 
用户说完话后,将提交到云端搜索: 
在云端搜索完成后,返回打印数据:

Android 轻松实现语音识别的完整代码    

* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
 
package com.example.android.apis.app;
 
import com.example.android.apis.R;
 
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
 
import java.util.ArrayList;
import java.util.List;
 
/**
* Sample code that invokes the speech recognition intent API.
*/
public class VoiceRecognition extends Activity implements OnClickListener {
 
private static final int VOICE_RECOGNITION_REQUEST_CODE = 1234;
 
private ListView mList;
 
/**
* Called with the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
 
// Inflate our UI from its XML layout description.
setContentView(R.layout.voice_recognition);
 
// Get display items for later interaction
Button speakButton = (Button) findViewById(R.id.btn_speak);
 
mList = (ListView) findViewById(R.id.list);
 
// Check to see if a recognition activity is present
PackageManager pm = getPackageManager();
List activities = pm.queryIntentActivities(
new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
if (activities.size() != 0) {
speakButton.setOnClickListener(this);
} else {
speakButton.setEnabled(false);
speakButton.setText("Recognizer not present");
}
}
 
/**
* Handle the click on the start recognition button.
*/
public void onClick(View v) {
if (v.getId() == R.id.btn_speak) {
startVoiceRecognitionActivity();
}
}
 
/**
* Fire an intent to start the speech recognition activity.
*/
private void startVoiceRecognitionActivity() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Speech recognition demo");
startActivityForResult(intent, VOICE_RECOGNITION_REQUEST_CODE);
}
 
/**
* Handle the results from the recognition activity.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == VOICE_RECOGNITION_REQUEST_CODE && resultCode == RESULT_OK) {
// Fill the list view with the strings the recognizer thought it could have heard
ArrayList matches = data.getStringArrayListExtra(
RecognizerIntent.EXTRA_RESULTS);
mList.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1,
matches));
}
 
super.onActivityResult(requestCode, resultCode, data);
}
}

Google推出开源语音视频聊天软件WebRTC

天极网软件频道2011-06-22 09:30  分享到:我要吐槽

  Google近期推出了开源的语音视频聊天软件WebRTC。有消息称Google将把这款功能类似Skype的软件集成到Chrome浏览器中。

  WebRTC源于Google 2010年收购的互联网电话和视频会议公司Global IP Solutions(以下简称“GIPS”)。当Chrome浏览器集成WebRTC之后,Gmail的受益最明显。目前,Gmail拥有语音视频聊天功能,但用户需要安装Google私有的插件。随着Google VoIP业务的成熟,以及Gmail对Google Voice/Talk等的集成,Gmail聊天已经越来越重要。

  不过,Google对WebRTC有更高的期望。Google希望WebRTC能成为互联网视频会议和点对点通信的标准,并在此基础之上制定网 络通信协议。Google在官方博客中表示,WebRTC将是开源、无版权费的,Google还将就WebRTC与Mozilla和Opera等其他浏览器厂商合作。

  Google是网页应用和云计算的积极支持者。网页应用能够很方便地跨操作系统,不仅可以用于Windows和Mac OS X,也可以被智能手机使用。预计WebRTC将很快进入Chrome浏览器。

  Google GIPS一名程序员亨里克·安德里森(Henrik Andreasson)表示:“我们的目标是通过简单的Javascript应用程序接口,使Chrome拥有实时通信能力(RTC)。我们正在努力使Chrome全面支持RTC,从WebKit到语音视频通信功能。”

  WebRTC使用了GIPS的两款语音编解码器,包括用于宽带连接的iSAC和用于窄带连接的iLBC。视频部分,WebRTC使用了Google的VP8编解码器,这也是一项开源、无版权费的技术。

 几个常见的语音交互平台的简介和比较
分类: 多媒体技术 2014-01-19 16:04 2762人阅读 评论(0) 收藏 举报

目录(?)[+]
1.概述

最近做了两个与语音识别相关的项目,两个项目的主要任务虽然都是语音识别,或者更确切的说是关键字识别,但开发的平台不同, 一个是windows下的,另一个是android平台的,于是也就选用了不同的语音识别平台,前者选的是微软的Speech API开发的,后者则选用 的是CMU的pocketsphinx,本文主要将一些常见的语音交互平台进行简单的介绍和对比。

这里所说的语音交互包含语音识别(Speech Recognition,SR,也称为自动语音识别,Automatic Speech Recognition,ASR)和语音 合成(Speech Synthesis,SS,也称为Text-To-Speech,简记为TTS)两种技术,另外还会提到声纹识别(Voice Print Recognition, 简记为VPR)技术。

语音识别技术是将计算机接收、识别和理解语音信号转变为相应的文本文件或者命令的技术。它是一门涉及到语音语言学、信号处理、 模式识别、概率论和信息论、发声机理和听觉机理、人工智能的交叉学科。在语音识别系统的帮助下,即使用户不懂电脑或者无法使用 电脑,都可以通过语音识别系统对电脑进行操作。

语音合成,又称文语转换(Text to Speech)技术,能将任意文字信息实时转化为标准流畅的语音朗读出来,相当于给机器装上了人工 嘴巴。它涉及声学、语言学、数字信号处理、计算机科学等多个学科技术,是中文信息处理领域的一项前沿技术,解决的主要问题就是如何 将文字信息转化为可听的声音信息,也即让机器像人一样开口说话。

下面按平台是否开源来介绍几种常见的语音交互平台,关于语音识别和语音合成技术的相关原理请参见我接下来的其他文章。
2.商业化的语音交互平台
1)微软Speech API

微软的Speech API(简称为SAPI)是微软推出的包含语音识别(SR)和语音合成(SS)引擎的应用编程接口(API),在Windows下应用 广泛。目前,微软已发布了多个SAPI版本(最新的是SAPI 5.4版),这些版本要么作为于Speech SDK开发包发布,要么直接被包含在windows 操作系统中发布。SAPI支持多种语言的识别和朗读,包括英文、中文、日文等。SAPI的版本分为两个家族,1-4为一个家族,这四个版本彼此 相似,只是稍微添加了一些新的功能;第二个家族是SAPI5,这个系列的版本是全新的,与前四个版本截然不同。

最早的SAPI 1.0于1995年发布,支持Windows 95和Windows NT 3.51。这个版本的SAPI包含比较初级的直接语音识别和直接语音合成的API, 应用程序可以直接控制识别或合成引擎,并简化更高层次的语音命令和语音通话的API。SAPI3.0于97年发布,它添加了听写语音识别(非连续 语音识别)和一些应用程序实例。98年微软发布了SAPI4.0,这个版本不仅包含了核心的COM API,用C++类封装,使得用C++来编程更容易, 而且还有ActiveX控件,这个控件可以再VB中拖放。这个版本的SS引擎随Windows2000一起发布,而SR引擎和SS引擎又一起以SDK的形式发布。

SAPI5.0 于2000年发布,新的版本将严格将应用与引擎分离的理念体现得更为充分,所有的调用都是通过动态调用sapi.dll来实现的, 这样做的目的是使得API更为引擎独立化,防止应用依赖于某个具有特定特征的引擎,这种改变也意图通过将一些配置和初始化的代码放 到运行时来使得应用程序的开发更为容易。
2).IBM viaVoice

IBM是较早开始语音识别方面的研究的机构之一,早在20世纪50年代末期,IBM就开始了语音识别的研究,计算机被设计用来检测特定的语言 模式并得出声音和它对应的文字之间的统计相关性。在1964年的世界博览会上,IBM向世人展示了数字语音识别的“shoe box recognizer”。 1984年,IBM发布的语音识别系统在5000个词汇量级上达到了95%的识别率。

1992年,IBM引入了它的第一个听写系统,称为“IBM Speech Server Series (ISSS)”。1996年发布了新版的听写系统,成为“VoiceType3.0”, 这是viaVoice的原型,这个版本的语音识别系统不需要训练,可以实现孤立单词的听写和连续命令的识别。VoiceType3.0支持Windows95系统, 并被集成到了OS/2 WARP系统之中。与此同时,IBM还发布了世界上首个连续听写系统“MedSpeak Radiology”。最后,IBM及时的在假日购物季节 发布了大众化的实用的“VoiceType Simply Speaking”系统,它是世界上首个消费版的听写产品(the world’s first consumer dictation product).

1999年,IBM发布了VoiceType的一个免费版。2003年,IBM授权ScanSoft公司拥有基于ViaVoice的桌面产品的全球独家经销权,而ScanSoft公司 拥有颇具竞争力的产品“Dragon NaturallySpeaking”。两年后,ScanSoft与Nuance合并,并宣布公司正式更名为Nuance Communications,Inc。 现在很难找到IBM viaVoice SDK的下载地址了,它已淡出人们的视线,取而代之的是Nuance。
3)Nuance

Nuance通讯是一家跨国计算机软件技术公司,总部设在美国马萨诸塞州伯灵顿,主要提供语音和图像方面的解决方案和应用。目前的业务集中 在服务器和嵌入式语音识别,电话转向系统,自动电话目录服务,医疗转录软件与系统,光学字符识别软件,和台式机的成像软件等。

Nuance语音技术除了语音识别技术外,还包扩语音合成、声纹识别等技术。世界语音技术市场,有超过80%的语音识别是采用Nuance识别引擎技术, 其名下有超过1000个专利技术,公司研发的语音产品可以支持超过50种语言,在全球拥有超过20亿用户。据传,苹果的iPhone 4S的Siri语音识别中 应用了Nuance的语音识别服务。另外,据Nuance公司宣布的重磅消息,其汽车级龙驱动器Dragon Drive将在新奥迪A3上提供一个免提通讯接口, 可以实现信息的听说获取和传递。

Nuance Voice Platform(NVP)是Nuance公司推出的语音互联网平台。Nuance公司的NVP平台由三个功能块组成:Nuance Conversation Server 对话服务器,Nuance Application Environment (NAE)应用环境及Nuance Management Station管理站。Nuance Conversation Server对话服务 器包括了与Nuance语音识别模块集成在一起的VoiceXML解释器,文语转换器(TTS)以及声纹鉴别软件。NAE应用环境包括绘图式的开发工具, 使得语音应用的设计变得和应用框架的设计一样便利。Nuance Management Station管理站提供了非常强大的系统管理和分析能力,它们是为了 满足语音服务的独特需要而设计的。
4)科大讯飞——讯飞语音

提到科大讯飞,大家都不陌生,其全称是“安徽科大讯飞信息科技股份有限公司”,它的前身是安徽中科大讯飞信息科技有限公司,成立于99 年12月,07年变更为安徽科大讯飞信息科技股份有限公司,现在是一家专业从事智能语音及语音技术研究、软件及芯片产品开发、语音信息服务 的企业,在中国语音技术领域可谓独占鳌头,在世界范围内也具有相当的影响力。

科大讯飞作为中国最大的智能语音技术提供商,在智能语音技术领域有着长期的研究积累,并在中文语音合成、语音识别、口语评测等多项 技术上拥有国际领先的成果。03年,科大讯飞获迄今中国语音产业唯一的“国家科技进步奖(二等)”,05年获中国信息产业自主创新最高荣誉 “信息产业重大技术发明奖”。06年至11年,连续六届英文语音合成国际大赛(Blizzard Challenge)荣获第一名。08年获国际说话人识别评测 大赛(美国国家标准技术研究院—NIST 2008)桂冠,09年获得国际语种识别评测大赛(NIST 2009)高难度混淆方言测试指标冠军、通用测试 指标亚军。

科大讯飞提供语音识别、语音合成、声纹识别等全方位的语音交互平台。拥有自主知识产权的智能语音技术,科大讯飞已推出从大型电信级 应用到小型嵌入式应用,从电信、金融等行业到企业和家庭用户,从PC到手机到MP3/MP4/PMP和玩具,能够满足不同应用环境的多种产品,科大 讯飞占有中文语音技术市场60%以上市场份额,语音合成产品市场份额达到70%以上。
5)其他

其他的影响力较大商用语音交互平台有谷歌的语音搜索(Google Voice Search),百度和搜狗的语音输入法等等,这些平台相对于以上的4个 语音交互平台,应用范围相对较为局限,影响力也没有那么强,这里就不详细介绍了。
3.开源的语音交互平台
1)CMU-Sphinx

CMU-Sphinx也简称为Sphinx(狮身人面像),是卡内基 - 梅隆大学( Carnegie Mellon University,CMU)开发的一款开源的语音识别系统, 它包括一系列的语音识别器和声学模型训练工具。

Sphinx有多个版本,其中Sphinx1~3是C语言版本的,而Sphinx4是Java版的,另外还有针对嵌入式设备的精简优化版PocketSphinx。Sphinx-I 由李开复(Kai-Fu Lee)于1987年左右开发,使用了固定的HMM模型(含3个大小为256的codebook),它被号称为第一个高性能的连续语音识别 系统(在Resource Management数据库上准确率达到了90%+)。Sphinx-II由Xuedong Huang于1992年左右开发,使用了半连续的HMM模型, 其HMM模型是一个包含了5个状态的拓扑结构,并使用了N-gram的语言模型,使用了Fast lextree作为实时的解码器,在WSJ数据集上的识别率 也达到了90%+。

Sphinx-III主要由Eric Thayer 和Mosur Ravishankar于1996年左右开发,使用了完全连续的(也支持半连续的)HMM模型,具有灵活 的feature vector和灵活的HMM拓扑结构,包含可选的两种解码器:较慢的Flat search和较快的Lextree search。该版本在BN(98的测评数据 集)上的WER(word error ratio)为19%。Sphinx-III的最初版还有很多limitations,诸如只支持三音素文本、只支持Ngram模型(不 支持CFG/FSA/SCFG)、对所有的sound unit其HMM拓扑结构都是相同的、声学模型也是uniform的。Sphinx-III的最新版是09年初发布的0.8版, 在这些方面有很多的改进。

最新的Sphinx语音识别系统包含如下软件包:
? Pocketsphinx — recognizer library written in C.
? Sphinxbase — support library required by Pocketsphinx
? Sphinx4 — adjustable, modifiable recognizer written in Java
? CMUclmtk — language model tools
? Sphinxtrain — acoustic model training tools
这些软件包的可执行文件和源代码在sourceforge上都可以免费下载得到。
2)HTK

HTK是Hidden Markov Model Toolkit(隐马尔科夫模型工具包)的简称,HTK主要用于语音识别研究,现在已经被用于很多其他方面的研究, 包括语音合成、字符识别和DNA测序等。

HTK最初是由剑桥大学工程学院(Cambridge University Engineering Department ,CUED)的机器智能实验室(前语音视觉及机器人组) 于1989年开发的,它被用来构建CUED的大词汇量的语音识别系统。93年Entropic Research Laboratory Inc.获得了出售HTK的权利,并在95年 全部转让给了刚成立的Entropic Cambridge Research Laboratory Ltd,Entropic一直销售着HTK,直到99年微软收购了Entropic,微软重新 将HTK的版权授予CUED,并给CUED提供支持,这样CUED重新发布了HTK,并在网络上提供开发支持。

HTK的最新版本是09年发布的3.4.1版,关于HTK的实现原理和各个工具的使用方法可以参看HTK的文档HTKBook。
3)Julius

Julius是一个高性能、双通道的大词汇量连续语音识别(large vocabulary continues speech recognition,LVCSR)的开源项目, 适合于广大的研究人员和开发人员。它使用3-gram及上下文相关的HMM,在当前的PC机上能够实现实时的语音识别,单词量达到60k个。

Julius整合了主要的搜索算法,高度的模块化使得它的结构模型更加独立,它同时支持多种HMM模型(如shared-state triphones 和 tied-mixture models等),支持多种麦克风通道,支持多种模型和结构的组合。它采用标准的格式,这使得和其他工具箱交叉使用变得 更容易。它主要支持的平台包括Linux和其他类Unix系统,也适用于Windows。它是开源的,并使用BSD许可协议。

自97年后,Julius作为日本LVCSR研究的一个自由软件工具包的一部分而延续下来,后在2000年转由日本连续语音识别联盟(CSRC)经营。 从3.4版起,引入了被称为“Julian”的基于语法的识别解析器,Julian是一个改自Julius的以手工设计的DFA作为语言模型的版本,它可以 用来构建小词汇量的命令识别系统或语音对话系统。
4)RWTH ASR

该工具箱包含最新的自动语音识别技术的算法实现,它由 RWTH Aachen 大学的Human Language Technology and Pattern Recognition Group 开发。

RWTH ASR工具箱包括声学模型的构建、解析器等重要部分,还包括说话人自适应组件、说话人自适应训练组件、非监督训练组件、个性化 训练和单词词根处理组件等,它支持Linux和Mac OS等操作系统,其项目网站上有比较全面的文档和实例,还提供了现成的用于研究目的的 模型等。

该工具箱遵从一种从QPL发展而来的开源协议,只允许用于非商业用途。
5)其他

上面提到的开源工具箱主要都是用于语音识别的,其他的开源语音识别项目还有Kaldi 、simon 、iATROS-speech 、SHoUT 、 Zanzibar OpenIVR 等。

常见的语音合成的开源工具箱有MARY、SpeakRight、Festival 、FreeTTS 、Festvox 、eSpeak 、Flite 等。

常见的声纹识别的开源工具箱有Alize、openVP等。
4.小结

本文介绍了几种常见的语音交互平台,主要是语音识别、语音合成的软件或工具包,还顺便提到了声纹识别的内容, 下面做一个简单的总结:
以上总结的表格希望对读者有用!

参考文献

[1]语音识别-维基百科:http://zh.wikipedia.org/wiki/语音识别 
[2]语音合成-百度百科:http://baike.baidu.com/view/549184.htm 
[3] Microsoft Speech API:http://en.wikipedia.org/wiki/Speech_Application_Programming_Interface#SAPI_1 
[4] MSDN-SAPI:http://msdn.microsoft.com/zh-cn/library/ms723627.aspx 
[5] 微软语音技术 Windows 语音编程初步:http://blog.csdn.net/yincheng01/article/details/3511525 
[6]IBM Human Language Technologies History:http://www.research.ibm.com/hlt/html/history.html 
[7] Nuance: http://en.wikipedia.org/wiki/Nuance_Communications 
[8] 科大讯飞:http://baike.baidu.com/view/362434.htm 
[9] CMU-Sphinx: http://en.wikipedia.org/wiki/CMU_Sphinx 
[10] CMU Sphinx homepage:http://cmusphinx.sourceforge.net/wiki/ 
[11] HTK Toolkit:http://htk.eng.cam.ac.uk/ 
[12] Julius:http://en.wikipedia.org/wiki/Julius_(software) 
[13] RWTH ASR:http://en.wikipedia.org/wiki/RWTH_ASR 
[14] List of speech recognition software: http://en.wikipedia.org/wiki/List_of_speech_recognition_software 
[15] Speech recognition: http://en.wikipedia.org/wiki/Speech_recognition 
[16] Speech synthesis: http://en.wikipedia.org/wiki/Speech_synthesis 
[17] Speaker recognition: http://en.wikipedia.org/wiki/Speaker_recognition
 Sphinx-4一个完全采用Java开的语音识别器.
  http://cmusphinx.sourceforge.net/sphinx4/#what_is_sphinx4

Google推出开源语音视频聊天软件WebRTC

天极网软件频道2011-06-22 09:30  分享到:我要吐槽

  Google近期推出了开源的语音视频聊天软件WebRTC。有消息称Google将把这款功能类似Skype的软件集成到Chrome浏览器中。

  WebRTC源于Google 2010年收购的互联网电话和视频会议公司Global IP Solutions(以下简称“GIPS”)。当Chrome浏览器集成WebRTC之后,Gmail的受益最明显。目前,Gmail拥有语音视频聊天功能,但用户需要安装Google私有的插件。随着Google VoIP业务的成熟,以及Gmail对Google Voice/Talk等的集成,Gmail聊天已经越来越重要。

  不过,Google对WebRTC有更高的期望。Google希望WebRTC能成为互联网视频会议和点对点通信的标准,并在此基础之上制定网 络通信协议。Google在官方博客中表示,WebRTC将是开源、无版权费的,Google还将就WebRTC与Mozilla和Opera等其他浏览器厂商合作。

  Google是网页应用和云计算的积极支持者。网页应用能够很方便地跨操作系统,不仅可以用于Windows和Mac OS X,也可以被智能手机使用。预计WebRTC将很快进入Chrome浏览器。

  Google GIPS一名程序员亨里克·安德里森(Henrik Andreasson)表示:“我们的目标是通过简单的Javascript应用程序接口,使Chrome拥有实时通信能力(RTC)。我们正在努力使Chrome全面支持RTC,从WebKit到语音视频通信功能。”

  WebRTC使用了GIPS的两款语音编解码器,包括用于宽带连接的iSAC和用于窄带连接的iLBC。视频部分,WebRTC使用了Google的VP8编解码器,这也是一项开源、无版权费的技术。

一个简单语音识别系统:

http://www.cnblogs.com/zhuweisky/p/4059048.html

调用科大讯飞:

http://www.apkbus.com/android-170225-1-1.html

开源语音格式speex教程(for iOS):

http://www.cocoachina.com/industry/20121105/5029.html

Tp5使用命令行模式,自定义指令

https://blog.csdn.net/benben0729/article/details/82286450

第一步,创建一个自定义命令类文件,新建application/common/command/Hello.php

<?phpnamespace app\common\command; use think\console\Command;use think\console\Input;use think\console\input\Argument;use think\console\input\Option;use think\console\Output; class Hello extends Command{    protected function configure()    {        $this->setName('hello')        	->addArgument('name', Argument::OPTIONAL, "your name")            ->addOption('city', null, Option::VALUE_REQUIRED, 'city name')        	->setDescription('Say Hello');    }     protected function execute(Input $input, Output $output)    {    	$name = trim($input->getArgument('name'));      	$name = $name ?: 'thinkphp'; 		if ($input->hasOption('city')) {        	$city = PHP_EOL . 'From ' . $input->getOption('city');        } else {        	$city = '';        }                $output->writeln("Hello," . $name . '!' . $city);    }}

这个文件定义了一个叫hello的命令,并设置了一个name参数和一个city选项。

第二步,配置application/command.php文件

<?phpreturn [    'app\common\command\Hello',];

V5.1.24+版本开始,你可以定义为下面的方式提高性能。

<?phpreturn [	// 指令名 =》完整的类名    'hello'	=>	'app\common\command\Hello',];

第三步,测试-命令帮助-命令行下运行

php think

输出

Think Console version 0.1 Usage:  command [options] [arguments] Options:  -h, --help            Display this help message  -V, --version         Display this console version  -q, --quiet           Do not output any message      --ansi            Force ANSI output      --no-ansi         Disable ANSI output  -n, --no-interaction  Do not ask any interactive question  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug Available commands:  build              Build Application Dirs  clear              Clear runtime file  hello              Say Hello   help               Displays help for a command  list               Lists commands make  make:controller    Create a new resource controller class  make:model         Create a new model class optimize  optimize:autoload  Optimizes PSR0 and PSR4 packages to be loaded with classmaps too, good for production.  optimize:config    Build config and common file cache.  optimize:schema    Build database schema cache. 

第四步,运行hello命令

php think hello

输出

Hello thinkphp!

添加命令参数

php think hello liuchen

输出

Hello liuchen!

添加city选项

php think hello liuchen --city shanghai

输出

Hello thinkphp!From shanghai

注意看参数和选项的调用区别

在控制器中调用命令

支持在控制器的操作方法中直接调用命令,例如:

<?phpnamespace app\index\controller; use think\Console;use think\Controller; class Index extends Controller{    public function hello($name)    {        $output = Console::call('hello ' . $name);         return $output->fetch();    }}

访问该操作方法后,例如:

http://tp5.com/index/index/hello/name/thinkphp

页面会输出

Hello thinkphp!

一旦在控制器中调用命令,并不会生成cli命令行日志,而是在普通的web日志中记录。

快速生成指令(V5.1.24+

V5.1.24+版本开始,你可以通过命令行指令快速生成一条指令,包括指令类文件,例如:

php think make:command First first

First表示类名,实际生成的指令类则是app\command\First(自动生成的指令类的命名空间默认都是 app\command)。

first表示指令名,建议统一使用小写,如果不传入该参数,则默认用类名的小写作为指令名。

为了让指令可以无需定义自动调用,需要在你的应用配置的console.php配置文件中,增加下面的配置参数:

'auto_path' => env('app_path') . 'command/',

配置后, 你可以在命令行测试下刚才新生成的指令。

php think first

输出结果为:

first

你可以编辑app\command\First类,完成实际的指令任务。

注意,如果你生成了指定命名空间的指令,但又不是你配置的自动加载目录,那么仍然需要在command.php 文件中定义指令。

如果需要生成一个指定的命名空间,可以使用:

php think make:command app\index\Second second

转自:看云https://www.kancloud.cn/manual/thinkphp5_1/354146

java显示声音波形图示例

这篇文章主要介绍了java显示声音波形图示例,需要的朋友可以参考下

package _tmp;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.SourceDataLine;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class SoundTest {

 public static class WaveformGraph extends JFrame {
  private Deque deque = new LinkedList();
  private Timer timer;
  private Image buffered;
  private Image showing;

  public WaveformGraph(int width, int height) {
   setSize(width, height);
   timer = new Timer();
   buffered = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
   timer.schedule(new TimerTask() {
    @Override public void run() {
     Graphics g = buffered.getGraphics();
     g.setColor(Color.WHITE);
     g.fillRect(0, 0, getWidth(), getHeight());
     g.setColor(Color.BLACK);

     g.translate(10, getHeight()/2);
     synchronized (deque) {
      float heightRate = 1;
      if(deque.size() > 1) {
       Iterator iter = deque.iterator();
       Short p1 = iter.next();
       Short p2 = iter.next();
       int x1 = 0, x2 = 0;
       while(iter.hasNext()) {
        g.drawLine(x1, (int)(p1heightRate), x2, (int)(p2heightRate));

        p1 = p2;
        p2 = iter.next();
        x1 = x2;
        x2 += 1;
       }
      }
     }
     g.dispose();

     SwingUtilities.invokeLater(new Runnable() {
      @Override public void run() {
       showing = buffered;
       repaint();
       showing = null;
      }
     });
    }
   }, 100, 100);
  }
  @Override
  public void paint(Graphics g) {
   super.paint(g);
   if(buffered!=null) {
    g.drawImage(buffered, 0, 0, null);
   }
  }

  public void put(short v) {
   synchronized (deque) {
    deque.add(v);
    if(deque.size() > 500) {
     deque.removeFirst();
    }
   }
  }

  public void clear() {
   deque.clear();
  }
 }

 public static void main(String[] args) throws Exception {
//  record();
  WaveformGraph waveformGraph = new WaveformGraph(500, 300);
  waveformGraph.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  waveformGraph.setVisible(true);

  AudioInputStream ais = AudioSystem.getAudioInputStream(new File(“C:\Documents and Settings\wml\My Documents\My Music\苏仨 – 失眠症.wav”));
  printFormat(ais.getFormat());
  
  SourceDataLine player = AudioSystem.getSourceDataLine(ais.getFormat());

  player.open();
  player.start();
  byte[] buf = new byte[4];
  int len;
  while((len=ais.read(buf))!=-1) {

   if(ais.getFormat().getChannels() == 2) {
    if(ais.getFormat().getSampleRate() == 16) {
     waveformGraph.put((short) ((buf[1] << 8) | buf[0]));//左声道
//     waveformGraph.put((short) ((buf[3] << 8) | buf[2]));//右声道
    } else {
     waveformGraph.put(buf[1]);//左声道
     waveformGraph.put(buf[3]);//左声道

//     waveformGraph.put(buf[2]);//右声道
//     waveformGraph.put(buf[4]);//右声道
    }
   } else {
    if(ais.getFormat().getSampleRate() == 16) {
     waveformGraph.put((short) ((buf[1] << 8) | buf[0]));
     waveformGraph.put((short) ((buf[3] << 8) | buf[2]));
    } else {
     waveformGraph.put(buf[1]);
     waveformGraph.put(buf[2]);
     waveformGraph.put(buf[3]);
     waveformGraph.put(buf[4]);
    }
   }

   player.write(buf, 0, len);
  }

  player.close();
  ais.close();
 }
 public static void printFormat(AudioFormat format) {
  System.out.println(format.getEncoding() + ” => “
    + format.getSampleRate()+” hz, “
    + format.getSampleSizeInBits() + ” bit, “
    + format.getChannels() + ” channel, “
    + format.getFrameRate() + ” frames/second, “
    + format.getFrameSize() + ” bytes/frame”);
 }
// public static void record() throws LineUnavailableException,
//   InterruptedException {
//  AudioFormat audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 48000F, 16, 1, 2, 48000F, false);
//  Info recordDevInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
//
//  final TargetDataLine recordLine = (TargetDataLine) AudioSystem.getLine(recordDevInfo);
//  final SourceDataLine playLine = AudioSystem.getSourceDataLine(audioFormat);
//  
//  recordLine.open(audioFormat, recordLine.getBufferSize());
//  playLine.open(audioFormat, recordLine.getBufferSize());
//  
//  Thread recorder = new Thread() {
//   public void run() {
//    recordLine.start();
//    playLine.start();
//    
//    FloatControl fc = (FloatControl) playLine.getControl(FloatControl.Type.MASTER_GAIN);
//    double value = 2;
//    float dB = (float) (Math.log(value == 0.0 ? 0.0001 : value) / Math.log(10.0) * 20.0);
//    fc.setValue(dB);
//    
//    try {
//     AudioInputStream in = new AudioInputStream(recordLine);
//     byte[] buf = new byte[recordLine.getBufferSize()];
//     int len;
//     while((len=in.read(buf)) != -1) {
//      playLine.write(buf, 0, len);
//     }
//    } catch (IOException e) {
//     e.printStackTrace();
//    } finally {
//     recordLine.stop();
//     playLine.stop();
//    }
//   };
//  };
//  recorder.start();
//  recorder.join();
// }
}

Java实现Shazam声音识别算法的实例代码

https://www.jb51.net/article/147139.htm

Shazam算法采用傅里叶变换将时域信号转换为频域信号,并获得音频指纹,最后匹配指纹契合度来识别音频。这篇文章给大家介绍Java实现Shazam声音识别算法的实例代码,需要的朋友参考下吧

Shazam算法采用傅里叶变换将时域信号转换为频域信号,并获得音频指纹,最后匹配指纹契合度来识别音频。

1、AudioSystem获取音频

奈奎斯特-香农采样定理告诉我们,为了能捕获人类能听到的声音频率,我们的采样速率必须是人类听觉范围的两倍。人类能听到的声音频率范围大约在20Hz到20000Hz之间,所以在录制音频的时候采样率大多是44100Hz。这是大多数标准MPEG-1 的采样率。44100这个值最初来源于索尼,因为它可以允许音频在修改过的视频设备上以25帧(PAL)或者30帧( NTSC)每秒进行录制,而且也覆盖了专业录音设备的20000Hz带宽。所以当你在选择录音的频率时,选择44100Hz就好了。

定义音频格式:

123456789public static float sampleRate = 44100;public static int sampleSizeInBits = 16;public static int channels = 2; // doublepublic static boolean signed = true; // Indicates whether the data is signed or unsignedpublic static boolean bigEndian = true; // Indicates whether the audio data is stored in big-endian or little-endian orderpublic AudioFormat getFormat() {return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed,bigEndian);}

调用麦克风获取音频,保存到out中

123456789101112131415161718public static ByteArrayOutputStream out = new ByteArrayOutputStream();1try {AudioFormat format = smartAuto.getFormat(); // Fill AudioFormat with the settingsDataLine.Info info = new DataLine.Info(TargetDataLine.class, format);startTime = new Date().getTime();System.out.println(startTime);SmartAuto.line = (TargetDataLine) AudioSystem.getLine(info);SmartAuto.line.open(format);SmartAuto.line.start();new FileAnalysis().getDataToOut("");while (smartAuto.running) {checkTime(startTime);}SmartAuto.line.stop();SmartAuto.line.close();} catch (Throwable e) {e.printStackTrace();}

获取到的out数据需要通过傅里叶变换,从时域信号转换为频域信号。

傅里叶变换

1234567891011121314151617181920212223242526272829303132333435public Complex[] fft(Complex[] x) {int n = x.length;// 因为exp(-2i*n*PI)=1,n=1时递归原点if (n == 1){return x;}// 如果信号数为奇数,使用dft计算if (n % 2 != 0) {return dft(x);}// 提取下标为偶数的原始信号值进行递归fft计算Complex[] even = new Complex[n / 2];for (int k = 0; k < n / 2; k++) {even[k] = x[2 * k];}Complex[] evenValue = fft(even);// 提取下标为奇数的原始信号值进行fft计算// 节约内存Complex[] odd = even;for (int k = 0; k < n / 2; k++) {odd[k] = x[2 * k + 1];}Complex[] oddValue = fft(odd);// 偶数+奇数Complex[] result = new Complex[n];for (int k = 0; k < n / 2; k++) {// 使用欧拉公式e^(-i*2pi*k/N) = cos(-2pi*k/N) + i*sin(-2pi*k/N)double p = -2 * k * Math.PI / n;Complex m = new Complex(Math.cos(p), Math.sin(p));result[k] = evenValue[k].add(m.multiply(oddValue[k]));// exp(-2*(k+n/2)*PI/n) 相当于 -exp(-2*k*PI/n),其中exp(-n*PI)=-1(欧拉公式);result[k + n / 2] = evenValue[k].subtract(m.multiply(oddValue[k]));}return result;}

计算out的频域值

123456789101112131415161718192021private void setFFTResult(){byte audio[] = SmartAuto.out.toByteArray();final int totalSize = audio.length;System.out.println("totalSize = " + totalSize);int chenkSize = 4;int amountPossible = totalSize/chenkSize;//When turning into frequency domain we'll need complex numbers: SmartAuto.results = new Complex[amountPossible][];DftOperate dfaOperate = new DftOperate();//For all the chunks: for(int times = 0;times < amountPossible; times++) {Complex[] complex = new Complex[chenkSize];for(int i = 0;i < chenkSize;i++) {//Put the time domain data into a complex number with imaginary part as 0: complex[i] = new Complex(audio[(times*chenkSize)+i], 0);}//Perform FFT analysis on the chunk: SmartAuto.results[times] = dfaOperate.fft(complex);}System.out.println("results = " + SmartAuto.results.toString());}

总结

以上所述是小编给大家介绍的Java实现Shazam声音识别算法的实例代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

Java图片上查找图片算法

之前用按键精灵写过一些游戏辅助,里面有个函数叫FindPic,就是在屏幕范围查找给定的一张图片,返回查找到的坐标位置。

  现在,Java来实现这个函数类似的功能。

  算法描述:

  1. 屏幕截图,得到图A,(查找的目标图片为图B);
  2. 遍历图A的像素点,根据图B的尺寸,得到图B四个角映射到图A上的四个点;
  3. 得到的四个点与图B的四个角像素点的值比较。如果四个点一样,执行步骤4;否则,回到步骤2继续;
  4. 进一步对比,将映射范围内的全部点与图B全部的点比较。如果全部一样,则说明图片已找到;否则,回到步骤2继续;

  这里,像素之间的比较是通过BufferedImage对象获取每个像素的RGB值来比较的。如下,将BufferedImage转换为int二维数组:

/**
* 根据BufferedImage获取图片RGB数组
* @param bfImage
* @return
*/
public static int[][] getImageGRB(BufferedImage bfImage) {
int width = bfImage.getWidth();
int height = bfImage.getHeight();
int[][] result = new int[height][width];
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
//使用getRGB(w, h)获取该点的颜色值是ARGB,而在实际应用中使用的是RGB,所以需要将ARGB转化成RGB,即bufImg.getRGB(w, h) & 0xFFFFFF。
result[h][w] = bfImage.getRGB(w, h) & 0xFFFFFF;
}
}
return result;
}

比较两个像素点的RGB值是否相同,是通过异或操作比较的(据说比==效率更高),如果异或操作后得到的值为0,说明两个像素点的RGB一样,否则不一样。

  下面附上算法完整java代码:

package com.jebysun.test.imagefind;

import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
/**

  • 屏幕上查找指定图片
  • @author Jeby Sun
  • @date 2014-09-13
  • @website http://www.jebysun.com
    */
    public class ImageFindDemo { BufferedImage screenShotImage; //屏幕截图
    BufferedImage keyImage; //查找目标图片 int scrShotImgWidth; //屏幕截图宽度
    int scrShotImgHeight; //屏幕截图高度 int keyImgWidth; //查找目标图片宽度
    int keyImgHeight; //查找目标图片高度 int[][] screenShotImageRGBData; //屏幕截图RGB数据
    int[][] keyImageRGBData; //查找目标图片RGB数据 int[][][] findImgData; //查找结果,目标图标位于屏幕截图上的坐标数据 public ImageFindDemo(String keyImagePath) {
    screenShotImage = this.getFullScreenShot();
    keyImage = this.getBfImageFromPath(keyImagePath);
    screenShotImageRGBData = this.getImageGRB(screenShotImage);
    keyImageRGBData = this.getImageGRB(keyImage);
    scrShotImgWidth = screenShotImage.getWidth();
    scrShotImgHeight = screenShotImage.getHeight();
    keyImgWidth = keyImage.getWidth();
    keyImgHeight = keyImage.getHeight(); //开始查找 this.findImage(); } /**
    • 全屏截图
    • @return 返回BufferedImage
      */
      public BufferedImage getFullScreenShot() {
      BufferedImage bfImage = null;
      int width = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
      int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
      try {
      Robot robot = new Robot();
      bfImage = robot.createScreenCapture(new Rectangle(0, 0, width, height));
      } catch (AWTException e) {
      e.printStackTrace();
      }
      return bfImage;
      } /**
    • 从本地文件读取目标图片
    • @param keyImagePath – 图片绝对路径
    • @return 本地图片的BufferedImage对象
      */
      public BufferedImage getBfImageFromPath(String keyImagePath) {
      BufferedImage bfImage = null;
      try {
      bfImage = ImageIO.read(new File(keyImagePath));
      } catch (IOException e) {
      e.printStackTrace();
      }
      return bfImage;
      } /**
    • 根据BufferedImage获取图片RGB数组
    • @param bfImage
    • @return
      */
      public int[][] getImageGRB(BufferedImage bfImage) {
      int width = bfImage.getWidth();
      int height = bfImage.getHeight();
      int[][] result = new int[height][width];
      for (int h = 0; h < height; h++) {
      for (int w = 0; w < width; w++) {
      //使用getRGB(w, h)获取该点的颜色值是ARGB,而在实际应用中使用的是RGB,所以需要将ARGB转化成RGB,即bufImg.getRGB(w, h) & 0xFFFFFF。
      result[h][w] = bfImage.getRGB(w, h) & 0xFFFFFF;
      }
      }
      return result;
      } /**
    • 查找图片
      */
      public void findImage() {
      findImgData = new int[keyImgHeight][keyImgWidth][2];
      //遍历屏幕截图像素点数据
      for(int y=0; y<scrShotImgHeight-keyImgHeight; y++) {
      for(int x=0; x<scrShotImgWidth-keyImgWidth; x++) {
      //根据目标图的尺寸,得到目标图四个角映射到屏幕截图上的四个点,
      //判断截图上对应的四个点与图B的四个角像素点的值是否相同,
      //如果相同就将屏幕截图上映射范围内的所有的点与目标图的所有的点进行比较。
      if((keyImageRGBData[0][0]^screenShotImageRGBData[y][x])==0
      && (keyImageRGBData[0][keyImgWidth-1]^screenShotImageRGBData[y][x+keyImgWidth-1])==0
      && (keyImageRGBData[keyImgHeight-1][keyImgWidth-1]^screenShotImageRGBData[y+keyImgHeight-1][x+keyImgWidth-1])==0
      && (keyImageRGBData[keyImgHeight-1][0]^screenShotImageRGBData[y+keyImgHeight-1][x])==0) { boolean isFinded = isMatchAll(y, x); //如果比较结果完全相同,则说明图片找到,填充查找到的位置坐标数据到查找结果数组。 if(isFinded) { for(int h=0; h<keyImgHeight; h++) { for(int w=0; w<keyImgWidth; w++) { findImgData[h][w][0] = y+h; findImgData[h][w][1] = x+w; } } return; } } } }
      } /**
    • 判断屏幕截图上目标图映射范围内的全部点是否全部和小图的点一一对应。
    • @param y – 与目标图左上角像素点想匹配的屏幕截图y坐标
    • @param x – 与目标图左上角像素点想匹配的屏幕截图x坐标
    • @return
      */
      public boolean isMatchAll(int y, int x) {
      int biggerY = 0;
      int biggerX = 0;
      int xor = 0;
      for(int smallerY=0; smallerY=scrShotImgHeight || biggerX>=scrShotImgWidth) {
      return false;
      }
      xor = keyImageRGBData[smallerY][smallerX]^screenShotImageRGBData[biggerY][biggerX];
      if(xor!=0) {
      return false;
      }
      }
      biggerX = x;
      }
      return true;
      } /**
    • 输出查找到的坐标数据
      */
      private void printFindData() {
      for(int y=0; y<keyImgHeight; y++) {
      for(int x=0; x<keyImgWidth; x++) {
      System.out.print(“(“+this.findImgData[y][x][0]+”, “+this.findImgData[y][x][1]+”)”);
      }
      System.out.println();
      }
      }
    • public static void main(String[] args) {
      String keyImagePath = “D:/key.png”;
      ImageFindDemo demo = new ImageFindDemo(keyImagePath);
      demo.printFindData();
      }

}