0

繼續解決 Windows 的問題

| 11/16/2010
- 刪除沒有辦法刪除的檔案
有的時候你會發現某些檔案沒有辦法刪除,你會得到存取被拒的結果。這些檔案有可能是你電腦在重灌之前留下來的檔案,因為使用者資訊改變的關係,那些本來被設定成特殊權限的系統檔案一時之間找不到主人,自然你就缺少對該檔案控制的權力。使用下面的指令可以幫你重新取回檔案的所有權:

takeown /F filename /R

這個指令會將檔案的所有者轉移到現在登入的使用者身上,然後再改變該檔案的存取權限,讓管理者擁有最高控制權:

icacls filename /grant Administrators:F

接下來,你就可以快樂的砍掉他了!
這一連串的指令當然可以寫成 batch 檔案,之後好存取:

@echo off
takeown /F %1 /R
icacls %1 /grant Administrators:F
del %1

然後將檔案存成 forcedel.bat 就大功告成啦!只要把檔案拖到這個 batch file,檔案自然會被摧毀!

- 開啟檔案位置
這是一個從 Vista 開始很好用的功能。你可以經由這個右鍵指令直接找到捷徑本來的檔案位置。不知道從何時開始,你會發現當你按下去之後,這個指令一點反應都沒有!解決的辦法:

找出 Internet Explorer 9 的更新,然後反安裝他吧…老師說過,Beta 版的東西少碰為妙…
0

wchar_t 的長度…

| 7/02/2010
wchar_t 是 ANCI C 提供支援 Unicode 的方案。使用方式跟一般 char * 字串很像,整個介面都是差不多的。

char * anciStr = "測試"
wchar_t * uniStr = L"測試" // 注意這裡需要加上一個 micro

而我們常用的字串函式 strlen, strcpy 等,自然就會有對應的東西出現:wcslen, wcscpy。這些東西則是定義再 wchar.h 裡面。

雖然支援了 Unicode 是很好,但是並不是所有的作業系統對這些東西的編碼定義都相同。今天如果要做跨平台的系統程式設計,就得要不斷的面對每個系統對字元符號的編碼定義不同的問題。好比如說,在 Windows 上面, char 字元預設是 ASCII +1 的 8bit 字,wchar_t 則是 UTF-16 Little Endian。這看起來像是很合情合理,但是像是 Linux、Mac OS X 這種全面支援 Unicode 的系統而言,char 早就已經是 Unicode 了。同樣是 Unicode ,卻依然有大大的不同!在這兩的系統上面,char 是被定義成 UTF-8(今天 Windows 在 UTF-8 上面是有 BOM 的,而這裡沒有。其實 BOM 對 UTF-8 而言很沒意義),而 wchar_t 則是 UTF-32(Big Endian 跟 Little Endian 都有可能,看 CPU)。所以說,我們用函式測試的時候就會出現這樣的結果:

strlen(anciStr) // Windows 會出現 4,Linux、Mac 則是 6
wcslen(uniStr) // 這裡統一會出現 2
看的懂差異的來源嗎?

今天 Cocoa Foundation 提供 NSString 的 Class,這個 Class 是以 UTF-16 Big Endian 儲存的。但是該 Class 本身具備好了許多編碼轉換的函式,可以讓你在 不同的編碼之間轉換!所以說,NSString 跟 wchar_t 之間的變化,應該是:

wchar_t * string = malloc([nsString length] * sizeof(wchar_t) + 1);
wcscpy(string, (const wchar_t *)[nsString cStringUsingEncoding:NSUTF32LittleEndianStringEncoding]);
0

Play Music using AudioUnit

| 6/29/2010
AudioUnit playback - Introtuction of CoreAudio Programming
AudioUnit 是 Mac OS X 在 Audio Programming 上面使用,代表一段音樂資料的基本單位。一般來說,如果只是要讓應用程式具有播放音樂的能力,並不需要使用到這種東西。連同蘋果 10.2 開始提供的 CoreAudio API 在內,都屬於低階程式編輯的東西。一般來說,如果只是要讓你的程式播放音樂,還有 QTKit 之類高階的 API 讓你簡單的達到播放音樂的功能。但是如果你希望做到一些更加進階的功能:編輯、Codec 的開發,你就必須要瞭解這個東西。這邊只有簡單記錄一下要如何使用 AudioUnit 來播放音樂。

