天行健 君子当自强而不息

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

 

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


接口:

#define EVENT_NUM       32

BOOL set_ds_buffer_volume(LPDIRECTSOUNDBUFFER ds_buffer, 
long percent);
BOOL set_audio_path_volume(IDirectMusicPerformance8* dm_perf, 
long percent);
BOOL set_performance_tempo(IDirectMusicPerformance8* dm_perf, 
long percent);

//======================================================================================
// This class encapsulate for DirectPerformance and DirectLoader and sound play event handle.
//======================================================================================
typedef class SOUND
{
public:
    SOUND();
    ~SOUND();
    
    
// assign and release events
    BOOL assign_event_for_sound_channel(SOUND_CHANNEL* sound_channel, short* event_index, HANDLE* event_handle);
    BOOL release_event(SOUND_CHANNEL* sound_channel, 
short* event_index);

    
// function to retrieve com interfaces
    IDirectSound8*              get_directsound();
    IDirectSoundBuffer*         get_ds_buffer();
    IDirectMusicPerformance8*   get_dm_performance();
    IDirectMusicLoader8*        get_dm_loader();

    
// init and shutdown functions
    BOOL init(HWND hwnd, long frequency, short channels, short bits_per_sample, long coop_level);
    
void shutdown();

    
// volume get/get
    long get_volume();
    BOOL set_volume(
long percent);

    
// restore system to known state
    void restore();

private:
    
/////////////////////////////// Sound system related ///////////////////////////////

    HWND    m_hwnd;      
// pointer to parent window handle
    long    m_volume;    // global sound buffer volume

    // Events for all sound channel, the last event takes charge to close all other events.
    HANDLE  m_event_handle[EVENT_NUM+1]; 

    
// all sound channel pointers
    SOUND_CHANNEL*  m_sound_channel[EVENT_NUM];

    HANDLE  m_thread_handle;        
    DWORD   m_thread_id;
    BOOL    m_thread_active;

    
static DWORD    handle_notifications(LPVOID data);

    
/////////////////////////////// Sound related ///////////////////////////////

    IDirectSound8*          m_ds;
    IDirectSoundBuffer*     m_ds_buffer;

    
long    m_coop_level;
    
long    m_frequency;
    
short   m_channels;
    
short   m_bits_per_sample;

    
/////////////////////////////// Music related - MIDI ///////////////////////////////

    IDirectMusicPerformance8*   m_dm_perf;
    IDirectMusicLoader8*        m_dm_loader;
} *SOUND_PTR;

实现:
#define err_msg_box(msg) MessageBox(NULL, msg, "Error", MB_OK)

//------------------------------------------------------------------------------
// Set volume for direct sound buffer.
//------------------------------------------------------------------------------
BOOL set_ds_buffer_volume(LPDIRECTSOUNDBUFFER ds_buffer, long percent)
{
    
long volume;

    
if(ds_buffer == NULL)
        
return FALSE;

    
// calculate a usable volume level
    if(percent == 0)
        volume = DSBVOLUME_MIN;
    
else
        volume = -20 * (100 - (percent % 101));

    
if(FAILED(ds_buffer->SetVolume(volume)))
        
return FALSE;

    
return TRUE;
}

//------------------------------------------------------------------------------
// Set volume for the default audio path of DirectMusic perforfamce.
//------------------------------------------------------------------------------
BOOL set_audio_path_volume(IDirectMusicPerformance8* dm_perf, long percent)
{
    IDirectMusicAudioPath8* audio_path;
    
long volume;

    
if(dm_perf == NULL)
        
return FALSE;

    
// retrieves the default audiopath
    if(FAILED(dm_perf->GetDefaultAudioPath(&audio_path)))
        
return FALSE;

    
// calculate a usable volume level
    if(percent == 0)
        volume = -9600;
    
else
        volume = (
long) (-32.0 * (100.0 - (float)(percent % 101)));

    
// set the audio volume on the audiopath, the volume can be faded in or out.
    if(FAILED(audio_path->SetVolume(volume, 0)))
    {
        audio_path->Release();
        
return FALSE;
    }

    audio_path->Release();    

    
return TRUE;
}

//------------------------------------------------------------------------------
// Set tempo for DirectMusic performance.
//------------------------------------------------------------------------------
BOOL set_performance_tempo(IDirectMusicPerformance8* dm_perf, long percent)
{
    
if(dm_perf == NULL)
        
return FALSE;

    
// calculate tempo setting based on percentage
    float tempo = (float) percent / 100.0f;

    
// set master performance tempo
    if(FAILED(dm_perf->SetGlobalParam(GUID_PerfMasterTempo, (void*) &tempo, sizeof(float))))
        
return FALSE;

    
return TRUE;
}

//////////////////////////////////// defines for class SOUND ////////////////////////////////////

//------------------------------------------------------------------------------
// Constructor, initialize member data.
//------------------------------------------------------------------------------
SOUND::SOUND()
{
    
// initialize com
    CoInitialize(NULL);

    memset(
this, 0, sizeof(*this));
}

