[译文]JOAL教程 第五课 多声源共享缓冲区

[译文]JOAL教程

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

原文作者:Athomas Goldberg

译文:三向板砖

转载请保留以上信息。

 

本节对应的学习笔记:http://blog.csdn.net/shuzhe66/article/details/40295895


第五课 多声源共享缓冲区

 

本文是DevMaster.net(http://devmaster.net/)的OpenAL教程对应的JOAL版本。C语言版原文作者为JesseMaurais

 

本次将会向大家展示在多个声源间共享缓冲区的方法。这个过程整体下来显得非常自然且合乎逻辑,它是如此地简单以至于你们当中的有些人已经自己掌握了。如果你是其中一位,那么你可以跳过本次教程了。但对于那些特别渴望着获得全部信息的人来讲,我当然会把这部分知识毫无保留的交给你,你会发现其中还是有很多乐趣的。顺带一提,这次我们会使用第四节课程中的内容直接实现Alc层,总之,你今后可能用得着本次实例程序。

 

好的,我们开始。

import java.io.*;
import java.nio.*;
import java.util.*;

import com.jogamp.openal.*;
import com.jogamp.openal.util.*;

public class SourceSharingBuffers {

static ALC alc;
static AL al;

// 缓冲区索引标记
public static final int THUNDER 	 = 0;
public static final int WATERDROP   = 1;
public static final int STREAM      = 2;
public static final int RAIN        = 3;

public static final int CHIMES      = 4;
public static final int OCEAN       = 5;
public static final int NUM_BUFFERS = 6;

//装载声音数据的缓冲区
static int[] buffers = new int[NUM_BUFFERS];

//声源列表
static List sources = new ArrayList();

//声源位置
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 };

//听众朝向
static float[] listenerOri = { 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f };

首先我们使用一系列静态常量来标记缓冲区对应在数组中的位置。我们使用了各种wav文件源,所以这里有多个缓冲区。我们使用一个线性表来代替数组储存声源,因为这样支持动态改变声源的数量。我们可以不断把声源加入到场景当中直到OpenAL将它们用完,这是教程中首次把声源作为一种可被用完的资源,确实,它们是有限的。

static int initOpenAL() {
    al = ALFactory.getAL();
    alc = ALFactory.getALC(); 
    ALCdevice device;
    ALCcontext context;
    String deviceSpecifier;
    String deviceName = "DirectSound3D"; //你可以指定一个其它的设备
    deviceName = null; //使用null可以获得系统默认的设备

    //得到设备句柄
    device = alc.alcOpenDevice(deviceName);

    //获得设备标识符
    deviceSpecifier = alc.alcGetString(device, ALC.ALC_DEVICE_SPECIFIER);

    System.out.println("Using device " + deviceSpecifier);

    //创建音频上下文
    context = alc.alcCreateContext(device, null);

    //将其设置为当前上下文
    alc.alcMakeContextCurrent(context);

    //检测错误
    if (alc.alcGetError(device) != ALC.ALC_NO_ERROR)
        return AL.AL_FALSE;

    return AL.AL_TRUE;
}

以上的代码来自于上一个教程,我们获得了DirectSound3D的设备句柄,并为我们的程序创建一个渲染上下文,之后将其设置为当前上下文并在返回成功前检测错误以保证一切都顺利完成。


static void exitOpenAL() {
    ALCcontext curContext;
    ALCdevice curDevice;

    //获得当前上下文
    curContext = alc.alcGetCurrentContext();

    //由上下文获得设备
    curDevice = alc.alcGetContextsDevice(curContext);

    //重置当前上下文为null
    alc.alcMakeContextCurrent(null);

    //释放上下文及设备
    alc.alcDestroyContext(curContext);
    alc.alcCloseDevice(curDevice);
}

以上方法将会作出与上一个代码片段相反的行为,它重新获得程序中使用的上下文及设备并释放它们,之后将当前上下文设置为null(默认值)以停止所有OpenAL对声音数据的处理过程。将当前上下文置为null极其重要,否则你可能会使用一个无效的上下文来处理音频数据,这么做的结果是不可预料的。

如果你的程序使用多个上下文,那么你可能需要一个更高级的方法来初始化与关闭,我建议使用全局变量保存所有的上下文及设备并在程序结束时逐一关闭而非只处理当前上下文。


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];

    //将数据放入缓冲区
    al.alGenBuffers(NUM_BUFFERS, buffers, 0);

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

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


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


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


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


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


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


    //进行错误检测并返回
    if (al.alGetError() != AL.AL_NO_ERROR)
        return AL.AL_FALSE;

    return AL.AL_TRUE;
}

我们把声源的创建过程完全移出了这个方法,因为本次我们将单独装载声源。


static void addSource(int type) {
    int[] source = new int[1];

    al.alGenSources(1, source, 0);

    if (al.alGetError() != AL.AL_NO_ERROR) {
        System.err.println("Error generating audio source.");
        System.exit(1);
    }

    al.alSourcei (source[0], AL.AL_BUFFER,   buffers[type]);
    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,  AL.AL_TRUE      );

    al.alSourcePlay(source[0]);

    sources.add(new Integer(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);
}

这个函数用来为我们创建声源,它将为每一个我们在之前代码中装载的缓冲区创建一个单独的声源。将之前代码中定义的静态缓冲区索引作为type参数传入,并在最终进行一次错误检测以保证确实创建了一个有效的声源(如我所说,它们是有限的)。如果声源无法分配,程序将会退出。


static void killALData() {

    Iterator iter = sources.iterator();
    while(iter.hasNext()) {
        al.alDeleteSources(1, new int[] { ((Integer)iter.next()).intValue() }, 0);
    }
    sources.clear();
    al.alDeleteBuffers(NUM_BUFFERS, buffers, 0);
    exitOpenAL();
}

这个函数由于线性表的存在而发生少许变化,我们逐一删除每一个声源并将线性表清空,这将有效的销毁它们。


public static void main(String[] args) {
    try {
      initOpenAL();
    } catch (ALException e) {
      e.printStackTrace();
      System.exit(1);
    }
    if (loadALData() == AL.AL_FALSE)
      System.exit(1);
    setListenerValues();

    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: \n" +
                               "\t'w' for Water Drop\n" +
                               "\t't' for Thunder\n" +
                               "\t's' for Stream\n" +
                               "\t'r' for Rain\n" +
                               "\t'o' for Ocean\n" +
                               "\t'c' for Chimes\n" +
                               "\n'q' to Quit\n");
            buf.read(c);
            switch(c[0]) {
                case 'w': addSource(WATERDROP); break;
                case 't': addSource(THUNDER); break;
                case 's': addSource(STREAM); break;
                case 'r': addSource(RAIN); break;
                case 'o': addSource(OCEAN); break;
                case 'c': addSource(CHIMES); break;
            }
        } catch (IOException e) {
			System.exit(1);
        }
    }
    killALData();
  } // main
} // class

这里是程序的主循环。大体上讲它等待一个键盘输入某一特定按键并依据按键的不同向场景中加入不同类型的声源。本质上将,这里做的内容非常像现实中人们听录音带休闲的情景。我们的程序可以让用户来选择当前播放的背景音乐,而且代码干净利落不是么?我在编码时就听着它,多么富有禅意啊(现在我还在听着)。

 

这个程序可以拓展到处理更多种类的wav文件,并添加在任意位置加入声源的功能,你也可以为加入的声源设置播放频率而不是单一循环。然而,这可能需要设计一个GUI程序,它已经超出了本次教程的范围。能够做出一个全功能的“天气引擎”就已经足够漂亮了;)





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值