我們會使用到一些 Framework,必須要把他們連結進來:
  1. AudioUnit.framework
  2. AudioToolbox.framework
  3. CoreService.framework

首先,我們使用 Component 來取得使用者設定的 Default Output Device Unit。AudioUnit 分成許多不同的種類,他們可以是 Outputs, Mixers, or DSP。閱讀 AUComponent.h 可以得到其他資訊。我們可以簡單的使用 ComponentDescription 來取得 Output Audio Unit。

Code:
ComponentDescription desc;
Component comp;
AudioUnit outputUnit;
OSStatus err;

// 設定 Component 型態
desc.componentType = kAudioUnitType_Output;
// 每一種型態都有一個 sub type,用來深入描述該型態
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
// 下面的部份,所有的 AudioUint 都是一樣的,依樣畫葫蘆就可以了
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;

// 取得與描述相同的 Component
comp = FindNextComponent(NULL, &desc);
if (comp == NULL) exit(-1);
// 取得 Output AudioUnit
err = OpenAComponent(comp, &outputUnit);

接下來,我們要設定 AudioUnit 的參數。
AudioUnit 有許多不同的參數可以設定,其中最常使用到的兩個函數就屬於 AudioUnitGetProperty 以及 AudioUnitSetProperty 兩個。其中,我們也經常使用 AudioUnitGetPropertyInfo 來取得一些資訊用以避免設定上的錯誤。

在 AudioUnit 的眾多參數裡面,最重要的一項就是 AudioStreamBasicDescription (ASBD)。這項變數裡面存放了關於 Audio Stream 的描述,包含 simple rate、Package information 以及 Stream 格式。我們可以思考 AudioUnit 包含了兩個最大的部份:input 跟 output。所以說我們可以為這兩個部分設定 ASBD。(注意:AudioUnit 型態本身就是個 Reference)

Code:
UInt32 size;
Boolean isWritable;
AudioStreamBasicDescription outputDesc;
AudioStreamBasicDescription inputDesc;

// 取得 Stream format 的 size,以及是否可以寫入。
err = AudioUnitGetPropertyInfo(outputUnit,
                               kAudioUnitPorperity_StreamFormat,
                               kAudioUnitScope_Output,
                               0, &size, &isWritable);

// 取得 Stream Format
err = AudioUnitGetProperty(outputUnit,
                           kAudioUnitProperty_StreamFormat,
                           kAudioUnitScope_Output,
                           0, &outputDesc, &size);

// 將 input 跟 output 的 stream format 設定相同
err = AudioUnitSetProperty(outputUnit,
                           kAudioUnitProperty_StreamFormat,
                           kAudioUnitScope_Input,
                           0, &outputDesc, &size);

// 注意,在所有 Property 設定結束之後才使用 AudioUnitInitialize。這個函式的消費很大。
err = AudioUintInitialize(outputUint);
這裡需要注意的地方還有一個,所有的 err 都可以作為結束的判斷式。

我們得到的 ASBD 資料格式應該如下面的範例:
接下來我們要設定一個 Render Callback 來做為 AudioUnit 取得播放資料的緩衝區的地方。我們使用 kAudioUnitProperty_SetRenderCallback 以及 AURenderCallbackStruct。被設定的函式只有在 Audio Converter 需要資料的時候才會被呼叫。在這裡我們假設被呼叫的函式名稱為 MyFileRenderProcAURenderCallbackStruct 有兩個 Member:inputProc 以及 inputProcRefCon。前者為我們要設定的 callback function 名稱,後者則是該函式的參數。

Code:
AURenderCallbackStruct renderCallback;
memset(&renderCallback, 0, sizeof(AURenderCallbackStruct));

// 設定 Render Callback。因為播放的時候不需要傳入參數,所以 RefCon 設定為 0
renderCallback.inputProc = MyFileRenderProc;
renderCallback.inputProcRefCon = 0;

// 設定到 AudioUnit 裡面
err = AudioUnitSetProperty(outputUnit,
                           kAudioUnitProperty_SetRenderCallback,
                           kAudioUnitScope_Input,
                           0, &renderCallback,
                           sizeof(AURenderCallbackStruct));


