:::
顯示具有 CKEditor 標籤的文章。 顯示所有文章

我讓CKEditor也可錄音了:Recordmp3js插件 / Recordmp3js Plugin: Record Voice in CKEditor

我讓CKEditor也可錄音了:Recordmp3js插件 / Recordmp3js Plugin: Record Voice in CKEditor

2014-09-06_004725

我做了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的特色如下:

  • 使用HTML5的錄音功能,不需要Flash或Java Applet等其他插件。
  • 錄音功能旁有個音量顯示器,可以知道自己的麥克風有沒有啟用或收到聲音。
  • 限制錄音長度:最短3秒,最長60秒。
  • 錄音檔案可以壓縮成MP3,而不是預設產生WAVE。
  • MP3檔案預設以Base64編碼儲存,也可以儲存到PHP File Host專案上。

安裝說明請看專案的README

使用介紹 / Usage

2014-09-06_005312

安裝好之後會看到一個麥克風的圖示gentleface.com free icon set

2014-09-06_012542 - copy

開啟視窗之後,要先按瀏覽器的「允許」麥克風。

PicPick 2014-09-06 01-23-32 2

然後主要操作介面才能正常啟用。左邊是錄音的按鈕,右邊是音量的顯示器,未開始錄音時是以單色顯示。讓我們按下錄音按鈕。

PicPick 2014-09-06 01-00-52 1

開始錄音。你可以看到限制錄音時間,右邊的音量顯示器變成彩色了。

PicPick 2014-09-06 01-36-14 5

停止錄音之後,要等待系統轉檔與上傳。大致上錄音1分鐘就需要1分鐘轉檔。

2014-09-06_004725

轉檔完成,下面會出現音樂播放界面。按下音樂下載圖示則可以下載錄音檔MP3music_down。錄音檔案的時間長度比實際錄音的時間還要長,這是已知的正常BUG (參考Known issues那段),目前沒有解決方法。

2014-09-06_014012

按下「確定」之後,就可以在編輯器內插入<audio>標籤了。

開發感想 / Development

萬用的Base64編碼 / Amazing Base64 Encoding

2014-09-06_100638

其實前一篇開發的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拍照與錄影等各種應用應該都沒問題才是。有機會再來研究看看吧。

(more...)

jQuery動態載入CKEditor注意事項

jQuery動態載入CKEditor注意事項

image

最近在寫標註工具的API,完成之前沒有好好做的載入器(loader),目標是要讓人用一行程式碼就能載入全部需要的工具。

在我做的KALS標註工具當中使用到了CKEditor,而要讓他與jQuery搭配,則需要額外載入一個調配器(adapters)。但是傳統的用法都是在<head>寫<script>標籤,這跟我要的動態載入不太一樣。

在四處碰壁嘗試之後,終於找到了正確的作法。在此做個筆記。


不可用的方法

由於我是要在網頁讀取完之後才開始動態載入,因此不能用document.write,否則會洗掉整個網頁。

原本我也想利用DOM建立<script>標籤,內含src屬性載入CKEditor,插入<head>當中,但卻發現這個插入的<script>標籤並不會執行,簡單來說就是沒有效果。

後來改用jQuery的getScript()方法,就能夠確實地讀取並執行CKEditor。不過仍有其他細節需要注意,以下說明。

正確的方法

正確的作法是:

  1. 建立<script src=”[CKEditor的網址]”></script>貼到<head>之中,供CKEditor本身去搜尋連結,自動載入其他檔案。
  2. 以jQuery.getScript()載入並執行CKEditor。
  3. 先載入CKEditor主程式,再載入調配器。

以下是簡單的範例程式碼:

//在這之前,你已經確認載入了jQuery

//路徑宣告
var _ckeditor_url = "/ckeditor/ckeditor.js";
var _adapters_url = "/ckeditor/adapters/jquery.js";

//載入函式
var _load_lib = function (_url, _callback) {
var _script_tag = $('<script type="text/javascript" src="' + _url + '"></script>');
_script_tag.appendTo($('head'));

$.getScript(_url, function () {
if (typeof(_callback) == 'function')
_callback();
});
};

//開始載入
_load_lib(_ckeditor_url, function () {
_load_lib(_adapters_url, function () {

//你接下來要做的其他程式

});
});

這是截自KALS載入器的部分程式,並沒有經過測試。有任何錯誤,請提醒我再檢查、修正。

(more...)

CKEditor設定超連結的目標預設為「_blank」的方法

布丁布丁吃布丁

CKEditor設定超連結的目標預設為「_blank」的方法

image

利用CKEditor作為所見即得編輯器的討論區會遇到一種情況,允許使用者在自己文章中加入超連結,但是因為預設的超連結目標(target屬性)是自己網頁,所以當其他使用者點下超連結時就會將從討論區網頁跳到其他網站去。

為了避免這個問題,簡單的作法是修改CKEditor中超連結預設的目標選項。根據「Set link default target to _blank」討論區中的指示,主要要修改檔案中的設定即可。

  1. 找尋檔案[CKEditor存放路徑]/plugins/link/dialogs/link.js
  2. 搜尋設定:
    label:a.lang.common.target,'default':'notSet'
    並將之改成
    label:a.lang.common.target,'default':'_blank'
  3. 搜尋設定:
    label:a.lang.link.targetFrameName,'default':''
    並將之改成
    label:a.lang.link.targetFrameName,'default':'_blank'

image

由於預設的程式碼是經過壓縮的,一開始打開的時候可能會覺得程式碼都擠在一起很難閱讀,但仔細找一下還是可以找到的。

我將改完之後的檔案上傳供大家使用:link.js。請解壓縮後放置在[CKEditor存放路徑]/plugins/link/dialogs/link.js即可。來源是CKEditor 3.4.1

(more...)