天行健 君子当自强而不息

创建游戏内核(19)【OO改良版】

 

本篇是创建游戏内核(18)【OO改良版】的续篇,关于该内核的细节说明请参阅创建游戏内核(19)


接口:

//======================================================================================
// This class encapsulate sound buffer playing.
//======================================================================================
typedef class SOUND_CHANNEL
{
    friend 
class SOUND;

public:
    SOUND_CHANNEL();
    ~SOUND_CHANNEL();

    IDirectSoundBuffer8*    get_ds_buffer();
    IDirectSoundNotify8*    get_ds_notify();

    BOOL create(SOUND_PTR sound, 
long frequency, short channels, short bits_per_sample);
    
void free();

    BOOL play(
const SOUND_DATA_PTR sound_data, long volume_percent, long loop_times);
    
void stop();

    
long get_volume();
    BOOL set_volume(
long percent);

    signed 
long get_pan();
    BOOL set_pan(signed 
long level);

    
long get_frequency();
    BOOL set_frequency(
long frequency);

    BOOL is_playing();

protected:
    BOOL _buffer_data();
    
void _update();

protected:
    SOUND_PTR               m_sound;         
// pointer to parent sound object
    IDirectSoundBuffer8*    m_ds_buffer;     // pointer to DirectSound buffer object
    IDirectSoundNotify8*    m_ds_notify;     // pointer to DirectSound notify object

    
short                   m_event_index;   

    
long                    m_volume;        // sound buffer volume
    signed long             m_pan;           // sound buffer pan
    BOOL                    m_is_playing;    // sound buffer playing flag
    long                    m_loop_times;    // loop times

    
long                    m_frequency;
    
short                   m_bits_per_sample;
    
short                   m_channels;

    SOUND_DATA              m_sound_data;

    
short                   m_load_section;  // sound section will to be loaded
    short                   m_stop_section;  // sound section will to be stoped
    short                   m_next_notify;   // sound notification index will to be played
} *SOUND_CHANNEL_PTR;

实现:
//------------------------------------------------------------------------------
// Constructor, initialize member data.
//------------------------------------------------------------------------------
SOUND_CHANNEL::SOUND_CHANNEL()
{
    m_sound     = NULL;
    m_ds_buffer = NULL;
    m_ds_notify = NULL;

    m_event_index = -1;

    m_volume     = 0;
    m_pan        = 0;
    m_frequency  = 0;
    m_is_playing = FALSE;
}

//------------------------------------------------------------------------------
// Destructor, release sound buffer and sound notification, set the event state 
// to nonsignaled.
//------------------------------------------------------------------------------
SOUND_CHANNEL::~SOUND_CHANNEL()
{
    free();
}

//------------------------------------------------------------------------------
// Release sound buffer and sound notification, set the event state to nonsignaled.
//------------------------------------------------------------------------------
void SOUND_CHANNEL::free()
{
    
// stop any playback
    stop();

    
// release the sound notification and sound buffer
    release_com(m_ds_notify);    
    release_com(m_ds_buffer);

    
// release event from parent SOUND class
    m_sound->release_event(this, &m_event_index);

    
// set to no parent sound
    m_sound = NULL;
}

//------------------------------------------------------------------------------
// Stop playing DirectSound buffer.
//------------------------------------------------------------------------------
void SOUND_CHANNEL::stop()
{
    
if(m_ds_buffer)
        m_ds_buffer->Stop();

    m_is_playing = FALSE;
}

//------------------------------------------------------------------------------
// Return pointer to DirectSound buffer.
//------------------------------------------------------------------------------
IDirectSoundBuffer8* SOUND_CHANNEL::get_ds_buffer()
{
    
return m_ds_buffer;
}

//------------------------------------------------------------------------------
// Return pointer to DirectSound notify.
//------------------------------------------------------------------------------
IDirectSoundNotify8* SOUND_CHANNEL::get_ds_notify()
{
    
return m_ds_notify;
}