這些設定都設定完成之後,我們就來讀取音樂檔案,並且放置到緩衝區裡面。當 AudioUnit 在播放的時候他會自動取得緩衝區裡面的資料使用。如果是要用來播放其他格式的檔案,那麼我們就必須要擁有其他格式的 Codec 來將檔案解碼成系統所支援的格式。這裡,我們就使用蘋果提供的 Audio File API 來讀取檔案。記住,在這裡我們只有假設我們讀取的是很小的音樂檔案,所以說我們將整個檔案都放到了緩衝區中。事實上,當你的音樂比較龐大的時候,最好是將你解碼的部份分段放入緩衝區中,解碼的動作可能必須要開一個新的執行緒以避免和主程序互衝。

Code:
UInt64 totalPacketCount;
UInt64 fileByteCount;
UInt32 packetSize;
...
OSStatus
OpenAudioFile(AudioFileID * fileID,
                       AudioStreamBasicDescription * fileASBD,
                       const char * filename)

{
OSStatus err = noErr;
Uint32 size;

// 產生路徑,記住檔案路徑是以 UTF8 編碼的
CFURLRef fileURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8*)filename, strlen(filename), NO);

// AudioFileOpen 已經不再被支援了
err = AudioFileOpenURL(fileURL, kAudioFileReadPermission, 0, fileID);

// 讀取 Packet 資訊,這個資訊會在 AudioConverter 中使用
// 我們已經確定了 Format Size 了,這裡不再使用 AudioUnitGetPropertyInfo
size = sizeof(AudioStreamBasicDescription);
memset(fileASBD, 0, size);
err = AudioFileGetProperty(*fileID, kAudioFilePropertyDataFormat, &size, fileASBD);
  
size = sizeof(totalPacketCount);
err = AudioFileGetProperty(*fileID, kAudioFilePropertyAudioDataPacketCount, &size, &totalPacketCount);
  
size = sizeof(fileByteCount);
err = AudioFileGetProperty(*fileID, kAudioFilePropertyAudioDataByteCount, &size, &fileByteCount);
  
size = sizeof(packetSize);
err = AudioFileGetProperty(*fileID, kAudioFilePropertyMaximumPacketSize, &size, &packetSize);

return err;
}

int main(int argc, char ** argv)
{
AudioFileID fileID;
UInt32 bytesReturned = 0;
UInt32 packets = totalPacketCount;
char * audioBuffer = malloc(fileByteCount);

...
// 將整個音樂檔案放到記憶體中
err = OpenAudioFile(&fileID, &inputDesc, argv[1]);
err = AudioFileReadPackets(fileID, NO, &butesReturned, NULL, 0, &packets, audioBuffer);
}

接下來,我們就來撰寫確實的 Render Callback。這個 Callback 會在 AudioUnit 需要 Audio Frame 的時候被呼叫。我們在這裡使用 AudioConverter 將讀近來的檔案轉換成系統支援的格式。這裡我們不多寫程式碼,所以我們就只有在 PCM 之間轉換。也就是說,這個程式碼只能夠播放 PCM 的檔案格式:WAV、AIF。

Code:
AudioConverter converter;
AudioConverterNew(&inputDesc, &outputDesc, &converter);

OSStatus MyFileRenderProc(void * inRefCon,
                              AudioUnitRenderActionFlags *inActionFlag,
                              const AudioTimeStamp *timeStamp, UInt32 inBusNumber,
                              UInt32 inNumFrames, AudioBufferList *ioData)
{
    OSStatus err = noErr;
  
    AudioConverterFillComplexBuffer(audioConverter,
                                    MyACComplexInputProc,
                                    0, &inNumFrames, ioData, 0);
  
    return err;
}

