2018年3月10日 星期六

[Python] Google Voice Kit 與 Raspberry Pi 3

AIY4

上一次 Pi 1 開機已經想不起來了,大概是 2013 年的事吧,那年在研究 Raspbmc / Airplay 吧,大概是配置的 SDCard 太慢了,再加上已經長期投資 Linode 後,就漸漸把機器擺進防潮箱了,最近則是因為智慧喇吧的興起,想挑一款產品試試,想著想著,就想到去年就曾看到 Google Voice Kit ,而當初買不下去是因為美國才賣 10 美金,但台灣賣超過 30 美金,更別說美國那邊可是 35 美金含 Pi 3 的方案啊 XD

後來想通後,把多出來的價碼當作代購吧 Orz 何必為了一點還可以承擔的錢,延遲或放棄學習的機會?時間可是不等人的!總之,就這樣噴了將近快台幣 3000 吧,一張近似裸裝 Pi 3 (13XX) + Google Voice Kit (900) + microSD (300),各別都還要加上運費和轉帳費。(若單純對智慧音箱有興趣的,建議可以直接買 Google Home mini ,在露天代購不用兩千,甚至接近一千五,跟定價 49.99 美金非常接近)

AIY3

安裝硬件跟紙盒還滿快的,15 分鐘內應當可以搞定,接下來是軟體與線上服務的搭配,開通 GOOGLE ASSISTANT API 及 OAuth 認證後,就可以執行非常簡單的 demo 機制。

寫到這,的確可以穩穩體驗一下,但突然很反骨的想自己試試一些“中文”,需要搞定的項目為:
  • 語音輸入
  • 語音播放
  • 語音辨識
  • 語音合成(Text to Speech)
整個流程就是善用資源,哪邊有 code 要翻出來拼裝,像是語音輸入跟播放就用 Google 寫好的 aiy.audio,而語音辨識則是 SpeechRecognition ,語音合成則是 gTTS。比較特別的是語音播放是用 wav 格式,而 gTTS 的語音合成產出是 mp3 格式,所以再透過 pydub 轉成 wav (底層是 ffmpeg)

其中找資料的過程,主要是從 ~/Desktop/check_audio.desktop 進而研究 AIY-projects-python/checkpoints/check_audio.py 的寫法,找出底層實作的方式(command line!):

AIY-projects-python/src/aiy/audio.py
aiy._drivers._player
aiy._drivers._recorder
aiy._drivers._tts

AIY-projects-python/src/aiy/_drivers/_recorder.py
$ arecord --version
arecord: version 1.1.3 by Jaroslav Kysela <perex@perex.cz>

AIY-projects-python/src/aiy/_drivers/_player.py
$ aplay --version
aplay: version 1.1.3 by Jaroslav Kysela <perex@perex.cz>

AIY-projects-python/src/aiy/_drivers/_tts.py
$ pico2wave --usage
Usage: pico2wave [-?] [-w|--wave=filename.wav] [-l|--lang=lang] [-?|--help] [--usage] <words>


如果有興趣想追 GOOGLE ASSISTANT API 的實作,也可以多看看這些:

~/AIY-projects-python/src/examples/voice/assistant_library_with_button_demo.py
~/AIY-projects-python/src/examples/voice/assistant_library_demo.py
~/AIY-projects-python/src/aiy/assistant/library.py


很快就會追到 google.assistant.library,接著發現無法欣賞他如何實作 https://github.com/googlesamples/assistant-sdk-python/issues/77

最後,嘗試中文的 Speech to text 跟 text to speech ,再順便加一點點天氣回應,有興趣再到這邊看看,最大的關鍵點是用 AIY 的元件(arecord / aplay) 跟其他 gTTS / pydub 亂串時,發現有一些 async 狀態(要播放剛剛製成的音訊資料時,發現資料未完全寫入檔案,最後靠 time.sleep(3) 來解掉):

https://github.com/changyy/GoogleVoiceKitStudy/blob/master/detect-and-echo.py

2018年1月27日 星期六

[PHP] 使用 PHP built-in web server 及 PHP CodeIgniter framework

