2010年3月4日 星期四

試作TTS (Text-To-Speech) for Flash


今天有朋友分享一個Flash的Text-To-Speech服務,網址在這
http://www.flashrealtime.com/real-text-to-speech-for-your-flash-apps/。



這讓我突然想起來,之前因為專案的需求,曾經找過類似的資料,當時由於忙別的東西,所以就停了下來沒把這TTS寫出來。
其實這種TTS的服務有一個遠在天邊近在眼前,那就是Google。

Google並沒有真的「公開」的提供TTS的api給大家用,但是它卻有一個服務是有TTS功能的,而且TTS因為是聲音檔的關係,所以並不需要crossdomain policy就可以播放(當然,如果你要把它丟進mixer裡就是另一回事了)。因此這個服務即使Google沒有置放crossdomain.xml,也可以「不要太明目張膽」的使用。(如果要商業上使用請小心啊... 最好不要亂用...)
喔對,這個服務就是「Google翻譯」。

不過不管是上面那個FlashRealtime的TTS或是Google翻譯TTS,都.不.支.援.中.文!!

但我還是寫了一下powered by Google的TTS。

swf在這裡

整個程式十分簡單,就是直接new一個Sound類別去load一串網址而已:
http://translate.google.com.tw/translate_tts?tl=en&q=whatever you want to speek

就結束了。

因為之前有寫好了mp3切割及串接的類別檔,想說既然是可以直接用Sound類別讀進來,那應該就是mp3格式了吧?
於是順便的Google丟出來的東西丟到我的類別檔裡,但是結果卻發現它不是正確的mp3格式!?
這個問題目前還是無解,挺怪。

另外一個奇怪的問題,在本機寫這個TTS的時候,我送出request一直收到IOError,但是一擺到瀏覽器裡去看就ok!?
我猜Google應該是有做了什麼手腳限定一定要用瀏覽器才給資料吧?

總之就是這樣。

2010年3月1日 星期一

令人眼睛一亮的BingMaps


今天把玩了一下SilverLight版本的BingMaps,覺得它很威。
微軟很久以前就有個計劃叫PhotoSynth,現在透過BingMaps終於看到實體應用了,不過GoogleMap之前就已經擁有類似PhotoSynth的功能,所以相對的在BingMaps上看到就比較不這麼讓人驚豔,但是微軟在Demo時有demo到「live video」的synth,就很讓人想要鼓掌了。
Video在這邊:


PhotoSynth:


點Overhead View之後,會發現它把所有附近的圖片都依照地理位置湊起來了,有的點甚至多到可以看出大概地貌,很威。(我記得這也是當初PhotoSynth的終極目標)



除了PhotoSynth以外,我還很喜歡BingMaps的Bird’s Eye模式,這一點也是GoogleMap缺乏的,Bird’s Eye可以很清楚的看到當地的建築物高度位置,比衛星空照圖要人性化多了。


而且還可以旋轉,哈哈!


挺好玩的,另外一部份比較讓我感到驚訝的是SilverLight的發展速度,不久之前還挺陽春的,現在已經搞這麼完整了。嘖嘖。
是該時候再注意一下SilverLight的狀況了。

2010年2月24日 星期三

FlashSURF - AS3的圖像辨識


應該是有點累格了,不過是我這幾天看到的東西。
這東西發展起來的話應用範圍會很廣,跟一般的AR不一樣,雖然無法做到3D定位,但是優點是可以隨時選擇想要判斷的圖像,效能上感覺起來也不會太差,在目前flash上的AR都需要marker的情況之下,這一套markerless的技術應該可以產生另外一種玩法。

Demo影片:

Playing with flashSURF from Eugene Zatepyakin on Vimeo.

作者Blog:
http://blog.inspirit.ru/?p=343

AS3的MP3音樂編輯器!?


