From a4c804b49ec872b71bd5a0167c3ad45704a3cc30 Mon Sep 17 00:00:00 2001 From: adiblol Date: Thu, 8 Mar 2012 19:32:05 +0100 Subject: Initial commit, Copyright (C) 2001-2008, Daniel ROUX & EPSITEC SA, www.epsitec.ch --- src/sound.cpp | 1640 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1640 insertions(+) create mode 100644 src/sound.cpp (limited to 'src/sound.cpp') diff --git a/src/sound.cpp b/src/sound.cpp new file mode 100644 index 0000000..ac06e14 --- /dev/null +++ b/src/sound.cpp @@ -0,0 +1,1640 @@ +// sound.cpp + +#define STRICT +#define D3D_OVERLOADS + +#include +#include +#include "language.h" +#include "struct.h" +#include "iman.h" +#include "math3d.h" +#include "sound.h" + + +///////////////////////////////////////////////////////////////////////////// + + +#define LXIMAGE 640 +#define LYIMAGE 480 + + + +// Header .WAV file. + +struct WaveHeader +{ + BYTE RIFF[4]; // "RIFF" + DWORD dwSize; // size of data to follow + BYTE WAVE[4]; // "WAVE" + BYTE fmt_[4]; // "fmt " + DWORD dw16; // 16 + WORD wOne_0; // 1 + WORD wChnls; // number of Channels + DWORD dwSRate; // sample Rate + DWORD BytesPerSec; // sample Rate + WORD wBlkAlign; // 1 + WORD BitsPerSample; // sample size + BYTE DATA[4]; // "DATA" + DWORD dwDSize; // number of Samples +}; + + + + +// Affiche une erreur DirectSound. + +void DisplayError(char *name, Sound sound, HRESULT err) +{ + char s[100]; + unsigned int i = err; + if ( err == DS_OK ) return; + sprintf(s, "SoundError in %s, sound=%d err=%d\n", name, sound, i); + OutputDebugString(s); + + if ( err == DSERR_ALLOCATED ) OutputDebugString("DSERR_ALLOCATED\n"); + if ( err == DSERR_CONTROLUNAVAIL ) OutputDebugString("DSERR_CONTROLUNAVAIL\n"); + if ( err == DSERR_INVALIDPARAM ) OutputDebugString("DSERR_INVALIDPARAM\n"); + if ( err == DSERR_INVALIDCALL ) OutputDebugString("DSERR_INVALIDCALL\n"); + if ( err == DSERR_GENERIC ) OutputDebugString("DSERR_GENERIC\n"); + if ( err == DSERR_PRIOLEVELNEEDED ) OutputDebugString("DSERR_PRIOLEVELNEEDED\n"); + if ( err == DSERR_OUTOFMEMORY ) OutputDebugString("DSERR_OUTOFMEMORY\n"); + if ( err == DSERR_BADFORMAT ) OutputDebugString("DSERR_BADFORMAT\n"); + if ( err == DSERR_UNSUPPORTED ) OutputDebugString("DSERR_UNSUPPORTED\n"); + if ( err == DSERR_NODRIVER ) OutputDebugString("DSERR_NODRIVER\n"); + if ( err == DSERR_ALREADYINITIALIZED ) OutputDebugString("DSERR_ALREADYINITIALIZED\n"); + if ( err == DSERR_NOAGGREGATION ) OutputDebugString("DSERR_NOAGGREGATION\n"); + if ( err == DSERR_BUFFERLOST ) OutputDebugString("DSERR_BUFFERLOST\n"); + if ( err == DSERR_OTHERAPPHASPRIO ) OutputDebugString("DSERR_OTHERAPPHASPRIO\n"); + if ( err == DSERR_UNINITIALIZED ) OutputDebugString("DSERR_UNINITIALIZED\n"); + if ( err == DSERR_NOINTERFACE ) OutputDebugString("DSERR_NOINTERFACE\n"); + if ( err == DSERR_ACCESSDENIED ) OutputDebugString("DSERR_ACCESSDENIED\n"); +} + +// Retourne le nom de dossier en cours. + +void GetCurrentDir(char *pName, int lg) +{ + int i; + + strncpy(pName, _pgmptr, lg-1); + pName[lg-1] = 0; + + lg = strlen(pName); + if ( lg == 0 ) return; + + for ( i=0 ; i 0 ) + { + lg --; + if ( pName[lg] == '\\' ) + { + pName[lg+1] = 0; + break; + } + } + + if ( lg > 6 && strcmp(pName+lg-6, "\\debug\\") == 0 ) + { + pName[lg-5] = 0; // ignore le dossier \debug ! + } + + if ( lg > 6 && strcmp(pName+lg-6, "\\release\\") == 0 ) + { + pName[lg-7] = 0; // ignore le dossier \release ! + } +} + + + + +///////////////////////////////////////////////////////////////////////////// + + +// Modifie le volume midi. +// Le volume est compris entre 0 et 20 ! + +void InitMidiVolume(int volume) +{ + int nb, i, n; + MMRESULT result; + HMIDIOUT hmo = 0; + + static int table[21] = + { + 0x00000000, + 0x11111111, + 0x22222222, + 0x33333333, + 0x44444444, + 0x55555555, + 0x66666666, + 0x77777777, + 0x88888888, + 0x99999999, + 0xAAAAAAAA, + 0xBBBBBBBB, + 0xCCCCCCCC, + 0xDDDDDDDD, + 0xEEEEEEEE, + 0xF222F222, + 0xF555F555, + 0xF777F777, + 0xFAAAFAAA, + 0xFDDDFDDD, + 0xFFFFFFFF, + }; + + if ( volume < 0 ) volume = 0; + if ( volume > MAXVOLUME ) volume = MAXVOLUME; + + nb = midiOutGetNumDevs(); + for ( i=0 ; i MAXVOLUME ) volume = MAXVOLUME; + + // Open the mixer. This opens the mixer with a deviceID of 0. If you + // have a single sound card/mixer, then this will open it. If you have + // multiple sound cards/mixers, the deviceIDs will be 0, 1, 2, and + // so on. + rc = mixerOpen(&hMixer, 0,0,0,0); + if ( rc != MMSYSERR_NOERROR ) + { + return FALSE; // Couldn't open the mixer. + } + + // Initialize MIXERLINE structure. + ZeroMemory(&mxl,sizeof(mxl)); + mxl.cbStruct = sizeof(mxl); + + // Specify the line you want to get. You are getting the input line + // here. If you want to get the output line, you need to use + // MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT. + mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC; + + rc = mixerGetLineInfo((HMIXEROBJ)hMixer, &mxl, + MIXER_GETLINEINFOF_COMPONENTTYPE); + if ( rc != MMSYSERR_NOERROR ) + { + return FALSE; // Couldn't get the mixer line. + } + + // Get the control. + ZeroMemory(&mxlc, sizeof(mxlc)); + mxlc.cbStruct = sizeof(mxlc); + mxlc.dwLineID = mxl.dwLineID; +//? mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_PEAKMETER; +//? mxlc.dwControlType = MIXERCONTROL_CONTROLF_UNIFORM; + mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; + mxlc.cControls = 1; + mxlc.cbmxctrl = sizeof(mxc); + mxlc.pamxctrl = &mxc; + ZeroMemory(&mxc, sizeof(mxc)); + mxc.cbStruct = sizeof(mxc); + rc = mixerGetLineControls((HMIXEROBJ)hMixer,&mxlc, + MIXER_GETLINECONTROLSF_ONEBYTYPE); +//? MIXER_GETLINECONTROLSF_ALL); + if ( rc != MMSYSERR_NOERROR ) + { + return FALSE; // Couldn't get the control. + } + + // After successfully getting the peakmeter control, the volume range + // will be specified by mxc.Bounds.lMinimum to mxc.Bounds.lMaximum. + + MIXERCONTROLDETAILS mxcd; // Gets the control values. + MIXERCONTROLDETAILS_SIGNED volStruct; // Gets the control values. + + volStruct.lValue = volume*(mxc.Bounds.lMaximum-mxc.Bounds.lMinimum); + volStruct.lValue /= MAXVOLUME; + volStruct.lValue += mxc.Bounds.lMinimum; + + // Initialize the MIXERCONTROLDETAILS structure + ZeroMemory(&mxcd, sizeof(mxcd)); + mxcd.cbStruct = sizeof(mxcd); + mxcd.cbDetails = sizeof(volStruct); + mxcd.dwControlID = mxc.dwControlID; + mxcd.paDetails = &volStruct; + mxcd.cChannels = 1; + + // Get the current value of the peakmeter control. Typically, you + // would set a timer in your program to query the volume every 10th + // of a second or so. + rc = mixerSetControlDetails((HMIXEROBJ)hMixer, &mxcd, + MIXER_SETCONTROLDETAILSF_VALUE); + if ( rc != MMSYSERR_NOERROR ) + { + return FALSE; // Couldn't get the current volume. + } +#endif + + return TRUE; +} + + +///////////////////////////////////////////////////////////////////////////// + + +// Constructeur. + +CSound::CSound(CInstanceManager* iMan) +{ + int i; + + m_iMan = iMan; + m_iMan->AddInstance(CLASS_SOUND, this); + + m_bEnable = FALSE; + m_bState = FALSE; + m_bAudioTrack = TRUE; + m_ctrl3D = TRUE; + m_bDebugMode = FALSE; + m_MidiDeviceID = 0; + m_MIDIMusic = 0; + m_audioVolume = 20; + m_midiVolume = 15; + m_lastMidiVolume = 0; + m_listener = 0; + m_lastTime = 0.0f; + m_playTime = 0.0f; + m_uniqueStamp = 0; + m_maxSound = MAXSOUND; + m_eye = D3DVECTOR(0.0f, 0.0f, 0.0f); + m_hWnd = 0; + + m_lpDS = NULL; + + ZeroMemory(m_channel, sizeof(SoundChannel)*MAXSOUND); + for ( i=0 ; iStop(); + m_channel[i].soundBuffer->Release(); + m_channel[i].soundBuffer = 0; + m_channel[i].bUsed = FALSE; + } + } + + if ( m_listener != NULL ) + { + m_listener->Release(); + m_listener = NULL; + } + + if ( m_lpDS != NULL ) + { + m_lpDS->Release(); + m_lpDS = NULL; + } +} + + +// Spécifie si on est en mode debug. + +void CSound::SetDebugMode(BOOL bMode) +{ + m_bDebugMode = bMode; +} + + +// Initialisation de DirectSound. + +BOOL CSound::Create(HWND hWnd, BOOL b3D) +{ + LPDIRECTSOUNDBUFFER primary; + DSBUFFERDESC dsbdesc; + DSCAPS dscaps; + WAVEFORMATEX wfx; + HRESULT hr; + + if ( !DirectSoundCreate(NULL, &m_lpDS, NULL) == DS_OK ) + { + OutputDebugString("Fatal error: DirectSoundCreate\n"); + m_bEnable = FALSE; + return FALSE; + } + +//? m_lpDS->SetCooperativeLevel(hWnd, DSSCL_NORMAL); + m_lpDS->SetCooperativeLevel(hWnd, DSSCL_PRIORITY); + + if ( !RetSound3DCap() ) b3D = FALSE; + + m_ctrl3D = FALSE; + if ( b3D ) + { + // Obtain primary buffer, asking it for 3D control. + ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) ); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER|DSBCAPS_CTRL3D; + hr = m_lpDS->CreateSoundBuffer( &dsbdesc, &primary, NULL ); + if ( hr == S_OK ) + { + m_ctrl3D = TRUE; + } + } + + if ( !m_ctrl3D ) + { + // Obtain primary buffer, without 3D control. + ZeroMemory( &dsbdesc, sizeof(DSBUFFERDESC) ); + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; + hr = m_lpDS->CreateSoundBuffer( &dsbdesc, &primary, NULL ); + if ( hr != S_OK ) + { + return FALSE; + } + m_ctrl3D = FALSE; + } + + if ( m_ctrl3D ) + { + hr = primary->QueryInterface( IID_IDirectSound3DListener, + (VOID**)&m_listener ); + if ( hr != S_OK ) + { + primary->Release(); + return FALSE; + } + } + + // Set primary buffer format to 44kHz and 16-bit output. + ZeroMemory( &wfx, sizeof(WAVEFORMATEX) ); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = 22050; +//? wfx.nSamplesPerSec = 44100; + wfx.wBitsPerSample = 16; + wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + hr = primary->SetFormat(&wfx); + if ( hr != S_OK ) + { + DisplayError("SetFormat", SOUND_CLICK, hr); + } + + // Release the primary buffer, since it is not need anymore. + primary->Release(); + + // Search the maximum possible voices. + if ( m_ctrl3D ) + { + ZeroMemory( &dscaps, sizeof(DSCAPS) ); + dscaps.dwSize = sizeof(DSCAPS); + hr = m_lpDS->GetCaps(&dscaps); + if ( hr == DS_OK ) + { + m_maxSound = dscaps.dwMaxHwMixingAllBuffers; + if ( dscaps.dwMaxHw3DAllBuffers > 0 && + m_maxSound > (int)dscaps.dwMaxHw3DAllBuffers ) + { + m_maxSound = dscaps.dwMaxHw3DAllBuffers; + } + if ( m_maxSound > MAXSOUND ) m_maxSound = MAXSOUND; + } + } + + m_bEnable = TRUE; + m_hWnd = hWnd; + return TRUE; +} + + +// Indique s'il faut jouer les sons en 3D ou pas. + +void CSound::SetSound3D(BOOL bMode) +{ + StopAll(); + + if ( m_listener != NULL ) + { + m_listener->Release(); + m_listener = NULL; + } + + if ( m_lpDS != NULL ) + { + m_lpDS->Release(); + m_lpDS = NULL; + } + + Create(m_hWnd, bMode); +} + +BOOL CSound::RetSound3D() +{ + return m_ctrl3D; +} + +// Indique s'il est possible de jouer les sons en 3D. + +BOOL CSound::RetSound3DCap() +{ + DSCAPS dscaps; + HRESULT hr; + + ZeroMemory( &dscaps, sizeof(DSCAPS) ); + dscaps.dwSize = sizeof(DSCAPS); + hr = m_lpDS->GetCaps(&dscaps); + if ( hr != DS_OK ) return FALSE; + + return ( dscaps.dwMaxHw3DAllBuffers > 0 ); +} + + + +// Retourne l'état de DirectSound. + +BOOL CSound::RetEnable() +{ + return m_bEnable; +} + + +// Enclenche ou déclenche le son. + +void CSound::SetState(BOOL bState) +{ + m_bState = bState; +} + +// Spécifie le chamin d'accès au CD. + +void CSound::SetCDpath(char *path) +{ + strcpy(m_CDpath, path); +} + +// Enclenche ou déclenche les musiques CD-audio. + +void CSound::SetAudioTrack(BOOL bAudio) +{ + m_bAudioTrack = bAudio; +} + + +// Gestion des volumes audio (.wav) et midi (.mid). + +void CSound::SetAudioVolume(int volume) +{ + m_audioVolume = volume; +} + +int CSound::RetAudioVolume() +{ + if ( !m_bEnable ) return 0; + return m_audioVolume; +} + +void CSound::SetMidiVolume(int volume) +{ + m_midiVolume = volume; + + if ( m_bAudioTrack ) + { + InitAudioTrackVolume(m_midiVolume); + } +} + +int CSound::RetMidiVolume() +{ + if ( !m_bEnable ) return 0; + return m_midiVolume; +} + + +// Lit un fichier. + +BOOL CSound::ReadFile(Sound sound, char *metaname, char *filename) +{ + WaveHeader wavHdr; + DWORD size; + int err; + + // Open the wave file. + err = g_metafile.Open(metaname, filename); + if ( err != 0 ) return FALSE; + + // Read in the wave header. + g_metafile.Read(&wavHdr, sizeof(wavHdr)); + + // Figure out the size of the data region. + size = wavHdr.dwDSize; + + if ( m_files[sound] != 0 ) + { + free(m_files[sound]); + } + m_files[sound] = (char*)malloc(sizeof(WaveHeader)+size); + + memcpy(m_files[sound], &wavHdr, sizeof(WaveHeader)); + g_metafile.Read(m_files[sound]+sizeof(WaveHeader), size); + + // Close out the wave file. + g_metafile.Close(); + return TRUE; +} + +// Cache tous les ficheirs son (.wav). + +void CSound::CacheAll() +{ + int i; + char meta[50]; + char name[50]; + + if ( !m_bEnable ) return; + + if ( m_bDebugMode ) + { + strcpy(meta, ""); + } + else + { +#if _SCHOOL + strcpy(meta, "ceebot3.dat"); +#else + strcpy(meta, "colobot3.dat"); +#endif + } + + for ( i=0 ; iGetStatus(&status); + if ( (status&DSBSTATUS_PLAYING) == 0 ) + { + m_channel[i].priority = priority; + m_channel[i].uniqueStamp = m_uniqueStamp++; + channel = i; + bAlreadyLoaded = TRUE; + return TRUE; + } + } +#endif + + // Cherche un canal complètement libre. + for ( i=0 ; iGetStatus(&status); + if ( (status&DSBSTATUS_PLAYING) == 0 ) + { + m_channel[i].soundBuffer->Release(); + m_channel[i].soundBuffer = 0; + m_channel[i].priority = priority; + m_channel[i].uniqueStamp = m_uniqueStamp++; + + channel = i; + bAlreadyLoaded = FALSE; + return TRUE; + } + } + + // Cherche un canal utilisé moins prioritaire. + for ( i=0 ; i= priority ) continue; + + m_channel[i].soundBuffer->Stop(); + m_channel[i].soundBuffer->Release(); + m_channel[i].soundBuffer = 0; + m_channel[i].priority = priority; + m_channel[i].uniqueStamp = m_uniqueStamp++; + + channel = i; + bAlreadyLoaded = FALSE; + return TRUE; + } + + // Cherche un canal utilisé moins prioritaire ou identique. + for ( i=0 ; i priority ) continue; + + m_channel[i].soundBuffer->Stop(); + m_channel[i].soundBuffer->Release(); + m_channel[i].soundBuffer = 0; + m_channel[i].priority = priority; + m_channel[i].uniqueStamp = m_uniqueStamp++; + + channel = i; + bAlreadyLoaded = FALSE; + return TRUE; + } + + char s[100]; + sprintf(s, "Sound %d forget (priority=%d)\n", sound, priority); + OutputDebugString(s); + + return FALSE; +} + +// Reads in data from a wave file. + +BOOL CSound::ReadData(LPDIRECTSOUNDBUFFER lpDSB, Sound sound, DWORD size) +{ + LPVOID pData1; + DWORD dwData1Size; + LPVOID pData2; + DWORD dwData2Size; + HRESULT hr; + + // Lock data in buffer for writing. + hr = lpDSB->Lock(0, size, &pData1, &dwData1Size, &pData2, &dwData2Size, DSBLOCK_FROMWRITECURSOR); + if ( hr != DS_OK ) + { + return FALSE; + } + + // Read in first chunk of data. + if ( dwData1Size > 0 ) + { + memcpy(pData1, m_files[sound]+sizeof(WaveHeader), dwData1Size); + } + + // Read in second chunk if necessary. + if ( dwData2Size > 0 ) + { + memcpy(pData2, m_files[sound]+sizeof(WaveHeader)+dwData1Size, dwData2Size); + } + + // Unlock data in buffer. + hr = lpDSB->Unlock(pData1, dwData1Size, pData2, dwData2Size); + if ( hr != DS_OK ) + { + return FALSE; + } + + return TRUE; +} + +// Creates a DirectSound buffer. + +BOOL CSound::CreateSoundBuffer(int channel, DWORD size, DWORD freq, + DWORD bitsPerSample, DWORD blkAlign, + BOOL bStereo) +{ + PCMWAVEFORMAT pcmwf; + DSBUFFERDESC dsbdesc; + DS3DBUFFER bufferParams; // 3D buffer properties + HRESULT hr; + + // Set up wave format structure. + memset( &pcmwf, 0, sizeof(PCMWAVEFORMAT) ); + pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM; + pcmwf.wf.nChannels = bStereo ? 2 : 1; + pcmwf.wf.nSamplesPerSec = freq; + pcmwf.wf.nBlockAlign = (WORD)blkAlign; + pcmwf.wf.nAvgBytesPerSec = pcmwf.wf.nSamplesPerSec * pcmwf.wf.nBlockAlign; + pcmwf.wBitsPerSample = (WORD)bitsPerSample; + + // Set up DSBUFFERDESC structure. + memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); // Zero it out. + dsbdesc.dwSize = sizeof(DSBUFFERDESC); + if ( m_ctrl3D ) + { + dsbdesc.dwFlags = DSBCAPS_CTRL3D|DSBCAPS_MUTE3DATMAXDISTANCE| + DSBCAPS_LOCDEFER| + DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLFREQUENCY; + } + else + { + dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME|DSBCAPS_CTRLPAN|DSBCAPS_CTRLFREQUENCY; + } + dsbdesc.dwBufferBytes = size; + dsbdesc.lpwfxFormat = (LPWAVEFORMATEX)&pcmwf; + + hr = m_lpDS->CreateSoundBuffer(&dsbdesc, &m_channel[channel].soundBuffer, NULL); + if ( hr != DS_OK ) return FALSE; + + if ( m_ctrl3D ) + { + hr = m_channel[channel].soundBuffer->QueryInterface + ( + IID_IDirectSound3DBuffer, + (VOID**)&m_channel[channel].soundBuffer3D + ); + if ( hr != DS_OK ) return FALSE; + } + + m_channel[channel].bUsed = TRUE; + m_channel[channel].bMute = FALSE; + return TRUE; +} + +// Creates a DirectSound buffer from a wave file. + +BOOL CSound::CreateBuffer(int channel, Sound sound) +{ + WaveHeader* wavHdr; + DWORD size; + BOOL bStereo; + + if ( m_files[sound] == 0 ) return FALSE; + + wavHdr = (WaveHeader*)m_files[sound]; + size = wavHdr->dwDSize; + bStereo = wavHdr->wChnls > 1 ? TRUE : FALSE; + + // Create the sound buffer for the wave file. + if ( !CreateSoundBuffer(channel, size, wavHdr->dwSRate, + wavHdr->BitsPerSample, wavHdr->wBlkAlign, bStereo) ) + { + return FALSE; + } + + // Read the data for the wave file into the sound buffer. + if ( !ReadData(m_channel[channel].soundBuffer, sound, size) ) + { + return FALSE; + } + + m_channel[channel].type = sound; + + // Close out the wave file. + return TRUE; +} + +// Calcule le volume et le panoramique d'un son, en mode non 3D. + +void CSound::ComputeVolumePan2D(int channel, const D3DVECTOR &pos) +{ + float dist, a, g; + + if ( pos.x == m_eye.x && + pos.y == m_eye.y && + pos.z == m_eye.z ) + { + m_channel[channel].volume = 1.0f; // volume maximal + m_channel[channel].pan = 0.0f; // au centre + return; + } + +#if _TEEN + dist = Length(pos, m_eye); + if ( dist >= 210.0f ) // très loin ? + { + m_channel[channel].volume = 0.0f; // silence + m_channel[channel].pan = 0.0f; // au centre + return; + } + if ( dist <= 10.0f ) // très proche ? + { + m_channel[channel].volume = 1.0f; // volume maximal + m_channel[channel].pan = 0.0f; // au centre + return; + } + m_channel[channel].volume = 1.0f-((dist-10.0f)/200.0f); +#else + dist = Length(pos, m_eye); + if ( dist >= 110.0f ) // très loin ? + { + m_channel[channel].volume = 0.0f; // silence + m_channel[channel].pan = 0.0f; // au centre + return; + } + if ( dist <= 10.0f ) // très proche ? + { + m_channel[channel].volume = 1.0f; // volume maximal + m_channel[channel].pan = 0.0f; // au centre + return; + } + m_channel[channel].volume = 1.0f-((dist-10.0f)/100.0f); +#endif + + a = RotateAngle(m_lookat.x-m_eye.x, m_eye.z-m_lookat.z); + g = RotateAngle(pos.x-m_eye.x, m_eye.z-pos.z); + m_channel[channel].pan = sinf(Direction(a, g)); +} + +// Fait entendre un son au milieu. +// Retourne le canal associé ou -1. + +int CSound::Play(Sound sound, float amplitude, float frequency, BOOL bLoop) +{ + return Play(sound, m_lookat, amplitude, frequency, bLoop); +} + +// Fait entendre un son à une position donnée. +// Retourne le canal associé ou -1. + +int CSound::Play(Sound sound, D3DVECTOR pos, + float amplitude, float frequency, BOOL bLoop) +{ + DS3DBUFFER sb; + int channel, iVolume, iPan, iFreq, uniqueStamp; + BOOL bAlreadyLoaded; + DWORD flag, freq; + HRESULT err; + + if ( !m_bEnable ) return -1; + if ( !m_bState || m_audioVolume == 0 ) return -1; + +//? if ( Length(pos, m_eye) > 100.0f ) return -1; + + if ( !SearchFreeBuffer(sound, channel, bAlreadyLoaded) ) return -1; + + if ( !bAlreadyLoaded ) + { + if ( !CreateBuffer(channel, sound) ) + { + if ( m_channel[channel].bUsed && + m_channel[channel].soundBuffer != 0 ) + { + m_channel[channel].soundBuffer->Release(); + m_channel[channel].soundBuffer = 0; + } + m_channel[channel].bUsed = FALSE; + return -1; + } + } + + m_channel[channel].pos = pos; + + if ( m_ctrl3D ) + { + m_channel[channel].volume = 1.0f; + m_channel[channel].pan = 0.0f; + } + else + { + ComputeVolumePan2D(channel, pos); + } + +#if 0 + DWORD status; + m_channel[channel].soundBuffer->GetStatus(&status); + char s[100]; + sprintf(s, "Play sound=%d status=%d channel=%d flag=%d\n", sound, status, channel, bAlreadyLoaded); + OutputDebugString(s); +#endif + + m_channel[channel].oper[0].bUsed = FALSE; + m_channel[channel].startAmplitude = amplitude; + m_channel[channel].startFrequency = frequency; + m_channel[channel].changeFrequency = 1.0f; + + if ( m_ctrl3D ) + { + sb.dwSize = sizeof(DS3DBUFFER); + err = m_channel[channel].soundBuffer3D->GetAllParameters(&sb); + DisplayError("GetAllParameters", sound, err); + + sb.vPosition = pos; +//? sb.dwInsideConeAngle = 90; +//? sb.dwOutsideConeAngle = 180; +//? sb.vConeOrientation = D3DVECTOR(0.0f, 1.0f, 0.0f); + sb.lConeOutsideVolume = DSBVOLUME_MIN; +#if _TEEN + sb.flMinDistance = 50.0f; +#else + sb.flMinDistance = 20.0f; +#endif + sb.flMaxDistance = DS3D_DEFAULTMAXDISTANCE; + + err = m_channel[channel].soundBuffer3D->SetAllParameters(&sb, DS3D_IMMEDIATE); + DisplayError("SetAllParameters", sound, err); + } + + amplitude *= m_channel[channel].volume; + amplitude *= (float)m_audioVolume/MAXVOLUME; + iVolume = (int)((powf(amplitude, 0.2f)-1.0f)*10000.0f); + if ( iVolume > 0 ) iVolume = 0; + err = m_channel[channel].soundBuffer->SetVolume(iVolume); + DisplayError("SetVolume", sound, err); + + if ( !m_ctrl3D ) + { + iPan = (int)(m_channel[channel].pan*10000.0f); + err = m_channel[channel].soundBuffer->SetPan(iPan); + DisplayError("SetPan", sound, err); + } + + if ( !bAlreadyLoaded ) + { + err = m_channel[channel].soundBuffer->GetFrequency(&freq); + DisplayError("GetFrequency", sound, err); + m_channel[channel].initFrequency = freq; + } + iFreq = (int)(frequency*m_channel[channel].initFrequency); + err = m_channel[channel].soundBuffer->SetFrequency(iFreq); + DisplayError("SetFrequency", sound, err); + + err = m_channel[channel].soundBuffer->SetCurrentPosition(0); + DisplayError("SetCurrentPosition", sound, err); + + flag = bLoop?DSBPLAY_LOOPING:0; +//? flag |= DSBPLAY_LOCHARDWARE|DSBPLAY_TERMINATEBY_DISTANCE; +//? flag |= DSBPLAY_TERMINATEBY_DISTANCE; + err = m_channel[channel].soundBuffer->Play(0, 0, flag); + DisplayError("Play", sound, err); + if ( err == DSERR_BADFORMAT ) + { + iFreq = m_channel[channel].initFrequency; + err = m_channel[channel].soundBuffer->SetFrequency(iFreq); + DisplayError("SetFrequency (repeat)", sound, err); + + err = m_channel[channel].soundBuffer->Play(0, 0, flag); + DisplayError("Play (repeat)", sound, err); + } + + uniqueStamp = m_channel[channel].uniqueStamp; + return channel | ((uniqueStamp&0xffff)<<16); +} + +// Check un numéro de canal. +// Adapte le canal pour qu'il puisse être utilisé comme offset +// dans m_channel. + +BOOL CSound::CheckChannel(int &channel) +{ + int uniqueStamp; + + uniqueStamp = (channel>>16)&0xffff; + channel &= 0xffff; + + if ( !m_bEnable ) return FALSE; + if ( !m_bState || m_audioVolume == 0 ) return FALSE; + + if ( channel < 0 || channel >= m_maxSound ) return FALSE; + if ( !m_channel[channel].bUsed ) return FALSE; + + if ( m_channel[channel].uniqueStamp != uniqueStamp ) return FALSE; + + return TRUE; +} + +// Supprime toutes les enveloppes. + +BOOL CSound::FlushEnvelope(int channel) +{ + if ( !CheckChannel(channel) ) return FALSE; + + m_channel[channel].oper[0].bUsed = FALSE; + return TRUE; +} + +// Ajoute une opération d'enveloppe. + +BOOL CSound::AddEnvelope(int channel, float amplitude, float frequency, + float time, SoundNext oper) +{ + int i; + + if ( !CheckChannel(channel) ) return FALSE; + + for ( i=0 ; iSetPosition(pos.x, pos.y, pos.z, DS3D_DEFERRED); + } + else + { + ComputeVolumePan2D(channel, pos); + + if ( !m_channel[channel].oper[0].bUsed ) + { + amplitude = m_channel[channel].startAmplitude; + amplitude *= m_channel[channel].volume; + amplitude *= (float)m_audioVolume/MAXVOLUME; + iVolume = (int)((powf(amplitude, 0.2f)-1.0f)*10000.0f); + if ( iVolume > 0 ) iVolume = 0; + err = m_channel[channel].soundBuffer->SetVolume(iVolume); + DisplayError("SetVolume", m_channel[channel].type, err); + } + + pan = m_channel[channel].pan; + iPan = (int)(pan*10000.0f); + err = m_channel[channel].soundBuffer->SetPan(iPan); + DisplayError("SetPan", m_channel[channel].type, err); + } + return TRUE; +} + +// Modifie la fréquence d'un son. +// 0.5 descend d'une octave et 2.0 monte d'une octave. + +BOOL CSound::Frequency(int channel, float frequency) +{ + HRESULT err; + int iFreq; + + if ( !CheckChannel(channel) ) return FALSE; + + m_channel[channel].changeFrequency = frequency; + + if ( !m_channel[channel].oper[0].bUsed ) + { + iFreq = (int)(frequency*m_channel[channel].initFrequency); + err = m_channel[channel].soundBuffer->SetFrequency(iFreq); + DisplayError("Frequency", m_channel[channel].type, err); + } + + return TRUE; +} + +// Stoppe un son. + +BOOL CSound::Stop(int channel) +{ + if ( !CheckChannel(channel) ) return FALSE; + + m_channel[channel].soundBuffer->Stop(); + return TRUE; +} + +// Stops all sounds. + +BOOL CSound::StopAll() +{ + DWORD status; + int i; + + for ( i=0 ; iGetStatus(&status); + if ( (status&DSBSTATUS_PLAYING) == DSBSTATUS_PLAYING ) + { + m_channel[i].soundBuffer->Stop(); + } + m_channel[i].soundBuffer->Stop(); + m_channel[i].soundBuffer->Release(); + m_channel[i].soundBuffer = 0; + + m_channel[i].bUsed = FALSE; + } + return TRUE; +} + +// Silent all sounds. + +BOOL CSound::MuteAll(BOOL bMute) +{ + int i; + + for ( i=0 ; iSetVolume(-10000); // silence + continue; + } + + m_channel[i].oper[0].currentTime += rTime; + + progress = m_channel[i].oper[0].currentTime / m_channel[i].oper[0].totalTime; + if ( progress > 1.0f ) progress = 1.0f; + + volume = progress; + volume *= m_channel[i].oper[0].finalAmplitude-m_channel[i].startAmplitude; + volume += m_channel[i].startAmplitude; + volume *= m_channel[i].volume; + volume *= (float)m_audioVolume/MAXVOLUME; + iVolume = (int)((powf(volume, 0.2f)-1.0f)*10000.0f); + if ( iVolume > 0 ) iVolume = 0; + m_channel[i].soundBuffer->SetVolume(iVolume); + + freq = progress; + freq *= m_channel[i].oper[0].finalFrequency-m_channel[i].startFrequency; + freq += m_channel[i].startFrequency; + freq *= m_channel[i].changeFrequency; + iFreq = (int)(freq*m_channel[i].initFrequency); + err = m_channel[i].soundBuffer->SetFrequency(iFreq); + DisplayError("FrameMove::Frequency", m_channel[i].type, err); + + if ( m_channel[i].oper[0].currentTime >= + m_channel[i].oper[0].totalTime ) + { + next = m_channel[i].oper[0].nextOper; + + if ( next == SOPER_LOOP ) + { + m_channel[i].oper[0].currentTime = 0.0f; + } + else + { + OperNext(i); + + if ( next == SOPER_STOP ) + { + m_channel[i].soundBuffer->Stop(); + } + } + } + } + + m_lastTime += rTime; + if ( m_lastTime >= 0.05f && m_listener != 0 ) + { + m_lastTime = 0.0f; + m_listener->CommitDeferredSettings(); + } +} + +// Spécifie la position de l'auditeur. +// Doit être appelé chaque fois que la caméra se déplace. + +void CSound::SetListener(D3DVECTOR eye, D3DVECTOR lookat) +{ + DS3DLISTENER listenerParams; + HRESULT err; + float amplitude, pan; + int i, iVolume, iPan; + + m_eye = eye; + m_lookat = lookat; + + if ( m_listener == 0 ) + { + if ( m_ctrl3D ) return; + + for ( i=0 ; i 0 ) iVolume = 0; + err = m_channel[i].soundBuffer->SetVolume(iVolume); + DisplayError("SetVolume", m_channel[i].type, err); + } + + pan = m_channel[i].pan; + iPan = (int)(pan*10000.0f); + err = m_channel[i].soundBuffer->SetPan(iPan); + DisplayError("SetPan", m_channel[i].type, err); + } + return; + } + + // Get listener parameters. + listenerParams.dwSize = sizeof(DS3DLISTENER); + m_listener->GetAllParameters(&listenerParams); + + listenerParams.vPosition = eye; + listenerParams.vOrientFront = lookat-eye; + listenerParams.vOrientTop = D3DVECTOR(0.0f, 1.0f, 0.0f); + listenerParams.flDistanceFactor = 10.0f; + listenerParams.flRolloffFactor = 1.0f; + + m_listener->SetAllParameters(&listenerParams, DS3D_DEFERRED); +} + + + + +// Uses MCI to play a MIDI file. The window procedure +// is notified when playback is complete. + +BOOL CSound::PlayMusic(int rank, BOOL bRepeat) +{ + MCI_OPEN_PARMS mciOpenParms; + MCI_PLAY_PARMS mciPlayParms; + DWORD dwReturn; + char filename[MAX_PATH]; + + m_bRepeatMusic = bRepeat; + m_playTime = 0.0f; + + if ( m_midiVolume == 0 ) return TRUE; + + if ( m_bAudioTrack ) + { + return PlayAudioTrack(rank); + } + + if ( !m_bEnable ) return TRUE; + InitMidiVolume(m_midiVolume); + m_lastMidiVolume = m_midiVolume; + + GetCurrentDir(filename, MAX_PATH-30); + sprintf(filename+strlen(filename), "sound\\music%.3d.blp", rank-1); + + // Open the device by specifying the device and filename. + // MCI will attempt to choose the MIDI mapper as the output port. + mciOpenParms.lpstrDeviceType = "sequencer"; + mciOpenParms.lpstrElementName = filename; + dwReturn = mciSendCommand(NULL, + MCI_OPEN, + MCI_OPEN_TYPE|MCI_OPEN_ELEMENT, + (DWORD)(LPVOID)&mciOpenParms); + if ( dwReturn != 0 ) + { + mciGetErrorString(dwReturn, filename, 128); + // Failed to open device. Don't close it; just return error. + return FALSE; + } + + // The device opened successfully; get the device ID. + m_MidiDeviceID = mciOpenParms.wDeviceID; + + // Begin playback. + mciPlayParms.dwCallback = (DWORD)m_hWnd; + dwReturn = mciSendCommand(m_MidiDeviceID, + MCI_PLAY, + MCI_NOTIFY, + (DWORD)(LPVOID)&mciPlayParms); + if ( dwReturn != 0 ) + { + mciGetErrorString(dwReturn, filename, 128); + StopMusic(); + return FALSE; + } + + m_MIDIMusic = rank; + return TRUE; +} + +// Uses MCI to play a CD-audio track. The window procedure +// is notified when playback is complete. +// The rank parameter is in space [1..n] ! +// For CD mix (data/audio), it will be [2..n] ! + +BOOL CSound::PlayAudioTrack(int rank) +{ +#if _SOUNDTRACKS + MCI_OPEN_PARMS mciOpenParms; + MCI_PLAY_PARMS mciPlayParms; + MCI_SET_PARMS mciSetParms; + DWORD dwReturn; + char filename[MAX_PATH]; + char device[10]; + + if ( !m_bEnable ) return TRUE; +//? if ( m_midiVolume == 0 ) return TRUE; + InitAudioTrackVolume(m_midiVolume); + m_lastMidiVolume = m_midiVolume; + + // Open the device by specifying the device and filename. + // MCI will attempt to choose the MIDI mapper as the output port. + memset(&mciOpenParms, 0, sizeof(MCI_OPEN_PARMS)); +//? mciOpenParms.lpstrDeviceType = (LPCTSTR)MCI_DEVTYPE_CD_AUDIO; +//? dwReturn = mciSendCommand(NULL, +//? MCI_OPEN, +//? MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID, +//? (DWORD)(LPVOID)&mciOpenParms); + mciOpenParms.lpstrDeviceType = (LPCTSTR)MCI_DEVTYPE_CD_AUDIO; + if ( m_CDpath[0] == 0 ) + { + dwReturn = mciSendCommand(NULL, + MCI_OPEN, + MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID, + (DWORD)(LPVOID)&mciOpenParms); + } + else + { + device[0] = m_CDpath[0]; + device[1] = ':'; + device[2] = 0; + mciOpenParms.lpstrElementName = device; + dwReturn = mciSendCommand(NULL, + MCI_OPEN, + MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID|MCI_OPEN_ELEMENT, + (DWORD)(LPVOID)&mciOpenParms); + } + if ( dwReturn != 0 ) + { + mciGetErrorString(dwReturn, filename, 128); + // Failed to open device. Don't close it; just return error. + return FALSE; + } + + // The device opened successfully; get the device ID. + m_MidiDeviceID = mciOpenParms.wDeviceID; + + // Set the time format to track/minute/second/frame (TMSF). + memset(&mciSetParms, 0, sizeof(MCI_SET_PARMS)); + mciSetParms.dwTimeFormat = MCI_FORMAT_TMSF; + dwReturn = mciSendCommand(m_MidiDeviceID, + MCI_SET, + MCI_SET_TIME_FORMAT, + (DWORD)&mciSetParms); + if ( dwReturn != 0 ) + { + mciGetErrorString(dwReturn, filename, 128); + StopMusic(); + return FALSE; + } + + // Begin playback. + memset(&mciPlayParms, 0, sizeof(MCI_PLAY_PARMS)); + mciPlayParms.dwCallback = (DWORD)m_hWnd; + mciPlayParms.dwFrom = MCI_MAKE_TMSF(rank+0, 0, 0, 0); + mciPlayParms.dwTo = MCI_MAKE_TMSF(rank+1, 0, 0, 0); + dwReturn = mciSendCommand(m_MidiDeviceID, + MCI_PLAY, + MCI_NOTIFY|MCI_FROM|MCI_TO, + (DWORD)(LPVOID)&mciPlayParms); + if ( dwReturn != 0 ) + { + mciGetErrorString(dwReturn, filename, 128); + StopMusic(); + return FALSE; + } + + m_MIDIMusic = rank; +#endif + return TRUE; +} + +// Restart the MIDI player. + +BOOL CSound::RestartMusic() +{ + if ( !m_bRepeatMusic ) return FALSE; + + OutputDebugString("RestartMusic\n"); + if ( !m_bEnable ) return TRUE; +//? if ( m_midiVolume == 0 ) return TRUE; + if ( m_MIDIMusic == 0 ) return FALSE; + if ( m_playTime < 5.0f ) return FALSE; + + return PlayMusic(m_MIDIMusic, TRUE); +} + +// Shuts down the MIDI player. + +void CSound::SuspendMusic() +{ + if ( !m_bEnable ) return; + +//? if ( m_MidiDeviceID && m_midiVolume != 0 ) + if ( m_MidiDeviceID ) + { + if ( m_bAudioTrack ) mciSendCommand(m_MidiDeviceID, MCI_STOP, 0, NULL); + mciSendCommand(m_MidiDeviceID, MCI_CLOSE, 0, NULL); + } + m_MidiDeviceID = 0; +} + +// Shuts down the MIDI player. + +void CSound::StopMusic() +{ + SuspendMusic(); + m_MIDIMusic = 0; +} + +// Retourne TRUE si une musique est en cours. + +BOOL CSound::IsPlayingMusic() +{ + return (m_MIDIMusic != 0); +} + +// Adapte le volume de la musique en cours, si nécessaire. + +void CSound::AdaptVolumeMusic() +{ + if ( m_midiVolume != m_lastMidiVolume ) + { + if ( m_bAudioTrack ) + { + InitAudioTrackVolume(m_midiVolume); + } + else + { + InitMidiVolume(m_midiVolume); + } + m_lastMidiVolume = m_midiVolume; + RestartMusic(); + } +} + -- cgit v1.2.3-1-g7c22