OSStatus MyACComplexInputProc(AudioConverterRef inAudioConverter,
                              UInt32 * ioNumberDataPackets,
                              AudioBufferList * ioData,
                              AudioStreamPacketDescription ** ioDataPacketDescription,
                              void * inUserData)
{
    OSStatus err = noErr;
    UInt32 bytesCopied = 0;
  
    ioData->mBuffers[0].mData = NULL;
    ioData->mBuffers[0].mDataByteSize = 0;
  
    if (playedPacketOffset + *ioNumberDataPackets > totalPacketCount)
        *ioNumberDataPackets = totalPacketCount - playedPacketOffset;
  
    if (*ioNumberDataPackets) {
        if (sourceBuffer != NULL) {
            free(sourceBuffer);
            sourceBuffer = NULL;
        }
      
        bytesCopied = *ioNumberDataPackets * packetSize;
        sourceBuffer = calloc(1, bytesCopied);
        memcpy(sourceBuffer, audioBuffer + playedByteOffset, bytesCopied);
      
        playedByteOffset += bytesCopied;
        playedPacketOffset += *ioNumberDataPackets;
      
        ioData->mBuffers[0].mData = sourceBuffer;
        ioData->mBuffers[0].mDataByteSize = bytesCopied;
    }
    else {
        ioData->mBuffers[0].mData = NULL;
        ioData->mBuffers[0].mDataByteSize = 0;
        isPlaying = NO;
        err = noErr;
    }

    return err;
}

以上,是使用 AudioUnit 來播放音樂檔案的簡單程式。如果有可能,我會繼續將學習 CoreAudio 時所學到的東西繼續發佈上來。也請發現有問題的前輩多多指教。

Download Source Code Here.
How to compile: gcc auplaysample.m -framework AudioUnit -framework AudioToolbox -framework CoreService -o AudioPlay

P.S. 話說回來,如果我用 NSSound 的話,上面一大串就剩下兩行了!高階 API 跟低階 API 差距好大啊…
0

Eternal recurrence

| 5/29/2010
Japanese Game "星空のメモリア" OP


歌:橋本みゆき

仰望夜空 流星閃耀
穿越時光 協同願望
遙遠記憶 在小小的山丘上
互相言語 那深刻的未來之夢

牽繞的手 相互分離
立下約定 將永恆不忘

那願望實現的故事 在眼中上映
將兩人結合的棱鏡
你在哪裡 看見這片相同的天空?
我想傳達給你 永恆(Eternal)


幾千星座 隨季轉變
長大成人 我們距離變化
不知何時 無法傳達的言語
從胸中 漸漸離去

回憶中的地方 不曾改變
將遺忘的夢想 描繪出來

你那時所告訴我的光芒
是將兩人結合的棱鏡
那個願望 將傳達到天空的彼方
引導著命運


閃耀的光輝落下的夜晚
將兩人的思緒包圍

那願望實現的故事 在眼中上映
將兩人結合的棱鏡
你在哪裡 看見這片相同的天空?
我想傳達給你 永恆(Eternal)


簡介:
最重要的人,最遙遠的存在。幼小的心靈裡面,萌生的淡淡的愛意。他們有個願望,卻沒能說出口。在此分離,將希望寄託於兩人約定的那顆星星,以及,未來的可能性。

山丘:觀景台
棱鏡:望遠鏡
光芒:七夕星
0

星空的回憶

| 2/13/2010
Japanese Game "星空のメモリア" 夢ED



仰望著那片不變的天空
依然憶起舊時所言的話語
只在那短暫的瞬間 指尖中所傳出
在那遙遠的過去 萌生的 戀情

即便分離 我依然堅信
就猶如這片閃耀的星空般
經過長如永恆的時光 依然不會改變
這股想要傳達給你的思念

我們以交互的雙手 立下了約定
這段感情 絕對會再度相合
尋找著現而即逝 卻又無可替代
耀眼動人的 Shooting Star

閉上雙眼 那臉龐所照映著
是你的身影
捧著隨時都會凋零的花朵
期待著這將會是永恆

懷念的清風之中
回憶就如同他在耳邊的細語
毫無改變的那抹微笑
以及想要傳達給你的思念

不曾忘記 你說過的那一切
那個夜晚 緩緩流動的星河
宛如在夜空中跑動的一道彩虹
架起了橋樑 延續到你的身邊

我想要守護著你的幸福
即便是在虛幻的夢境之中

總有一刻 我們將會重逢
宣洩出來的愛意就在這片佈滿繁星的夜空之下
永遠在心中懷抱著對你的思念

即便分離 也一定會實現
就猶如這片閃耀的星空般
經過長如永恆的時光 依然不會改變
這股想要傳達給你的思念

