www.pudn.com > T3D-3.rar > t3dlib3.cpp
// T3DLIB3.CPP - Game Engine Part III, sound & music // INCLUDES /////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN #include// include important windows stuff #include #include #include #include // include important C/C++ stuff #include #include #include #include #include #include #include #include #include #include #include #include #include // directX includes #include #include #include #include #include #include "T3DLIB3.H" // DEFINES //////////////////////////////////////////////// // TYPES ////////////////////////////////////////////////// // PROTOTYPES ///////////////////////////////////////////// // EXTERNALS ///////////////////////////////////////////// extern HWND main_window_handle; // access to main window handle in main module // GLOBALS //////////////////////////////////////////////// // directsound stuff LPDIRECTSOUND lpds = NULL; // directsound interface pointer DSBUFFERDESC dsbd; // directsound description DSCAPS dscaps; // directsound caps HRESULT dsresult; // general directsound result DSBCAPS dsbcaps; // directsound buffer caps LPDIRECTSOUNDBUFFER lpdsbprimary = NULL; // the primary mixing buffer pcm_sound sound_fx[MAX_SOUNDS]; // the array of secondary sound buffers WAVEFORMATEX pcmwf; // generic waveformat structure // direct music globals IDirectMusicPerformance *dm_perf = NULL; // the directmusic performance manager IDirectMusicLoader *dm_loader = NULL; // the directmusic loader // this hold all the directmusic midi objects DMUSIC_MIDI dm_midi[DM_NUM_SEGMENTS]; int dm_active_id = -1; // currently active midi segment // FUNCTIONS ////////////////////////////////////////////// int DSound_Load_WAV(char *filename, int control_flags) { // this function loads a .wav file, sets up the directsound // buffer and loads the data into memory, the function returns // the id number of the sound HMMIO hwav; // handle to wave file MMCKINFO parent, // parent chunk child; // child chunk WAVEFORMATEX wfmtx; // wave format structure int sound_id = -1, // id of sound to be loaded index; // looping variable UCHAR *snd_buffer, // temporary sound buffer to hold voc data *audio_ptr_1=NULL, // data ptr to first write buffer *audio_ptr_2=NULL; // data ptr to second write buffer DWORD audio_length_1=0, // length of first write buffer audio_length_2=0; // length of second write buffer // step one: are there any open id's ? for (index=0; index < MAX_SOUNDS; index++) { // make sure this sound is unused if (sound_fx[index].state==SOUND_NULL) { sound_id = index; break; } // end if } // end for index // did we get a free id? if (sound_id==-1) return(-1); // set up chunk info structure parent.ckid = (FOURCC)0; parent.cksize = 0; parent.fccType = (FOURCC)0; parent.dwDataOffset = 0; parent.dwFlags = 0; // copy data child = parent; // open the WAV file if ((hwav = mmioOpen(filename, NULL, MMIO_READ | MMIO_ALLOCBUF))==NULL) return(-1); // descend into the RIFF parent.fccType = mmioFOURCC('W', 'A', 'V', 'E'); if (mmioDescend(hwav, &parent, NULL, MMIO_FINDRIFF)) { // close the file mmioClose(hwav, 0); // return error, no wave section return(-1); } // end if // descend to the WAVEfmt child.ckid = mmioFOURCC('f', 'm', 't', ' '); if (mmioDescend(hwav, &child, &parent, 0)) { // close the file mmioClose(hwav, 0); // return error, no format section return(-1); } // end if // now read the wave format information from file if (mmioRead(hwav, (char *)&wfmtx, sizeof(wfmtx)) != sizeof(wfmtx)) { // close file mmioClose(hwav, 0); // return error, no wave format data return(-1); } // end if // make sure that the data format is PCM if (wfmtx.wFormatTag != WAVE_FORMAT_PCM) { // close the file mmioClose(hwav, 0); // return error, not the right data format return(-1); } // end if // now ascend up one level, so we can access data chunk if (mmioAscend(hwav, &child, 0)) { // close file mmioClose(hwav, 0); // return error, couldn't ascend return(-1); } // end if // descend to the data chunk child.ckid = mmioFOURCC('d', 'a', 't', 'a'); if (mmioDescend(hwav, &child, &parent, MMIO_FINDCHUNK)) { // close file mmioClose(hwav, 0); // return error, no data return(-1); } // end if // finally!!!! now all we have to do is read the data in and // set up the directsound buffer // allocate the memory to load sound data snd_buffer = (UCHAR *)malloc(child.cksize); // read the wave data mmioRead(hwav, (char *)snd_buffer, child.cksize); // close the file mmioClose(hwav, 0); // set rate and size in data structure sound_fx[sound_id].rate = wfmtx.nSamplesPerSec; sound_fx[sound_id].size = child.cksize; sound_fx[sound_id].state = SOUND_LOADED; // set up the format data structure memset(&pcmwf, 0, sizeof(WAVEFORMATEX)); pcmwf.wFormatTag = WAVE_FORMAT_PCM; // pulse code modulation pcmwf.nChannels = 1; // mono pcmwf.nSamplesPerSec = 11025; // always this rate pcmwf.nBlockAlign = 1; pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign; pcmwf.wBitsPerSample = 8; pcmwf.cbSize = 0; // prepare to create sounds buffer dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = control_flags | DSBCAPS_STATIC | DSBCAPS_LOCSOFTWARE; dsbd.dwBufferBytes = child.cksize; dsbd.lpwfxFormat = &pcmwf; // create the sound buffer if (FAILED(lpds->CreateSoundBuffer(&dsbd,&sound_fx[sound_id].dsbuffer,NULL))) { // release memory free(snd_buffer); // return error return(-1); } // end if // copy data into sound buffer if (FAILED(sound_fx[sound_id].dsbuffer->Lock(0, child.cksize, (void **) &audio_ptr_1, &audio_length_1, (void **)&audio_ptr_2, &audio_length_2, DSBLOCK_FROMWRITECURSOR))) return(0); // copy first section of circular buffer memcpy(audio_ptr_1, snd_buffer, audio_length_1); // copy last section of circular buffer memcpy(audio_ptr_2, (snd_buffer+audio_length_1),audio_length_2); // unlock the buffer if (FAILED(sound_fx[sound_id].dsbuffer->Unlock(audio_ptr_1, audio_length_1, audio_ptr_2, audio_length_2))) return(0); // release the temp buffer free(snd_buffer); // return id return(sound_id); } // end DSound_Load_WAV /////////////////////////////////////////////////////////// int DSound_Replicate_Sound(int source_id) { // this function replicates the sent sound and sends back the // id of the replicated sound, you would use this function // to make multiple copies of a gunshot or something that // you want to play multiple times simulataneously, but you // only want to load once if (source_id!=-1) { // duplicate the sound buffer // first hunt for an open id for (int id=0; id < MAX_SOUNDS; id++) { // is this sound open? if (sound_fx[id].state==SOUND_NULL) { // first make an identical copy sound_fx[id] = sound_fx[source_id]; // now actually replicate the directsound buffer if (FAILED(lpds->DuplicateSoundBuffer(sound_fx[source_id].dsbuffer, &sound_fx[id].dsbuffer))) { // reset sound to NULL sound_fx[id].dsbuffer = NULL; sound_fx[id].state = SOUND_NULL; // return error return(-1); } // end if // now fix up id sound_fx[id].id = id; // return replicated sound return(id); } // end if found } // end for id } // end if else return(-1); // else failure return(-1); } // end DSound_Replicate_Sound ////////////////////////////////////////////////////////// int DSound_Init(void) { // this function initializes the sound system static int first_time = 1; // used to track the first time the function // is entered // test for very first time if (first_time) { // clear everything out memset(sound_fx,0,sizeof(pcm_sound)*MAX_SOUNDS); // reset first time first_time = 0; // create a directsound object if (FAILED(DirectSoundCreate(NULL, &lpds, NULL))) return(0); // set cooperation level if (FAILED(lpds->SetCooperativeLevel((HWND)main_window_handle,DSSCL_NORMAL))) return(0); } // end if // initialize the sound fx array for (int index=0; index Stop(); // release the buffer sound_fx[index].dsbuffer->Release(); } // end if // clear the record out memset(&sound_fx[index],0,sizeof(pcm_sound)); // now set up the fields sound_fx[index].state = SOUND_NULL; sound_fx[index].id = index; } // end for index // return sucess return(1); } // end DSound_Init /////////////////////////////////////////////////////////// int DSound_Shutdown(void) { // this function releases all the memory allocated and the directsound object // itself // first turn all sounds off DSound_Stop_All_Sounds(); // now release all sound buffers for (int index=0; index Release(); // now release the directsound interface itself if (lpds) lpds->Release(); // return success return(1); } // end DSound_Shutdown /////////////////////////////////////////////////////////// int DSound_Play(int id, int flags, int volume, int rate, int pan) { // this function plays a sound, the only parameter that // works is the flags which can be 0 to play once or // DSBPLAY_LOOPING if (sound_fx[id].dsbuffer) { // reset position to start if (FAILED(sound_fx[id].dsbuffer->SetCurrentPosition(0))) return(0); // play sound if (FAILED(sound_fx[id].dsbuffer->Play(0,0,flags))) return(0); } // end if // return success return(1); } // end DSound_Play /////////////////////////////////////////////////////////// int DSound_Set_Volume(int id,int vol) { // this function sets the volume on a sound 0-100 if (sound_fx[id].dsbuffer->SetVolume(DSVOLUME_TO_DB(vol))!=DS_OK) return(0); // return success return(1); } // end DSound_Set_Volume /////////////////////////////////////////////////////////// int DSound_Set_Freq(int id,int freq) { // this function sets the playback rate if (sound_fx[id].dsbuffer->SetFrequency(freq)!=DS_OK) return(0); // return success return(1); } // end DSound_Set_Freq /////////////////////////////////////////////////////////// int DSound_Set_Pan(int id,int pan) { // this function sets the pan, -10,000 to 10,0000 if (sound_fx[id].dsbuffer->SetPan(pan)!=DS_OK) return(0); // return success return(1); } // end DSound_Set_Pan //////////////////////////////////////////////////////////// int DSound_Stop_Sound(int id) { // this function stops a sound from playing if (sound_fx[id].dsbuffer) { sound_fx[id].dsbuffer->Stop(); sound_fx[id].dsbuffer->SetCurrentPosition(0); } // end if // return success return(1); } // end DSound_Stop_Sound /////////////////////////////////////////////////////////// int DSound_Delete_All_Sounds(void) { // this function deletes all the sounds for (int index=0; index < MAX_SOUNDS; index++) DSound_Delete_Sound(index); // return success always return(1); } // end DSound_Delete_All_Sounds /////////////////////////////////////////////////////////// int DSound_Delete_Sound(int id) { // this function deletes a single sound and puts it back onto the available list // first stop it if (!DSound_Stop_Sound(id)) return(0); // now delete it if (sound_fx[id].dsbuffer) { // release the com object sound_fx[id].dsbuffer->Release(); sound_fx[id].dsbuffer = NULL; // return success return(1); } // end if // return success return(1); } // end DSound_Delete_Sound /////////////////////////////////////////////////////////// int DSound_Stop_All_Sounds(void) { // this function stops all sounds for (int index=0; index GetStatus(&status); // return the status return(status); } // end if else // total failure return(-1); } // end DSound_Status_Sound /////////////////////////////////////////////////////////// int DMusic_Load_MIDI(char *filename) { // this function loads a midi segment DMUS_OBJECTDESC ObjDesc; HRESULT hr; IDirectMusicSegment* pSegment = NULL; int index; // loop var // look for open slot for midi segment int id = -1; for (index = 0; index < DM_NUM_SEGMENTS; index++) { // is this one open if (dm_midi[index].state == MIDI_NULL) { // validate id, but don't validate object until loaded id = index; break; } // end if } // end for index // found good id? if (id==-1) return(-1); // get current working directory char szDir[_MAX_PATH]; WCHAR wszDir[_MAX_PATH]; if(_getcwd( szDir, _MAX_PATH ) == NULL) { return(-1);; } // end if MULTI_TO_WIDE(wszDir, szDir); // tell the loader were to look for files hr = dm_loader->SetSearchDirectory(GUID_DirectMusicAllTypes,wszDir, FALSE); if (FAILED(hr)) { return (-1); } // end if // convert filename to wide string WCHAR wfilename[_MAX_PATH]; MULTI_TO_WIDE(wfilename, filename); // setup object description DD_INIT_STRUCT(ObjDesc); ObjDesc.guidClass = CLSID_DirectMusicSegment; wcscpy(ObjDesc.wszFileName, wfilename ); ObjDesc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME; // load the object and query it for the IDirectMusicSegment interface // This is done in a single call to IDirectMusicLoader::GetObject // note that loading the object also initializes the tracks and does // everything else necessary to get the MIDI data ready for playback. hr = dm_loader->GetObject(&ObjDesc,IID_IDirectMusicSegment, (void**) &pSegment); if (FAILED(hr)) return(-1); // ensure that the segment plays as a standard MIDI file // you now need to set a parameter on the band track // Use the IDirectMusicSegment::SetParam method and let // DirectMusic find the trackby passing -1 (or 0xFFFFFFFF) in the dwGroupBits method parameter. hr = pSegment->SetParam(GUID_StandardMIDIFile,-1, 0, 0, (void*)dm_perf); if (FAILED(hr)) return(-1); // This step is necessary because DirectMusic handles program changes and // bank selects differently for standard MIDI files than it does for MIDI // content authored specifically for DirectMusic. // The GUID_StandardMIDIFile parameter must be set before the instruments are downloaded. // The next step is to download the instruments. // This is necessary even for playing a simple MIDI file // because the default software synthesizer needs the DLS data // for the General MIDI instrument set // If you skip this step, the MIDI file will play silently. // Again, you call SetParam on the segment, this time specifying the GUID_Download parameter: hr = pSegment->SetParam(GUID_Download, -1, 0, 0, (void*)dm_perf); if (FAILED(hr)) return(-1); // at this point we have MIDI loaded and a valid object dm_midi[id].dm_segment = pSegment; dm_midi[id].dm_segstate = NULL; dm_midi[id].state = MIDI_LOADED; // return id return(id); } // end DMusic_Load_MIDI ////////////////////////////////////////////////////////// int DMusic_Play(int id) { // play sound based on id if (dm_midi[id].dm_segment && dm_midi[id].state!=MIDI_NULL) { // if there is an active midi then stop it if (dm_active_id!=-1) DMusic_Stop(dm_active_id); // play segment and force tracking of state variable dm_perf->PlaySegment(dm_midi[id].dm_segment, 0, 0, &dm_midi[id].dm_segstate); dm_midi[id].state = MIDI_PLAYING; // set the active midi segment dm_active_id = id; return(1); } // end if else return(0); } // end DMusic_Play ////////////////////////////////////////////////////////// int DMusic_Stop(int id) { // stop a midi segment if (dm_midi[id].dm_segment && dm_midi[id].state!=MIDI_NULL) { // play segment and force tracking of state variable dm_perf->Stop(dm_midi[id].dm_segment, NULL, 0, 0); dm_midi[id].state = MIDI_STOPPED; // reset active id dm_active_id = -1; return(1); } // end if else return(0); } // end DMusic_Stop /////////////////////////////////////////////////////////// int DMusic_Delete_MIDI(int id) { // this function deletes one MIDI segment // Unload instruments this will cause silence. // CloseDown unloads all instruments, so this call is also not // strictly necessary. if (dm_midi[id].dm_segment) { dm_midi[id].dm_segment->SetParam(GUID_Unload, -1, 0, 0, (void*)dm_perf); // Release the segment and set to null dm_midi[id].dm_segment->Release(); dm_midi[id].dm_segment = NULL; dm_midi[id].dm_segstate = NULL; dm_midi[id].state = MIDI_NULL; } // end if return(1); } // end DMusic_Delete_MIDI ////////////////////////////////////////////////////////// int DMusic_Delete_All_MIDI(void) { // delete all the MIDI int index; // loop var // free up all the segments for (index = 0; index < DM_NUM_SEGMENTS; index++) { // Unload instruments this will cause silence. // CloseDown unloads all instruments, so this call is also not // strictly necessary. if (dm_midi[index].dm_segment) { dm_midi[index].dm_segment->SetParam(GUID_Unload, -1, 0, 0, (void*)dm_perf); // Release the segment and set to null dm_midi[index].dm_segment->Release(); dm_midi[index].dm_segment = NULL; dm_midi[index].dm_segstate = NULL; dm_midi[index].state = MIDI_NULL; } // end if } // end for index return(1); } // end DMusic_Delete_All_MIDI ////////////////////////////////////////////////////////// int DMusic_Status_MIDI(int id) { // this checks the status of a midi segment if (dm_midi[id].dm_segment && dm_midi[id].state !=MIDI_NULL ) { // get the status and translate to our defines if (dm_perf->IsPlaying(dm_midi[id].dm_segment,NULL) == S_OK) dm_midi[id].state = MIDI_PLAYING; else dm_midi[id].state = MIDI_STOPPED; return(dm_midi[id].state); } // end if else return(0); } // end DMusic_Status_MIDI /////////////////////////////////////////////////////////// int DMusic_Init(void) { // this function initializes directmusic, it also checks if directsound has // been initialized, if so it connect the wave output to directsound, otherwise // it creates it's own directsound object, hence you must start directsound up // first if you want to use both directsound and directmusic int index; // looping var // set up directmusic // initialize COM if (FAILED(CoInitialize(NULL))) { // Terminate the application. return(0); } // end if // create the performance if (FAILED(CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC, IID_IDirectMusicPerformance, (void**)&dm_perf))) { // return null return(0); } // end if // initialize the performance, check if directsound is on-line if so, use the // directsound object, otherwise create a new one if (FAILED(dm_perf->Init(NULL, lpds, main_window_handle))) { return(0);// Failure -- performance not initialized } // end if // add the port to the performance if (FAILED(dm_perf->AddPort(NULL))) { return(0);// Failure -- port not initialized } // end if // create the loader to load object(s) such as midi file if (FAILED(CoCreateInstance( CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC, IID_IDirectMusicLoader, (void**)&dm_loader))) { // error return(0); } // end if // reset all the midi segment objects for (index = 0; index < DM_NUM_SEGMENTS; index++) { // reset the object dm_midi[index].dm_segment = NULL; dm_midi[index].dm_segstate = NULL; dm_midi[index].state = MIDI_NULL; dm_midi[index].id = index; } // end for index // reset the active id dm_active_id = -1; // all good baby return(1); } // end DMusic_Init //////////////////////////////////////////////////////////// int DMusic_Shutdown(void) { int index; // If there is any music playing, stop it. This is // not really necessary, because the music will stop when // the instruments are unloaded or the performance is // closed down. if (dm_perf) dm_perf->Stop(NULL, NULL, 0, 0 ); // delete all the midis if they already haven't been DMusic_Delete_All_MIDI(); // CloseDown and Release the performance object. if (dm_perf) { dm_perf->CloseDown(); dm_perf->Release(); } // end if // Release the loader object. if (dm_loader) dm_loader->Release(); // Release COM CoUninitialize(); // return success return(1); } // end DMusic_Shutdown