创建游戏内核(17)

创建游戏内核(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函数可以得到指向内存缓冲区的指针,可以安全地使用此指针存储声音。


你可能感兴趣的:(创建游戏内核(17))