Android语音操作网易云音乐播放器
这是个来自日常生活的需求。我个人喜欢在洗澡的时候带着手机随机播放歌曲,第一首往往还好,因为是自己选好播放的。但是后面往往不会每次都随机到我满意的歌曲,这就是个很尴尬的时候。全身湿透了,用手切歌太麻烦,于是就有了标题的需求——语音控音乐播放器。
这个需求就俩个目标:
- 解放双手,语音唤醒程序进行不同的操作
- 操作网易云音乐,获得下一首、上一首、播放、暂停的操作
第一个目标还算比较简单,市场上的产品如讯飞语音,百度语音等都有语音唤醒的SDK,直接接过来即可。因为讯飞好像要钱,我这里就直接使用的百度的语音唤醒SDK。
第二个目标就是控制第三方的播放器 ,这个点说难也不难,但是的确花费了我很多时间去寻找控制方法。下面是我找的几个解决方案,最后我还是使用了模拟线控的方式去做这件事。因为现在的音乐APP基本上都会支持线控,那么我只需要能够模拟出线控操作,就可以实现我的功能。
线控这里有个不大不小的坑,那就是模拟线控的时候需要两个操作,down和up,缺一不可。另,不同的音乐APP可能设置的线控按键位置不同,这个需要自己去针对修改。http://www.cnblogs.com/xinye/archive/2012/06/06/music_info.html
http://blog.sina.com.cn/s/blog_14a226ae20102wthi.html
http://blog.csdn.net/qq_26440221/article/details/71512648
https://dev.mi.com/doc/p=6298/
https://segmentfault.com/a/1190000000713535
百度语音唤醒
- 在百度语音平台创建应用,设置对应的服务,设置指定的安卓包名称
- 下载对应功能的SDk
- 添加SDK文件、so文件及配置
- bdasr_V3_20170801_60da871.jar Jar文件复制到工程libs中,添加引用
- 【app\src\main】中的assets文件夹和jniLibs文件夹复制到工程同目录;assets中的WakeUp.bin文件是语音唤醒词文件,需要根据自己的需要替换;可在http://yuyin.baidu.com/wake中自定义唤醒词并下载
- 设置AndroidManifest.xml 文件(可在http://yuyin.baidu.com/docs/asr/186中查看)
- 权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- app_id,app_key,app_secret:在应用中可以查找到
<!-- 请填写真实的APP_ID API_KEY SECRET_KEY--> <meta-data android:name="com.baidu.speech.APP_ID" android:value="你的APPID" /> <!-- 再次重复!!填写APP_ID 时请检查在官网的该应用内是否设置了包名。否则会导致唤醒词及离线功能无法使用。 本demo的包名是com.baidu.speech.recognizerdemo,在build.gradle文件中查看。 --> <!-- 正式发布时,请替换成您自己的appId 本demo的appId会不定时下线 --> <meta-data android:name="com.baidu.speech.API_KEY" android:value="你的APIKEY" /> <meta-data android:name="com.baidu.speech.SECRET_KEY" android:value="你的SECRETKEY" />
- service:
<service android:name="com.baidu.speech.VoiceRecognitionService" android:exported="false" />
- 权限:
- 代码实例:
- 实例化EventManager并且添加监听
private EventManager wakeup; protected Button btn; protected Button stopBtn; protected TextView txtLog; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity2); //界面控件 btn = (Button) findViewById(R.id.button4); stopBtn = (Button) findViewById(R.id.button5); txtLog = (TextView) findViewById(R.id.textView); //实例化EventManager并且添加监听 wakeup = EventManagerFactory.create(this,"wp"); wakeup.registerListener(this); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //开始监听唤醒词 start(); } }); stopBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //停止监听 stop(); } }); //android 6.0 以上需要动态申请权限 initPermission(); }
- 开启监听:
private void start() { Map<String, Object> params = new LinkedHashMap<String, Object>(); params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false); params.put(SpeechConstant.WP_WORDS_FILE, "assets:///WakeUp.bin"); String json = null; // 这里可以替换成你需要测试的json json = new JSONObject(params).toString(); wakeup.send(SpeechConstant.WAKEUP_START, json, null, 0, 0); txtLog.append("canshu:"+json+"\n"); }
- 关闭监听:
private void stop() { wakeup.send(SpeechConstant.WAKEUP_STOP, null, null, 0, 0); // }
- 动态申请权限:
/** * android 6.0 以上需要动态申请权限 */ private void initPermission() { String permissions[] = {Manifest.permission.RECORD_AUDIO, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.INTERNET, Manifest.permission.READ_PHONE_STATE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; ArrayList<String> toApplyList = new ArrayList<String>(); for (String perm :permissions){ if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) { toApplyList.add(perm); //进入到这里代表没有权限. } } String tmpList[] = new String[toApplyList.size()]; if (!toApplyList.isEmpty()){ ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123); } }
- 最后,最重要的唤醒回调函数:
// EventListener 回调方法 @Override public void onEvent(String name, String params, byte[] data, int offset, int length) { String logTxt = "name: " + name; if (params != null && !params.isEmpty()) { logTxt += " ;params :" + params; try { JSONObject json = new JSONObject(params); if("wp.data".equals(name)){ String wakeWord = json.getString("word"); if (!wakeWord.isEmpty()&&wakeWord.equals("下一首")) { musicControl(KeyEvent.KEYCODE_HEADSETHOOK); musicControl(KeyEvent.KEYCODE_HEADSETHOOK); } if (!wakeWord.isEmpty()&&wakeWord.equals("上一首")) { musicControl(KeyEvent.KEYCODE_HEADSETHOOK); musicControl(KeyEvent.KEYCODE_HEADSETHOOK); musicControl(KeyEvent.KEYCODE_HEADSETHOOK); } if (!wakeWord.isEmpty()&&wakeWord.equals("播放")) { musicControl(KeyEvent.KEYCODE_HEADSETHOOK); } if (!wakeWord.isEmpty()&&wakeWord.equals("暂停")) { musicControl(KeyEvent.KEYCODE_HEADSETHOOK); } if (!wakeWord.isEmpty()&&wakeWord.equals("百度一下")) { musicControl(KeyEvent.KEYCODE_HEADSETHOOK); } } } catch (JSONException e) { e.printStackTrace(); } } else if (data != null) { logTxt += " ;data length=" + data.length; } txtLog.append("Result:"+logTxt+"\n"); }
- 实例化EventManager并且添加监听
- 到此百度语音唤醒部分已经完成,剩下的就是根据不同的唤醒词去模拟不同的线控操作了
模拟线控
- 先看下网易云音乐是如何使用线控的,对应的KeyEvent就是KEYCODE_HEADSETHOOK,所以我们只需要模拟这一个按键就可以了。模拟双击操作的时候,需要在KeyEvent中添加eventTime。
- 模拟按下,因为需要双击,这里就直接将时间添加进去:
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); //模拟单击 //KeyEvent keyEvent = new KeyEvent (KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_HEADSETHOOK) ; //模拟双击操作需要添加evnetTime long eventTime = SystemClock.uptimeMillis(); KeyEvent keyEvent = new KeyEvent(eventTime,eventTime,KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_HEADSETHOOK,0,0); intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); sendBroadcast(intent);
- 模拟抬起也是一样的操作,改变一个ACTIONKEY就可以了
- 模拟线控方法:
private void musicControl(int eventKey) { Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); //模拟单击 //KeyEvent keyEvent = new KeyEvent (KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_HEADSETHOOK) ; //模拟双击操作需要添加evnetTime long eventTime = SystemClock.uptimeMillis(); KeyEvent keyEvent = new KeyEvent(eventTime,eventTime,KeyEvent.ACTION_DOWN,eventKey,0,0); intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); sendBroadcast(intent); Intent intent2 = new Intent(Intent.ACTION_MEDIA_BUTTON); //模拟单击 //KeyEvent keyEvent2 = new KeyEvent (KeyEvent.ACTION_UP,KeyEvent.KEYCODE_HEADSETHOOK) ; //模拟双击操作需要添加evnetTime KeyEvent keyEvent2 = new KeyEvent(eventTime,eventTime,KeyEvent.ACTION_UP,eventKey,0,0); intent2.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent2); sendBroadcast(intent2); }
到此,两个部分都已经完成,打包测试。
在完成功能以后,我突然想到手机在同时播放音乐的同时去监听唤醒词,会不会因为本身音乐的干扰而识别不出。实际上是有影响的,想要提高唤醒成功率,只能人为的靠近手机去呐喊了W( ̄_ ̄)W本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
评论已关闭