我讓CKEditor也可錄音了:Recordmp3js插件 / Recordmp3js Plugin: Record Voice in CKEditor
我做了CKEditor的插件Recordmp3js,這是以HTML5的新功能為基礎,建構在Recordmp3js專案上實作的插件。以下介紹這個套件的功能。
I developed a CKEditor pluing: Recordmp3js. This is based on HTML5 audio function and Recordmp3js project. You can use Recordmp3js plugin to record voice in CKEditor. This article introduce Recordmp3js and some development memo.
CKEditor插件:Recordmp3js / CKEditor Plugin: Recordmp3js
- Recordmp3js 專案首頁:https://github.com/pulipulichen/kals/tree/pudding/libraries/ckeditor/plugins/recordmp3js (這是依附在KALS專案底下)
- CKEditor Add-ons Recordmp3js: http://ckeditor.com/addon/recordmp3js
recordmp3js的特色如下:
- 使用HTML5的錄音功能,不需要Flash或Java Applet等其他插件。
- 錄音功能旁有個音量顯示器,可以知道自己的麥克風有沒有啟用或收到聲音。
- 限制錄音長度:最短3秒,最長60秒。
- 錄音檔案可以壓縮成MP3,而不是預設產生WAVE。
- MP3檔案預設以Base64編碼儲存,也可以儲存到PHP File Host專案上。
安裝說明請看專案的README。
使用介紹 / Usage
開啟視窗之後,要先按瀏覽器的「允許」麥克風。
然後主要操作介面才能正常啟用。左邊是錄音的按鈕,右邊是音量的顯示器,未開始錄音時是以單色顯示。讓我們按下錄音按鈕。
開始錄音。你可以看到限制錄音時間,右邊的音量顯示器變成彩色了。
停止錄音之後,要等待系統轉檔與上傳。大致上錄音1分鐘就需要1分鐘轉檔。
轉檔完成,下面會出現音樂播放界面。按下音樂下載圖示則可以下載錄音檔MP3。錄音檔案的時間長度比實際錄音的時間還要長,這是已知的正常BUG (參考Known issues那段),目前沒有解決方法。
按下「確定」之後,就可以在編輯器內插入<audio>標籤了。
開發感想 / Development
萬用的Base64編碼 / Amazing Base64 Encoding
其實前一篇開發的PHP File Host專案是為了保存這篇的錄音檔而開發,規劃上是最後才實作。不過真的摸到Recordmp3js之後,才發現原來保存二進位MP3檔案的方式竟然是用Base64編碼!
Base64編碼是一種將二進位檔案以字串的方式編碼的方法。一般HTML網頁是以純文字的方式保存,因此網頁上面的圖片、聲音檔案等二進位資料通常是另外儲存,再以<img>、<audio>標籤嵌入到HTML網頁中。可是這個動作是撰寫HTML網頁初學者的第一個難關:為什麼網頁跟圖片要分開儲存?這也造成了複製網頁檔案的困難,不能只有複製HTML網頁檔案本身,還得複製相關使用的二進位圖片檔案。
Base64編碼可以讓圖片以字串的方式保存在HTML網頁中,而不需要額外保存一個檔案。我以前也介紹過以Base64編碼保存圖片這件事情。在寫這個Recordmp3js插件時,也因為覺得還要另外找地方插入麥克風圖示很麻煩,所以直接用Base64編碼的圖片來插入。可是我還真沒想到原來Recordmp3js的MP3聲音檔案也是用Base64來儲存!
以前只是單純地使用Base64編碼的檔案,但是到這時候我才意識到Base64的麻煩之處。一般是使用file類型表單欄位來上傳檔案,例如:
<input type=”file” name=”file” />
但是由於Base64編碼是字串資料形態,所以用普通的text類型表單欄位來遞交:
<input type=”text” name=”file” />
這兩者的name雖然都是file,但是對伺服器來說卻是不一樣的。在PHP當中前者是要用$_FILES[“file”]來取用、後者是要用$_POST[“file”]。此外,由於Base64編碼並沒有指定檔案名稱,所以還需要用另一個text欄位來傳送檔案名稱。
而即使Base64編碼是字串形式,也不能用JSONP的方式來做跨網域傳輸。因為JSONP實際上是將資料輸入在網址上,而Base64編碼的結果通常很容易超過網址的2083字元限制。
這造成我得改寫PHP File Host的檔案接收方式,又花了我一番功夫。到最後索性乾脆讓Recordmp3js預設使用Base64編碼來儲存,讓PHP File Host變成選擇性支援。雖然這會造成插入CKEditor的資料變得超級長,但不需要額外架設伺服器保存MP3檔案這點還是蠻容易讓人使用的。
MP3轉檔 / MP3 Convert
一開始研究HTML5錄音功能時,最讓我困擾的地方在於HTML5預設的錄音結果是WAVE格式。WAVE是沒有壓縮的聲音檔案,因此聲音檔案的大小非常大,讓我難以直接使用HTML5的錄音功能來儲存錄音檔案。
本來還在考慮是否要在PHP File Host加上檔案轉檔功能,在上傳檔案之後轉檔成MP3或amr格式。可是後來意外發現Recordmp3js居然在Recorderjs專案之上加上了MP3轉檔libmp3lame.js的功能,讓MP3轉檔的工作直接就在客戶端瀏覽器上完成。這真的是太方便了。
當然,對很多網頁開發者來說,這是風險很高的事情。因為客戶端使用的瀏覽器跟電腦效能不一,因此完全不能寄望IE6或是在行動裝置上的瀏覽器能夠正常執行。我剛剛拿了Android的Chrome跟Firefox來玩玩看,的確是無法開啟。
不過我的應用場景比較特殊,我可以要求使用者以Chrome或Firefox來使用我的系統,這個顧慮就暫時不在我的考量之中。
其他的轉檔格式? / Other Better Audio Format?
這也讓我想到是否可能用更有效率的amr格式來為語音聲音檔案編碼,赫然發現GitHub上的確有amr.js專案,真是厲害。然而選用編碼格式也要顧慮到瀏覽器的支援,根據w3schools.com的HTLM5 Audio介紹,<audio>標籤只支援MP3、Wav跟Ogg格式,大部分瀏覽器都支援MP3,除了Opera。支援細節如下:
Browser | MP3 | Wav | Ogg |
---|---|---|---|
Internet Explorer | YES | NO | NO |
Chrome | YES | YES | YES |
Firefox | YES | YES | YES |
Safari | YES | YES | NO |
Opera | NO | YES | YES |
HTML5的錄音元件 / Audio in HTML5
Recorderjs將HTML5的錄音元件包裝的非常好用,但是實際上使用之前,還是需要經過非常多的設定。你可以看看Recordmp3js的範例網頁程式碼,JavaScript設定那邊就一長串了,一開始研究的時候還真讓人感到困擾。Recordermp3js也具備了錄音完成之後上傳的功能,可是這功能卻寫死在Recordmp3js檔案裡面,而非開放設定的功能,讓我到最後還跑到Recordmp3js裡面去研究,也花了不少功夫。
在研究的過程中,越來越覺得audio真是有趣的應用。藉由結合多個音訊來源,我們可以動態控制各種聲音播放,就可以自行製作各種樂器。Web Audio Demos展示了很多聲音的應用,Web Audio Vocoder是其中一種強調混音的功能,這激起了很多不同應用的可能性。而我則是參考了AudioRecorer華麗的音量顯示器,做到Recordmp3js當中,讓錄音介面感覺漂亮一點。
錄音功能需要的Worker / Recorder and Worker
使用HTML5錄音跟MP3轉檔時,Recordmp3js都使用到了HTML5新元件Worker。一般來說,這個Worker必須是獨立的JavaScript程式碼,而且受限於同網域下執行的安全性問題。在跨網域應用中,這是一個很大的門檻。
但是我參考使用web workers這篇文章的說明,撰寫了worker_manger.js,將worker要執行的程式碼事先插入到目標網頁中,然後在以該網頁上的worker程式碼作為worker物件執行,就能夠保持在同網域執行的限制,迴避跨網域的問題。
雖然這個小功能我只用了很短的時間來做,但其實應該對很多人來說是很重要的功能吧?有機會再來正式發佈好了。
如何允許瀏覽器錄音 / How to Allow Browser Enable Microphone
使用HTML5錄音元件的另一個重點在於「允許」錄音檔案的事件。實際上啟用Recordmp3js的時候,跟HTML5元件宣告我要使用麥克風的時候,不是所有的瀏覽器就會跳出提示問你要不要啟用麥克風。
Firefox會在使用時直接跳出是否允許啟用,一個網頁只會問一次,Chrome有些版本也是如此。但是我電腦的Chrome卻不會在Recordmp3js載入時跳出來詢問,而是要離開網頁、再回到網頁時才會詢問。我索性加入一個<iframe>標籤,讓網頁觸發讀取事件,好讓Chrome問我要不要允許使用麥克風。但是離開網頁又回來時,Chrome還是會問我要不要允許麥克風,還好這動作也只要問一次而已。
很多使用者應該會忘記做「允許」麥克風這個動作。Chrome跟Firefox的介面都不太容易讓人理解,我可以預期這個動作應該是最讓人感到困擾的地方吧。
播放介面與<audio>標籤 / Audio Controller <audio> Tag
至於播放介面,我就直接使用HTML5的<audio>標籤。雖然<audio>標籤提供了播放/暫停、時間、播放進度條、聲音控制等功能,但是各個瀏覽器上<audio>長得都不太一樣,因此網路上大多建議是自行重寫<audio>的操作介面。但想到這個<audio>是要插入到CKEditor當中使用,寫得太複雜也不利於編輯。所以我還是選用最基本的<audio>標籤來顯示。
CKEditor插件開發 / About CKEditor Plugin Development
最後來講講CKEditor插件的開發。
一開始我是看为CKEditor在线编辑器增加一个自定义插件這篇,一步一步來實際操作看看。但是做到最後並沒有如期地做出可以用的CKEditor插件。我也去翻了CKEditor的Plugin教學跟它的API,但後來還是覺得乾脆直接複製既有的Plugin開始做起好了,所以就打開被壓縮的程式碼來看,直接從可以運作的功能中找尋我需要的程式片段。
雖然並不是什麼正規的學習方式,但這樣也在一片混亂中寫完了這個插件。有多混亂呢,看看插件的程式碼就知道了orz
總之最後還是完成了,也發佈到CKEditor的Add-on頁面去,也算是對這份研究有個交待啦!
話說回來,一開始會全部都重頭製作的原因就是因為在CKEditor與TinyMCE上找不到這種錄音的插件。現在我可以用這種方式來製作錄音插件的話,那其實像是webcam拍照與錄影等各種應用應該都沒問題才是。有機會再來研究看看吧。