[譯文]JOAL教程 第三課 多聲源播放

[譯文]JOAL教程

原文地址:http://jogamp.org/joal-demos/www/devmaster/lesson3.html

原文作者:Athomas Goldberg

譯文:三向板磚

轉載請保留以上信息。


本節對應的連續代碼頁及學習筆記:http://blog.csdn.net/shuzhe66/article/details/40260861

第三課 多聲源

 

本文是DevMaster.net(http://devmaster.net/)的OpenAL教程對應的JOAL版本。C語言版原文作者爲JesseMaurais

 

大家好,距離上次發佈教程也有一段時間了,但遲到總比沒有要好,我猜大家都渴望着閱讀新一期教程,於是我便着手去寫了。

本期教程將教會大家如何同時播放多個音頻。在很多激烈的遊戲當中包含有各種各樣的元素,在它們被觸發時往往伴隨着各類音效,這實現起來並不困難,處理多路音頻與處理單路音頻的方法極爲相似。

import java.nio.ByteBuffer;
import java.util.Random;

import com.jogamp.openal.AL;
import com.jogamp.openal.ALFactory;
import com.jogamp.openal.util.ALut;
  
public class MultipleSources {

    static AL al;

    // 所需緩衝區的最大數量.
    static final int NUM_BUFFERS = 3;

    // 所需聲源的最大數量
    static final int NUM_SOURCES = 3;

    // 下面三個變量標記了不同的聲源
    static final int BATTLE = 0;
    static final int GUN1   = 1;
    static final int GUN2   = 2;

    // 容納聲音數據的緩衝區
    static int[] buffers = new int[NUM_BUFFERS];

    // 播放聲音的點聲源
    static int[] sources = new int[NUM_SOURCES];

    // 各個聲源的位置
    static float[][] sourcePos = new float[NUM_SOURCES][3];

    // 各個聲源的速度
    static float[][] sourceVel = new float[NUM_SOURCES][3];

    // 聽衆的位置
    static float[] listenerPos = { 0.0f, 0.0f, 0.0f };

    // 聽衆的速度
    static float[] listenerVel = { 0.0f, 0.0f, 0.0f };

    // 聽衆的朝向
    static float[] listenerOri = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f };

我相信上面這段代碼對於看過前兩個章節的人來講會非常熟悉,唯一的不同在於我們需要將三種不同的音效裝入OpenAL系統中。


static int loadALData() {

        //需要填裝的值

        int[] format = new int[1];
        int[] size = new int[1];
        ByteBuffer[] data = new ByteBuffer[1];
        int[] freq = new int[1];
        int[] loop = new int[1];
        
        // 將wav文件讀入緩衝區

        al.alGenBuffers(NUM_BUFFERS, buffers, 0);
        if (al.alGetError() != AL.AL_NO_ERROR) {
            return AL.AL_FALSE;
        }

        ALut.alutLoadWAVFile(
            "wavdata/Battle.wav",
            format,
            data,
            size,
            freq,
            loop);
        al.alBufferData(
            buffers[BATTLE],
            format[0],
            data[0],
            size[0],
            freq[0]);


        ALut.alutLoadWAVFile(
            "wavdata/Gun1.wav",
            format,
            data,
            size,
            freq,
            loop);
        al.alBufferData(
            buffers[GUN1],
            format[0],
            data[0],
            size[0],
            freq[0]);


        ALut.alutLoadWAVFile(
            "wavdata/Gun2.wav",
            format,
            data,
            size,
            freq,
            loop);
        al.alBufferData(
            buffers[GUN2],
            format[0],
            data[0],
            size[0],
            freq[0]);


        //將緩衝區與聲源綁定
        al.alGenSources(NUM_SOURCES, sources, 0);

        al.alSourcei(sources[BATTLE], AL.AL_BUFFER, buffers[BATTLE]);
        al.alSourcef(sources[BATTLE], AL.AL_PITCH, 1.0f);
        al.alSourcef(sources[BATTLE], AL.AL_GAIN, 1.0f);
        al.alSourcefv(sources[BATTLE], AL.AL_POSITION, sourcePos[BATTLE], 0);
        al.alSourcefv(sources[BATTLE], AL.AL_VELOCITY, sourceVel[BATTLE], 0);
        al.alSourcei(sources[BATTLE], AL.AL_LOOPING, AL.AL_TRUE);

        al.alSourcei(sources[GUN1], AL.AL_BUFFER, buffers[GUN1]);
        al.alSourcef(sources[GUN1], AL.AL_PITCH, 1.0f);
        al.alSourcef(sources[GUN1], AL.AL_GAIN, 1.0f);
        al.alSourcefv(sources[GUN1], AL.AL_POSITION, sourcePos[GUN1], 0);
        al.alSourcefv(sources[GUN1], AL.AL_VELOCITY, sourceVel[GUN1], 0);
        al.alSourcei(sources[GUN1], AL.AL_LOOPING, AL.AL_FALSE);

        al.alSourcei(sources[GUN2], AL.AL_BUFFER, buffers[GUN2]);
        al.alSourcef(sources[GUN2], AL.AL_PITCH, 1.0f);
        al.alSourcef(sources[GUN2], AL.AL_GAIN, 1.0f);
        al.alSourcefv(sources[GUN2], AL.AL_POSITION, sourcePos[GUN2], 0);
        al.alSourcefv(sources[GUN2], AL.AL_VELOCITY, sourceVel[GUN2], 0);
        al.alSourcei(sources[GUN2], AL.AL_LOOPING, AL.AL_FALSE);

        // 檢查錯誤並返回結果
        if (al.alGetError() != AL.AL_NO_ERROR) {
            return AL.AL_FALSE;
        }

        return AL.AL_TRUE;
    }

上面的代碼看起來與之前有些許不同,實際上並不是這樣。大體上講,我們只是將三個音頻文件裝載到緩衝區中,並將三個緩衝區與對應的聲源綁定起來。唯一一點不同是Battle.wav(對應source[0])被設定爲循環播放而其它兩個音頻則沒有。


static void setListenerValues() {
        al.alListenerfv(AL.AL_POSITION, listenerPos, 0);
        al.alListenerfv(AL.AL_VELOCITY, listenerVel, 0);
        al.alListenerfv(AL.AL_ORIENTATION, listenerOri, 0);
    }

    static void killAllData() {
        al.alDeleteBuffers(NUM_BUFFERS, buffers, 0);
        al.alDeleteSources(NUM_SOURCES, sources, 0);
        ALut.alutExit();
    }

上面的代碼與之前相比沒有什麼不同。


public static void main(String[] args) {
        al = ALFactory.getAL();

	//初始化OpenAL,並將錯誤標記重置

        ALut.alutInit();
        al.alGetError();
        
	//裝載音頻數據.

        if(loadALData() == AL.AL_FALSE) {
            System.exit(1);    
        }
        setListenerValues();

	//播放“戰鬥”的背景音樂

        al.alSourcePlay(sources[BATTLE]);
        long startTime = System.currentTimeMillis();
        long elapsed = 0;
        long totalElapsed = 0;
        Random rand = new Random();
        int[] state = new int[1];
        while (totalElapsed < 10000) {
            elapsed = System.currentTimeMillis() - startTime;
            if (elapsed > 50) {
                totalElapsed += elapsed;
                startTime = System.currentTimeMillis();
				
                //隨機的選擇除背景樂外的兩個音效之一,判斷它是否在播放中              
		int pick = Math.abs((rand.nextInt()) % 2) + 1;
               al.alGetSourcei(sources[pick], AL.AL_SOURCE_STATE, state, 0);
				
                if (state[0] != AL.AL_PLAYING) {
				
		    //在聽衆周圍選擇一個隨機的位置播放聲音
				
                    double theta = (rand.nextInt() % 360) * 3.14 / 180.0;
                    sourcePos[pick][0] = - ((float) Math.cos(theta));
                    sourcePos[pick][1] = - ((float) (rand.nextInt() % 2));
                    sourcePos[pick][2] = - ((float) Math.sin(theta));

                    al.alSourcefv(
                        sources[pick],
                        AL.AL_POSITION,
                        sourcePos[pick], 0);

                    al.alSourcePlay(sources[pick]);
                }
            }
        }
        killAllData();
    }
}

這裏便是本次教程中最有趣的部分,我們一次檢測各個聲源是否處於播放當中,對沒有播放的聲源下達播放命令,而且我們在播放前爲其設定了一個三維空間中的隨機新位置(這樣僅僅是爲了好玩~)。

 

最後我們運行它,伴隨着混合音效,我們成功了!大部分讀者可能已經意識到,爲了讓聲源一起播放,我們僅僅下達了命令而未做什麼特別的事情,OpenAL已經處理了混音部分,並讓實際播放的聲音與聲源、聽衆的位置、速度相符,這難道不是OpenAL的魅力所在嗎?

 

這是如此的簡單,以至於我自己都懷疑爲什麼本篇教程會拖這麼久。如果讀者朋友們想要一些其它特別的教程(不一定是OpenAL,我的知識量是很龐大的)可以通過[email protected]聯繫我,不出意外的話,我打算在未來的教程部分講解緩衝區共享與多普勒效應的有關內容,祝大家編碼愉快!







發佈了42 篇原創文章 · 獲贊 50 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章