//------------------------------------------------------------------------------
// Destructor, release main sound buffer, close all event and thread.
//------------------------------------------------------------------------------
SOUND::~SOUND()
{
    shutdown();

    
// uninitialize com
    CoUninitialize();
}

//------------------------------------------------------------------------------
// Release main sound buffer, close all events and threads.
//------------------------------------------------------------------------------
void SOUND::shutdown()
{
    
// stop the music, and close down.
    if(m_dm_perf)
    {
        m_dm_perf->Stop(NULL, NULL, 0, 0);
        m_dm_perf->CloseDown();
    }

    
// release the DirectMusic objects
    release_com(m_dm_perf);
    release_com(m_dm_loader);

    
// go through all used sound channels and free them
    for(short i = 0; i < EVENT_NUM; i++)
    {
        
if(m_sound_channel[i])
        {
            m_sound_channel[i]->free();
            m_sound_channel[i] = NULL;
        }

        
// clear the event status
        if(m_event_handle[i])
            ResetEvent(m_event_handle[i]);
    }

    
// stop the primary channel from playing
    if(m_ds_buffer)
        m_ds_buffer->Stop();

    
// release the DirectSound objects
    release_com(m_ds_buffer);
    release_com(m_ds);

    
// force a closure of the thread by triggering the last event and waiting for it to terminate
    if(m_thread_handle)
    {
        
if(m_event_handle[EVENT_NUM])
        {
            
while(m_thread_active)
                
// set the specified event object to the signaled state
                SetEvent(m_event_handle[EVENT_NUM]);
        }        
    }

    
// close all event handles
    for(short i = 0; i < EVENT_NUM+1; i++)
    {
        
if(m_event_handle[i])
        {
            CloseHandle(m_event_handle[i]);
            m_event_handle[i] = NULL;
        }
    }

    
// free the thread handle
    if(m_thread_handle)
    {
        CloseHandle(m_thread_handle);
        m_thread_handle = NULL;
    }

    m_thread_id = 0;
}

//------------------------------------------------------------------------------
// Initialize DierctSound and DirectMusic, create a thread for handling notifications.
//------------------------------------------------------------------------------
BOOL SOUND::init(HWND hwnd, long frequency, short channels, short bits_per_sample, long coop_level)
{
    
// shutdown system in case of prior install
    shutdown();

    
// save parent window handle
    if((m_hwnd = hwnd) == NULL)
        
return FALSE;

    
///////////////////////////////////////////////////////////////////
    // Initialize DirectSound
    ///////////////////////////////////////////////////////////////////

    
// save settings for sound setup
    if(coop_level == DSSCL_NORMAL)
        coop_level = DSSCL_PRIORITY;

    m_coop_level      = coop_level;
    m_frequency       = frequency;
    m_channels        = channels;
    m_bits_per_sample = bits_per_sample;

    
// create an IDirectSound8 object
    if(FAILED(DirectSoundCreate8(NULL, &m_ds, NULL)))
        
return FALSE;

    
// set cooperative mode
    if(FAILED(m_ds->SetCooperativeLevel(m_hwnd, m_coop_level)))
        
return FALSE;

    
// create sound buffer

    DSBUFFERDESC _ds_buffer_desc;

    
// set sound buffer description

    ZeroMemory(&_ds_buffer_desc, 
sizeof(DSBUFFERDESC));

    _ds_buffer_desc.dwSize        = 
sizeof(DSBUFFERDESC);
    _ds_buffer_desc.dwFlags       = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
    _ds_buffer_desc.dwBufferBytes = 0;
    _ds_buffer_desc.lpwfxFormat   = NULL;

    
if(FAILED(m_ds->CreateSoundBuffer(&_ds_buffer_desc, &m_ds_buffer, NULL)))
        
return FALSE;

    
// set wave format for sound buffer

    WAVEFORMATEX _wave_format;

    
// set the primary buffer 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;

    
if(FAILED(m_ds_buffer->SetFormat(&_wave_format)))
        
return FALSE;

    
// create the events, plus an extra one for thread termination.
    for(short i = 0; i < EVENT_NUM+1; i++)
    {
        
if((m_event_handle[i] = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
            
return FALSE;
    }

    
// create a thread for handling notifications
    m_thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) handle_notifications, this, 0, &m_thread_id);

    
if(m_thread_handle == NULL)
        
return FALSE;

    
///////////////////////////////////////////////////////////////////
    // Initialize DirectMusic
    ///////////////////////////////////////////////////////////////////

    
// create the DirectMusic loader object
    CoCreateInstance(CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC, IID_IDirectMusicLoader8, (void**) &m_dm_loader);

    
// create the DirectMusic performance object
    CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC, IID_IDirectMusicPerformance8, (void**) &m_dm_perf);

    
// Initialize the performance with the standard audio path.
    // This initializes both DirectMusic and DirectSound and sets up the synthesizer. 
    m_dm_perf->InitAudio(NULL, NULL, m_hwnd, DMUS_APATH_SHARED_STEREOPLUSREVERB, 128, DMUS_AUDIOF_ALL, NULL);

    
