天行健 君子当自强而不息

创建游戏内核(17)

 

有关DirectAudio和DirectShow的基础知识请参阅用DirectX Audio和DirectShow播放声音和音乐

 

声音内核为快速和容易地将声音和音乐加入到游戏中提供了一种解决方案,声音内核包含6个类组件,如下表所示:
 

说明
SOUND 包含DirectSound和DirectMusic对象,并控制音频流(sound streaming)。
SOUND_DATA 这个类包含了使用SOUND_CHANNEL播放的波形数据。
SOUND_CHANNEL 这个类用于播放单个声音,这种类一次最多可以同时使用32个(也就是说可以播放32个同步的声音)。
MUSIC_CHANNEL 可以使用这个类播放单个歌曲文件,无论此文件是MIDI文件还是DirectMusic本地歌曲,一次只能使用一个这样的类。
DLS 可下载声音(downloadable sound)类对象,这个类允许用户将不同的乐器加载到MUSIC_CHANNEL对象。
MP3 一个.mp3音乐播放类对象,这个类允许用户播放.mp3歌曲并检测这些歌曲的当前播放状态。

 

波形数据和SOUND_DATA

SOUND_DATA类对象用于描述和包含单个的声音(波形)。声音频率(sound frequency)、采样精度(bits-per-sample)、音频声道数(number of channels)、文件大小以及声源(source)都包含在SOUND_DATA类的声明中,来看看它的定义:

//======================================================================================
// This class encapsulate how to load sound data.
//======================================================================================
class SOUND_DATA
{
private:
    friend class SOUND_CHANNEL; // let SOUND_CHANNEL can use this class's member data and function

protected:
    long _frequency;
    short _channels;
    short _bits_per_sample;

    FILE*   _fp;                // pointer to sound file
    char*   _ptr;               // pointer to current sound buffer will to be played
    char*   _buf;               // buffer to store sound data

    long    _buffer_size;       // sound buffer size
    long    _left_size;         // left size of sound buffer which need to loaded

    long    _file_start_pos;    // start position of wave file will to be loaded
    long    _file_curr_pos;     // current position of wave file will to be loaded

public:
    SOUND_DATA();
    ~SOUND_DATA();

    char* get_ptr();
    long  get_size();

    BOOL create();
    BOOL create(long size);
    void free();

    void set_format(long frequency, short channels, short bits_per_sample);
    void set_source(FILE* fp, long pos = -1, long size = -1);
    void set_source(void* ptr, long pos = -1, long size = -1);

    BOOL load_wav(char* filename, FILE* fp = NULL);
    BOOL load_wav_header(char* filename, FILE* fp = NULL);

    BOOL copy(SOUND_DATA* source);
};
 

#pragma pack(1)

// .WAV file header
struct WAVE_HEADER
{
    
char    riff_sig[4];            // 'RIFF'
    long    waveform_chunk_size;    // 8
    char    wave_sig[4];            // 'WAVE'
    char    format_sig[4];          // 'fmt ' (notice space after)
    long    format_chunk_size;      // 16;
    short   format_tag;             // WAVE_FORMAT_PCM
    short   channels;               // # of channels
    long    sample_rate;            // sampling rate
    long    bytes_per_sec;          // bytes per second
    short   block_align;            // sample block alignment
    short   bits_per_sample;        // bits per second
    char    data_sig[4];            // 'data'
    long    data_size;              // size of waveform data
};

#pragma pack()
 

接着来看看SOUND_DATA的实现:

//------------------------------------------------------------------------------
// Constructor, initialize member data.
//------------------------------------------------------------------------------
SOUND_DATA::SOUND_DATA()
{
    memset(
this, 0, sizeof(*this));

    _frequency       = 22050;
    _channels        = 1;
    _bits_per_sample = 16;    
}

//------------------------------------------------------------------------------
// Destructor, release sound data buffer.
//------------------------------------------------------------------------------
SOUND_DATA::~SOUND_DATA()
{
    free();
}

//------------------------------------------------------------------------------
// Create sound data buffer.
//------------------------------------------------------------------------------
BOOL SOUND_DATA::create()
{
    
return create(_buffer_size);
}

//------------------------------------------------------------------------------
// Create sound data buffer with specified size.
//------------------------------------------------------------------------------
BOOL SOUND_DATA::create(long size)
{
    
// free prior allocated data
    free();

    
// check for valid size
    if((_buffer_size = size) == 0)
        
return FALSE;

    
// create a new buffer
    _buf = new char[_buffer_size];
    
if(_buf == NULL)
        
return FALSE;

    
// clear out new buffer
    ZeroMemory(_buf, _buffer_size);

    
// point to new buffer
    _ptr = _buf;
    _fp  = NULL;    

    
return TRUE;
}

//------------------------------------------------------------------------------
// Release sound data buffer.
//------------------------------------------------------------------------------
void SOUND_DATA::free()
{
    
if(_buf != NULL)
    {
        delete[] _buf;
        _buf = NULL;
    }

    _ptr = NULL;
    _buffer_size = 0;
}

//------------------------------------------------------------------------------
// Get pointer to sound data buffer.
//------------------------------------------------------------------------------
char* SOUND_DATA::get_ptr()
{
    
return _buf;
}

//------------------------------------------------------------------------------
// Get size of sound data buffer.
//------------------------------------------------------------------------------
long SOUND_DATA::get_size()
{
    
return _buffer_size;
}

//------------------------------------------------------------------------------
// Set play property for sound data buffer.
//------------------------------------------------------------------------------
void SOUND_DATA::set_format(long frequency, short channels, short bits_per_sample)
{
    _frequency  = frequency;
    _channels   = channels;
    _bits_per_sample = bits_per_sample;
}

