<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 0,  comments - 6,  trackbacks - 0
    小節(jié)4.5 Creating a Real-Time Echo Filter
    小節(jié) 4.6 Emulating 3D Sound
    小節(jié)4.5 Creating a Real-Time Echo Filter
    由于以上小節(jié)內(nèi)容我暫時(shí)沒(méi)有用到,所以先跳過(guò)。

    小節(jié) 4.7 創(chuàng)建聲音管理器 Creating a Sound Manager

     

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

    譯:當(dāng)用 SimpleSoundPlayer 播放一個(gè)新的聲音時(shí),會(huì)經(jīng)過(guò)以下步驟:

    1. Create a new thread.  創(chuàng)建一個(gè)新線(xiàn)程。

    2. Create a new byte buffer. 創(chuàng)建一個(gè)新字節(jié)緩沖區(qū)。

    3. Create, open, and start a new Line. 創(chuàng)建、打開(kāi)并啟動(dòng)一個(gè)新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.

    譯:以上過(guò)程會(huì)創(chuàng)建很多對(duì)象,外加上必須啟動(dòng)一個(gè)新線(xiàn)程和分配用于操作Line的被本地資源。所有這些加到一起導(dǎo)致一件事情:延遲。從請(qǐng)求播放聲音到聲音確實(shí)被播放經(jīng)過(guò)了過(guò)長(zhǎng)的時(shí)間。

    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.

    雖然在有些情況下延遲沒(méi)有什么問(wèn)題,如當(dāng)你播放一個(gè)啟動(dòng)聲音時(shí)。但有時(shí)延遲是個(gè)很明顯的問(wèn)題,例如當(dāng)你每秒鐘連續(xù)射擊時(shí),你想盡可能的消除延遲。

    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.

    譯:為了減少延遲,需要重構(gòu)聲音播放體系結(jié)構(gòu)。用第一章“Java 線(xiàn)程”講到的ThreadPool類(lèi)采用的線(xiàn)程池機(jī)制,使用若干準(zhǔn)備好的線(xiàn)程,等待分配一個(gè)聲音來(lái)播放,而不是每播放一個(gè)聲音都啟動(dòng)一個(gè)新的線(xiàn)程。

    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.

    譯:第二,每一個(gè)線(xiàn)程池中的線(xiàn)程都擁有它們自己的緩沖區(qū)和Line對(duì)象,從而避免每次播放聲音時(shí)都分配一個(gè)緩沖區(qū)和Line對(duì)象。

    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.

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

    Sound 類(lèi) 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.

    譯:現(xiàn)在讓我們來(lái)實(shí)現(xiàn)這個(gè)體系結(jié)構(gòu)。首先你要?jiǎng)?chuàng)建一個(gè)包含一個(gè)聲音樣本的簡(jiǎn)單類(lèi),叫做Sound,請(qǐng)看代碼清單 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 類(lèi)是聲音樣本的容器。聲音樣本是格式無(wú)關(guān)的,存放成字節(jié)數(shù)組

    */

    public class Sound {

        private byte[] samples;

        /**

            Create a new Sound object with the specified byte array.

            The array is not copied. 

    用指定字節(jié)數(shù)組創(chuàng)建一個(gè)新的Sound對(duì)象并不復(fù)制數(shù)組。

        */

        public Sound(byte[] samples) {

            this.samples = samples;

        }

        /**

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

            以字節(jié)數(shù)組返回Sound對(duì)象的樣本

        */

        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 類(lèi)是一個(gè)包含聲音樣本的字節(jié)緩沖區(qū)的包裝類(lèi)。該類(lèi)的主要目的是增加代碼可讀性和隱藏音頻數(shù)據(jù)的內(nèi)容,以便將來(lái)能夠進(jìn)行擴(kuò)展。

    SoundManager 類(lèi)  /  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.

    譯:下一步通過(guò)代碼清單4.10 來(lái)創(chuàng)建 SoundManager 類(lèi)。除了實(shí)現(xiàn)SimpleSoundPlayer 類(lèi)似的功能外,如從一個(gè)文件加載一個(gè)聲音,SoundManager 減少了延遲,并聽(tīng)提供了暫停聲音播放的功能。

    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 類(lèi)管理音頻播放。SoundManager 是一個(gè)線(xiàn)程池,控制每個(gè)線(xiàn)程
    一次播放一個(gè)音頻。這樣做可以讓 SoundManager 輕松限制同時(shí)播放音頻的數(shù)量。
    <p>擴(kuò)展該類(lèi)的一些想法:
    <ul>
          <li>添加一個(gè)setMasterVolume()方法,使用 Controls 來(lái)設(shè)置每一個(gè)Line對(duì)象的音量
          <li>如果播放一個(gè)聲音延遲了500毫秒就不要再播放了
    </ul>
    */ 
    public class SoundManager extends ThreadPool {
     
        private AudioFormat playbackFormat; //音頻格式
        private ThreadLocal localLine;  //存放 Line 對(duì)象的線(xiàn)程局部變量
        private ThreadLocal localBuffer; //存放字節(jié)緩沖區(qū)的線(xiàn)程局部變量
        private Object pausedLock; //暫停同步鎖對(duì)象
        private boolean paused; //暫停標(biāo)志 true 暫停;false 非暫停
     
        /**
            Creates a new SoundManager using the maximum number of
            simultaneous sounds. 
    使用最大同時(shí)播放音頻的數(shù)量創(chuàng)建一個(gè)新的 SoundManager
        */
        public SoundManager(AudioFormat playbackFormat) {
            this(playbackFormat,
                getMaxSimultaneousSounds(playbackFormat));
        }
     
     
        /**
            Creates a new SoundManager with the specified maximum
            number of simultaneous sounds.
            使用參數(shù)指定的最大同時(shí)播放音頻的數(shù)量創(chuàng)建一個(gè)新的 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 
    //通知線(xiàn)程池中的線(xiàn)程可以啟動(dòng)了
            synchronized (this) {
                notifyAll();
            }
        }
     
     
        /**
            Gets the maximum number of simultaneous sounds with the
            specified AudioFormat that the default mixer can play.
            取得默認(rèn)混頻器能夠播放參數(shù)指定音頻格式的音頻的最大同時(shí)播放數(shù)量。
        */
        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. 關(guān)閉之前執(zhí)行清理
        */
        protected void cleanUp() {
            // signal to unpause 結(jié)束暫停狀態(tài)
            setPaused(false);
     
            // close the mixer (stops any running sounds) 
    //關(guān)閉混頻器(停止播放正在播放的任何音頻)
            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.
            設(shè)置暫停狀態(tài)。音頻播放可能不會(huì)立即暫停。
        */
        public void setPaused(boolean paused) {
            if (this.paused != paused) {
                synchronized (pausedLock) {
                    this.paused = paused;
                    if (!paused) {
                        // restart sounds啟動(dòng)音頻播放
                        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. 從文件系統(tǒng)載入音頻。如果發(fā)生錯(cuò)誤,則返回 null 
        */
        public Sound getSound(String filename) {
            return getSound(getAudioInputStream(filename));
        }
     
     
        /**
            Loads a Sound from an AudioInputStream. 從一個(gè)音頻輸入流載入音頻
        */
        public Sound getSound(AudioInputStream audioStream) {
            if (audioStream == null) {
                return null;
            }
     
            // get the number of bytes to read  獲得音頻的字節(jié)數(shù)
            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. 通過(guò)文件系統(tǒng)的音頻文件創(chuàng)建一個(gè)音頻輸入流
        */
        public AudioInputStream getAudioInputStream(String filename) {
     
            try {
                // open the source file  打開(kāi)源文件
                AudioInputStream source =
                AudioSystem.getAudioInputStream(new File(filename));
     
                // convert to playback format轉(zhuǎn)換成指定播放格式
                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.
            使用可選的音頻過(guò)濾器播放音頻,并指定是否循環(huán)播放。該方法立即返回。
        */
        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. 通過(guò)一個(gè)輸入流播放音頻。該方法立即返回。
        */
        public InputStream play(InputStream is) {
            return play(is, null);
        }
     
     
        /**
            Plays a sound from an InputStream with an optional
            sound filter. This method returns immediately.
            通過(guò)一個(gè)輸入流使用可選的音頻過(guò)濾器播放音頻。該方法立即返回。
        */
        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. 
    指示一個(gè)池化線(xiàn)程已經(jīng)啟動(dòng)。創(chuàng)建線(xiàn)程的Line對(duì)象和字節(jié)緩沖區(qū)
        */
        protected void threadStarted() {
            // wait for the SoundManager constructor to finish
    //等待 SoundManager 構(gòu)造器執(zhí)行完畢
            synchronized (this) {
                try {
                    wait();
                }
                catch (InterruptedException ex) { }
            }
     
            // use a short, 100ms (1/10th sec) buffer for filters that
            // change in real-time
    //對(duì)實(shí)時(shí)改變的過(guò)濾使用短小的,100毫秒(1/10秒)緩沖區(qū)
            int bufferSize = playbackFormat.getFrameSize() *
                Math.round(playbackFormat.getSampleRate() / 10);
     
            // create, open, and start the line創(chuàng)建,打開(kāi)并啟動(dòng)Line對(duì)象
            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對(duì)象不可用,結(jié)束當(dāng)前線(xiàn)程
                Thread.currentThread().interrupt();
                return;
            }
     
            line.start();
     
            // create the buffer  創(chuàng)建一個(gè)字節(jié)緩沖區(qū)
            byte[] buffer = new byte[bufferSize];
     
            // set this thread's locals
    //Line對(duì)象和字節(jié)緩沖區(qū)保存在線(xiàn)程局部變量中
            localLine.set(line);
            localBuffer.set(buffer);
        }
     
     
        /**
            Signals that a PooledThread has stopped. Drains and
            closes the Thread's Line. 
            SoundPlayer 類(lèi)是 PooledThreads 要運(yùn)行的任務(wù)。它從 ThreadLocal 
    獲取線(xiàn)程的Line對(duì)象和字節(jié)緩沖區(qū),并通過(guò)輸入流來(lái)播放音頻。
        */
        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 類(lèi)是 PooledThreads 要運(yùn)行的任務(wù)。它從 ThreadLocal 
    獲取線(xiàn)程的Line對(duì)象和字節(jié)緩沖區(qū),并通過(guò)輸入流來(lái)播放音頻。
    <p> 本類(lèi)只能用于PooledThread 調(diào)用時(shí)
        */
        protected class SoundPlayer implements Runnable {
     
            private InputStream source;
     
            public SoundPlayer(InputStream source) {
                this.source = source;
            }
     
            public void run() {
                // get line and buffer from ThreadLocals從線(xiàn)程局部變量獲取line和緩沖區(qū)
                SourceDataLine line = (SourceDataLine)localLine.get();
                byte[] buffer = (byte[])localBuffer.get();
                if (line == null || buffer == null) {
                    // the line is unavailable line對(duì)象不可用
                    return;
                }
     
                // copy data to the line拷貝數(shù)據(jù)到line
                try {
                    int numBytesRead = 0;
                    while (numBytesRead != -1) {
                        // if paused, wait until unpaused如果暫停,等待暫停結(jié)束
                        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 類(lèi)繼承了被我們移到了 com.brackeen.javagamebook.util 包下的ThreadPool 類(lèi)。

     

    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 類(lèi)有一個(gè)內(nèi)部類(lèi),SoundPlayer,負(fù)責(zé)拷貝音頻數(shù)據(jù)到一個(gè)Line對(duì)象。SoundPlayerRunnable接口的一個(gè)實(shí)現(xiàn),因此它可以被線(xiàn)程池中的線(xiàn)程作為一個(gè)任務(wù)來(lái)執(zhí)行。SoundPlayer SimpleSoundPlayer更強(qiáng)的一點(diǎn)是當(dāng)SoundManager 處于暫停狀態(tài)時(shí),它可以停止復(fù)制數(shù)據(jù)。在暫停狀態(tài),SoundPlayer 調(diào)用 wait()方法,使線(xiàn)程轉(zhuǎn)為閑置等待狀態(tài)直到線(xiàn)程被通知繼續(xù)運(yùn)行。SoundManager 結(jié)束暫停時(shí)會(huì)通知所有正在等待的線(xiàn)程。

    線(xiàn)程局部變量  / 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中,一件你想要完成的事是確保每一個(gè)線(xiàn)程都有它自己的Line對(duì)象和字節(jié)緩沖區(qū),以便你可以重復(fù)使用它們,而不用每次播放音頻時(shí)都創(chuàng)建新的對(duì)象。為了給線(xiàn)程池中的每一個(gè)線(xiàn)程提供獨(dú)有的Line對(duì)象和字節(jié)緩沖區(qū),你需要利用線(xiàn)程局部變量。

    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.

    : 局部變量是代碼塊中聲明的變量,而線(xiàn)程局部變量是每一個(gè)線(xiàn)程都擁有不同的值的變量。在本例中,SoundManager 類(lèi)有localLine localBuffer 兩個(gè)線(xiàn)程局部變量。每個(gè)線(xiàn)程訪(fǎng)問(wèn)這些變量只能獲取它們自己獨(dú)有的 Line 對(duì)象和 字節(jié)緩沖區(qū),一個(gè)線(xiàn)程不能訪(fǎng)問(wèn)另外一個(gè)線(xiàn)程的線(xiàn)程局部變量。線(xiàn)程局部變量通過(guò) ThreadLocal 類(lèi)創(chuàng)建。

    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:

    : 為了能夠使用上述線(xiàn)程局部變量,我們需要修改 ThreadPool 類(lèi)。你需要一種方式在線(xiàn)程啟動(dòng)時(shí)創(chuàng)建線(xiàn)程局部變量,在線(xiàn)程終止時(shí)清理局部變量。為了做到這一點(diǎn),在 PooledThread 類(lèi)中,當(dāng)每一個(gè)線(xiàn)程啟動(dòng)和終止時(shí),我們通知ThreadPool 類(lèi):

    public void run() {
        // signal that this thread has started指示線(xiàn)程已經(jīng)啟動(dòng)
        threadStarted();
     
        while (!isInterrupted()) {
     
            // get a task to run  獲取要運(yùn)行的任務(wù)
            Runnable task = null;
            try {
                task = getTask();
            }
            catch (InterruptedException ex) { }
     
            // if getTask() returned null or was interrupted,
            // close this thread. 如果 getTask() 返回 null 中斷時(shí)關(guān)閉線(xiàn)程
            if (task == null) {
                break;
            }
            // run the task, and eat any exceptions it throws 
    // 運(yùn)行任務(wù),捕捉拋出的任何異常
            try {
                task.run();
            }
            catch (Throwable t) {
                uncaughtException(this, t);
            }
        }
    // signal that this thread has stopped
    // 指示這個(gè)線(xiàn)程已經(jīng)停止
        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 類(lèi)中, threadStarted() 方法和threadStopped() 方法什么都不做。但是在 SoundManager 類(lèi)中,他們被覆蓋使用。threadStarted() 方法創(chuàng)建一個(gè) Line 和一個(gè) 新的字節(jié)緩沖區(qū),并將它們保存到線(xiàn)程局部變量。在 threadStopped() 方法中,Line 被排空并關(guān)閉。

    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 類(lèi)還提供了更簡(jiǎn)單的音頻播放方法。這個(gè)音頻播放方法負(fù)責(zé)創(chuàng)建 ByteArrayInputStreams FilteredSoundStreams,因此你只需要傳遞一個(gè) Sound 對(duì)象和一個(gè)可選的SoundFilter 參數(shù)給它就行了。

    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.

    譯:這樣我們就完成了一個(gè)簡(jiǎn)單小巧的新音頻管理器。到此為止,你已經(jīng)學(xué)會(huì)了播放音頻,使用音頻過(guò)濾器,甚至是仿真 3D 音頻。接下來(lái)我們將繼續(xù)另一個(gè)主題:音樂(lè)。

    學(xué)軟件開(kāi)發(fā),到蜂鳥(niǎo)科技!
    超強(qiáng)的師資力量 、完善的課程體系 、超低的培訓(xùn)價(jià)格 、真實(shí)的企業(yè)項(xiàng)目。
    網(wǎng)址:www.ntcsoft.com 
    電話(huà):0371-63839606 
    鄭州軟件開(kāi)發(fā)興趣小組群:38236716

     

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

    只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    留言簿(2)

    我參與的團(tuán)隊(duì)

    文章檔案(22)

    搜索

    •  

    最新評(píng)論

    主站蜘蛛池模板: 无码精品人妻一区二区三区免费看 | 国产精品无码亚洲一区二区三区| 亚洲黄色免费观看| 亚洲V无码一区二区三区四区观看| 免费很黄无遮挡的视频毛片| 在线A级毛片无码免费真人| 亚洲一欧洲中文字幕在线| 99热精品在线免费观看| 亚洲妇熟XXXX妇色黄| 精品人妻系列无码人妻免费视频| 免费一级毛片女人图片| 久久亚洲AV成人无码国产最大| 男男AV纯肉无码免费播放无码| 亚洲国产综合在线| 曰批视频免费40分钟试看天天| 亚洲邪恶天堂影院在线观看| 国产成人精品一区二区三区免费| 亚洲国产一二三精品无码| 色吊丝性永久免费看码| 午夜亚洲福利在线老司机| 香蕉视频亚洲一级| 国产精品二区三区免费播放心 | 亚洲AV无码一区二区三区在线观看| 亚洲精品动漫免费二区| 岛国av无码免费无禁网站| 亚洲国产精品综合久久20| 成年免费大片黄在线观看岛国| 亚洲男人的天堂在线| 亚洲性线免费观看视频成熟| 亚洲国产av高清无码| 4虎永免费最新永久免费地址| 亚洲国产av一区二区三区丶| 午夜国产精品免费观看| 久久精品国产亚洲AV忘忧草18| 最近最新的免费中文字幕| 一区二区亚洲精品精华液 | 一级毛片免费在线| 在线亚洲人成电影网站色www| 十八禁视频在线观看免费无码无遮挡骂过 | 亚洲熟妇无码久久精品| 在线看片无码永久免费视频|