回想起來,我大概斷斷續續用了 CodeIgniter 七年了,最近才準備在本地開發 XD 開發上就在想 node.js 都有 webpack 等工具了,為啥 PHP 開發上還要先架設個 Web server 而感到納悶。

然而,其實 PHP 早已有 built-in web server 了,雖然只是 single-thread 但在開發上已經非常夠用,就來摸一下怎樣整再一起

$ cd project_web_document_root
$ php -S localhost:8000


接著就在 http://localhost:8000 就可以運行了!
然而,許多 web framework 都靠 routing 把 requests 統一集中到一支程式判斷,似乎已經是個非常基本的設計概念,那在 php built-in web server 也是可以的,他可以設定 routing 機制

$ cd project/web
$ cat ../tools/routing.php
$BASE_DIR = __DIR__ . '/../web' ;
if (file_exists($BASE_DIR . $_SERVER['REQUEST_URI']))
return false;
$_SERVER['SCRIPT_NAME'] = '/index.php';
include_once ($BASE_DIR . '/index.php');
$ php -S localhost:8000 ../tools/routing.php
PHP 7.0.27 Development Server started at Tue Jan 23 12:31:32 2018
Listening on http://localhost:8000
Document root is C:\Users\user\Desktop\ci-project\web
Press Ctrl-C to quit.


更多筆記:changyy/codeiginiter-and-php-built-in-web-server

2018年1月20日 星期六

AWS 筆記 - 使用 Ubuntu server 更換 Windows server 2012 帳號狀態

AWS Windows server

公司在三年前在 AWS Beijing 開了一台 Windows server 2012 運行一些套裝軟體,然而,負責的 IT 在整理系統帳號時,把 guest 關閉了。接著發生很多奇妙事件,最恐怖的是機器無法遠端登入了 Orz

一開始想朝更換 Administrator 密碼試試,結果一看過程步驟也不少:
使用 EC2Config 重置 Windows 管理员密码 -
https://docs.aws.amazon.com/zh_cn/AWSEC2/latest/WindowsGuide/ResettingAdminPassword_EC2Config.html
接著,反而朝惡搞的方式進去研究,例如:如何修改機碼啟用 guest 帳號


最後,使用了前一份(一年前版本)的全系統備份,開了一台機器,把他的 C:/Windows/System32/config/SAM 複製出來,覆蓋掉無法登入的機器上,如此一來就搞定了!

情況簡介:
A server: 壞掉的 Windows server 2012
B server: 從 A server 現狀建置 AMI 而開啟的機器
C server: 前一版次備份的 Windows server
D server: Ubuntu 14.04 server
A 計畫:
將 B server 的 C 槽卸下,並掛到 C server 上進行 B server 的 C槽 資料修改,改完後再卸下,重裝到 B server,把 B server 重新啟動,看看是否正常

然而,A 計劃失敗了,在 AWS console 可以看到系統無法正常通過檢驗,於是啟動了 B 計畫

B 計畫:
再次建置乾淨的 B server,將 B server 的 C 槽卸下,也把 C server - C 槽卸下,並一起掛到 D server 上,拿著 C server - C 槽關鍵資訊,直接覆蓋掉 B server - C 槽 資料,修改完後再卸下,重裝到 B server,再把 B server 重新啟動
果真 B 計劃成功了 :p 猜測 A方案 可能因為修改的是 Windows OS level 資訊,修改完可能也改變了 B server - C 槽狀態,以至於重新啟用時反而資訊對不上而無法開機。

AWS Windows server

其他筆記:

  1. Windows 需要 stop 機器,才能卸下指定硬碟,請開啟 “終止保護“ 避免不小心做錯事
  2. Windows 的系統碟(C:)根设备用 /dev/sda1 資訊,卸下後要重新裝上時,記得要用原本的 /dev/sda1 才行
  3. 在 EC2 - EBS 管理介面要卸下某硬碟時,建議把 Name 帶點原本的機器資訊,方便找尋
  4. 在 Ubuntu 要 mount NTFS 都還算簡單,sudo mount /dev/xsgf1 /mnt/Win2012CDisk 
  5. Windows 的帳號資訊擺在 C:\Windows\System32\config\SAM 檔案,可以單純做 cp /mnt/C_server_c_diskt/Windows/System32/config/SAM /mnt/B_server_c_diskt/Windows/System32/config/SAM 
  6. 驗證完畢後,就可以安心地把 A server 先暫停 -> 把系統槽卸下 -> 掛到 Ubuntu server -> mount 起來 -> 覆蓋掉 SAM -> 卸下 -> 掛回 A server -> A server 重新開機