//------------------------------------------------------------------------------
// Set position and size for sound data buffer.
//------------------------------------------------------------------------------
void SOUND_DATA::set_source(FILE* fp, long pos, long size)
{
    _fp  = fp;
    _ptr = NULL;

    
if(pos != -1)
        _file_start_pos = _file_curr_pos = pos;

    
if(size != -1)
        _buffer_size = _left_size = size;
}

//------------------------------------------------------------------------------
// Set position and size for sound buffer.
//------------------------------------------------------------------------------
void SOUND_DATA::set_source(void* ptr, long pos, long size)
{
    _fp = NULL;
    _ptr = (
char*) ptr;

    
if(pos != -1)
        _file_start_pos = _file_curr_pos = pos;

    
if(size != -1)
        _buffer_size = _left_size = size;
}

//------------------------------------------------------------------------------
// Load in wave file. 
//------------------------------------------------------------------------------
BOOL SOUND_DATA::load_wav(char* filename, FILE* fp)
{
    
// load wave header information first
    if(! load_wav_header(filename, fp))
        
return FALSE;

    
// create sound data buffer.
    if(! create())
        
return FALSE;

    
// open file, seek to position and read in data.

    
if(filename != NULL)
    {
        
if((fp = fopen(filename, "rb")) == NULL)
            
return FALSE;
    }

    fseek(fp, _file_start_pos, SEEK_SET);
    fread(_buf, 1, _buffer_size, fp);

    
// reset start position and current position
    _file_start_pos = _file_curr_pos = 0;

    
// close file
    if(filename != NULL)
        fclose(fp);

    
return TRUE;
}

//------------------------------------------------------------------------------
// Load wave header from file.
//------------------------------------------------------------------------------
BOOL SOUND_DATA::load_wav_header(char* filename, FILE* fp)
{
    
if(filename == NULL && fp == NULL)
        
return FALSE;

    
if(filename != NULL)
    {
        
if((fp = fopen(filename, "rb")) == NULL)
            
return FALSE;
    }

    
// save position in file
    long org_file_pos = ftell(fp);

    BOOL rv = FALSE;

    
// read in header and parse
    WAVE_HEADER wave_header;

    fread(&wave_header, 1, 
sizeof(WAVE_HEADER), fp);

    
// check signature
    if(!memcmp(wave_header.riff_sig, "RIFF", 4) && !memcmp(wave_header.wave_sig, "WAVE", 4) &&
       !memcmp(wave_header.format_sig, "fmt ", 4) && !memcmp(wave_header.data_sig, "data", 4))
    {
        
// all signatures satisfied

        _frequency = wave_header.sample_rate;
        _channels  = wave_header.channels;
        _bits_per_sample = wave_header.bits_per_sample;

        _buffer_size = _left_size = wave_header.data_size;
        _file_start_pos = _file_curr_pos = ftell(fp);

        rv = TRUE;
    }

    
// close if we opened file, otherwise return to original position.
    if(filename != NULL)
        fclose(fp);
    
else
        fseek(fp, org_file_pos, SEEK_SET);

    
return rv;
}

//------------------------------------------------------------------------------
// Copy sound data from another sound data.
//------------------------------------------------------------------------------
BOOL SOUND_DATA::copy(SOUND_DATA* source)
{
    
if(source == NULL)
        
return FALSE;

    
// note that _buf is not assigned!!

    _frequency       = source->_frequency;
    _channels        = source->_channels;
    _bits_per_sample = source->_bits_per_sample;

    _fp              = source->_fp;
    _ptr             = source->_ptr;
    _buffer_size     = source->_buffer_size;
    _left_size       = source->_left_size;
    _file_curr_pos   = source->_file_curr_pos;
    _file_start_pos  = source->_file_start_pos;

    
return TRUE;
}
 

使用SOUND_DATA类对象存储回放格式和声音的数据源,声音有两个来源:一个文件或内存缓冲区。另外,如果声音太大而不能放进内存中,可以设置成从音频流读取。

加载单个.wav文件,最快的方法就是使用SOUND_DATA::load_wav函数。load_wav函数带有两个参数:要加载的.wav文件的文件名以及源文件指针。对于这两个参数,只能用其中一个,同时将另外一个设置为NULL。源文件指针使程序员能够将多个.wav文件打包进单个文件,并且仍然能够单独加载这些被打包的.wav文件。

除了加载单个的.wav文件外,还可以采用设置声音的数据源这种方式。当声音文件太大(超过64k)而无法存放到声音缓冲区中的时候,这种方式就特别有用,方法就是将文件或内存缓冲区中的声音数据放到流中。SOUND_DATA::set_source函数正好用于解决这个问题,此函数有两个版本可供使用:

void set_source(FILE* fp, long pos = -1, long size = -1);
void set_source(void* ptr, long pos = -1, long size = -1);

可以选择一个源文件指针或一个内存指针,pos参数将声音数据的起始位置(偏移量)传送给SOUND_DATA类,size参数用于设置流的总字节数(声音的大小)。

注意pos和size的缺省值都是-1,就使得类能够对文件位置进行设置。为了达到设置声音数据源的目的,首先必须使用set_format函数设置回放格式,然后必须使用load_wav_header函数解析波形文件头,此函数所带参数的含义同load_wav函数相同。

另外,如果声音被存储到内存中以及从内存中流出,就必须使用SOUND_DATA::create函数创建此内存缓冲区。可以自己指定缓冲区的大小,也可以让create函数使用通过load_wav_header函数解析出来的缓冲区大小。调用SOUND_DATA::get_ptr函数可以得到指向内存缓冲区的指针,可以安全地使用此指针存储声音。


posted on 2007-09-25 00:23 lovedday 阅读(325) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论