[译文]JOAL教程 第一课 单一固定声源

前端之家收集整理的这篇文章主要介绍了[译文]JOAL教程 第一课 单一固定声源前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

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

原文作者:Athomas Goldberg

译文:三向板砖

转载请保留以上信息。

本节课程的学习笔记,记录了课程中值得注意的问题以及方便复制测试的连续代码片段:

http://www.jb51.cc/article/p-zvxoaybm-ye.html

第一课 单一固定声源

本文是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 };
    //听众的位置
    static float[] listenerPos = { 0.0f,0.0f };
    //听众的速度矢量
    static float[] listenerVel = { 0.0f,0.0f };
    //听众的朝向. (前三个参数表示“脸”的正对方向,后三个参数表示“头顶”方向)[原文为first 3 elements are "at",second 3 are "up"]
    static float[] listenerOri = { 0.0f,-1.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,0);
        	al.alDeleteSources(1,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——译者注],但我们总得从这里开始,随着我们的进一步深入还会介绍更为高级的部分。

猜你在找的设计模式相关文章