小節 4.8播放音樂Playing Music
Although background
music isn't found in every game you play, it can play an important role in a
game. Music can set the mood—for example, an action game could play fast-paced
music, while slower music might be better suited for a game that requires more
thought.
譯:雖然并不是每個游戲都有背景音樂,但背景音樂在游戲中的作用非常重要。音樂可以調動情緒,如動作游戲可以播放快節奏的背景音樂而智力游戲更適合慢節奏的背景音樂。
Also, music can
change the player's emotions toward elements of the game. Is the music happy?
Or is it dramatic? Happy music might be better suited for the easier levels,
such as the first few levels of a game. Dramatic music could be played for the
more difficult levels, such as when the player is fighting a powerful bad guy.
譯:此外,音樂還可以改變玩家對游戲元素的情感,音樂是歡快的嗎?是激烈的嗎?輕松歡快的音樂更適合簡單的關卡,如游戲的前幾個關卡。而激烈的音樂可以再高難度關卡播放,如當玩家正在和強大的壞蛋戰斗時。
When you've decided
on the type of music you want, the next step is to figure out where the music
comes from. Nope, it doesn't come from those voices in your head. Instead,
games typically play music in one of three ways:
譯:當你已經確定想要的音樂類型以后,下一步就要解決的是音樂的來源。不要懷疑,絕不是來自你腦袋里想到的聲音。游戲通常使用以下三種音樂中的一種:
·
Streaming
music from an audio track on a CD // 來自光盤音頻磁道中的音樂流;
·
Playing
compressed music such as MP3 or Ogg Vorbis
//播放MP3 或Ogg Vorbis之類的被壓縮過的音樂;
·
Playing
MIDI music // 播放MIDI(迷笛)音樂。
播放CD音頻 Playing CD Audio
Some CD-ROM games
have plain old Red Book Audio (the standard audio CD format) right on the CD.
The benefit here is you get great-quality sound and it's easy to implement:
Just tell the sound card to start playing the CD, without any other involvement
from the game. The other cool aspect is that players can slip the game CD into
their audio CD player and groove to the game's tracks.
譯:一些 CD-ROM游戲(光盤游戲)光盤中包含了符合紅皮書標準(標準CD音頻格式)的舊式音頻,好處是音樂的音質好,而且容易實現:只要讓聲卡開始播放光盤,而不必讓游戲參與任何工作。另一個好處是玩家可以將游戲光盤放進音頻光盤播放器中,刻錄游戲的音軌。
Unfortunately, CD
audio takes up a lot of space, typically around 30MB for a three-minute song.
If you have four three-minute songs, that's 120MB of space that could be used
for more graphics or bigger levels. In addition, the Java Sound implementation
doesn't support playback from a CD, so this option is out for you.
譯:但是,CD音頻占用空間比較大,通常時長三分鐘的歌曲要39 MB。如果游戲中需要使用四個時長三分鐘的歌曲,則會占用120MB的空間,而這些被占用的空間本來可以用于更多的圖形和較大的游戲關卡。另外。Java Sound實現不支持播放CD音頻,因此無法選擇CD音頻。
播放MP3與Vorbis / Playing MP3 and Ogg Vorbis
The second option is
compressed music. MP3 and Ogg Vorbis formats are much smaller than that for CD
audio, typically around 3MB for a three-minute song, and have near-CD quality.
They've become increasingly more popular in games.
譯:第二個選項是壓縮音樂。MP3和 Ogg
Vorbis格式比CD音頻小的多,通常三分鐘歌曲只有3MB左右,而且音質接近CD,在游戲中越來越普及。
The drawback here is
that decoding MP3 or Ogg Vorbis files takes quite a chunk of processor power.
Sound cards can't play compressed music directly, so the music is decoded while
it's played. This means the extra processor use can interfere with other parts
of your game. On faster, modern machines, the decoding won't be noticeable, but
on slower, older machines, the decoding could take 20% to 40% of the processor
or more. That could make other parts of the game, such as animation, seem slow
or jerky.
譯:壓縮音樂的缺點在于編碼 MP3和Ogg Vorbis文件需要占用大量處理器資源。聲卡不能直接播放壓縮音樂,因此要邊播放邊解碼。過多的處理器資源被占用可能會影響游戲的其他部分的運行。在現代高速計算機上,解碼的影響并不明顯,但在老式低速計算機上,解碼占用處理器資源高達的20%到40%,甚至更多。這就有可能使游戲其他部分看起來運行緩慢或閃爍,比如動畫。
If the processor
time doesn't matter to your game, you'll need to get an MP3 or Ogg Vorbis Java
decoder. Java Zoom, at www.javazoom.net, has both. This site provides a plug-in
through Java's Service Provider Interface that enables you to get an AudioInputStream that decodes an MP3 or Ogg Vorbis stream.
Just include the necessary .jar file, and be sure to convert the AudioInputStream, like so:
譯:如果對你的游戲來說處理器不是問題,那么你還需要獲得 MP3和Ogg Vorbis的解碼器。在java Zoom網站上(網址 www.javazoom.net)你可以獲得這兩個解碼器。該站點提供基于Java's Service Provider Interface 編寫的插件,通過它可以獲得解碼MP3或OGG Vorbis流得到的AudioInputStream 。你只需要將必要的.Jar文件包含到你的應用中,并確保將AudioInputStream進行如下轉換:
名詞解釋:Service Provider Interface(服務提供商接口),滿足某種服務標準的供應商提供的符合該標準的應用程序接口,SPI應該和該服務的API標準是兼容的,應用程序一般應該是基于API編寫,除非是SPI中包含API中沒有提供的功能而又必須使用。
// create the format used for playback
// (uncompressed, 44100Hz, 16-bit, mono, signed)
//創建播放采用的格式( 未壓縮,44100Hz,16位,單聲道,帶符號,以little-endian字節順序存儲單個樣本中的數據)
AudioFormat playbackFormat =
new AudioFormat(44100, 16, 1, true, false);
// open the source file //打開源文件
AudioInputStream source =
AudioSystem.getAudioInputStream(new File("music.ogg"));
// convert to playback format //轉換成變成播放格式
source = AudioSystem.getAudioInputStream(playbackFormat, source);
Also, be sure not to
load the samples into memory. A compressed sound file might take up only 1MB
for each minute of music, but the uncompressed samples would take 10MB for each
minute. Instead, play any large sounds directly from the AudioInputStream, which streams the sound from disk.
譯:壓縮聲音文件每分鐘可能只有1MB,但解壓縮后的樣本則可能高達10MB。因此,一定不要把樣本裝載到內存中。相反,我們直接通過 AudioInputStream播放從磁盤打開的大體積聲音。
But which one should
your game use, MP3 or Ogg Vorbis? Although MP3 is incredibly popular, Ogg
Vorbis is license-free and claims better sound quality. The people playing your
game won't notice or care, so go with whichever one suits your needs better. You
can find more information on Ogg Vorbis at www.xiph.org/ogg/vorbis.
Also, be sure to look into MP3 licensing issues at www.mp3licensing.com.
譯:那么我們在游戲中選MP3還是Ogg
Vorbis呢?盡管MP3已經越來越普及,但是Ogg
Vorbis是無需可證的,而且音質更好。由于玩家不在乎音樂的格式,所以你可以選擇更符合游戲需求的。你可以在www.xiph.org/ogg/vorbis 找到關于Ogg
vorbis 的更多信息。在www.mp3licensing.com可以了解MP3許可證的信息。
播放MIDI音樂 Playing MIDI Music
Finally, there's
譯:最后我們來了解MIDI音樂。MIDI音樂不是樣本音樂那樣的聲音格式,而是類似與作曲家的樂譜,通過指令指示樂器播放什么音符。音頻系統會根據音調,樂器,音高和其他參數平,合成每個音符。
Because MIDI files
contain instructions instead of samples,
譯:因為MIDI文件包含指令而不是樣本,因此比采樣聲音格式小的多,其大小通常都是以KB單位,而不是MB。
Because the music is
synthesized, the quality might not be as high as that of sampled music. Some
instruments won't sound realistic, or the music might sound a little too
mechanical. A creative musician can usually mask the deficiencies of
譯:因為MIDI音樂是人工合成的,通常它的音質不如采樣音樂。一些樂器聽起來不夠逼真,可能聽起來有點兒過于機械。然而又創造力的音樂家通常能夠掩蓋MIDI音樂的缺陷。
The Java Sound API
synthesizes
譯:Java
Sound API 通過聲音樂隊(sound bank 程序模擬的一組樂器)合成MIDI音樂。不幸的是雖然Java SDK中包含聲音樂隊,而Java運行時環境卻不包含。如果找不到聲音樂隊,則會使用硬件MIDI端口,它的定時功能不穩定。所以,建議使用聲音樂隊。
The Java SDK
includes a minimal-quality soundbank, and you can download higher-quality
soundbanks from http://java.sun.com/products/java-media/sound/soundbanks.html
and include them with your game.
譯:Java SDK中的聲音樂隊效果很差,你可以到http://java.sun.com/products/java-media/sound/soundbanks.html下載高質量的聲音樂隊并將其包含在你的游戲中。
The Java Sound API
provides
譯:Java Sound API 在java.soundbaks.midi包中提供了MIDI聲音功能。要播放MIDI樂音,需要這個包中的2個對象:Sequence和Sequencer。Sequence對象包含MIDI數據。而Sequencer能將一個Sequence發送到MIDI合成器中。下面是播放MIDI音樂的例子:
// open the midi file // 打開midi文件
Sequence sequence = MidiSystem.getSequence(new File(filename));
// open the sequencer //打打開序例化器
Sequencer sequencer = MidiSystem.getSequencer();
sequencer.open();
// play the midi sequence //播放midi序例
sequencer.setSequence(sequence);
sequencer.start();
By default, the Sequencer plays a Sequence once and
then stops. But in a game, you usually want to loop the music. To loop a Sequence, you need to be notified when the music is done
playing and start the Sequencer again.
譯:默認情況下,Sequencer播放Sequence一次,然后停止。但游戲中通常需要循環播放音樂。要循環Sequence,就要在音樂播放完畢時重新啟動Sequencer。
An example of how to
loop music is in the MidiPlayer class in Listing
4.11. It implements the MetaEventListener interface, which notifies the class when
the Sequence is done playing via the end-of-track
message.
譯:清單4.11的MidiPlayer類是循環播放音樂的例子。它實現MetaEventListener接口,通過軌道末尾消息通知音樂播放已經完畢了。
清單 Listing 4.11 MidiPlayer.java
package com.brackeen.javagamebook.sound;
import java.io.File;
import java.io.IOException;
import javax.sound.midi.*;
public class MidiPlayer implements MetaEventListener {
// Midi meta event //Midi事件
public static final int END_OF_TRACK_MESSAGE = 47;
private Sequencer sequencer;
private boolean loop;
private boolean paused;
/**
Creates a new MidiPlayer object.//創建一個新的MidiPlayer對象
*/
public MidiPlayer() {
try {
sequencer = MidiSystem.getSequencer();
sequencer.open();
sequencer.addMetaEventListener(this);
}
catch ( MidiUnavailableException ex) {
sequencer = null;
}
}
/**
Loads a sequence from the file system. Returns null if
an error occurs. 從文件系統中載入序例,當發生錯誤時返回null
*/
public Sequence getSequence(String filename) {
try {
return MidiSystem.getSequence(new File(filename));
}
catch (InvalidMidiDataException ex) {
ex.printStackTrace();
return null;
}
catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
/**
Plays a sequence, optionally looping. This method returns
immediately. The sequence is not played if it is invalid.
播放sequence ,可以循環或不循環。這個方法立即返回。不可用的sequence將不會被播放
*/
public void play(Sequence sequence, boolean loop) {
if (sequencer != null && sequence != null) {
try {
sequencer.setSequence(sequence);
sequencer.start();
this.loop = loop;
}
catch (InvalidMidiDataException ex) {
ex.printStackTrace();
}
}
}
/**
This method is called by the sound system when a meta
event occurs. In this case, when the end-of-track meta
event is received, the sequence is restarted if
looping is on.
發生元事件時,這個方法將被聲音系統調用。當收到軌道末尾元事件時,如果處于循環播放模式,則sequence重新啟動。
*/
public void meta(MetaMessage event) {
if (event.getType() == END_OF_TRACK_MESSAGE) {
if (sequencer != null && sequencer.isOpen() && loop) {
sequencer.start();
}
}
}
/**
停止sequencer,將其復位為0
Stops the sequencer and resets its position to 0.
*/
public void stop() {
if (sequencer != null && sequencer.isOpen()) {
sequencer.stop();
sequencer.setMicrosecondPosition(0);
}
}
/**
Closes the sequencer. //關閉sequencer
*/
public void close() {
if (sequencer != null && sequencer.isOpen()) {
sequencer.close();
}
}
/**
Gets the sequencer. //獲得sequencer
*/
public Sequencer getSequencer() {
return sequencer;
}
/**
Sets the paused state. Music may not immediately pause.
設置暫停狀態,音樂不一定立即停止
*/
public void setPaused(boolean paused) {
if (this.paused != paused && sequencer != null) {
this.paused = paused;
if (paused) {
sequencer.stop();
}
else {
sequencer.start();
}
}
}
/**
Returns the paused state. //返回暫停狀態
*/
public boolean isPaused() {
return paused;
}
}
MidiPlayer also provides methods for opening Sequences and pausing the music. To pause, MidiPlayer calls the stop() method of the Sequencer object, which stops the Sequence without resetting its position. Calling start() resumes the paused sequence. All this happens in MidiPlayer's setPaused() method.
譯:MidiPlayer還提供打開Sequence
和暫停播放音樂的方法。要暫停音樂,MidiPlayer調用Sequencer對象的stop()方法,在不復位的情況下停止播放Sequence 。調用Start()方法則繼續播放暫停的sequence 。這一切都在MidiPlayer的setPaused()方法中實現。
創建自適應的音樂Creating Adaptive Music
Now that you can
play
譯:現在你能夠播放MIDI音樂了,接著讓我們來討論一下MIDI音樂的一個優點:很容易實現自適應的音樂。自適應音樂是根據游戲狀態自動改變的音樂。如玩家正在與大量的敵人戰斗,則音樂可能快速而激烈,相反,當玩家單獨在房屋中搜索時,音樂聲會非常小。
The change in music
could happen at any time—for example, the player could be strolling along one
second, and then 100 robots could be trying to kill him the next. So, changing
the music smoothly can be a challenge.
譯:音樂的改變可能隨時發生,例如玩家前一秒可能還沒有遇到一個敵人,后一秒突然有100個機器人要殺他。因此,想要平滑地改變音樂是一個很大的挑戰。
You can adapt songs
to the game state in two ways:
譯:可以用2種方法在游戲狀態中適配音樂:
·
Change
songs 改變歌曲;
·
Modify
the song currently playing 修改當前播放的歌曲。
Because the actions
of a player can change at any time, changing songs is a more difficult task.
The change can't be abrupt, or it will be distracting. Songs can be designed so
that they have "change points" to signify places where a song change
can occur.
譯:由于玩家的動作可能隨時改變,因此實時改變歌曲是一個非常困難的任務。音樂的改變不能太突然,否則會分散注意力。歌曲可以被設計包含一些 “變化點”,以指示一首歌曲在哪里可以發生改變。
Also, songs need to
transition smoothly. To do this, the first song can fade out while the next
song is fading in. Also, while the first song is fading out, its tempo could
change to match the tempo of the next song.
譯:歌曲需要平滑地過度。為此,當切換音樂時可以讓前一首歌逐漸退出,后一首歌逐漸進入。另外,在前一首歌逐漸退出時,其速度可以漸變成后一首歌的速度。
Or, you could just
take the easy way out and insert a sound of a scratching phonograph needle。
譯:或者,你可以簡單地取出和插入一個聲音的一個刻痕留聲機針。(這句翻不成,在網上翻譯的,高手幫著看看)
The second option is
to simply modify the exiting song. You can do this by changing the tempo or
volume, or adding another instrument to it.
譯:第二個選擇是簡單地修正在退出的歌曲。你可以改變歌曲的速度或音量,或添加另一個樂器到歌曲中。
Adding or taking
away an instrument is easy to do with
譯:MIDI sequence很容易添加和移除樂器。MIDI音樂通常由多音軌組成,每一個音軌演奏特定的樂器。例如,可以有一個吉他音軌,一個鍵盤音軌等。
You can mute or
unmute a track with one method:
譯:可以調用一個方法開啟或關閉某個音軌的聲音:
sequencer.setTrackMute(trackNum, true);
Here, trackNum is an integer representing the track number you want
to mute. If you don't know what track belongs to what instrument in your
譯:其中參數trackNum是整型,表示要靜音的音軌號。如果不知道MIDI文件中那個音軌屬與那個樂器,你可以將所有音軌一個一個地靜音,來通過實驗辨別。
Listing 4.12 MidiTest.java
import java.io.File;
import java.io.IOException;
import javax.sound.midi.*;
import com.brackeen.javagamebook.sound.MidiPlayer;
/**
An example that plays a Midi sequence. First, the sequence
is played once with track 1 turned off. Then the sequence is
played once with track 1 turned on. Track 1 is the drum track
in the example midi file.
一個播放 MIDI sequence 的例子。首先,sequence 在音軌1被關閉的情況下被播放了一次。然后,sequence 在音軌1被打開的情況下被播放了一次。音軌1是樣例文件的鼓樂音軌。
*/
public class MidiTest implements MetaEventListener {
// The drum track in the example Midi file 鼓樂音軌
private static final int DRUM_TRACK = 1;
public static void main(String[] args) {
new MidiTest().run();
}
private MidiPlayer player;
public void run() {
player = new MidiPlayer();
// load a sequence 加載 sequence
Sequence sequence =
player.getSequence("../sounds/music.midi");
// play the sequence 播放 sequence
player.play(sequence, true);
// turn off the drums 關閉 鼓
System.out.println("Playing (without drums)...");
Sequencer sequencer = player.getSequencer();
sequencer.setTrackMute(DRUM_TRACK, true);
sequencer.addMetaEventListener(this);
}
/**
This method is called by the sound system when a meta
event occurs. In this case, when the end-of-track meta
event is received, the drum track is turned on.
當一個元事件發生時聲音系統會調用該方法。當收到音軌末尾元事件,鼓樂音軌被打開。
*/
public void meta(MetaMessage event) {
if (event.getType() == MidiPlayer.END_OF_TRACK_MESSAGE) {
Sequencer sequencer = player.getSequencer();
if (sequencer.getTrackMute(DRUM_TRACK)) {
// turn on the drum track 打開鼓樂音軌
System.out.println("Turning on drums...");
sequencer.setTrackMute(DRUM_TRACK, false);
}
else {
// close the sequencer and exit 關閉 sequencer 并退出虛擬機
System.out.println("Exiting...");
player.close();
System.exit(0);
}
}
}
}
Just like when you
play sampled sound, you must explicitly exit the VM when you're done. MidiTest exits the VM when it receives the second end-of-track
message.
譯:就像你播放樣本音樂一樣,你必須在播放完畢時明確地退出虛擬機。MidiTest 在收到第二個音軌末尾消息時退出虛擬機。
總結Summary
In this chapter, you
learned the basics of sound, sound filters, and music. You made real-time echo
and psuedo-3D filters, and you created a cool sound manager to handle game
sound effects. You also made some adaptive music and learned how to play back
MP3 and Ogg Vorbis sound files. Combining this knowledge with graphics and
interactivity from the previous chapters, you're ready to create your own game.
譯:在本章中,你學習了聲音基礎、聲音過濾器和播放音樂的相關知識,創建了實時回響過濾器和偽-3D過濾器,還創建了一個很酷的聲音管理器用于處理游戲聲效。你也創建了一些自適應音樂并學習了如何播放MP3 和 Ogg
Vorbis 音頻文件。將這些知識結合前面章節學習的圖形和交互性知識,你已經做好準備,可以開始創建自己的游戲了。
學軟件開發,到蜂鳥科技!
超強的師資力量 、完善的課程體系 、超低的培訓價格 、真實的企業項目。
網址:
電話:
鄭州軟件開發興趣小組群: