2008-12-30 10:44:00 yincheng01 阅读数 28565
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    6114 人正在学习 去看看 杨波

1.   基础概念:

RCEngine

是一个封装了语音识别,语音文件操作,电话控制的类,它派生自 RCEngineInterface 抽象基类。所以要在程序中调用 Nuance 的语音识别功能你就必须实例化 RCEngine

NotifiableAdapter

RCEngine 的所有函数都是异步函数,它使用确认 --- 通知形式与用户程序通信,要获取到这些确认和通知消息你必须建立一个消息处理类,而这个类必须派生自 NotifiableAdapter ,并且在实例化 RCEngine 时把本类指针交给 RCEngine

Application object

通常 Nuance 把一个派生自 NotifiableAdapter 的且与一个 RCEngine 对象对应得实例化对象称为一个 Application object 。一个应用程序可以根据自己有多少个 license 来创建多个 Application object 。在 Nuance 的例子和我写的客户端中 RCAPP 就是一个 Application object

Dispatcher object :

他是个事件分发对象,他一直都在不断的作把识别事件分发给各个 RCEngine 的循环,当所有的 RCEngine 都摧毁时他便自动结束事件循环。

Triggerable

触发器,你若要让 Dispatcher 自动在某触发事件放生时调用你的处理函数,那么你的处理函数实现的类必须要派生于 Triggerable 。所以我的代码中 RCAPP 也派生于这个类。

 

2.   本客户端组成:

    RCAPP : 担当 Application object Triggerable 角色。

AudioSampleFetcher 一个为 RCEngine 提供识别数据的辅助类。

CRCDispatcher 担当 Dispatcher object 角色。

3.   本程序中各个类之间的关系:

     每个 RCAPP 包含一个 AudioSampleFetcher 为它在识别时提供数据。

系统中可根据 license 个数创建多个 RCAPP 。各个 RCAPP 的消息需要由 CRCDispatcher 分发。而 CRCDispatcher 是个消息分发的循环体,它只有在所有 RCAPP 都被删除时才退出循环。 CRCDispatcher 在系统中只可以有一个。

4.   识别客户端初始化流程:

     一个客户端从创建到开始可以进行识别需要一系列对象创建过程,这个过程很重要,而且也比较复杂。下面我将一步步地把这个过程描述出来:

4.1. Dispatcher object 的创建:

这是所有操作的开始点。我们需要一些准备工作来创建这个 Dispatcher 对象:

4.1.1. 创建一个 NuanceConfig 对象:

  对于单独语法包的客户端,我们可以用函数 NuanceConfigBuild ()来创建,只要向它提供语法包路径就可以了。对于多语法包的客户端我们要用 NuanceConfigBuildFromCommandLine ()函数来创建,创建时把所有参数作为一个存放字符串数组 (char**) 放到它的第二个参数里,它第一个参数是参数个数。这里必须要注意第二个参数的数据的格式和空格大小写等,任何一个错误都会引起创建失败。我的做法是用一个配置文件存储所有的参数,然后逐一读出,组装成一个字符串指针数组。下面是我的配置文件的部分:

packagedir=H:/GHT/ICA/CVP_M

packagedir=H:/GHT/ICA/CVP_C

audio.Provider=mem

client.Behaviors=calllog,timeout

  。。。。

读出整理后要求的格式是:

-package

H:/GHT/ICA/CVP_M

–package

H:/GHT/ICA/CVP_M

audio.Provider=mem

client.Behaviors=calllog,timeout

。。。。

上面每一行作为字符串( char* )分别存储到以下的 punit 数组中:

typedef char* _tCmdLnUnit;

_tCmdLnUnit punit[256];

然后再调用 NuanceConfigBuildFromCommandLine ()

4.1.2.     创建一个 Dispatcher

这部分比较简单只要把上一步创建的 NuanceConfig 作为参数传入 Dispatcher 的构造函数即可。

 

4.2. 创建 Application object

4.2.1. 创建 RCAPP

      本客户端的 RCAPP 对象就是 Application object 对象。它派生于 NotifiableAdapter Triggerable ,并且它有一个 RCEngine * 的内部成员。

4.2.2.     创建 RCEngine

     其构造函数为: RCEngine(NuanceConfig const* config, DispatcherInterface & dispatcher, Notifiable & notifiable, NuanceStatus & status) ;把刚才创建的 NuanceConfig 对象、 Dispatcher 对象、 RCAPP 对象的指针作为参数即可。

