[譯文]JOAL教程 第一課 單一固定聲源

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

原文作者:Athomas Goldberg

譯文:三向板磚

轉載請保留以上信息。

 

本節課程的學習筆記,記錄了課程中值得注意的問題以及方便複製測試的連續代碼片段:

http://blog.csdn.net/shuzhe66/article/details/40213193

 

第一課 單一固定聲源

 

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

 

歡迎來到令人激動的OpenAL世界!OpenAL目前仍在不斷成長,仍有一大批後續API沒有達到它的全部潛能。這其中最主要的原因是由於某些聲卡仍然不支持硬件加速。

然而,OpenAL項目的主要貢獻者、同時也是最大的聲卡生產商之一的Creative Labs公司,已經承諾在不久的將來全面支持聲卡的硬件加速。

 

OpenAL僅有的另一位的主要貢獻者Loki已經不知去向,所以OpenAL在Linux平臺上的發展前景尚不明朗,但你仍可在一些第三方頁面上下載到支持Linux的OpenAL類庫

 

目前,OpenAL仍未在主流商業產品中出現,這也許會妨礙到它的成長。據我所知唯一一款使用了OpenAL的PC遊戲是合金裝備2(最近我發現虛幻2引擎也使用了它)。流行的建模工具Blender3D同樣適用OpenAL作爲其音頻播放組件。拋開這些,其他使用OpenAL的地方恐怕也只有其SDK中的例子和出現在其它網站上的零散教程了。

 

但讓我們直面現實吧,OpenAL確實很有潛力。有很多其它的音頻庫都需要與硬件一起工作在底層,但是OpenAL的設計者在其設計中改良了很多部分,使OpenAL成爲了一個高級API。

 

首先,它以之前設計的最棒API之一:OpenGL作爲其模板參考,較高的靈活度使不同編程方式和硬件實現變得容易。當然,具有OpenGL學習經驗的人會很快學會OpenAL。

其次,OpenAL在創建3D環繞立體聲時具有其他音頻庫無法比擬的優勢。

最最給力得一點,加以拓展的OpenAL可以與EAX和AC3完美地融合,據我所知沒有任何其它音頻庫具有這個能力。

 

如果你還是拿不定注意是否需要它,這裏倒是有一個:它很酷,它是一個優雅的API,可以和你的代碼完美地組合在一起,你可以使用它做很多音頻特效,但在我們開始之前,必須來學習一些基礎知識。

 

不多說了,一起來編碼吧!

import com.jogamp.openal.*;
import com.jogamp.openal.util.*;
import java.io.*;
import java.nio.ByteBuffer;
public class SingleStaticSource {
   static AL al = ALFactory.getAL();
    //緩衝區儲存音頻數據
   static int[] buffer = new int[1];;
   //聲源播放聲音
   static int[] source = new int[1];
和OpenGL處理程序使用的“紋理對象”(或是紋理名稱)時的過程相似,OpenAL使用同樣的方法處理音頻採樣。在OpenAL中有三種基本的對象:儲存着播放所需全部信息與聲音數據的緩衝區、在空間中發出聲音的點聲源以及一個聽衆。

聲源本身並不是音頻採樣,這一點極其重要。聲源只是負責讀取綁定在它身上的音頻數據緩衝區並播放音頻,我們可以爲聲源設置位置和速度來改變聲音的特性[這裏的速度不是指播放速度,而是聲源的物理速度,利用這個特點可以模擬聲音的多普勒效應等——譯者注]

只有一個聽衆對象,它代表着用戶的位置,聽衆與聲源的屬性共同決定了用戶實際聽到的音頻樣本,例如其相對位置決定了音頻強度。


    //聲源的位置矢量
    static float[] sourcePos = { 0.0f, 0.0f, 0.0f };
    //聲源的速度矢量
    static float[] sourceVel = { 0.0f, 0.0f, 0.0f };
    //聽衆的位置
    static float[] listenerPos = { 0.0f, 0.0f, 0.0f };
    //聽衆的速度矢量
    static float[] listenerVel = { 0.0f, 0.0f, 0.0f };
    //聽衆的朝向. (前三個參數表示“臉”的正對方向,後三個參數表示“頭頂”方向)[原文爲first 3 elements are "at", second 3 are "up"]
    static float[] listenerOri = { 0.0f, 0.0f, -1.0f,  0.0f, 1.0f, 0.0f };
在上述代碼中,我們爲聲源與聽衆設置了位置與速度,這些數組是基於直角座標系的,你也可以使用結構體或類來完成同樣的功能,我這裏使用數組只是爲了方便。

 

下面我們創建一個可以從文件中讀取音頻數據的方法

    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(1, buffer, 0);
        if (al.alGetError() != AL.AL_NO_ERROR)
            return AL.AL_FALSE;