我們以交互的雙手 立下了約定
這段感情 絕對會再度相合
尋找著現而即逝 卻又無可替代
耀眼動人的 Shooting Star
0

Flash 再度掀起話題(←台灣記者風)

| 2/01/2010
2007 年,一款特別的手機上市了!iPhone 成功了改變了市場上對智慧型手機的定義。它帶著今天已經是智慧型手機的「標準」介面跟特殊的輸入方式與大家見面,打著即使你在手機上面也可以體驗到「完整的」網際網路體驗,可是!這個完整的體驗卻是不包含 Flash 在內的。依照 iPhone 的性能上來看,要在上面實現 Flash 並非不可能。但是不論 Adobe 怎麼對 Apple 叩關,這到防線到了 2010 年的今天,卻依然沒有著落。而 iPad 的發表也就再度引燃了許多人對 Apple 的臆測跟批評!他們再度將 Adobe Flash 拒之於門外!

網路多媒體這個詞,多年來總是離不開 Flash 的支配。他不只提供了網頁介面上面多元化的媒體資訊,更是提供了與使用者互動的平台。即便是微軟的 Silverlight 也沒有辦法撼動這個地位。但是我們還是仔細來瞧瞧這個 Flash。除了少數像是 Youtube 這種影片分享的網站、遊戲、電影等等的介紹頁,絕大多數都是應用在一些讓人看了就心煩的廣告上面。好吧,姑且不去在意這些屬於應用層面的東西好了。

誠如上面所說的,的確 Flash 已經獨霸網頁媒體載具的身分很多年,但是它卻沒有離開一個堆積以久的老問題:「效能」。不論是 Macromedia 時代,到了今天 Adobe 將之納入為己用、這兩年他們想要發展的 AIR 平台,Flash 應用程式始終大幅度佔用了處理器的資源。那小小看似沒什麼大不了的 Banner,想不到它在背後裡面浪費了多少不必要的時間呢?提個最有名氣的例子,就是 Facebook 上面那些以 Flash 作為開發平台的小遊戲。簡單的介面跟遊戲規則,豐富的想像力跟可愛的造型,外加上你永遠解決不了的緩慢操作速度跟反應。眼下大家都在為了 iPhone,iPad 不支援 Flash 的事情喧鬧,殊不知這些東西是很容易要了行動產品的命門:「續航力」。

上述是屬於 Flash 的缺點,但是其實也不是缺少解決的方案。要在網頁上面內嵌影片,又不要使用到 Flash、更需要有良性的使用者互動、效能,去年拍板定案的 HTML5 事實上就考量到了這一層。眼下在行動裝置平台最熱門的就屬於 WebKit 的瀏覽器,這些都是完全支援 HTML5 的規範,支援著影片、繪圖區等等的新玩意。雖然這樣說來聽起來有點像是為蘋果的決策找藉口,但是肯定不久之後, HTML5 變成大家撰寫網頁的基準,又會有誰想要去用那些額外的 Plugin 來降低自己製作網頁的相容性呢?現在眼下 Youtube 也開始加緊腳步進入 HTML5 的測試,相信不久之後,它就會變成 Youtube 使用的選項之一,甚至針對使用者的瀏覽器判斷,成為預設的也說不一定。雖然不能說會變得完全不需要 Flash 的使用,但是一定程度上還是大幅度的降低對於 Flash 的依賴度。如果說 Flash 能解決掉佔用太大的資源固然事件好事情,但是我在這裡也是樂見 HTML5 其成!

Ehrippura
0

又一你不可不知道微軟神秘現象

| 1/16/2010
最近發現一件微軟的神秘設計。超級大 Bug!明顯到一個不可思議的程度。感謝我不認識的 Armlor 網友發現問題。

自從 XP 開始,大家可以針對每個資料夾裡面的內容作不同的顯示設定。好比如說,你 A 資料夾裡面放的是音樂、B 資料夾裡面是文件,這個時候你可以讓 A 資料夾檔案顯示的是音樂的長度、演唱者;而 B 資料夾則是作者、修改時間。不同的顯示方式讓你閱讀資料夾有很大的幫助。

