游戏开发论坛

 找回密码
 立即注册
搜索
查看: 4176|回复: 0

Openal教程(六)

[复制链接]

108

主题

180

帖子

250

积分

中级会员

Rank: 3Rank: 3

积分
250
QQ
发表于 2004-9-4 15:16:00 | 显示全部楼层 |阅读模式
Openal教程(六)
高级导入和错误处理
虽然现在,我们能做出一些漂亮的东西,但是这些都没有要求我们精确
的处理他们。原因是我们写的代码是为了便于学习。因此,我们将移进
一些高级的东西。最重要的是我们将学习更高级的处理错误的方法。我们
也将重新改写载入声音数据的方法。
我们首先考虑这些函数将做什么。
string GetALErrorString(ALenum err);
/*
* 1) 识别错误代码
* 2)返回错误
*/


ALuint LoadALBuffer(string path);
/*
* 1) 建立缓冲区
* 2) 导入WAV文件
* 3) 返回缓冲区ID
*/

ALuint GetLoadedALBuffer(string path);
/*
* 1) 检测文件是否载入
* 2) 如果载入,返回缓冲区ID
* 3) 如果失败,重新载入并返回缓冲区ID
*/

ALuint LoadALSample(string path, bool loop);
/*
* 1) 建立源
* 2) 调用‘GtLoadedALBuffer' 中的’PATH‘加载文件?
* 3)返回源ID
*/

void KillALLoadedData();
/*
* 1)释放数据?
*/

bool LoadALData();
/*
* 1) 载入所有的源和缓冲区
*/

void KillALData();
/*
* 1) 释放所有的缓冲区
* 2) 释放所有的源
*/

vector<string> LoadedFiles; // 文件路径
vector<ALuint> Buffers; // 缓冲区.
vector<ALuint> Sources; // 源.

看一下这个函数,想一下他做什么。我们试着建立一个关于缓冲区和源
的系统。我们能调用来自文件的源并且系统能处理缓冲区的建立,因此,
我们不用复制缓冲区。系统将处理缓冲区并且将有效的处理资源。

string GetALErrorString(ALenum err)
{
    switch(err)
    {
        case AL_NO_ERROR:
            return string("AL_NO_ERROR");
        break;

        case AL_INVALID_NAME:
            return string("AL_INVALID_NAME");
        break;

        case AL_INVALID_ENUM:
            return string("AL_INVALID_ENUM");
        break;

        case AL_INVALID_VALUE:
            return string("AL_INVALID_VALUE");
        break;

        case AL_INVALID_OPERATION:
            return string("AL_INVALID_OPERATION");
        break;

        case AL_OUT_OF_MEMORY:
            return string("AL_OUT_OF_MEMORY");
        break;
    };
}
函数的功能是转换错误代码为字符。OPENAL SDK说返回'AL_OUT_OF_MEMORY'
是错误的,我们应认真对待所有错误,使我们的代码用最新的版本处理
数据。
string GetALCErrorString(ALenum err)
{
    switch(err)
    {
        case ALC_NO_ERROR:
            return string("AL_NO_ERROR");
        break;

        case ALC_INVALID_DEVICE:
            return string("ALC_INVALID_DEVICE");
        break;

        case ALC_INVALID_CONTEXT:
            return string("ALC_INVALID_CONTEXT");
        break;

        case ALC_INVALID_ENUM:
            return string("ALC_INVALID_ENUM");
        break;

        case ALC_INVALID_VALUE:
            return string("ALC_INVALID_VALUE");
        break;

        case ALC_OUT_OF_MEMORY:
            return string("ALC_OUT_OF_MEMORY");
        break;
    };
}
这个函数的功能是说明ALC错误。OPENAL和ALC共享ID,但是他们在一些功能上
不相等。
函数 'alGetError'应注意:OPENAL SDK定义一次只能得到一个错误。
当函数被调用时,他返回他得到的第一个错误,并且清错误为'AL_NO_ERROR'
ALuint LoadALBuffer(string path)
{
    // Variables to store data which defines the buffer.
    ALenum format;
    ALsizei size;
    ALvoid* data;
    ALsizei freq;
    ALboolean loop;

    // 缓冲区ID和错误检测变量
    ALuint buffer;
    ALenum result;

    // 产生缓冲区,看他是否成功建立.
    alGenBuffers(1, &buffer);

    if ((result = alGetError()) != AL_NO_ERROR)
        throw GetALErrorString(result);

    // 读WAV数据,检测是否成功。
    alutLoadWAVFile(szFilePath, &format, &data, &size, &freq, &loop);

    if ((result = alGetError()) != AL_NO_ERROR)
        throw GetALErrorString(result);

    // 装载WAV数据,检测是否成功
    alBufferData(buffer, format, data, size, freq);

    if ((result = alGetError()) != AL_NO_ERROR)
        throw GetALErrorString(result);

    // 出去临时数据
    alutUnloadWAV(format, data, size, freq);

    if ((result = alGetError()) != AL_NO_ERROR)
        throw GetALErrorString(result);

    //返回缓冲区ID
    return buffer;
}

