[译文]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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章