可是,很讓人不感到意外的是(或者說是預料中事),微軟達到這個功能的實作方式是將每個資料夾的設定寫入註冊表裡面。這種作法跟現在很多作業系統的做法有很大的出入。當然這裡不是要談論誰是誰非的問題,而是如果你把所有的資訊寫入註冊表裡面,該註冊表就會被占用到很大一塊空間。很明顯的,如果我每次開啟資料夾的時候,他都得要去搜尋這個資料夾的顯示設定,當設定變的很龐大的時候就會拖垮系統的速度。那,微軟究竟是用什麼方式來解決這個問題呢?答案非常的一般,限制最大資料夾數,變數名稱是 BagMRU Size。說起來微軟好像很喜歡用限制數量的作法來達到各種目的,像是同時最大連線數 10 人的問題。

在 XP 時代,考量到當時系統效能的不足(我不懂為什麼不在個別資料夾放入設定檔,這種作法也不會花費到多少的空間),這個變數被設定在 400。雖然說我們可以透過修改註冊表的方式來改變這個變數,但是基本上考量到效能之外,依然是設定了一個上限。當你所使用的資料夾大於這個上限值的時候,系統就會根據一個不知道什麼的方法開始刪除掉某些資料夾的設定,並且用新的資料夾設定來取代他。至於這個不知名的辦法我實在無法理解是什麼。他寧可不去刪除已經不存在的資料夾的設定,也非要刪除掉我常用資料夾的設定。就這個樣子,我的資料夾顯示設定老是會自動跑掉,重新設定的動作煩不甚煩。尤其令人感到弔詭的是,微軟號稱這個數值在 Vista 的時候已經修改成 5000 了!5000 是多少?起碼對一般使用者來說是個足夠的數字了,但是顯示設定跑位的現象卻不見減少,反而有增加的趨勢!

探討這個問題,當然就是直接對 BagMRU Size 這個變數下手,我們找到了這個變數的位置 HKCU\Software\Microsoft\Windows\Shell 下面,很明顯的,確實有這個變數,且數值是設定在 5000 沒錯。既然這個數字設定的好好的,為什麼就是沒有用呢?Armlor 網友給了我們解答。他使用了 Process Monitor 去檢驗了 explorer.exe 程序,發現到在 Vista 下面,當你開啟資料夾的時候,他居然是在 HKCU\Software\Classes\Local Settings\Software\Microsoft\Windows\Shell 資料夾下面檢察 BagMRU Size 變數,而不是上述的資料夾。簡單來說,Vista 裡面explorer.exe 寫入註冊表的地方是個新位置,卻沒有把變數設定在這個新的場所,反而是沿用了原先 XP 時代的註冊表設定(這時候我真想說,蘋果,你每次酸註冊表設計很爛是對的)。所以說每次當他尋找不到變數的時候,自然就以某個既定的預設大小來取代。至於這個大小是多少?不知道,總之很低就是了。

既然這樣,那解決辦法就很明顯了,只要在後面所述的位置加入一個名為BagMRU Size 的 DWORD 變數,並且設定成 5000、或者是一個大一點的數字就可以了。說真的,這個 Bug 還真讓人感到有點哭笑不得…超低級。

如果是 64bit 的作業系統,因為它同時會包含 explorer.exe,32bit 與 64bit 各一份,所以說還有另外一個位置需要修改 HKCU\Software\Classes\Wow6432Node\Local Settings\Software\Microsoft\Windows\Shell。話雖如此,我覺得這兩個位置的變數好像是同步的,有點怪怪,因為不太可能。不管怎樣,如果說你在這個資料夾下面沒有發現這個變數的話,那就自己新增一個吧!
0

雖然快過了一個月了

| 1/14/2010
新的一年的第一篇文章,首先當然還是要說一聲新年快樂了。不過因為我們會過兩次新年的關係,一月1日反而顯得沒有這麼重要。(喔,這絕對跟錢沒什麼關係)