4.3. 启动 Dispatcher 的消息循环:

4.3.1.     创建一个线程:

Dispatcher 的消息循环需要独占一个线程。

4.3.2.     执行消息循环:

只要执行 Dispatcher Dispatch ()函数即可。该函数不会退出,除非所有 RCEngine 都被删掉。当它退出时,就是这个 Dispatcher 应该被删除的时候了。

4.4. HandleInitializationCompleted 被调用:

       当你的 Application object HandleInitializationCompleted 被调用时且通告状态值是 NUANCE_OK 时,说明你的 RCEngine 已初始化成功了。但注意这里并不是表示你可以进行识别了,你还需要做以完一下工作:

 

4.4.1.     设置播音为外部:

因为我们使用的是自己的播放音平台,所以必须设定是外部播音。

4.4.2. 打开数据库

    我们要用到动态语法所以必须要打开数据库。用函数 OpenDatabase ()打开数据库,这里要提供 odbc 数据源名称,用户帐号,数据库类型的信息。

4.4.3. 打开 calllog 通道

   调用 OpenCalllogChannel ()函数打开 callog 通道让 Nuance callog 放到 NuanceConfig 对象创建时参数指定的位置,否则 callog 会放到客户端程序同样路径下。

4.4.4. 创建 AudioSampleFetcher

这是个为识别提供数据的辅助类,在这里创建比较合适。

4.5. HandleNuanceDBOpened

通告状态值是 NUANCE_OK 时,说明你的数据库打开成功。这个时候,你的初始化成功完成了。你可以进入下一步,开始识别了。

4.6. 启动识别:

4.6.1. 设定识别阀值:

     当的识别得分,低于这个值时,识别结果就会被拒绝。

4.6.2. 设定 NoSpeechTimeoutSecs 值:

      RCEngine 启动超后在该值时间内没有人声输入,系统就会结束识别并返回 NoSpeechTimeout 信息。

4.6.3.     预定义输出结果格式:

           当需要格式化的识别结果时,要在这里设定好结果的输出格式。比如我们输入的格式是: <&confidence>spelling:<spelling> 则输出的结果可以是: 69 spelling:chai4 shan1 shan1 . 表示识别分数 69, 识别出来的 slot (这里是 <spelling> )对应的值是 chai4   shan1   shan1

4.6.4.     启动识别:

      使用 RCEngine RecognizeUtterance ()函数启动识别。这里要提供一个 Top grammar ,也就是静态语法中的 top grammar. 识别过程需要一定的时间等待结果,若你要在被过程中启动超时则可以通过设定 behavior.timeout.ExternalPromptDone TRUE ,让之前设定的 NoSpeechTimeoutSecs 生效。

4.6.5.     启动录音:

AudioSampleFetcher StartPlatformDependentRecording 函数调用外部录音平台录音。 AudioSampleFetcher 会创建触发器,该触发器会每 100 毫秒(该值可以在启动触发器时自己定义,推荐用默认的 100 毫秒)进行一次录音数据的输入。

4.6.6.     发现人音 HandleStartOfSpeech

正常的话,当在音频数据中发现人声时,你的 application object HandleStartOfSpeech 被调用。这时你可以停掉外部的平台放音。

4.6.7.     发现语音结束点 HandleEndOfSpeech

当识别系统认为人声结束时,你的 application object HandleEndOfSpeech 会被调用。

4.6.8.     发现语音结束点 HandleRecognitionStopped

当系统识别结束时这个 application object HandleRecognitionStopped 会被调用。这时你要做的是用 AudioSampleFetcher StopPlatformDependentRecording 停掉外部平台录音。用 RecResultGetType ()获取识别结束原因,除了成功外,其中还饱含识别失败的原因等,用 RecResultGetTextResult 获取识别的格式化结果。至此一次识别结束。

5.   识别客户端的关闭:

 

识别客户端关闭的具体步骤是:

5.1. 停止所有识别任务:

RCEngine Abort ()函数终止一切操作。

5.2. 关闭动态语法数据库:

        RCEngine CloseDatabase ()关闭语法数据库。

5.3. 删除所有 Application object( 在它释构时删除它的 RCEngine 对象成员 )

        即删除本客户端的 RCAPP 对象。