//------------------------------------------------------------------------------
// Create sound buffer, set sound notification and event.
//------------------------------------------------------------------------------
BOOL SOUND_CHANNEL::create(SOUND_PTR sound, long frequency, short channels, short bits_per_sample)
{
    
// free a prior channel
    free();

    m_sound = sound;

    
if(m_sound == NULL || m_sound->get_directsound() == NULL)
        
return FALSE;

    
// save playback format
    m_frequency       = frequency;
    m_bits_per_sample = bits_per_sample;
    m_channels        = channels;

    WAVEFORMATEX _wave_format;

    
// create a new sound buffer for this channel, using specified format.

    ZeroMemory(&_wave_format, 
sizeof(WAVEFORMATEX));

    _wave_format.wFormatTag      = WAVE_FORMAT_PCM;
    _wave_format.nChannels       = (WORD) m_channels;
    _wave_format.nSamplesPerSec  = m_frequency;
    _wave_format.wBitsPerSample  = (WORD) m_bits_per_sample;
    _wave_format.nBlockAlign     = _wave_format.wBitsPerSample / 8 * _wave_format.nChannels;
    _wave_format.nAvgBytesPerSec = _wave_format.nSamplesPerSec * _wave_format.nBlockAlign;

    DSBUFFERDESC _buffer_desc;

    ZeroMemory(&_buffer_desc, 
sizeof(DSBUFFERDESC));

    _buffer_desc.dwSize          = 
sizeof(DSBUFFERDESC);
    _buffer_desc.dwFlags         = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY | 
                                   DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_LOCSOFTWARE;
    _buffer_desc.dwBufferBytes   = g_sound_buffer_size;
    _buffer_desc.lpwfxFormat     = &_wave_format;

    IDirectSoundBuffer* _ds_buffer;

    
if(FAILED(m_sound->get_directsound()->CreateSoundBuffer(&_buffer_desc, &_ds_buffer, NULL)))
        
return FALSE;

    
// query for newer interface
    if(FAILED(_ds_buffer->QueryInterface(IID_IDirectSoundBuffer8, (void**) &m_ds_buffer)))
    {
        _ds_buffer->Release();
        
return FALSE;
    }

    
// release old object - we have the newer one now
    _ds_buffer->Release();

    
// create the notification interface
    if(FAILED(m_ds_buffer->QueryInterface(IID_IDirectSoundNotify8, (void**) &m_ds_notify)))
        
return FALSE;

    HANDLE _event_handle;

    
// get an event for this sound channel
    if(! m_sound->assign_event_for_sound_channel(this, &m_event_index, &_event_handle))
        
return FALSE;

    DSBPOSITIONNOTIFY _pos_notify[4];

    
// setup the 4 notification positions
    _pos_notify[0].dwOffset     = g_sound_buffer_chunk - 1;
    _pos_notify[0].hEventNotify = _event_handle;
    _pos_notify[1].dwOffset     = g_sound_buffer_chunk * 2 - 1;
    _pos_notify[1].hEventNotify = _event_handle;
    _pos_notify[2].dwOffset     = g_sound_buffer_chunk * 3 - 1;
    _pos_notify[2].hEventNotify = _event_handle;
    _pos_notify[3].dwOffset     = g_sound_buffer_size - 1;
    _pos_notify[3].hEventNotify = _event_handle;

    
if(FAILED(m_ds_notify->SetNotificationPositions(4, _pos_notify)))
        
return FALSE;

    
// set pan and default volume
    set_volume(100);
    set_pan(0);

    
return TRUE;
}