很久以前剛開始碰byteArray類別時,覺得很多東西可以用byteArray來玩,就曾經去找過mp3的檔案結構來看,然後寫了一個可以把mp3切來切去組來組去的類別檔。
但是後來發現雖然可以切割組合,但是卻遇到了兩個問題:
1. 無法混音,因為mp3裡的音樂資料都是被編碼過的,以AS3的效能來看,即使有codec,AS3也跑不動。
2. Sound類別無法直接讀取byteArray來播放mp3。(這邊指的byteArray是用URLStream或FileReference所讀入的mp3的byteArray,而不是FP 10之後Sound類別透過SampleDataEvent發聲的那種byteArray。)既然無法直接將mp3 的byteArray丟到Sound裡,那麼這個將mp3拆解組合的類別基本上就是一個很雞肋的東西了。
由於遇到這兩個問題,因此我很久就沒再去想起這個類別。

但是最近在查資料的時候,卻意外的發現上述第二點有解法了!這個解法是由Christopher Martin-Sperry這個人所寫的(http://www.flexiblefactory.co.uk/flexible/?p=46),他主要的原理是先在記憶體裡產生一個swf bytecode,再將mp3的資料「注射」到這個swf裡,接著用loadBytes的方式實體化swf,再用getDefinition的方式取出Sound類別,有興趣的可以自己點上面的聯結過去,那個頁面裡有他的類別可以下載,基本上如果我們如果純粹只是要將mp3的byteArray轉成Sound的話,只會用到ByteArraySegment.as、MP3Parser.as及SoundClassSwfByteCode.as這三個類別而已。
發現了這個solution之後,我這兩天就又把之前我自己搞的那個類別拿出來玩了,即使沒辦法混音,想說這樣至少還可以線上切割組合mp3之後馬上播放來聽,而且可以在mp3裡加入一些防君子不防小人的版權保護的東西,於是很手癢的就動手寫了起來。
只不過該怎麼說呢,應該說沒有每天在過年的,我遇到了另一個問題,努力了兩天之後目前無解,情況如下:
現在拆解切割組合以及播放都很ok,歸功於FP10,組好的檔還可以直接存在本機端。但是不管我怎麼修改找問題,切出來的那一塊部份,總是跟我原本想切的那一塊有一點點的時間差,曲子越長誤差越大,可以誤差到約一秒多的時間。

flash點這裡
有興趣的可以自己點過去玩玩看(需要Flash Player10),上傳一個mp3(別太大,因為我又有跑Sound.extract(),所以mp3太大的話記憶體會很耗),拉一個區塊,點「播放被選取的」聽聽看,再點「另存新檔」,再聽聽實體檔,就知道我在說什麼了。

我後來想一想,會不會是我在拆解mp3的byteArray時查找frame header時出了問題?
一般header的格式是4個bytes,以binary的來看的話其格式會是像這樣『AAAAAAAA』『AAABBCCD』 『EEEEFFGH』『IIJJKLMM』的格式,每個字母代表一個意義,而且每個frame的header不一定會一樣,我卻是偷懶直接取header的前兩個bytes來當辨識元,只要符合0xFF 0xFB的我就把它視為是header,我知道光用兩個bytes就來判斷是否是header的做法很冒險,而且運氣差一點的話有些header的第二個byte搞不好根本不是0xFB。
但如果真的是frame header出了問題,我目前說真的還沒想到要怎樣很有效率的依序找出每一個frame的header。

算了,也許這個問題我就只好先擺著了。
如果路過的有高手知道我可能犯錯的問題在哪的話,還請幫忙解答一下~~
感謝!

2010年1月4日 星期一

Facebook Flash Application開發心得(5) - 新版的streamPublish


Facebook的api在之前偷偷的有更新過了一次,但是flash的swc似乎仍然沒有變動,基本上影響不大。
其中發佈到個人塗鴉牆的部份,Facebook打算捨棄原本的template,從原本FB.Connect.showFeedDialog改成FB.Connect.streamPublish,這樣也好,不然每次要弄一個新的format就要去註冊一個新的template,這樣真的很麻煩。由於舊方法目前還可以繼續使用,但是為了將來著想,最好還是將舊有的程式改寫成新方法會比較好。
說是對flash影響不大的原因,因為雖然flash api裡原本就有com.facebook.commands.feed.PublishTemplatizedAction這個類別來處理發佈到塗鴉牆的工作,但是如果使用者沒有去允許這個app擁有完整publish_stream的權限的話(一般來說使用者很少主動允許這個權限),從flash發佈出去的東西是不會主動呈現在使用者的塗鴉牆上的,需要使用者回到自己的塗鴉牆再去點選一次才會真的出現,因此實用性並不大,我也是通常都使用javascript來處理發佈的動作。

因此在這裡記錄一下streamPublish該怎麼處理好了。
新版的發佈動作是藉由javascript api裡的FB.Connect.streamPublish來進行的。
streamPublish(String user_message, Object attachment, Object action_links, String target_id, String user_message_prompt, Function callback, Boolean auto_publish, String actor_id)
這些參數的意義分別為:
1. user_message:要講的話,通常是空的,由使用者自己填寫。
2. attachment:這是一個Object,就是原本的template,用來制定塗鴉牆上顯示的格式,attachment的寫法是重點,待會再提。
3. action_links:塗鴉牆上的連結,一樣是Object型態,通常是[{ "text": "點這邊!!", "href": "http://www.yourdomain.com/xxx.html"}];
4. target_id:要發佈到誰的塗鴉牆上,如果是要發佈到使用者自己的塗鴉牆的話,這個值就要帶空值。
5. user_message_prompt:會出現在user_message之前的一個開頭語,例如『正在想』、『覺得』…etc這些東西。
6. callback:對話窗關閉後會回call的function,如果不打算接受這個事件就不用管。
7. auto_publish:如果使用者已經同意publish_stream的權限且auto_publish是帶true的話,那麼使用者將不會看到有對話窗跳出來,訊息會直接出現在塗鴉牆上。(個人十分討厭這種作法)
8. actor_id:這好像是粉絲頁面用的,可以選擇直接發佈到粉絲頁,但前提是這個使用者是該粉絲頁的管理者。


看起來是很簡單明瞭了。
所以主要的重點還是在attachment。其實attachment跟以前的template差別不大,分別只在於以前的template先制定好了『格式』,現在的attachment則是隨時可以帶入不同的Object,比較活。
官方wiki對於attachment的解說請參考這個網址
http://wiki.developers.facebook.com/index.php/Attachment_%28Streams%29

通常attachment會具有以下幾個properties:
1. name:就是主旨。
2. href:主旨點下去會連結的網址,通常是app或是某活動、官網的網址。
3. caption:跟name不一樣,也跟下面的description不一樣,這個caption通常會寫出誰誰誰做了什麼事,在caption裡加上{*actor*}的話這一串字串就會自動被取代成使用者的名稱,例如:『{*actor*}剛剛做了心理測驗』,出來的結果就會是『Jason剛剛做了心理測驗』
4. description:嗯,這個就是一大堆文字了,可以當成作文去寫,通常facebook裡那些心理測驗在解釋測驗結果的那一大堆話就是塞在這個屬性裡。
5. properties:這屬性我還沒實作過,功能還不明,跳過。
6. media:除了description以外最多人用也最有興趣的就是這個屬性了,這是一個陣列,定義了所有會出現的圖或flash或聲音,全都塞在這個陣列裡,陣列裡放的是Object,每一個Object會有三個屬性:type,src,href。type定義是什麼媒體,有image、flash及mp3三種,src則是這個媒體所在的網址,href就是點了之後會連到哪。所以media應該會是類似這樣的型態:[{'type': 'image', 'src': 'http://icanhascheezburger.files.wordpress.com/2009/03/funny-pictures-kitten-finished-his-milk-and-wants-a-cookie.jpg', 'href': 'http://icanhascheezburger.com/2009/03/30/funny-pictures-awlll-gone-cookie-now/'}, {'type': 'image', 'src': 'http://photos.icanhascheezburger.com/completestore/2009/1/18/128768048603560273.jpg', 'href': 'http://ihasahotdog.com/upcoming/?pid=20869'}]
7. 剩下的像comments_xid及自定義屬性,好像都是要用再更深入的機制上才會用到的,目前我也沒實作過,因此一樣跳過。
基本上attachment最常被使用到的就是這幾個屬性了,差不多瞭解了之後就可以很快的發佈屬於自己的塗鴉牆。


接下來,如何從Flash去呼叫FB.Connect.streamPublish,不用說一定是透過ExternalInterface,需要注意的是,因為attachment是一個Object,我試過直接用Flash丟Object出來給js,結果是失敗的,雖然js也是收到一個Object,但是其實這個Object已經不是原本的Object了,所以要嘛就是把要發佈的attachment先在js裡寫好,但是這麼一來整個code又變得很不活,也無法類別化,所以我都會改採另外一種方式:JSON。
在Flash裡,先產生相對映的Object,然後藉由Adobe as3corelib裡面com.adobe.serialization.json的package,JSONEncoder類別將Object轉換成JSON的字串後,用字串的方式傳遞給js,js裡再還原成Object就可以了,js裡字串還原成JSON的方法可以直接用eval,或是使用JSON.js來做parsing的動作(JSON.js可在此下載http://www.json.org/js.html)。
這樣做的好處是可以在flash裡動態的隨時產生不同的attachment,視不同的狀況發佈不同的塗鴉牆,比較靈活,js也不需要因為每個專案而再重新編寫。

2009年10月27日 星期二

Flash版GoogleMap使用街景


前一陣子(好大一陣子),GoogleMap的台北街景上線,順便的我發現GoogleMap api for flash也有多了一些東西,像是路徑規劃、3D地圖等等…。
3D地圖基本上只是把地圖變成可以旋轉改變角度而已,事實上仍然是平面的,「非常的」耗效能,國外是有人把建築物用pv3D直接疊在3D地圖上感覺像是Google Earth一樣,但是說真的我的電腦已經算不慢的了,跑起來還是慢到可怕…。

說起來比較讓人興奮的應該是路徑規劃了吧。
之前Flash版的GoogleMap少了這個總覺得少了什麼,現在有了就好多了。
不過這一篇不是要講路徑規劃的部份,而是要講街景。
之前因為台灣都沒有街景,所以一直沒去碰這個,現在台北有了,就很好奇的想把街景跟Flash的GoogleMap結合起來看看。
有把玩過GoogleMap街景的應該都會注意到,街景的介面是Flash的,我想這是因為Flash在處理圖片的移動及blur這些部份是比js來得方便的原因吧?但是目前為止雖然街景介面本身是個Flash,但是仍然無法直接被Flash版的GoogleMap直接載入使用,所以我只好用土法練鋼的方法 – 透過js溝通。

Google在文件上寫得還算清楚明白,大概看一下就知道怎麼叫出街景了,挺簡單的。
先從html開始,首先載入GoogleMap的js,要改變的只有key這個變數,key就是跟Google申請的api key。
然後在body上加入onload跟onunload的處理方法,我這邊是先分別指定initialize()跟unloadAll()兩個function來負責。onunload一定要處理,否則很有可能會造成記憶體沒有回收的狀況。


由於地圖是由Flash來處理,因此就不產生js的GoogleMap實體。
所以直接宣告廣域的街景物件(GStreetviewPanorama)及一個判別是否由flash版GoogleMap指定座標的flag。由於GStreetviewPanorama需要一個dom來放置,所以在html裡我們放一個div來裝GStreetviewPanorama。
然後就編寫initialize()及unloadAll()。


GStreetviewPanorama的error事件我們主要會用到的大概是兩個:
1. 錯誤代碼603:表示沒有Flash Player。
2. 錯誤代碼600:被指定的座標沒有街景。
基本上這兩個都要實作,以避免出問題。
而GStreetviewPanorama的initialized事件,則是當GStreetviewPanorama的座標位置改變的時候會被觸發,因此我們可以監聽這個事件來控管目前地圖跟街景之間座標的同步。
因此這兩個事件處理函式應該會很像是這樣子:


最後就很簡單了。
需要顯示哪邊的街景時,就直接從Flash版的GoogleMap用ExternalInterface去呼叫一個function來指定GStreetviewPanorama的座標就好。


在看文件時,其實js的GoogleMap有一個優點是可以把目前有支援街景的路線疊上一個overlay在地圖上,但是Flash版的沒有,所以如果要用Flash版的GoogleMap去結合街景,使用者只能用「試試看」的方式去點選某條路是否有街景,這是比較不方便的,也許也只能等Google下次更新Flash api時再期待會不會有這功能出現了。

2009年9月3日 星期四

Facebook Flash Application開發心得(4) – Iframe的架構下取得Session


相對於FB Connect在取得Facebook登入Session值的複雜度,Iframe架構下的Application在這方面反而方便許多。
前一篇已經有說到Flash api裡的FacebookSessionUtil在FB Connect的情況下會有bug,但是在iframe之下卻可以運作得非常順利。其中很大的原因,是因為iframe的架構原本就是附屬在Facebook裡的,登入的狀況都由Facebook先處理掉了,Facebook才會開始載入iframe,也就是說user很少會在還沒登入的狀況之下就接觸到你的iframe application,因此我們可以少掉一部份處理尚未登入的情況,但如果user真的有辦法在未登入的情況下進入我們的iframe application(會有,在某些情況下會發生),我們只要直接把頁面導回給Facebook處理就好,等登入後Facebook會自動再幫我們導回來(很方便吧!)。這個利用Facebook來做導向的機制也會發生在user第一次進入應用程式的時候的那個授權頁。
另一個很重要的原因,正由於iframe架構是由Facebook所載入的,因此Facebook「很貼心」的順便帶了一些十分有用的資訊放在url後面的變數裡給iframe,這一大堆的資訊裡面就包括了Session值。

既然知道了這很重要的兩點,就來實作吧。
首先當然是針對javascript下手。

依照剛剛所說的兩個重點,一開始先要取得的就是Facebook帶給iframe的所有必要參數。

上面這段code最重要的部份就在於取得所有網址列後面的變數並放在flashVars裡。
另一個重點就是fb_sig_added變數的取得,它代表了user是否已經授權使用你的application,如果是0(尚未登入的情況下好像也會是0),我們就把整個頁面導向到http://www.facebook.com/login.php?api_key=" + api_key + "&next=index.htm&v=1.0&canvas=1的這個網址,網址列後面的參數api_key就是你的api_key,next指的是授權後要再導回來的頁面,我習慣是再導回index.htm。(有興趣的人可以觀察一下,事實上Facebook如果是在你已經登入的情況但沒授權的時候是會導到另外一頁的,不過next也會跟著被帶過去,所以我們只要直接全交給login.php去處理就好)
當一切都就續時,我最後呼叫一個initFB的function,這個function裡我就開始初始化facebook javascript api。

初始化Facebook javascript api的部份,跟FB Connect不一樣,我這次採用FB_RequireFeatures來做為初始化的點,因為我很確定user已經是登入狀態,所以一開始就初始化所有該用到的東西。
而且如果我沒記錯的話(說真的印象有點久遠了),使用FB_RequireFeatures的話,Facebook會強制在你的appilication外層加上它的外框(上面的header及下面的footer),所以如果你是直接輸入你application實際的網址的話,遇到FB_RequireFeatures時還是會Facebook框起來。
因此之後有時候,我們將會遇到iframe及FB Connect同時應用在一個application的情況中,也就是說即使在iframe的情況之下,有時還是要用到FB Connect,這也是我先介紹FB Connect的原因之一。(如果我以後還會繼續寫下去的話)
至於FB_RequireFeatures裡所帶的一些值的意義,可以去官方的wiki上看,基本上我也沒有實際的去比較這幾個之間差多少,所以我就乾脆直接初始化三種我覺得可能會用到的。
通常這個function這樣應該就ok了,不需要改寫,直接拿去用就好。
等到一切都就緒後,就呼叫initFlash這個function來載入flash,這function我就不寫出來了,反正就是用swfObject載入就是了 (記得要將flashVars帶進去)

接下來就是Flash裡的code了。
既然已經說了FacebookSessionUtil在iframe裡很好用,就直接大方的用了吧。

基本上,user算是通過層層關卡才會到達flash這一層的驗證:Facebook本身一層,javascript有兩層驗證,第四層才會是FacebookSessionUtil,所以基本上在上面的code裡使用FacebookSessionUtil.login()是用於開發時直接在本機端跑的情況下,否則應該都是直接verifySession就可以取得登入uid了。
ps:如果是在開發的時候,api secret值需要寫入,否則無法登入,這就是Facebook建議我們不要把api secret值寫死在code裡的原因,因為一但被取得,其它cracker就可以利用本機端寫程式來破壞你的application,所以記得上線時要把api secret值從code裡拿掉,FacebookSessionUtil自動會從loadInfo裡所有的flashVars取得它該使用的secret值。