//////////////////////////////////////////////////////////// // sound.cpp // // Sound device classes for EDrive, a multiplatform driving simulator. // // Currently, there is just one class, sound (a sound effect), in this file. // // Copyright 2004 by Evan Alexander Weaver // // Despite the copyright, this is free software. See edrive.cpp for details. // // Date last modified: 31 May 2004 #include "platform.h" #include "error.h" #if ED_DIRECTX_9_AUDIO //*************************************************** // Use DirectX Audio (mostly DirectMusic with a bit of DirectSound) #define INITGUID #include "sound.h" #include // Performance object on which all audiopaths are played static IDirectMusicPerformance8 *Mperf = NULL; // Loader used to load sound data static IDirectMusicLoader8 *Mloader = NULL; // "safe" Release of COM objects #define RELEASE(x) if (x) { x->Release(); x = NULL; } #else //******************************************************************** // Use OpenAL #include "sound.h" #include // Has OpenAL been initialized? static bool Soundinited = false; #endif //******************************************************************* // The number of existing sound-related objects static int Mcount = 0; //Initialize sound libraries if necessary // #if ED_DIRECTX_9_AUDIO //*************************************************** bool createsoundifneeded(HWND hwnd) { bool rc = true; // Make the loader and performance objects, if possible, and initialize // the performance. // if (Mloader == NULL && S_OK != CoCreateInstance(CLSID_DirectMusicLoader, NULL, CLSCTX_INPROC, IID_IDirectMusicLoader8, (void **)&Mloader)) rc = false; else if (Mperf == NULL) { if (S_OK != CoCreateInstance(CLSID_DirectMusicPerformance, NULL, CLSCTX_INPROC, IID_IDirectMusicPerformance8, (void **)&Mperf)) rc = false; else if (FAILED(Mperf->InitAudio(NULL, NULL, hwnd, DMUS_APATH_DYNAMIC_MONO, 1, DMUS_AUDIOF_ALL, NULL))) { rc = false; RELEASE(Mperf) } } return rc; } #else //******************************************************************** bool createsoundifneeded(int *pargc, char **argv) { if (!Soundinited) { // Initialize OpenAL // alutInit(pargc, argv); if (AL_NO_ERROR != alGetError()) error("Couldn't initialize sound"); else { // Set initial listener position to the origin, looking // into the screen upright ALfloat zero[] = {0, 0, 0}, forward[] = {0, 0, -1, 0, 1, 0}; // forward & upright Soundinited = true; alListenerfv(AL_POSITION, zero); alListenerfv(AL_VELOCITY, zero); alListenerfv(AL_ORIENTATION, forward); alDistanceModel(AL_NONE); alGetError(); } } return Soundinited; } #endif //******************************************************************* // Shut down the sound libraries // void shutdownsound() { #if ED_DIRECTX_9_AUDIO //*********************************************** RELEASE(Mloader) if (Mperf) { Mperf->Stop(NULL, NULL, 0, 0); Mperf->CloseDown(); Mperf->Release(); Mperf = NULL; } #else //**************************************************************** if (Soundinited) { alutExit(); Soundinited = false; } #endif //*************************************************************** } // Initially, a sound is not associated with anything // sound::sound() { #if ED_DIRECTX_9_AUDIO //*********************************************** seg = NULL; path = NULL; buf = NULL; #endif //*************************************************************** Mcount++; // count this instance ok = false; // nothing to play yet } // Disassociate the sound from the hardware, and shut down the sound libraries // if this is the last sound-related object. // sound::~sound() { destroy(); if (0 == --Mcount) shutdownsound(); } // Disassociate the sound from the hardware // void sound::destroy() { #if ED_DIRECTX_9_AUDIO //*********************************************** deletebuffer(); RELEASE(seg) RELEASE(path) #else //**************************************************************** if (ok) { alSourceStop(src); alDeleteSources(1, &src); alDeleteBuffers(1, &buf); alGetError(); ok = false; } #endif //*************************************************************** } // Play the sound // void sound::play() { #if ED_DIRECTX_9_AUDIO //*********************************************** if (Mperf && seg) Mperf->PlaySegmentEx(seg, NULL, NULL, DMUS_SEGF_SECONDARY, 0, NULL, NULL, path); #else //**************************************************************** if (ok) { alSourcePlay(src); alGetError(); } #endif //*************************************************************** } // Stop playing the sound // void sound::stop() { #if ED_DIRECTX_9_AUDIO //*********************************************** if (Mperf && seg) Mperf->Stop(seg, NULL, 0, 0); #else //**************************************************************** if (ok) { alSourceStop(src); alGetError(); } #endif //*************************************************************** } // Associate the sound with an audio file ("file" ) and the sound hardware. // "repeat" is true if the sound should be continually repeated (or else it // is played only once whenever it is play()ed). Return true if successful. // #if ED_DIRECTX_9_AUDIO //*************************************************** bool sound::create(HWND hwnd, const char *file, bool repeat) #else //******************************************************************** bool sound::create(int *pargc, char ** argv, const char *file, bool repeat) #endif //******************************************************************* { bool rc; destroy(); // In case it is already associated #if ED_DIRECTX_9_AUDIO //*********************************************** // Ugh. Why did they have to make the Loader require a WCHAR file // name only without a char version? // WCHAR wfile[200]; for (int i = 0; file[i]; i++) wfile[i] = file[i]; wfile[i] = '\0'; // Initialize sound (if necessary), create an audiopath, load the // sound data, and uncover the sound buffer (so that we can manipulate // the frequency to change pitch). // rc = createsoundifneeded(hwnd) && SUCCEEDED(Mperf->CreateStandardAudioPath(DMUS_APATH_DYNAMIC_MONO, 1, TRUE, &path)) && SUCCEEDED(Mloader->LoadObjectFromFile(CLSID_DirectMusicSegment, IID_IDirectMusicSegment8, wfile, (void **)&seg)) && SUCCEEDED(path->GetObjectInPath(DMUS_PCHANNEL_ALL, DMUS_PATH_BUFFER, 0, GUID_NULL, 0, IID_IDirectSoundBuffer8, (void **)&buf)) && SUCCEEDED(seg->Download(Mperf)); if (rc) { // Make the sound repeat if so requested if (repeat) seg->SetRepeats(DMUS_SEG_REPEAT_INFINITE); } else destroy(); #else //**************************************************************** // First, initialize sound library if it hasn't already been // if (rc = createsoundifneeded(pargc, argv)) { void *data; ALsizei fmt, sz, bits, hz; ALfloat zero[] = {0, 0, 0}, backwards[] = {0, 0, 1}; // Load sound into a buffer and set up the sound source to use it. // alGenBuffers(1, &buf); if (rc = (AL_NO_ERROR == alGetError() && alutLoadWAV(file, &data, &fmt, &sz, &bits, &hz))) { alBufferData(buf, fmt, data, sz, hz); alutUnloadWAV(fmt, data, sz, hz); // deallocates alutLoadWAV alGenSources(1, &src); alSourcefv(src, AL_POSITION, backwards); alSourcefv(src, AL_VELOCITY, zero); alSourcefv(src, AL_DIRECTION, backwards); alSourcei(src, AL_BUFFER, buf); alSourcei(src, AL_LOOPING, repeat); // repeat if necessary rc = (AL_NO_ERROR == alGetError()); } } #endif //*************************************************************** if (rc) ok = true; else errorf("Couldn't load sound: %s", file); return rc; } // Return the frequency of the sound // unsigned int sound::frequency() { unsigned int rc = 0; #if ED_DIRECTX_9_AUDIO //*********************************************** if (buf) buf->GetFrequency((LPDWORD)&rc); #else //**************************************************************** if (ok) { ALfloat fr; alGetSourcef(src, AL_PITCH, &fr); if (AL_NO_ERROR == alGetError()) rc = (unsigned int) (10000 * fr); } #endif //*************************************************************** return rc; } // Change the frequency of the sound (which varies its pitch). You should // only slow the sound down (and not speed it up past its original frequency) // to avoid artifacts. // void sound::frequency(unsigned int f) { #if ED_DIRECTX_9_AUDIO //*********************************************** if (buf) buf->SetFrequency(f); #else //**************************************************************** if (ok) { ALfloat fr = f * 0.0001; alSourcef(src, AL_PITCH, fr); alGetError(); } #endif //*************************************************************** } // Stop playing the sound and, in Windows, delete the sound buffer, which // may be a necessary step in the process of detroying the sound. // void sound::deletebuffer() { #if ED_DIRECTX_9_AUDIO //*********************************************** if (buf) { stop(); RELEASE(buf); } #else //**************************************************************** stop(); #endif //*************************************************************** }