第四章 交易市場與網路
4.3 遊戲音樂
大部份撥放遊戲背景音樂有三種方式 l WAVE 格式
l CD 音軌 l MIDI
我們在遊戲中使用 MIDI,MIDI 是 Musical Instrument Digital Interface 的簡稱。MIDI 不必像 Wave 檔那樣做聲音抽樣,所以檔案不 大但可以長時間演奏。MIDI 是一種只紀錄演奏演奏相關資訊的檔案(像 樂譜),音樂本身並沒有存放在檔案中,所以再撥放時需要【相當於樂 器的裝置】。
在 CPU 不像現在一樣強大時,需要 MIDI 音源器才能撥放 MIDI,現 在的電腦都支援軟體 MIDI,直接使用 CPU 模擬出 MIDI 的聲音。
MIDI 缺點在使用軟體音源時,CPU 負荷高,且無法撥放音源能力 以上的聲音。超過音源的情況,一是無法放入【人聲】,二是同時最大 發音數不夠的情況下,可能有些音色聲音會不見,也就是,製作好的 音樂無法在使用者電腦上 100%呈現。
遊戲中撥放背景音樂方式就是循環撥放,直到遊戲結束。
我們是使用 Windows API 的 MCI(Multimedia Control Interface) 來演奏 MIDI Data。MCI 是 Windows 用來操作多媒體的工具,還可以操 控掃描器、CD、撥放動畫裝置、WAVE、VCR……。
MCI 的控制方法有兩種,Commend String 或 Command Message。
l Commend String 是把 MCI 指令寫成字串再丟出去。
l Command Message 以訊息和參數來組成必要的指令,控制方式較靈 活。
我們使用 mciSendCommand 函數把指令送到 MCI,函數根據欲傳送 的指令選用不同的旗標種類和參數類型。
MCIERROR mciSemdCommand(
MCIDEVICIED devicdID, //指定開啟裝置時所得到的裝置 ID UNIT uMessage, //指定傳送的訊息 midi.Open();
歌曲結束
midi.MciNotify();
停止 midi.Stop();
結束
midi.Close();
撥放音樂 midi.Play();
額外動作 重播音樂
midi.Replay();
choose1
Choose2
YES
NO
我們把使用到的相關函式包裝成一個類別好方便使用。類別定義 如下:
class CMidi { public:
CMidi(): Wnd(0), Id(0) {}
~CMidi() { Close(); }
BOOL Open(CWnd *wnd); //開啟裝置 BOOL Close(); //關閉裝置
BOOL Play(const char *name); //指定撥放的檔案 BOOL Replay(); //重覆撥放
BOOL Stop(); //停止
BOOL MciNotify(DWORD id); //音樂撥放完畢時,接受 //呼叫的函式
protected:
CWnd *Wnd;
DWORD Id; //MCI裝置 Id
} ;
Member Function Member Variables Open() CWnd *Wnd
Close() DWORD Id Play()
Replay() Stop() MciNotify()
圖 4.10 Cmidi 類別圖
了解界面後,接下來看看實作部分。
// 開啟 Midi
BOOL CMidi::Open(CWnd *wnd) { Wnd = wnd;
return TRUE;
}
// 關閉
BOOL CMidi::Close() { return TRUE;
}
// 撥放
BOOL CMidi::Play(const char *name) { DWORD err;
MCI_OPEN_PARMS open; // MCI_OPEN_PARMS結構體物件 // MIDI裝置型態是 sequencer
open.lpstrDeviceType = "sequencer"; open.lpstr ElementName = "itdontrg.mid"; // 初始檔案名稱
if ((err = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYP E|MCI_OPEN_ELEMENT|MCI_WAIT, (DWORD)&open)) != 0)
{ char errstr[256];
mciGetErrorString(err, errstr, sizeof(errst r));
TRACE1("%s\n", errstr);
Wnd->MessageBox(errstr);
return FALSE;
} // 當無法取得裝置時顯示錯誤訊息對話框 Id = open.wDeviceID; // 取得裝置 Id MCI_PLAY_PARMS play;
// 結束撥放時,MM_MCINOTIFY 訊息會被送到
//MCI_PLAY_PARMS結構體的 dwCallback 成員所指定的視窗 play.dwCallback = (DWORD)Wnd->GetSafeHwnd();
if (mciSendCommand(Id, MCI_PLAY, MCI_NOTIFY, (DWORD)
&play))
Id = 0;
return FALSE;
}
return TRUE;
}
// 從頭播放讀進的 Midi 檔案 BOOL CMidi::Replay() { MCI_PLAY_PARMS play;
//為達到重覆撥放,程式必須能夠接受 MM_MCINOTIFY 訊息 //函式的呼叫方式就是就是傳遞 MCI_PLAY 給裝置叫他開始撥放 // MCI_SEEK_TO_START演奏開始位置移動到 Data 最前面 // MCI_WAIT 結束撥放後傳回控制
// 下一次撥放結束時 MCI_NOTIFY 會通知程式
play.dwCallback = (DWORD)Wnd->GetSafeHwnd();
if (mciSendCommand(Id, MCI_SEEK,MCI_SEEK_TO_START|
MCI_WAIT, 0)
|| mciSendCommand(Id, MCI_PLAY, MCI_NOTIFY, (DW ORD)&play)) {
mciSendCommand(Id, MCI_CLOSE, MCI_WAIT, 0);
Id = 0;
}
return TRUE;
}
// 播放完畢
BOOL CMidi::Stop() { UINT id = Id;
if (Id == 0) return FALSE;
Id = 0;
DWORD err;
0)) != 0
|| (err = mciSendCommand(id, MCI_CLOSE, MCI_WAI T, 0)) != 0) { return FALSE; }
return TRUE;
}
// 播放完畢時,接受視窗呼叫的函式 BOOL CMidi::MciNotify(DWORD id) { if (Id != id)
return FALSE;
return TRUE;
}
至於音樂的來源,我們是抓別人的 midi sample,然後取樣,再以 音樂編輯軟體 remix 後輸出新的 midi。
軟體名稱:cakewalk SONAR 介面如下:
圖 4.11 SONAR 編輯音樂