posts - 0,  comments - 6,  trackbacks - 0
          小節4.5 Creating a Real-Time Echo Filter
          小節 4.6 Emulating 3D Sound
          小節4.5 Creating a Real-Time Echo Filter
          由于以上小節內容我暫時沒有用到,所以先跳過。

          小節 4.7 創建聲音管理器 Creating a Sound Manager

           

          Whenever you play a new sound with the SimpleSoundPlayer, the following steps occur:

          譯:當用 SimpleSoundPlayer 播放一個新的聲音時,會經過以下步驟:

          1. Create a new thread.  創建一個新線程。

          2. Create a new byte buffer. 創建一個新字節緩沖區。

          3. Create, open, and start a new Line. 創建、打開并啟動一個新Line

          That's a lot of object creation. Plus, a new thread must start and native resources that operate the Line must be allocated. All this adds up to one thing: lag. It takes too long from the time you request to play a sound to the time when the sound is actually played.

          譯:以上過程會創建很多對象,外加上必須啟動一個新線程和分配用于操作Line的被本地資源。所有這些加到一起導致一件事情:延遲。從請求播放聲音到聲音確實被播放經過了過長的時間。

          Although the lag might not matter in some instances, such as when you play a startup sound, other times the lag is very noticeable, such as when you're blasting a gun several times a second. You want to eliminate the lag as much as possible.

          雖然在有些情況下延遲沒有什么問題,如當你播放一個啟動聲音時。但有時延遲是個很明顯的問題,例如當你每秒鐘連續射擊時,你想盡可能的消除延遲。

          To reduce the lag, rework the sound-playing architecture. Instead of starting a new thread for every sound played, you'll use the ThreadPool class from Chapter 1, "Java Threads." This keeps several threads ready and waiting to be assigned a sound to play.

          譯:為了減少延遲,需要重構聲音播放體系結構。用第一章“Java 線程”講到的ThreadPool類采用的線程池機制,使用若干準備好的線程,等待分配一個聲音來播放,而不是每播放一個聲音都啟動一個新的線程。

          Second, each thread in the thread pool can contain its own buffer and Line object, so you don't have to allocate those every time a sound is played.

          譯:第二,每一個線程池中的線程都擁有它們自己的緩沖區和Line對象,從而避免每次播放聲音時都分配一個緩沖區和Line對象。

          The drawback to this technique is that every line must be assigned an AudioFormat beforehand, so you can play only one type of audio format. This shouldn't be a problem—just pick an audio format that you want to use for your game, and make sure all your sounds have that same format.

          譯:這個技巧的缺點是每一個line必須被預先指派一個 AudioFormat ,所以你只能播放一種聲頻格式。這并不是什么問題——為你的游戲選擇一個你想使用音頻格式并確保所有的音頻文件都是這種音頻格式就可以了。

          Sound 類 The Sound Class 

          Now let's implement this architecture. First you'll create a simple class that contains a sound sample, called Sound, in Listing 4.9.

          譯:現在讓我們來實現這個體系結構。首先你要創建一個包含一個聲音樣本的簡單類,叫做Sound,請看代碼清單 4.9 

          Listing 4.9 Sound.java

          package com.brackeen.javagamebook.sound;

          /**

              The Sound class is a container for sound samples. The sound

          samples are format-agnostic and are stored as a byte array.

          Sound 類是聲音樣本的容器。聲音樣本是格式無關的,存放成字節數組

          */

          public class Sound {

              private byte[] samples;

              /**

                  Create a new Sound object with the specified byte array.

                  The array is not copied. 

          用指定字節數組創建一個新的Sound對象并不復制數組。

              */

              public Sound(byte[] samples) {

                  this.samples = samples;

              }

              /**

                  Returns this Sound's objects samples as a byte array.

                  以字節數組返回Sound對象的樣本

              */

              public byte[] getSamples() {

                  return samples;

              }

          }

          The Sound class is a wrapper class for a byte buffer that contains sound samples. It doesn't serve much of a purpose other than making the code more understandable and hiding the contents of a sound, in case you want to change it later.

          譯:Sound 類是一個包含聲音樣本的字節緩沖區的包裝類。該類的主要目的是增加代碼可讀性和隱藏音頻數據的內容,以便將來能夠進行擴展。

          SoundManager   /  The SoundManager Class

          Next create the SoundManager class in Listing 4.10. Along with implementing similar functions to SimpleSoundPlayer, such as loading a sound from a file, SoundManager reduces the lag and gives you the capability to pause playing sounds.

          譯:下一步通過代碼清單4.10 來創建 SoundManager 類。除了實現SimpleSoundPlayer 類似的功能外,如從一個文件加載一個聲音,SoundManager 減少了延遲,并聽提供了暫停聲音播放的功能。

          Listing 4.10 SoundManager.java
          package com.brackeen.javagamebook.sound;
           
          import java.io.*;
          import javax.sound.sampled.*;
          import javax.sound.midi.*;
          import com.brackeen.javagamebook.util.ThreadPool;
          import com.brackeen.javagamebook.util.LoopingByteInputStream;
           
           
          /**
              The SoundManager class manages sound playback. The
              SoundManager is a ThreadPool, with each thread playing back
              one sound at a time. This allows the SoundManager to
              easily limit the number of simultaneous sounds being played.
          <p>Possible ideas to extend this class
          <ul>
              <li>add a setMasterVolume() method, which uses Controls to
                  set the volume for each line.    
          <li>don't play a sound if more than, say, 500ms have passed
                  since the request to play 
          </ul>
          譯:SoundManager 類管理音頻播放。SoundManager 是一個線程池,控制每個線程
          一次播放一個音頻。這樣做可以讓 SoundManager 輕松限制同時播放音頻的數量。
          <p>擴展該類的一些想法:
          <ul>
                <li>添加一個setMasterVolume()方法,使用 Controls 來設置每一個Line對象的音量
                <li>如果播放一個聲音延遲了500毫秒就不要再播放了
          </ul>
          */ 
          public class SoundManager extends ThreadPool {
           
              private AudioFormat playbackFormat; //音頻格式
              private ThreadLocal localLine;  //存放 Line 對象的線程局部變量
              private ThreadLocal localBuffer; //存放字節緩沖區的線程局部變量
              private Object pausedLock; //暫停同步鎖對象
              private boolean paused; //暫停標志 true 暫停;false 非暫停
           
              /**
                  Creates a new SoundManager using the maximum number of
                  simultaneous sounds. 
          使用最大同時播放音頻的數量創建一個新的 SoundManager
              */
              public SoundManager(AudioFormat playbackFormat) {
                  this(playbackFormat,
                      getMaxSimultaneousSounds(playbackFormat));
              }
           
           
              /**
                  Creates a new SoundManager with the specified maximum
                  number of simultaneous sounds.
                  使用參數指定的最大同時播放音頻的數量創建一個新的 SoundManager
              */
              public SoundManager(AudioFormat playbackFormat,
                  int maxSimultaneousSounds)
              {
                  super(maxSimultaneousSounds);
                  this.playbackFormat = playbackFormat;
                  localLine = new ThreadLocal();
                  localBuffer = new ThreadLocal();
                  pausedLock = new Object();
                  // notify threads in pool it's okay to start 
          //通知線程池中的線程可以啟動了
                  synchronized (this) {
                      notifyAll();
                  }
              }
           
           
              /**
                  Gets the maximum number of simultaneous sounds with the
                  specified AudioFormat that the default mixer can play.
                  取得默認混頻器能夠播放參數指定音頻格式的音頻的最大同時播放數量。
              */
              public static int getMaxSimultaneousSounds(
                  AudioFormat playbackFormat)
              {
                  DataLine.Info lineInfo = new DataLine.Info(
                      SourceDataLine.class, playbackFormat);
                  Mixer mixer = AudioSystem.getMixer(null);
                  return mixer.getMaxLines(lineInfo);
              }
           
           
              /**
                  Does any clean up before closing. 關閉之前執行清理
              */
              protected void cleanUp() {
                  // signal to unpause 結束暫停狀態
                  setPaused(false);
           
                  // close the mixer (stops any running sounds) 
          //關閉混頻器(停止播放正在播放的任何音頻)
                  Mixer mixer = AudioSystem.getMixer(null);
                  if (mixer.isOpen()) {
                      mixer.close();
                  }
              }
           
           
              public void close() {
                  cleanUp();
                  super.close();
              }
           
           
              public void join() {
                  cleanUp();
                  super.join();
              }
           
           
              /**
                  Sets the paused state. Sounds may not pause immediately.
                  設置暫停狀態。音頻播放可能不會立即暫停。
              */
              public void setPaused(boolean paused) {
                  if (this.paused != paused) {
                      synchronized (pausedLock) {
                          this.paused = paused;
                          if (!paused) {
                              // restart sounds啟動音頻播放
                              pausedLock.notifyAll();
                          }
                      }
                  }
              }
           
           
              /**
                  Returns the paused state.  返回是否暫停 true 暫停;false 非暫停
              */
              public boolean isPaused() {
                  return paused;
              }
           
           
              /**
                  Loads a Sound from the file system. Returns null if an
                  error occurs. 從文件系統載入音頻。如果發生錯誤,則返回 null 
              */
              public Sound getSound(String filename) {
                  return getSound(getAudioInputStream(filename));
              }
           
           
              /**
                  Loads a Sound from an AudioInputStream. 從一個音頻輸入流載入音頻
              */
              public Sound getSound(AudioInputStream audioStream) {
                  if (audioStream == null) {
                      return null;
                  }
           
                  // get the number of bytes to read  獲得音頻的字節數
                  int length = (int)(audioStream.getFrameLength() *
                      audioStream.getFormat().getFrameSize());
           
                  // read the entire stream  讀取輸入流
                  byte[] samples = new byte[length];
                  DataInputStream is = new DataInputStream(audioStream);
                  try {
                      is.readFully(samples);
                  }
                  catch (IOException ex) {
                      ex.printStackTrace();
                  }
           
                  // return the samples返回音頻樣本
                  return new Sound(samples);
              }
           
           
              /**
                  Creates an AudioInputStream from a sound from the file
                  system. 通過文件系統的音頻文件創建一個音頻輸入流
              */
              public AudioInputStream getAudioInputStream(String filename) {
           
                  try {
                      // open the source file  打開源文件
                      AudioInputStream source =
                      AudioSystem.getAudioInputStream(new File(filename));
           
                      // convert to playback format轉換成指定播放格式
                      return AudioSystem.getAudioInputStream(
                          playbackFormat, source);
                  }
                  catch (UnsupportedAudioFileException ex) {
                      ex.printStackTrace();
                  }
                  catch (IOException ex) {
                      ex.printStackTrace();
                  }
                  catch (IllegalArgumentException ex) {
                      ex.printStackTrace();
                  }
           
                  return null;
              }
           
           
              /**
                  Plays a sound. This method returns immediately. 
          播放音頻。該方法立即返回。
              */
              public InputStream play(Sound sound) {
                  return play(sound, null, false);
              }
           
           
              /**
                  Plays a sound with an optional SoundFilter, and optionally
                  looping. This method returns immediately.
                  使用可選的音頻過濾器播放音頻,并指定是否循環播放。該方法立即返回。
              */
              public InputStream play(Sound sound, SoundFilter filter,
                  boolean loop)
              {
                  InputStream is;
                  if (sound != null) {
                      if (loop) {
                          is = new LoopingByteInputStream(
                              sound.getSamples());
                      }
                      else {
                          is = new ByteArrayInputStream(sound.getSamples());
                      }
           
                      return play(is, filter);
                  }
                  return null;
              }
           
           
              /**
                  Plays a sound from an InputStream. This method
                  returns immediately. 通過一個輸入流播放音頻。該方法立即返回。
              */
              public InputStream play(InputStream is) {
                  return play(is, null);
              }
           
           
              /**
                  Plays a sound from an InputStream with an optional
                  sound filter. This method returns immediately.
                  通過一個輸入流使用可選的音頻過濾器播放音頻。該方法立即返回。
              */
              public InputStream play(InputStream is, SoundFilter filter) {
                  if (is != null) {
                      if (filter != null) {
                          is = new FilteredSoundStream(is, filter);
                      }
                      runTask(new SoundPlayer(is));
                  }
                  return is;
              }
           
           
              /**
                  Signals that a PooledThread has started. Creates the
                  Thread's line and buffer. 
          指示一個池化線程已經啟動。創建線程的Line對象和字節緩沖區
              */
              protected void threadStarted() {
                  // wait for the SoundManager constructor to finish
          //等待 SoundManager 構造器執行完畢
                  synchronized (this) {
                      try {
                          wait();
                      }
                      catch (InterruptedException ex) { }
                  }
           
                  // use a short, 100ms (1/10th sec) buffer for filters that
                  // change in real-time
          //對實時改變的過濾使用短小的,100毫秒(1/10秒)緩沖區
                  int bufferSize = playbackFormat.getFrameSize() *
                      Math.round(playbackFormat.getSampleRate() / 10);
           
                  // create, open, and start the line創建,打開并啟動Line對象
                  SourceDataLine line;
                  DataLine.Info lineInfo = new DataLine.Info(
                      SourceDataLine.class, playbackFormat);
                  try {
                      line = (SourceDataLine)AudioSystem.getLine(lineInfo);
                      line.open(playbackFormat, bufferSize);
                  }
                  catch (LineUnavailableException ex) {
                      // the line is unavailable - signal to end this thread
          // Line對象不可用,結束當前線程
                      Thread.currentThread().interrupt();
                      return;
                  }
           
                  line.start();
           
                  // create the buffer  創建一個字節緩沖區
                  byte[] buffer = new byte[bufferSize];
           
                  // set this thread's locals
          //Line對象和字節緩沖區保存在線程局部變量中
                  localLine.set(line);
                  localBuffer.set(buffer);
              }
           
           
              /**
                  Signals that a PooledThread has stopped. Drains and
                  closes the Thread's Line. 
                  SoundPlayer 類是 PooledThreads 要運行的任務。它從 ThreadLocal 
          獲取線程的Line對象和字節緩沖區,并通過輸入流來播放音頻。
              */
              protected void threadStopped() {
                  SourceDataLine line = (SourceDataLine)localLine.get();
                  if (line != null) {
                      line.drain();
                      line.close();
                  }
              }
           
           
              /**
                  The SoundPlayer class is a task for the PooledThreads to
                  run. It receives the thread's Line and byte buffer from
                  the ThreadLocal variables and plays a sound from an
                  InputStream.
                  <p>This class only works when called from a PooledThread.
                  SoundPlayer 類是 PooledThreads 要運行的任務。它從 ThreadLocal 
          獲取線程的Line對象和字節緩沖區,并通過輸入流來播放音頻。
          <p> 本類只能用于PooledThread 調用時
              */
              protected class SoundPlayer implements Runnable {
           
                  private InputStream source;
           
                  public SoundPlayer(InputStream source) {
                      this.source = source;
                  }
           
                  public void run() {
                      // get line and buffer from ThreadLocals從線程局部變量獲取line和緩沖區
                      SourceDataLine line = (SourceDataLine)localLine.get();
                      byte[] buffer = (byte[])localBuffer.get();
                      if (line == null || buffer == null) {
                          // the line is unavailable line對象不可用
                          return;
                      }
           
                      // copy data to the line拷貝數據到line
                      try {
                          int numBytesRead = 0;
                          while (numBytesRead != -1) {
                              // if paused, wait until unpaused如果暫停,等待暫停結束
                              synchronized (pausedLock) {
                                  if (paused) {
                                      try {
                                          pausedLock.wait();
                                      }
                                      catch (InterruptedException ex) {
                                          return;
                                      }
                                  }
                              }
                              // copy data
                              numBytesRead =
                                  source.read(buffer, 0, buffer.length);
                              if (numBytesRead != -1) {
                                 line.write(buffer, 0, numBytesRead);
                              }
                          }
                      }
                      catch (IOException ex) {
                          ex.printStackTrace();
                      }
                  }
              }
          }

          The SoundManager class extends the ThreadPool class, which we moved to the com.brackeen.javagamebook.util package.

          : SoundManager 類繼承了被我們移到了 com.brackeen.javagamebook.util 包下的ThreadPool 類。

           

          The SoundManager class has an inner class, SoundPlayer, which does the work of copying sound data to a Line. SoundPlayer is an implementation of the Runnable interface, so it can be used as a task for a thread in the thread pool. An addition to SoundPlayer over SimpleSoundPlayer is that it stops copying data if the SoundManager is in the paused state. If it is in the paused state, SoundPlayer calls wait(), which causes the thread to wait idly until it is notified. SoundManager notifies all waiting threads when it is unpaused.

          : SoundManager 類有一個內部類,SoundPlayer,負責拷貝音頻數據到一個Line對象。SoundPlayerRunnable接口的一個實現,因此它可以被線程池中的線程作為一個任務來執行。SoundPlayer SimpleSoundPlayer更強的一點是當SoundManager 處于暫停狀態時,它可以停止復制數據。在暫停狀態,SoundPlayer 調用 wait()方法,使線程轉為閑置等待狀態直到線程被通知繼續運行。SoundManager 結束暫停時會通知所有正在等待的線程。

          線程局部變量  / Thread-Local Variables

          One thing you wanted to accomplish in SoundManager is to make sure each thread has its own Line and byte buffer so you can reuse them without having to create new objects every time a sound is played. To give each thread in the thread pool its own Line and byte buffer, you'll take advantage of thread-local variables.

          :  SoundManager中,一件你想要完成的事是確保每一個線程都有它自己的Line對象和字節緩沖區,以便你可以重復使用它們,而不用每次播放音頻時都創建新的對象。為了給線程池中的每一個線程提供獨有的Line對象和字節緩沖區,你需要利用線程局部變量。

          Whereas local variables are variables that are local to a block of code, thread-local variables are variables that have a different value for every thread. In this example, the SoundManager class has the thread-local variables, localLine and localBuffer. Each thread that accesses these variables can have its own Line and byte buffer, and no other thread can access another thread's local variables. Thread-local variables are created with the ThreadLocal class.

          : 局部變量是代碼塊中聲明的變量,而線程局部變量是每一個線程都擁有不同的值的變量。在本例中,SoundManager 類有localLine localBuffer 兩個線程局部變量。每個線程訪問這些變量只能獲取它們自己獨有的 Line 對象和 字節緩沖區,一個線程不能訪問另外一個線程的線程局部變量。線程局部變量通過 ThreadLocal 類創建。

          For thread-local variables to work, you need to cheat a little here and update the ThreadPool class. You need a way to create the thread-local variables when a thread starts and to do any cleanup of the thread-local variables when the thread dies. To do this, in PooledThread, signal the ThreadPool class when each thread starts and stops:

          : 為了能夠使用上述線程局部變量,我們需要修改 ThreadPool 類。你需要一種方式在線程啟動時創建線程局部變量,在線程終止時清理局部變量。為了做到這一點,在 PooledThread 類中,當每一個線程啟動和終止時,我們通知ThreadPool 類:

          public void run() {
              // signal that this thread has started指示線程已經啟動
              threadStarted();
           
              while (!isInterrupted()) {
           
                  // get a task to run  獲取要運行的任務
                  Runnable task = null;
                  try {
                      task = getTask();
                  }
                  catch (InterruptedException ex) { }
           
                  // if getTask() returned null or was interrupted,
                  // close this thread. 如果 getTask() 返回 null 中斷時關閉線程
                  if (task == null) {
                      break;
                  }
                  // run the task, and eat any exceptions it throws 
          // 運行任務,捕捉拋出的任何異常
                  try {
                      task.run();
                  }
                  catch (Throwable t) {
                      uncaughtException(this, t);
                  }
              }
          // signal that this thread has stopped
          // 指示這個線程已經停止
              threadStopped();
          }

          In the ThreadPool class, the threadStarted() and threadStopped() methods don't do anything, but in SoundManager, they're put to use. The threadStarted() method creates a new Line and a new byte buffer, and adds them to the thread-local variables. In the threadStopped() method, the Line is drained and closed.

          譯:在 ThreadPool 類中, threadStarted() 方法和threadStopped() 方法什么都不做。但是在 SoundManager 類中,他們被覆蓋使用。threadStarted() 方法創建一個 Line 和一個 新的字節緩沖區,并將它們保存到線程局部變量。在 threadStopped() 方法中,Line 被排空并關閉。

          Besides reducing the lag and enabling you to pause playing sounds, SoundManager provides easier methods for playing sound. It takes care of the creation of ByteArrayInputStreams or FilteredSoundStreams, so all you have to do is pass it a Sound object and an optional SoundFilter.

          譯:除了減少延遲和允許暫停播放音頻外,SoundManager 類還提供了更簡單的音頻播放方法。這個音頻播放方法負責創建 ByteArrayInputStreams FilteredSoundStreams,因此你只需要傳遞一個 Sound 對象和一個可選的SoundFilter 參數給它就行了。

          That's it for the nifty new sound manager. So far, you've learned to play sound, use sound filters, and even emulate 3D sound. Now you'll move on to the other sound topic: music.

          譯:這樣我們就完成了一個簡單小巧的新音頻管理器。到此為止,你已經學會了播放音頻,使用音頻過濾器,甚至是仿真 3D 音頻。接下來我們將繼續另一個主題:音樂。

          學軟件開發,到蜂鳥科技!
          超強的師資力量 、完善的課程體系 、超低的培訓價格 、真實的企業項目。
          網址:www.ntcsoft.com 
          電話:0371-63839606 
          鄭州軟件開發興趣小組群:38236716

           

          posted on 2010-11-27 01:08 whistler 閱讀(638) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2025年5月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          留言簿(2)

          我參與的團隊

          文章檔案(22)

          搜索

          •  

          最新評論

          主站蜘蛛池模板: 龙江县| 河津市| 武山县| 宜兴市| 白朗县| 麻城市| 高安市| 儋州市| 合作市| 麟游县| 炉霍县| 苍溪县| 分宜县| 巴林左旗| 临海市| 伊通| 佛山市| 莱芜市| 汉沽区| 厦门市| 讷河市| 邯郸县| 墨竹工卡县| 昭苏县| 铁力市| 隆昌县| 卢湾区| 茂名市| 黔东| 五寨县| 三门县| 巩义市| 弥渡县| 民乐县| 潞城市| 吕梁市| 大名县| 蓝田县| 西安市| 洪湖市| 益阳市|