5.4. Dispatcher 退出消息循环时删除 Dispatcher object 对象:

      这里一定要等到 Dispatcher 自动退出循环才删掉这个对象,不然会引起错误。

2014-04-24 09:53:58 u011613729 阅读数 2605
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    6114 人正在学习 去看看 杨波

开始接触主线程和子线程之后觉得不应该在主线程中实现用户登录、语音识别还有合成这诸多工作,因为人们都说需要联网的工作最好另起新的线程。而且我调用语音包的程序跑起来也很复杂,所以本文就将介绍如何另起新线程运行语音识别。

关于前期准备工作参见前一篇博文

启动新的子线程主要要完成两个功能,一个是在子线程中定义如何运行自己的程序,第二个就是子线程完成了识别工作要及时通知主线程(又叫UI线程)以更新界面。

我的OutputActivity

public class OutputActivity extends Activity {
	private static final int ASK_USER_INPUT = 1;
	// Tip
	private Toast mToast;
	//语义理解结果显示
	private EditText mResultText;
	ClawnThread CoreThread = null;
	
	// Handler object
	private Handler MainHandler=new Handler(){
		public void handleMessage(Message msg){
			switch(msg.what){
			case ASK_USER_INPUT:
				mResultText.append(msg.obj.toString());
				//showSpeechUnderstanderNoPop();
				Log.e("haha","called");
				break;
			}
		}
	};
	class ClawnThread extends Thread{
		public void run(){
			//Init the message looper queue
			Looper.prepare();
			
			//User login
			SpeechUser.getUser().login(MyOutput.getAppContext(), null, null
					, "appid=" + getString(R.string.app_id), listener);
			DMCore.DialogTaskOnBeginSession();
			Log.d("ThreadId",""+Thread.currentThread().getId());
			DMCore.pDMCore.Execute(MainHandler);
			
			// 启动子线程消息循环队列
			Looper.loop();
		}
	}
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_output);
		
		//语义理解结果设置
		mResultText = (EditText) findViewById(R.id.txt_result);
		mResultText.setText(null);
		//findViewById(R.id.personal_layout).setVisibility(View.VISIBLE);	
		
		
		Log.d("ThreadId",""+Thread.currentThread().getId());
		CoreThread = new ClawnThread();
		CoreThread.start();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.output, menu);
		return true;
	}
	
	

	
	/**
	 * 用户登录回调监听器.
	 */
	private SpeechListener listener = new SpeechListener()
	{

		@Override
		public void onData(byte[] arg0) {
		}

		@Override
		public void onCompleted(SpeechError error) {
			if(error != null) {
				Toast.makeText(OutputActivity.this, getString(R.string.text_login_fail)
						, Toast.LENGTH_SHORT).show();
				
			}			
		}
		@Override
		public void onEvent(int arg0, Bundle arg1) {
		}		
	};

	private void showTip(String str)
	{
		if(!TextUtils.isEmpty(str))
		{
			mToast.setText(str);
			mToast.show();
		}
	}
}

在这个Activity中定义了一个private Handler,里边定义了handleMessage方法,其中定义收到子线程传来的信息作出相应的反应。这个handler是和UI线程绑定在一起的,必须要传递到子线程中,然后子线程用这个handler.sendMessage方法,就直接将message传递给主线程了,所以传递这个handler很重要。

另外自己定义的ClawnThread继承了Thread类,重写run方法,这里要特别注意,因为主线程的Looper队列是不需要另外声明,但是子线程则需要单独初始化循环队列。然后就是登陆,登陆的参数稍加修改,用的是MyOutput的getContext方法获取上下文对象(代码见前一篇博文),然后就是实现自己写的初始化、执行方法。在Execute中传递了handler参数。

在OnCreate中先打印线程id,然后创建新线程然后运行。同样可以在子线程中打印线程id,可以看出真的启动了一个新的线程!

在另外的调用语音识别程序的类中,创建两个private对象,一个Message用来存储要传递的信息,一个String JsonString用来保存识别的结果。然后在识别回调接口里定义完成识别后要传递的信息