        ALut.alutLoadWAVFile("wavdata/FancyPants.wav", format, data, size, freq, loop);
        al.alBufferData(buffer[0], format[0], data[0], size[0], freq[0]);
方法‘alGenBuffers‘將會創建緩衝區對象並將我們傳入的值存入其中,錯誤檢測功能極其重要,它確保一切執行順利進行。某些情況下,由於內存不足,OpenAL無法創建緩衝區對象,此時對其的設置將會出錯。Alut工具套件此時顯得十分有用,它打開文件並自動裝填創建緩衝區的必要信息,而我們所做的,只是調用一個簡潔高效的方法。


        // 將緩衝區綁定到聲源上.
        al.alGenSources(1, source, 0);

        if (al.alGetError() != AL.AL_NO_ERROR)
            return AL.AL_FALSE;

        al.alSourcei (source[0], AL.AL_BUFFER,   buffer[0]   );
        al.alSourcef (source[0], AL.AL_PITCH,    1.0f     );
        al.alSourcef (source[0], AL.AL_GAIN,     1.0f     );
        al.alSourcefv(source[0], AL.AL_POSITION, sourcePos, 0);
        al.alSourcefv(source[0], AL.AL_VELOCITY, sourceVel, 0);
        al.alSourcei (source[0], AL.AL_LOOPING,  loop[0]     );

產生聲源對象與產生緩衝區對象所用的方法相似。之後,我們定義聲源的各種播放所需屬性,這其中最爲重要的屬性是聲源所使用的緩衝區對象,它告訴了聲源將要播放哪一個聲音樣本,在本例中,只有一個音頻。當然,我們也會將之前定義好的聲源位置與速度告訴它。

’alGenBuffers’’alGenSources’有關的另一件事:在一些實例中,我見過這些函數會返回一個整型值來表示創建的緩衝區和聲源數量,我想這是一個由早期版本遺留下來的錯誤檢測機制,如果你在其它代碼片段中看到了這樣的寫法也不要仿照去寫,如果你想進行此類檢查,使用’alGetError’來代替(就像上面做的那樣)

	//再一次檢查並返回結果
        if(al.alGetError() == AL.AL_NO_ERROR)
            	return AL.AL_TRUE;

        	return AL.AL_FALSE;
    	}

最後,我們確保一切順利並返回成功。


    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 killALData() {
        	al.alDeleteBuffers(1, buffer, 0);
        	al.alDeleteSources(1, source, 0);
       		ALut.alutExit();
    	}
這裏是我們的關閉過程,釋放程序使用的音頻設備與內存資源是十分必要的。


    public static void main(String[] args) {
        //初始化OpenAL並重置錯誤檢測標記
        ALut.alutInit();
        al.alGetError();
alutIniti將會爲我們初始化Alc所需的一切。大體上講,Alut通過Alc創建一個OpenAL上下文並將其置爲當前上下文,在Windows平臺上,它初始化DirectSound。我們還對錯誤檢測函數進行初始化以清除之前無效的錯誤信息,每當我們調用glGetError時,它會將內部錯誤標記變量置爲'AL_NO_ERROR'。

        //裝載Wav數據.
        if (loadALData() == AL.AL_FALSE)
            System.exit(-1);

        setListenerValues();

        //設置一個鉤子,在系統退出時被執行。

        Runtime runtime = Runtime.getRuntime();
        runtime.addShutdownHook(
            new Thread(
                new Runnable() {
                    public void run() {
                        killALData();
                    }
                }
            )
        );
我們必須保證wav文件被正確裝入,否則系統必須退出。之後是對聽衆信息以及退出過程的設置。


        char[] c = new char[1];
        while(c[0] != 'q') {	
        try {
            BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("Press a key and hit ENTER: " +
                               "'p' to play, 's' to stop, 'h' to pause and 'q' to quit");
            buf.read(c);
            switch(c[0]) {
                case 'p':
                    //按p鍵開始播放
                    al.alSourcePlay(source[0]);
                    break;
                case 's':
                    //按s鍵停止播放
                    al.alSourceStop(source[0]);
                    break;
                case 'h':
                    //按h鍵暫停播放
                    al.alSourcePause(source[0]);
                    break;
                }
        } catch (IOException e) {
			System.exit(1);
        }
    }
}

}//類括號
這裏是教程最有趣的地方,我們只用一個基本的循環結構便控制了音頻播放器的播放、暫停、停止與退出。


好了,這一部分到這裏就結束了。這是你第一次進入OpenAL世界,我希望以上教程對你來說是足夠簡單的,當然,對於黑客而言是在太簡單了[原文中作者使用了“1337 h4X0r”,翻譯爲hacker——譯者注],但我們總得從這裏開始,隨着我們的進一步深入還會介紹更爲高級的部分。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章