在导入数据时,我们做了错误检测。没有任何错误将通过。当数据导入时,
没有足够的内存,WAV文件可能不退出,或者OPENAL函数中的错误数据
将产生错误。
ALuint GetLoadedALBuffer(string path)
{
    int count = 0; // 'count' 表明缓冲区列表

    ALuint buffer; // 用于导入缓冲区的缓冲区ID


    // 重复列表中的每个文件
    for(vector<string>::iterator iter = LoadedFiles.begin(); iter != LoadedFiles.end(); ++iter, count++)
    {
        // 如果文件已经导入,返回他的缓冲区ID.
        if(*iter == path)
            return Buffers[count];
    }

    // 如果文件是新的,我们将为他建立缓冲区.
    buffer = LoadALBuffer(path);

    // 添加缓冲区到列表,记录他已添加.
    Buffers.push_back(buffer);

    LoadedFiles.push_back(path);

    return buffer;
}
人们的麻烦通常在这里,但他确实不是很复杂。我们通过包含文件路径的列表
来检索。如果其中一个符合要求,我们直接返回他的ID到缓冲区。我们通过
这个函数导入我们的文件,这样就避免了复制时的浪费。每个文件应该保证
在自己的列表中。'Buffers'表类士于'LoadedFiles'表。
ALuint LoadALSample(string path, bool loop)
{
    ALuint source;
    ALuint buffer;
    ALenum result;

    // 得到文件缓冲区ID
    buffer = GetLoadedALBuffer(path);

    // 产生源.
    alGenSources(1 &source);

    if ((result = alGetError()) != AL_NO_ERROR)
        throw GetALErrorString(result);

    // 设置源属性.
    alSourcei (source, AL_BUFFER,   buffer   );
    alSourcef (source, AL_PITCH,    1.0      );
    alSourcef (source, AL_GAIN,     1.0      );
    alSourcefv(source, AL_POSITION, SourcePos);
    alSourcefv(source, AL_VELOCITY, SourceVel);
    alSourcei (source, AL_LOOPING,  loop     );

    // 保存源ID.
    Sources.push_back(source);

    // 返回源ID.
    return source;
}

现在我们已经建立处理缓冲区的系统,我们需要得到源的伸展。在这里,我们
得到了导入文件的缓冲区ID。这个缓冲区是一个新源,我们保存他并返回。
void KillALLoadedData()
{
    LoadedFiles.clear();
}
'gLoadedFilesv'存储在导入缓冲区的WAV文件的路径下,我们要处理他。
// 源ID's.

ALuint phaser1;
ALuint phaser2;

void LoadALData()
{
    // 你的应用在这里,不用担心缓冲区。
    phaser1 = LoadALSample("wavdata/phaser.wav", false);
    phaser2 = LoadALSample("wavdata/phaser.wav", true);

    KillLoadedALData();
}
他表示用于程序的所有的WAV应用的程序。我们能调用导入相同的WAV文件到不同的源,
'phaser.wav' 的缓冲区建立了一次,'gPhaser1' and 'gPhaser2' 用于背景
音乐的缓冲区。不用处理缓冲区因为系统会自动处理。
void KillALData()
{
    // 释放所有的缓冲区数据.
    for (vector<ALuint>::iterator iter = Buffers.begin(); iter != Buffers.end(); ++iter)
        alDeleteBuffers(1, iter);

    // 释放所有的源数据.
    for (vector<ALuint>::iterator iter = Sources.begin(); iter != Sources.end(); ++iter)
        alDeleteBuffers(1, iter);

    // 清除列表.
    Buffers.clear();
    Sources.clear();
}
我们已完成了前述的工作。然后就是释放他们。
try
    {
        InitOpenAL();

        LoadALData();
    }
    catch(string err)
    {
        cout << "OpenAL error: " << err.c_str() << endl;
    }
如果导入源时出错,我们将改正他。这将借助程序返回的报告。
That's it. A more advanced way of reporting errors, and a more robust way of loading your wav files. We may find we need to do some modifications in the future to allow for more flexibility, but for now we will be using this source for basic file loading in future tutorials. Expect future tutorials to expand on this code.

See the Java Bindings for OpenAL page for the Java version of this tutorial (adapted by: Athomas Goldberg)
如果大家有什么问题,请告诉我e-mailuyunfei12@mail.china.com.
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-8-18 02:01

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表