//------------------------------------------------------------------------------
// Play sound buffer.
//------------------------------------------------------------------------------
BOOL SOUND_CHANNEL::play(const SOUND_DATA_PTR sound_data, long volume_percent, long loop_times)
{
    
if(sound_data == NULL || m_ds_buffer == NULL || m_ds_notify == NULL)
        
return FALSE;

    
// stop any playback
    stop();

    
// restore a lost buffer just in case
    m_ds_buffer->Restore();

    
// setup playing information
    m_sound_data.copy_sound_data(sound_data);

    
// set looping times
    m_loop_times = loop_times;

    
// calculate stop section position
    if(m_loop_times == 0)
        m_stop_section = -1;
    
else
        m_stop_section = (
short
            (((m_sound_data.m_buffer_size * m_loop_times) % g_sound_buffer_size) / g_sound_buffer_chunk) ;

    m_load_section = 0;

    
// load sound data into sound buffer from sound file or sound data object
    _buffer_data();
    _buffer_data();
    _buffer_data();
    _buffer_data();

    
// set the volume
    set_volume(volume_percent);

    
// set position and begin play

    m_next_notify = 0;

    
if(FAILED(m_ds_buffer->SetCurrentPosition(0)))
        
return FALSE;

    
if(FAILED(m_ds_buffer->Play(0, 0, DSBPLAY_LOOPING)))
        
return FALSE;

    
// flag as playing
    m_is_playing = TRUE;

    
return TRUE;
}

//------------------------------------------------------------------------------
// Get sound buffer volume.
//------------------------------------------------------------------------------
long SOUND_CHANNEL::get_volume()
{
    
return m_volume;
}

//------------------------------------------------------------------------------
// Set volume for sound buffer.
//------------------------------------------------------------------------------
BOOL SOUND_CHANNEL::set_volume(long percent)
{
    
if(! set_ds_buffer_volume(m_ds_buffer, percent))
        
return FALSE;

    m_volume = percent % 101;

    
return TRUE;
}

//------------------------------------------------------------------------------
// Get sound buffer pan.
//------------------------------------------------------------------------------
signed long SOUND_CHANNEL::get_pan()
{
    
return m_pan;
}

//------------------------------------------------------------------------------
// Set pan for sound buffer.
//------------------------------------------------------------------------------
BOOL SOUND_CHANNEL::set_pan(long level)
{
    
if(! set_ds_buffer_pan(m_ds_buffer, level))
        
return FALSE;    

    m_pan = level % 101;

    
return TRUE;
}

//------------------------------------------------------------------------------
// Get sound buffer frequency.
//------------------------------------------------------------------------------
long SOUND_CHANNEL::get_frequency()
{
    
return m_frequency;
}

//------------------------------------------------------------------------------
// Set frequency for sound buffer.
//------------------------------------------------------------------------------
BOOL SOUND_CHANNEL::set_frequency(long frequency)
{
    
if(m_ds_buffer == NULL)
        
return FALSE;

    
if(FAILED(m_ds_buffer->SetFrequency(frequency)))
        
return FALSE;

    m_frequency = frequency;

    
return TRUE;
}

//------------------------------------------------------------------------------
// Checks whether sound buffer is playing.
//------------------------------------------------------------------------------
BOOL SOUND_CHANNEL::is_playing()
{
    
if(m_sound == NULL || m_ds_buffer == NULL || m_ds_notify == NULL)
        
return FALSE;

    
return m_is_playing;
}

//------------------------------------------------------------------------------
// Load sound data into sound buffer from sound file or sound data object.
//------------------------------------------------------------------------------
BOOL SOUND_CHANNEL::_buffer_data()
{
    
if(m_ds_buffer == NULL)
        
return FALSE;

    
// setup position to load in
    long _lock_pos = (m_load_section % 4) * g_sound_buffer_chunk;

    
long  _size;
    
char* _ptr;

    
// lock sound buffer to get pointer to sound data
    if(FAILED(m_ds_buffer->Lock(_lock_pos, g_sound_buffer_chunk, (void**) &_ptr, (DWORD*) &_size, NULL, NULL, 0)))
        
return FALSE;

    
// clear out buffer if nothing left to load
    if(m_sound_data.m_left_size == 0)
        ZeroMemory(_ptr, _size);
    
else
    {
        
// load in the data - take looping into account
        long _load_size = _size;
        
long _load_pos  = 0;

        
// load sound data until specfied load _size is satisfied
        for(;;)
        {
            
if(m_sound_data.m_left_size > _load_size)
            {
                
// load into sound data from buffer
                memcpy(&_ptr[_load_pos], &m_sound_data.m_ptr[m_sound_data.m_file_curr_pos], _load_size);

                
// decrease _size of sound data needed to load, advance current sound buffer position.
                m_sound_data.m_left_size     -= _load_size;
                m_sound_data.m_file_curr_pos += _load_size;

                
break;
            }
            
else        // m_sound_data._left_size <= _load_size
            {
                
// load in sound data from buffer
                memcpy(&_ptr[_load_pos], &m_sound_data.m_ptr[m_sound_data.m_file_curr_pos], m_sound_data.m_left_size);

                
// decrease _size of sound data needed to load, advance current sound buffer position.
                _load_size -= m_sound_data.m_left_size;
                _load_pos  += m_sound_data.m_left_size;

                
// check if we need to stop loop
                if(m_loop_times >= 1)
                {
                    m_loop_times--;

                    
if(m_loop_times == 0)
                    {
                        
// clear out remaining buffer space
                        if(_load_size)
                            ZeroMemory(&_ptr[_load_pos], _load_size);

                        m_sound_data.m_left_size = 0L;
                        
break;
                    }
                }

                
// reset sound data current position and left _size
                m_sound_data.m_file_curr_pos = m_sound_data.m_file_start_pos;
                m_sound_data.m_left_size     = m_sound_data.m_buffer_size;

                
// set if we need to stop loading data
                if(_load_size == 0)
                    
break;
            }
        }
    }

    
// unlock the buffer
    m_ds_buffer->Unlock(_ptr, _size, NULL, 0);

    
// mark next section to load
    if(++m_load_section > 3)
        m_load_section = 0;

    
return TRUE;
}

//------------------------------------------------------------------------------
// Update for sound buffer playing.
//------------------------------------------------------------------------------
void SOUND_CHANNEL::_update()
{
    
// check for end of sound
    if(m_next_notify == m_stop_section && m_sound_data.m_left_size == 0)
        stop();
    
else
    {
        
// buffer in more data
        _buffer_data();

        
if(++m_next_notify > 3)
            m_next_notify = 0;
    }
}

测试代码:
/*****************************************************************************
PURPOSE:
    Test for class SOUND, SOUND_DATA, SOUND_CHANNEL.
*****************************************************************************/


#include "core_common.h"
#include "core_framework.h"
#include "core_sound.h"

class APP : public FRAMEWORK
{
public:
    BOOL init()
    {
        
// Initialize DierctSound and DirectMusic.
        m_sound.init(g_hwnd, 22050, 1, 16, DSSCL_NORMAL);

        
// load into sound data from wave file
        m_sound_data[0].load_wav("test1.wav");
        m_sound_data[1].load_wav("test2.wav");
        
        
// create sound channel

        m_sound_channel[0].create(&m_sound, 
            m_sound_data[0].get_frequency(), m_sound_data[0].get_channels(), m_sound_data[0].get_bits_per_sample());

        m_sound_channel[1].create(&m_sound, 
            m_sound_data[1].get_frequency(), m_sound_data[1].get_channels(), m_sound_data[1].get_bits_per_sample());

        
// play sound
        m_sound_channel[0].play(&m_sound_data[0], 100, 1);
        m_sound_channel[1].play(&m_sound_data[1], 50, 0); 
// lopping forever   
        
        
return TRUE;
    }

    BOOL frame()
    {
        
return TRUE;
    }

    BOOL shutdown()
    {
        
return TRUE;
    }

private:
    SOUND m_sound;
    SOUND_DATA m_sound_data[2];
    SOUND_CHANNEL m_sound_channel[2];

    FILE* _fp;
};

int PASCAL WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
    APP app;

    
if(! build_window(inst, "MainClass", "MainWindow", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480))
        
return -1;
    
    app.run();

    
return 0;
}

posted on 2007-10-10 20:28 lovedday 阅读(223) 评论(0)  编辑 收藏 引用


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


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论