2017年12月30日 星期六

[Javascript] Google Sheets 與 Google app script - 處理 JSON API data

之前看到別人用 Google docs - sheets 與表單製作點飲料的功能(有點年紀的大概都會想到訂便當系統?),才發現 Google Sheets 其實有提供像 Excel - VBA 這種制定功能的架構,真是相見恨晚!最近幾年都改用Google docs做了很多事,面對數據整理也都在透過 Google sheets API 匯出後再整理回去,若有些很簡單的功能,不見得需要這樣做了!

舉個例:撈別人家的 API 資料,取出來參考。來個更精準的需求:查看某銀行的幣值變化或是某交易平台的虛擬幣價格。

這時就需要"存取網路"跟"JSON"處理方式,很佛心的,有人已提供了:

Import JSON into Google Sheets, this library adds various ImportJSON functions to your spreadsheet - https://github.com/bradjasper/ImportJSON

此目的是做成很通用的工具,但有時候,有些欄位資料想要自行客製化,那就寫一點 code 吧

1. 存取網路:

var jsondata = UrlFetchApp.fetch(url);

2. 處理 json format:

var object   = JSON.parse(jsondata.getContentText());

剩下的,對會寫程式的,就很簡單了,一切就是寫 javascript 啦,像是將 timestamp 轉 date:

var date_obj = new Date(input_timestamp_value * 1000);
var date_formated =
        date_obj.getFullYear() + '/' + (date_obj.getMonth() < 10 ? '0' : '' ) +(date_obj.getMonth()+1) +'/'+ (date_obj.getDate() < 10 ? '0' : '' ) + date_obj.getDate()
          ' ' + (date_obj.getHours() < 10 ? '0' : '') + date_obj.getHours() + ':' + date_obj.getMinutes() + ':' + (date_obj.getSeconds() < 10 ? '0': '') + date_obj.getSeconds();


後續在 Google app script 包裝成一個 function 後,並定義註解描述後,在 Google sheets 就可以用了!

/**
 * get_json_info
 *
 * @param {url}
 *
 * @customfunction
 **/
function get_json_info(url,fieldname) {
  var jsondata = UrlFetchApp.fetch(url);
  var object   = JSON.parse(jsondata.getContentText());
  return object[fieldname];
}

function main() { // 用來在 Google app script 裡 debug,可以指定跑此函數
  get_json_info("https://ipinfo.io/json","ip");
}


在 Google sheets 就簡單寫

=get_json_info("https://ipinfo.io/json","ip")

收工 XD

2017年11月27日 星期一

棋手 vs. 棋子

老闆相對於公司就是棋手,專門擬定策略;員工相對於公司就是棋子,負責執行老闆的策略。

許多時候,棋子待久了也會希望棋手多多用它,增加出場的機會,殊不知,使得出“將軍抽車”的招數不是常態,更多佈局可能是換掉敵方的棋子 XD

有意識的棋子,到底多公司是好是壞?這一切都是結果論 orz 只是,若棋手很威了,那棋子還是乖乖執行即可,完美地執行任務才能驗證棋手策略,若棋手不威,我看棋子在政治鬥爭下拿不到權力時,還是乖乖離開吧 XD

前幾天跟一位老棋手閒聊,老棋手說:你的經驗都只是當個棋子而已,我們的視野差距極大。過去老棋手在當棋子時,錯過了一些邀約,令旁人感到惋惜,但一切都基於對自己人生的選擇權,許多事物都是結果論的。老棋手說著說著,只希望每一次的戰場上的輸贏,都可以讓妻小安心地生活。這一切都只是選擇。並且叮嚀著:你無法選擇機運或聰明才智,但你可以選擇努力、用心地過日子,把握機會。

聽著聽著,原本想跟老棋手分享周邊朋友在外商新創打滾的故事,瞬間就不知該說沒,只能點頭唯喏了 XD