新的年頭,自然就會開始期待今年會有什麼新的產品會面世呢?果然到頭來還是很在意這個問題。無可厚非。作為一個科技人,消費者,嘗鮮…喔,不,這個還是交給別人來做好了。首先是第一個月份,第一個上檔的是 Google 自家品牌手機 nexus one。這支新的手機由 hTC 設計、生產,並且搭載了最新版本的 Android 系統,高檔的硬體儼然成為了眾家 Android 手機的龍頭!從 Google 的角度來說,他們並不希望這支手機成為真正「高檔」的行動裝置,反而是希望其他的手機廠商能以這支手機作為低標來設計。挾帶著 Google 超人的名氣出貨的這支手機到底有什麼樣特殊的地方?我覺得最重要的當然還是 Google 自家的軟體更新的差別。不少手機廠商雖然說有很強的硬體設計經驗,但是對於軟體這塊的經營並沒有下太大的功夫。所以說如果選擇如 Google 這種公司帶著自我品牌,或許會更有利於如韌體更新之類的動作。話雖這麼說,但是我們看到了 nexus one 銷售至今頭兩週的表現,20000 的成績甚至不能說是差強人意,可以用淒慘無比來形容了。就算我們沒有拿出 iPhone 來做比較,他也輸了其他如 Motorola Droid 之類的手機頭週銷售量一大截。這個 iPhone 殺手(不知道有多少手機被冠上這個名字?,從 iPhone 銷售前就出的 hTC touch 開始大家都這樣說) 看起來還有不少的路要走。當然就我的看法,其實這個銷售量也反映了人們在購買手機的時候的習慣。nexus one 不知道為什麼只有網路購買這一個途徑,售後服務也全部都是在網路上面解決。這個被動的行銷方式似乎造成了許多使用者對於購買 nexus one 保持著觀望的態度。

毫無疑問的接下來第二檔次的好戲又回到了 Apple 身上了。從去年中就開始吵得沸沸揚揚的平板電腦似乎就會在這個月底發表。當然蘋果依然保持著他們慣有的無言態度,但是網路上早就已經片佈各種不同的傳聞。從外觀、操作、硬體介面等等各式各樣都有。但是卻缺少了一種肯定性。雖然我很想知道蘋果到底會祭出什麼樣的產品;又有什麼樣的應用在上面。不過我最想要的東西當然還是新款的 Macbook Pro 了。這樣也好讓我的舊 PowerBook G4 退役。

後面還有令人期待的 nVIDIA GeForce 300 系列的顯示卡。當然,2010 年才剛剛開始!OK,接下來就談談自己的東西吧。最近開始在電腦裡面玩起了虛擬機器。說真的,沒去玩還不知道原來 這幾年的 x86 虛擬化技術有這麼大的變化。我一直把這些 VT 當成一種東西,只要有支援就好了。結果證明我的想法單純到有點白癡的境界。我覺得比較重要的是 MMU 根 IOMMU 兩種。前者是將系統不透過模擬的方式直接接觸硬體的指令,後者是 IO 指令不經過模擬。這兩種東西帶來了以前全部軟體模擬時代沒有的效能。看來我的 E8500 沒有 IOMMU 真的有點殘念。可是,當自己在虛擬機器上面安裝好 XP Professional、Ubuntu,就在啟動的那一瞬間,整個就是有感動到!當你的作業系統跑起了另外一套作業系統,你還不需要去做重新分配硬碟空間的動作、又不用擔心現在的系統會不會有影響,這種感覺真是不錯。也就是說,在這兩台機器上面安裝虛擬機器最大的用途當然是拿來做程式設計了。好在我這次重灌 Vista 之後,並沒有安裝任何的開發工具在電腦裡面。我總是覺得這些公具老是會把電腦搞得亂糟糟的。有時候甚至容易出問題。當你安裝了在虛擬機器的系統上面,喔,一切變得簡單多了。

當然,最令人在意的事情當然還是在效能上面。只要記憶體不要給得太少,在操作上面可以保持著不錯的效能。這當然不能拿來說,你要跑遊戲,跑遊戲還是在本機上面跑會比較好。輕度的 3D 繪圖能力是可以的,稍微重一點就得屈就於那緩慢的速度上面了。不過,就算是輕度的遊戲,看似絕大多數時間都還順暢,偶爾還是會有不明的小停頓現象出現。如果你在遊戲裡面開全螢幕,這個問題發生的頻率會減少很多,但是還是會出現(雖然以前就已經有聽說過全螢幕的效能遠高過視窗模式,但是沒想到這麼明顯)。看來現時間點,安裝 Linux + Windows 虛擬機來跑遊戲的夢想恐怕還是有一點遙遠呢。
Ehrippura