/**
	 * 语义理解回调监听器
	 */
	SpeechUnderstanderListener understanderListener = new SpeechUnderstanderListener()
	{
		@Override
		public void onBeginOfSpeech() {
			//showTip("开始说话");
		}

		@Override
		public void onError(SpeechError error) {
			//showTip(error.getPlainDescription(true));
		}

		@Override
		public void onEndOfSpeech() {
			//showTip("结束说话");
		}

		@Override
		public void onEvent(int eventType, int arg1, int arg2, String msg) {
			
		}

		@Override
		public void onResult(UnderstanderResult result) {
			message = new Message();
			message.what=1;
			JsonResult = result.getResultString();
			message.obj=(String)JsonResult;
			
		}

		@Override
		public void onVolumeChanged(int volume) {
			//showTip("当前正在说话,音量值为:" + volume);
			
		}
		
	};
这是传递主线程handler过来的函数,定义在自己的类中,在这个函数里边调用识别函数。
public void WaitForEvent(Handler mhMainHandler) {

		if (qpieEventQueue.isEmpty()) {
			// retrieve the current thread id
			//DWORD dwThreadId = GetCurrentThreadId();
			// call speech understand method
			JsonResult = "";
			showSpeechUnderstanderNoPop();
			Log.d(INPUTMANAGER_STREAM,JsonResult);
			// send a message to the galaxy interface to wait for input
			mhMainHandler.sendMessage(message);
			
			// log that we started waiting for an input
			Log.d(INPUTMANAGER_STREAM, "Waiting for interaction event ...");
这样当完成识别后主屏幕就会I显示识别结果出来。

2014-06-27 17:16:56 yujianxiang666 阅读数 5631
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    6114 人正在学习 去看看 杨波

      苹果在2014年6月3日的WWDC2014开幕式上推出了新版iOS8系统,界面上iOS8与iOS7相比变化不大,不过在功能方面进行了完善。iOS8通知中心更加强大,支持消息直接回复操作,并支持QuickType和第三方输入法。短信功能改进明显,支持群聊,发送语音、视频,分享地理位置等。从终端用户的角度看,iOS8的许多新功能早已出现在其他平台中。iOS8会向第三方软件开放TouchID访问,这意味着可以使用该感应器登陆银行应用等。

      第三方应用可以使用TouchID接口,意味着未来的很多应用都可以用指纹识别功能了。你可以选择Touch ID登陆第三方应用程序,不需要输入密码,你的指纹数据是被保护的,在没有被允许的情况下别的程序是访问不到它的。

                                

      根据苹果的解释,一个单一的注册指纹与别人指纹出现随机匹配的概率为五万分之一。

      苹果声称“Secure Enclave”模块系统能够安全地管理并识别用户的指纹,并将用户的指纹信息独立地保存在别的系统中,同时通过加密内存和一个硬件随机数字密码发生器进行管理。

      每个“Secure Enclave”是单独设置的,不能访问系统其他部分的,拥有自己的独立的UID(唯一的ID),连苹果也不知道这些UID。当设备启动时,Touch ID会临时创建一个秘钥,与“Secure Enclave”的UID配合,对设备的内存空间进行加密。

      而在苹果发布的文件中,苹果对A7处理器进行指纹识别授权的描述是:A7和Touch ID之间通过一个串行外设接口总线进行通信。A7处理器将数据发到“Secure Enclave”,但并不对数据内容进行读取。加密和身份验证都是使用Touch ID和“Secure Enclave”之间的共享密钥。通信密钥交换使用双方提供的一个随机AES密钥,并随机建立会话密钥和使用AES-CCM传输加密。

      据了解:iPhone 5s中的指纹传感器检测到的表皮上突起的纹线。它检测到的不是用户手指外部的死皮指纹,这种指纹很容易被复制。iPhone 5s的指纹传感器利用射频信号,检测用户手指表面下方那一层皮肤的“活”指纹。如果手指与人的身体分离,那么传感器是无法检测到这种指纹的。所以用户不用担心自己的指纹被复制或盗窃之后,被用于解锁设备,因为传感器是无法识别这种“死”指纹的。


      最近研究了下iOS8的文档,对指纹识别了解了下,并下载了一个官方提供的Demo。但是

      NS_CLASS_AVAILABLE(10_10, 8_0)

      从这句中可以看出,要想使用TouchID的接口,电脑的mac系统必须是10.10的,手机iOS系统必须是8.0,所以为了这个Demo我也没有升级电脑系统(毕竟还不稳定)。但根据Demo中的代码和文档可以看出,TouchID的基本用法。

1.首先要使用TouchID,要先导入依赖包:LocalAuthentication.framework

2.检查设备是否能用TouchID,返回检查结果BOOL类型success:

LAContext *context = [[LAContext alloc] init];
    __block  NSString *msg;
    NSError *error;
    BOOL success;
    
    // test if we can evaluate the policy, this test will tell us if Touch ID is available and enrolled
    success = [context canEvaluatePolicy: LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error];
    if (success) {
        msg =[NSString stringWithFormat:NSLocalizedString(@"TOUCH_ID_IS_AVAILABLE", nil)];
    } else {
        msg =[NSString stringWithFormat:NSLocalizedString(@"TOUCH_ID_IS_NOT_AVAILABLE", nil)];
    }

3.如果设备能使用TouchID,代码块中返回识别结果BOOL类型的success:

LAContext *context = [[LAContext alloc] init];
    __block  NSString *msg;
    
    // show the authentication UI with our reason string
    [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:NSLocalizedString(@"UNLOCK_ACCESS_TO_LOCKED_FATURE", nil) reply:
     ^(BOOL success, NSError *authenticationError) {
         if (success) {
             msg =[NSString stringWithFormat:NSLocalizedString(@"EVALUATE_POLICY_SUCCESS", nil)];
         } else {
             msg = [NSString stringWithFormat:NSLocalizedString(@"EVALUATE_POLICY_WITH_ERROR", nil), authenticationError.localizedDescription];
         }
     }];

4.对于检查和识别的两个方法在 LocalAuthentication.framework/Headers/LAContext.h 中定义的:

/// Determines if a particular policy can be evaluated.
///
/// @discussion Policies can have certain requirements which, when not satisfied, would always cause
///             the policy evaluation to fail. Examples can be a passcode set or a fingerprint
///             enrolled with Touch ID. This method allows easy checking for such conditions.
///
///             Applications should consume the returned value immediately and avoid relying on it
///             for an extensive period of time. At least, it is guaranteed to stay valid until the
///             application enters background.
///
/// @warning    Do not call this method in the reply block of evaluatePolicy:reply: because it could
///             lead to a deadlock.
///
/// @param policy Policy for which the preflight check should be run.
///
/// @param error Optional output parameter which is set to nil if the policy can be evaluated, or it
///              contains error information if policy evaluation is not possible.
///
/// @return YES if the policy can be evaluated, NO otherwise.
- (BOOL)canEvaluatePolicy:(LAPolicy)policy error:(NSError * __autoreleasing *)error;

/// Evaluates the specified policy.
///
/// @discussion Policy evaluation may involve prompting user for various kinds of interaction
///             or authentication. Actual behavior is dependent on evaluated policy, device type,
///             and can be affected by installed configuration profiles.
///
///             Be sure to keep a strong reference to the context while the evaluation is in progress.
///             Otherwise, an evaluation would be canceled when the context is being deallocated.
///
///             The method does not block. Instead, the caller must provide a reply block to be
///             called asynchronously when evaluation finishes. The block is executed on a private
///             queue internal to the framework in an unspecified threading context. Other than that,
///             no guarantee is made about which queue, thread, or run-loop the block is executed on.
///
///             Implications of successful policy evaluation are policy specific. In general, this
///             operation is not idempotent. Policy evaluation may fail for various reasons, including
///             user cancel, system cancel and others, see LAError codes.
///
/// @param policy Policy to be evaluated.
///
/// @param reply Reply block that is executed when policy evaluation finishes.
///
/// @param localizedReason Application reason for authentication. This string must be provided in correct
///                        localization and should be short and clear. It will be eventually displayed in
///                        the authentication dialog subtitle. A name of the calling application will be
///                        already displayed in title, so it should not be duplicated here.
///
/// @param success Reply parameter that is YES if the policy has been evaluated successfully or NO if
///                the evaluation failed.
///
/// @param error Reply parameter that is nil if the policy has been evaluated successfully, or it contains
///              error information about the evaluation failure.
///
/// @warning localizedReason parameter is mandatory and the call will throw NSInvalidArgumentException if
///          nil or empty string is specified.
///
/// @see LAError
///
/// Typical error codes returned by this call are:
/// @li          LAErrorUserFallback if user tapped the fallback button
/// @li          LAErrorUserCancel if user has tapped the Cancel button
/// @li          LAErrorSystemCancel if some system event interrupted the evaluation (e.g. Home button pressed).
- (void)evaluatePolicy:(LAPolicy)policy localizedReason:(NSString *)localizedReason reply:(void(^)(BOOL success, NSError *error))reply;

欢迎小伙伴们对测试结果检验一下啊!

(转载请注明出处,谢谢!http://blog.csdn.net/yujianxiang666/article/details/35280025)

2018-02-25 18:20:01 qq_20042935 阅读数 144
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    6114 人正在学习 去看看 杨波

命令模式

定义:

在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。而命令模式就是将“行为请者”与“行为实现者”解耦的。

类图:

这里写图片描述

举例(客户提出需求给需求部门、美工部门、开发部门):

描述:

客户(Client)如果提出一个需求(Command),增加语音识别功能。没使用命令模式之前,客户(Client)是这样操作的,先通知需求部门(Receiver1),再通知美工部门(Receiver2),再通知开发部门(Receiver3),这样的话,是十分繁琐的,所以引出了命令模式。
……
命令模式是这样处理的,找出一个代表(invoker),让代表去执行我发出的需求(Command)。这样做的话,可以把客户和所有部门解耦了,让代表去执行命令即可。

上面就描述完了业务了,下面看下代码吧(代码其实不是重点,主要是业务)


Receiver:

/**
*部门抽象
*/
public abstract class Group { 
    public abstract void find(); //被要求增加功能
    public abstract void add(); //被要求删除功能
    public abstract void delete(); //被要求修改功能
    public abstract void change();
    //被要求给出所有的变更计划 
    public abstract void plan();
}

/**
*需求部门
*/
public class RequirementGroup extends Group {

    public void find() {
        System.out.println("找到需求组..."); 
    }

    public void add() {
        System.out.println("客户要求增加一项需求..."); 
    }

    //客户要求修改一项需求 
    public void change() {
        System.out.println("客户要求修改一项需求...");
    }

    //客户要求删除一项需求 
    public void delete() {
        System.out.println("客户要求删除一项需求..."); 
    }

    //客户要求出变更计划 
    public void plan() {
        System.out.println("客户要求需求变更计划..."); 
    } 
}

/**
*美工部门
*/
public class PageGroup extends Group {
    ......
}

/**
*开发部门
*/
public class CodeGroup extends Group {
    ......
}

Command:

public abstract class Command {
    //需求组 
    protected RequirementGroup rg = new RequirementGroup(); 

    //美工组
    protected PageGroup pg = new PageGroup();  

    //代码组
    protected CodeGroup cg = new CodeGroup(); 

    //只要一个方法,你要我做什么事情 
    public abstract void execute();
}

/**
* 比如是增加需求命令
*/
public class AddRequirementCommand extends Command { 

    //执行增加一项需求的命令
    public void execute() {

    //找到需求组 
    super.rg.find();

    //增加一份需求 
    super.rg.add();

    //页面也要增加 
    super.pg.add();

    //功能也要增加 
    super.cg.add();

    //给出计划
    super.rg.plan(); 
    }
}

Invoker:

public class Invoker { 

    //什么命令
    private Command command;

    //客户发出命令
    public void setCommand(Command command){
        this.command = command; 
    }

    //执行客户的命令
    public void action(){
        this.command.execute();
    }
}

Client:

public class Client {
    public static void main(String[] args) { 

    //定义我们的接头人
    Invoker xiaoSan = new Invoker(); //接头人就是我小三

    //客户要求增加一项需求 
    System.out.println("-------------客户要求增加一项需求-----------------"); 

    //客户给我们下命令来
    Command command = new AddRequirementCommand();

    //接头人接收到命令 
    xiaoSan.setCommand(command);

    //接头人执行命令 
    xiaoSan.action();
}
2018-01-10 08:02:14 z2066411585 阅读数 288
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    6114 人正在学习 去看看 杨波

开发完成Notifications功能之后,如何测试该功能的方法:

  1. Amazon Alexa App 使能 Quote Maker Skill
  2. Quote Maker Skill是一个开发工具,允许开发人员实时创建通知,并触发将SetIndicator指令发送到与用户关联的所有启动Alexa设备.
  3. 如何使用Quote Maker
    Quote Maker
    • 打开Quote Maker : “Alexa, open Quote Maker”
    • 创建一条通知 : “Alexa, ask Quote Maker to create a notification”
    • 检索通知: ” Alexa, what are my notifications?”
没有更多推荐了,返回首页