// set the performance global volume to +10 decibels
    long _volume_level = 1000;

    
if(FAILED(m_dm_perf->SetGlobalParam(GUID_PerfMasterVolume, &_volume_level, sizeof(long))))
        
return FALSE;

    CHAR    _path[MAX_PATH];
    WCHAR   _w_path[MAX_PATH];

    
// tell DirectMusic where the default search path is

    GetCurrentDirectory(MAX_PATH, _path);
    MultiByteToWideChar(CP_ACP, 0, _path, -1, _w_path, MAX_PATH);

    m_dm_loader->SetSearchDirectory(GUID_DirectMusicAllTypes, _w_path, FALSE);

    
// set default volume to full
    set_volume(100);

    
return TRUE;
}

//------------------------------------------------------------------------------
// Return pointer to DirectSound.
//------------------------------------------------------------------------------
IDirectSound8* SOUND::get_directsound()
{
    
return m_ds;
}

//------------------------------------------------------------------------------
// Return pointer to primary DirectSound buffer.
//------------------------------------------------------------------------------
IDirectSoundBuffer* SOUND::get_ds_buffer()
{
    
return m_ds_buffer;
}

//------------------------------------------------------------------------------
// Return pointer to DirectMusic performance object.
//------------------------------------------------------------------------------
IDirectMusicPerformance8* SOUND::get_dm_performance()
{
    
return m_dm_perf;
}

//------------------------------------------------------------------------------
// Return pointer to DirectMusic loader object.
//------------------------------------------------------------------------------
IDirectMusicLoader8* SOUND::get_dm_loader()
{
    
return m_dm_loader;
}

//------------------------------------------------------------------------------
// Assign sound channel with specified event.
//------------------------------------------------------------------------------
BOOL SOUND::assign_event_for_sound_channel(SOUND_CHANNEL* sound_channel, short* event_index, HANDLE* event_handle)
{
    
for(short i = 0; i < EVENT_NUM; i++)
    {
        
if(m_event_handle[i] && m_sound_channel[i] == NULL)
        {
            
// set the specified event object to the nonsignaled state
            ResetEvent(m_event_handle[i]);

            m_sound_channel[i] = sound_channel;
            
            *event_index  = i;
            *event_handle = m_event_handle[i];

            
return TRUE;
        }
    }

    
return FALSE;
}

//------------------------------------------------------------------------------
// Set the event state to nonsignaled.
//------------------------------------------------------------------------------
BOOL SOUND::release_event(SOUND_CHANNEL* sound_channel, short* event_index)
{
    
if((unsigned short)(*event_index) < EVENT_NUM && m_sound_channel[*event_index] == sound_channel)
    {
        ResetEvent(m_event_handle[*event_index]);

        
// set event channel pointer with NULL
        m_sound_channel[*event_index] = NULL;
        *event_index = -1;

        
return TRUE;
    }

    
return FALSE;
}

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

//------------------------------------------------------------------------------
// Set the global sound volume.
//------------------------------------------------------------------------------
BOOL SOUND::set_volume(long percent)
{
    
if(! set_ds_buffer_volume(m_ds_buffer, percent))
        
return FALSE;

    m_volume = percent % 101;

    
return TRUE;
}

//------------------------------------------------------------------------------
// Handle all sound events.
//------------------------------------------------------------------------------
DWORD SOUND::handle_notifications(LPVOID data)
{
    MSG _msg;

    SOUND* _sound = (SOUND*) data;

    _sound->m_thread_active = TRUE;

    BOOL _complete = FALSE;

    
while(! _complete)
    {
        
// wait for a message
        DWORD _result = MsgWaitForMultipleObjects(EVENT_NUM+1, _sound->m_event_handle, FALSE, INFINITE, QS_ALLEVENTS);

        
// get channel index to update
        DWORD _channel_index = _result - WAIT_OBJECT_0;

        
// check for channel update
        if(_channel_index >= 0 && _channel_index < EVENT_NUM)
        {
            
if(_sound->m_sound_channel[_channel_index])
                _sound->m_sound_channel[_channel_index]->_update();
        }
        
else if(_channel_index == EVENT_NUM) // check for thread closure
            _complete = TRUE;
        
else if(_channel_index > EVENT_NUM)  // check for waiting messages
        {
            
while(PeekMessage(&_msg, NULL, 0, 0, PM_REMOVE))
            {
                
if(_msg.message == WM_QUIT)
                {
                    _complete = TRUE;
                    
break;
                }
            }
        }
    }

    _sound->m_thread_active = FALSE;

    
return 0L;
}

//------------------------------------------------------------------------------
// Restore primary DirectSound buffer and DirectSound channel buffer.
//------------------------------------------------------------------------------
void SOUND::restore()
{
    
// restore primary sound buffer
    if(m_ds_buffer != NULL)
        m_ds_buffer->Restore();

    
// restore all used sound channels buffer
    for(short i = 0; i < EVENT_NUM; i++)
    {
        
if(m_sound_channel[i] != NULL)
            m_sound_channel[i]->m_ds_buffer->Restore();
    }
}

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


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


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论