summaryrefslogtreecommitdiffhomepage
path: root/nGenEx/SoundD3D.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'nGenEx/SoundD3D.cpp')
-rw-r--r--nGenEx/SoundD3D.cpp1321
1 files changed, 1321 insertions, 0 deletions
diff --git a/nGenEx/SoundD3D.cpp b/nGenEx/SoundD3D.cpp
new file mode 100644
index 0000000..a148e69
--- /dev/null
+++ b/nGenEx/SoundD3D.cpp
@@ -0,0 +1,1321 @@
+/* Starshatter OpenSource Distribution
+ Copyright (c) 1997-2004, Destroyer Studios LLC.
+ All Rights Reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * Neither the name "Destroyer Studios" nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: SoundD3D.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ DirectSound and DirectSound3D (sound card) class
+*/
+
+//#define INITGUID
+#include <objbase.h>
+#include <cguid.h>
+#include <mmsystem.h>
+#include <dsound.h>
+
+
+#include "MemDebug.h"
+#include "SoundD3D.h"
+#include "Game.h"
+
+#ifdef DIRECT_SOUND_3D
+#include "ia3d.h"
+#endif
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* msg, ...);
+char* DSErrStr(HRESULT dserr);
+void SoundD3DError(const char* msg, HRESULT dserr);
+
+static int DS3D_report_errors = 1;
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+// +====================================================================+
+// | SOUND CARD D3D
+// +====================================================================+
+
+SoundCardD3D::SoundCardD3D(HWND hwnd)
+: soundcard(0), primary(0)
+{
+ HRESULT err = 0;
+ status = SC_ERROR;
+
+ // 1) Get interface to DirectSound object:
+
+#ifdef DIRECT_SOUND_3D
+ CoInitialize(NULL);
+ err = CoCreateInstance(CLSID_A3d, NULL, CLSCTX_INPROC_SERVER,
+ IID_IDirectSound, (VOID **)&soundcard);
+
+ if (SUCCEEDED(err)) {
+ soundcard->Initialize(NULL);
+ SoundD3DError("Initialized Aureal3D Sound", 0);
+ }
+ else {
+ SoundD3DError("Could not initialize Aureal3D Sound", err);
+ SoundD3DError("Proceding with standard DirectSoundCreate", 0);
+#endif
+
+ err = DirectSoundCreate(0, &soundcard, 0);
+ if (FAILED(err)) {
+ SoundD3DError("Could not create DirectSound object.", err);
+ soundcard = 0;
+ primary = 0;
+ return;
+ }
+
+#ifdef DIRECT_SOUND_3D
+ }
+#endif
+
+ // 2) Set the cooperative level:
+ err = soundcard->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
+ if (FAILED(err)) {
+ SoundD3DError("Could not set cooperative level.", err);
+ RELEASE(soundcard);
+ return;
+ }
+
+ // Prepare to initialize the primary buffer:
+ DSCAPS caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(caps);
+
+ err = soundcard->GetCaps(&caps);
+ if (FAILED(err)) {
+ SoundD3DError("Could not get soundcard caps.", err);
+ RELEASE(soundcard);
+ return;
+ }
+
+ if (caps.dwFlags & DSCAPS_EMULDRIVER)
+ Print(" WARNING: using DirectSound emulated drivers\n");
+
+ memset(&dsbd, 0, sizeof(dsbd));
+ dsbd.dwSize = sizeof(dsbd);
+
+#ifdef DIRECT_SOUND_3D
+ int use_ds3d = true;
+
+ // 3) Set up the primary buffer (try to use DS3D):
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D;
+
+ err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
+ if (err == DSERR_CONTROLUNAVAIL) {
+ use_ds3d = false;
+
+ // try again, without using DS3D
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLDEFAULT;
+
+ err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
+ if (FAILED(err)) {
+ SoundD3DError("Could not initialize primary buffer", err);
+ RELEASE(soundcard);
+ return;
+ }
+ else {
+ Print(" WARNING: DirectSound3D is not available, simulating instead\n");
+ }
+ }
+
+ // 4) Set up the listener:
+ if (primary && use_ds3d) {
+ err = primary->QueryInterface(IID_IDirectSound3DListener, (void**)&listener);
+ if (FAILED(err)) {
+ SoundD3DError("Could not get listener interface", err);
+ }
+ else {
+ listener->SetPosition(0.0f, 0.0f, 0.0f, DS3D_IMMEDIATE);
+ listener->SetOrientation(0.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f, DS3D_IMMEDIATE);
+ }
+ }
+
+#else
+ // 3) Set up the primary buffer:
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
+
+ err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
+ if (FAILED(err)) {
+ SoundD3DError("Could not initialize primary buffer", err);
+ RELEASE(soundcard);
+ return;
+ }
+#endif
+
+ // 5) Set primary buffer parameters to 16 bit STEREO at 44kHz
+ SetFormat(16, 2, 44100);
+
+ // read back the result format and display to the log file:
+ GetFormat(0);
+ ShowFormat();
+
+ status = SC_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+SoundCardD3D::~SoundCardD3D()
+{
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+ s->Stop();
+ }
+
+ sounds.destroy();
+
+#ifdef DIRECT_SOUND_3D
+ RELEASE(listener);
+#endif
+
+ RELEASE(primary);
+ RELEASE(soundcard);
+
+ Print(" SoundCardD3D: shutdown\n");
+
+ status = SC_UNINITIALIZED;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::SetFormat(int bits, int channels, int hertz)
+{
+ if (!soundcard) return false;
+
+ DSCAPS caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(caps);
+ soundcard->GetCaps(&caps);
+
+ if (!(caps.dwFlags & DSCAPS_PRIMARY16BIT)) bits = 8;
+ if (!(caps.dwFlags & DSCAPS_PRIMARYSTEREO)) channels = 1;
+
+ memset(&wfex, 0, sizeof(wfex));
+
+ wfex.wFormatTag = WAVE_FORMAT_PCM;
+ wfex.nChannels = channels;
+ wfex.nSamplesPerSec = hertz;
+ wfex.nBlockAlign = (channels * bits) / 8;
+ wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
+ wfex.wBitsPerSample = bits;
+
+ return SetFormat(&wfex);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::SetFormat(LPWAVEFORMATEX format)
+{
+ HRESULT err = E_FAIL;
+
+ if (primary)
+ err = primary->SetFormat(format);
+
+ return SUCCEEDED(err);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::GetFormat(LPWAVEFORMATEX format)
+{
+ if (!format) format = &wfex;
+
+ HRESULT err = E_FAIL;
+
+ if (primary)
+ err = primary->GetFormat(format, sizeof(WAVEFORMATEX), 0);
+
+ return SUCCEEDED(err);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundCardD3D::ShowFormat()
+{
+ Print(" SoundCardD3D Primary Buffer Format:\n");
+ Print(" bits: %d\n", wfex.wBitsPerSample);
+ Print(" chls: %d\n", wfex.nChannels);
+ Print(" rate: %d\n\n", wfex.nSamplesPerSec);
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+SoundCardD3D::CreateSound(DWORD flags, LPWAVEFORMATEX format)
+{
+ if (!soundcard) return 0;
+
+ Sound* result = new(__FILE__,__LINE__) SoundD3D(soundcard, flags, format);
+ if (result) AddSound(result);
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+SoundCardD3D::CreateSound(DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data)
+{
+ if (!soundcard) return 0;
+ Sound* result = new(__FILE__,__LINE__) SoundD3D(soundcard, flags, format, len, data);
+
+ if (flags & (Sound::STREAMED | Sound::OGGVORBIS)) {
+ if (result)
+ AddSound(result);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundCardD3D::SetListener(const Camera& cam, const Vec3& vel)
+{
+ Point pos = cam.Pos();
+
+#ifdef DIRECT_SOUND_3D
+ listener->SetPosition((float) pos.x, (float) pos.z, (float) pos.y, DS3D_IMMEDIATE);
+ listener->SetOrientation((float) cam.vpn().x, (float) cam.vpn().y, (float) cam.vpn().z,
+ (float) cam.vup().x, (float) cam.vup().y, (float) cam.vup().z,
+ DS3D_IMMEDIATE);
+ listener->SetVelocity(vel.x, vel.y, vel.z, DS3D_IMMEDIATE);
+#else
+ listener.Clone(cam);
+ listener.MoveTo(pos.x, pos.z, pos.y);
+ velocity = vel;
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::Pause()
+{
+ AutoThreadSync a(sync);
+
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+
+ if ((s->GetFlags() & Sound::INTERFACE) == 0)
+ s->Pause();
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::Resume()
+{
+ AutoThreadSync a(sync);
+
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+
+ if (s->IsReady())
+ s->Play();
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::StopSoundEffects()
+{
+ AutoThreadSync a(sync);
+
+ DWORD ok_sounds = (Sound::INTERFACE | Sound::OGGVORBIS | Sound::RESOURCE);
+
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+
+ if ((s->GetFlags() & ok_sounds) == 0)
+ s->Stop();
+ }
+
+ return true;
+}
+
+// +====================================================================+
+// | SOUND D3D
+// +====================================================================+
+
+SoundD3D::SoundD3D(LPDIRECTSOUND card, DWORD flag_req, LPWAVEFORMATEX format)
+: soundcard(card), buffer(0), min_dist(1.0f), max_dist(100000.0f),
+stream(0), stream_left(0), min_safety(0), read_size(0), transfer(0), w(0), r(0),
+stream_offset(0), data_len(0), data(0), moved(false), eos_written(false), eos_latch(0),
+ov_file(0), total_time(0)
+{
+ flags = flag_req;
+
+ CopyMemory(&wfex, format, sizeof(wfex));
+ ZeroMemory(&dsbd, sizeof(dsbd));
+
+ dsbd.dwSize = sizeof(dsbd);
+ dsbd.dwFlags = DSBCAPS_CTRLVOLUME /* | DSBCAPS_GETCURRENTPOSITION2 */;
+ dsbd.lpwfxFormat = &wfex;
+
+#ifdef DIRECT_SOUND_3D
+ sound3d = 0;
+ if (flags & LOCALIZED)
+ if (flags & LOC_3D)
+ dsbd.dwFlags |= DSBCAPS_CTRL3D;
+ else
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#else
+ dsbd.dwFlags |= DSBCAPS_LOCSOFTWARE;
+
+ if (flags & LOCALIZED)
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#endif
+
+}
+
+// +--------------------------------------------------------------------+
+// SOUND RESOURCE CONSTRUCTOR:
+// (Now also used to create Ogg Vorbis streaming sound objects)
+
+SoundD3D::SoundD3D(LPDIRECTSOUND card, DWORD flag_req, LPWAVEFORMATEX format, DWORD len, LPBYTE pData)
+: soundcard(card), buffer(0), min_dist(1.0f), max_dist(100000.0f),
+stream(0), stream_left(0), min_safety(0), read_size(0), transfer(0), w(0), r(0),
+stream_offset(0), data_len(0), data(0), moved(false), eos_written(false), eos_latch(0),
+ov_file(0)
+{
+ flags = flag_req;
+
+ if (!(flags & (STREAMED | OGGVORBIS)))
+ flags = flag_req | RESOURCE;
+
+ CopyMemory(&wfex, format, sizeof(wfex));
+ ZeroMemory(&dsbd, sizeof(dsbd));
+
+ dsbd.dwSize = sizeof(dsbd);
+ dsbd.dwFlags = DSBCAPS_CTRLVOLUME /* | DSBCAPS_GETCURRENTPOSITION2 */;
+ dsbd.lpwfxFormat = &wfex;
+
+#ifdef DIRECT_SOUND_3D
+ sound3d = 0;
+ if (flags & LOCALIZED)
+ if (flags & LOC_3D)
+ dsbd.dwFlags |= DSBCAPS_CTRL3D;
+ else
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#else
+ if (flags & LOCALIZED)
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#endif
+
+ if (len) {
+ // If this is an OGG VORBIS streaming sound,
+ // the parameter that normally points to the actual data
+ // is used to pass in an Ogg Vorbis file structure instead:
+
+ if (flags & OGGVORBIS) {
+ ov_file = (OggVorbis_File*) pData;
+ StreamOggFile();
+ }
+
+ else {
+ data_len = len;
+ data = new(__FILE__,__LINE__) BYTE[len];
+
+ if (!data) {
+ data_len = 0;
+ }
+
+ else {
+ CopyMemory(data, pData, data_len);
+ Load(data_len, data);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+SoundD3D::~SoundD3D()
+{
+ delete [] data;
+ delete [] transfer;
+
+ if (ov_file) {
+ ov_clear(ov_file);
+ delete ov_file;
+ }
+
+ else if (stream) {
+ fclose(stream);
+ }
+
+ RELEASE(buffer);
+
+#ifdef DIRECT_SOUND_3D
+ RELEASE(sound3d);
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3D::Update()
+{
+ if (!buffer || status != PLAYING) return; // nothing to do
+
+ DWORD dstat;
+ HRESULT hr = buffer->GetStatus(&dstat);
+ if (FAILED(hr)) {
+ SoundD3DError("Update: GetStatus failed", hr);
+ return;
+ }
+
+ AutoThreadSync a(sync);
+
+ if (sound_check) sound_check->Update(this);
+
+ if (!Game::Paused() || flags & STREAMED) {
+ // see if we are done:
+ if (!(dstat & DSBSTATUS_PLAYING)) {
+ status = DONE;
+ buffer->Stop();
+ return;
+ }
+
+ // if not done, see if we need to change location params:
+ if (moved) {
+ Localize();
+ }
+ }
+
+ // if not done, see if we need to load more data
+ // into the streaming buffer:
+ if (flags & STREAMED) {
+ buffer->GetCurrentPosition(&r, 0);
+
+ DWORD data_left;
+ if (w > r)
+ data_left = w - r;
+ else
+ data_left = w + (read_size + min_safety) - r;
+
+ // getting low, fill 'er up:
+ if (eos_written || data_left <= min_safety) {
+ StreamBlock();
+
+ if (stream_left == 0) {
+ // if this is the end of a looping stream,
+ if (flags & LOOP) {
+ RewindStream();
+ looped++;
+ }
+ else {
+ if (!eos_written) {
+ eos_written = true;
+ eos_latch = 3;
+ }
+
+ else if (--eos_latch == 0) {
+ status = DONE;
+ buffer->Stop();
+ }
+ }
+ }
+
+ status = PLAYING;
+ }
+ }
+}
+
+void
+SoundD3D::StreamBlock()
+{
+ if (flags & OGGVORBIS) {
+ StreamOggBlock();
+ return;
+ }
+
+ if (!stream || !stream_left)
+ return;
+
+ if (stream_left < read_size) {
+ if (stream_left > 0) {
+ fread(transfer, stream_left, 1, stream);
+ Load(stream_left, transfer);
+ stream_left = 0;
+ }
+ }
+
+ else if (read_size > 0) {
+ fread(transfer, read_size, 1, stream);
+ Load(read_size, transfer);
+ stream_left -= read_size;
+ }
+}
+
+int ogg_read_error_count = 0;
+
+void
+SoundD3D::StreamOggBlock()
+{
+ int current_bitstream;
+ DWORD bytes_read = 0;
+ long retval = 0;
+ char* p = (char*) transfer;
+
+ while (stream_left && bytes_read < read_size) {
+ retval = ov_read(ov_file, p, read_size-bytes_read, 0,2,1, &current_bitstream);
+
+ if (retval == 0) {
+ /* EOF */
+ stream_left = 0;
+ }
+ else if (retval < 0) {
+ /* error in the stream. Not a problem, just reporting it in
+ case the app cares. In this case, we don't. */
+ ogg_read_error_count++;
+ }
+ else {
+ /* we don't bother dealing with sample rate changes, etc, but you'll have to ??? */
+ bytes_read += retval;
+ stream_left -= retval;
+ p += retval;
+ }
+ }
+
+ if (bytes_read)
+ Load(bytes_read, transfer);
+}
+
+void
+SoundD3D::RewindStream()
+{
+ if (flags & OGGVORBIS) {
+ RewindOggStream();
+ return;
+ }
+
+ if (!stream || !buffer)
+ return;
+
+ // rewind the stream and keep going...
+ eos_written = false;
+ eos_latch = 0;
+ read_size = wfex.nAvgBytesPerSec / 2;
+
+ // find the size of the file:
+ fseek(stream, 0, SEEK_END);
+ stream_left = ftell(stream) - stream_offset;
+ fseek(stream, stream_offset, SEEK_SET);
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ status = DONE;
+ buffer->Stop();
+ }
+}
+
+void
+SoundD3D::RewindOggStream()
+{
+ if (!ov_file || !buffer)
+ return;
+
+ // rewind the stream and keep going...
+ eos_written = false;
+ eos_latch = 0;
+ read_size = wfex.nAvgBytesPerSec / 2;
+
+ // set the stream pointer back to the beginning:
+ ov_pcm_seek(ov_file, 0);
+
+ // find the size of the file:
+ stream_left = (DWORD) ov_pcm_total(ov_file,-1);
+ stream_offset = 0;
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ status = DONE;
+ buffer->Stop();
+ }
+}
+
+void
+SoundD3D::Localize()
+{
+#ifdef DIRECT_SOUND_3D
+ if (sound3d) {
+ sound3d->SetMinDistance(min_dist, DS3D_IMMEDIATE);
+ sound3d->SetMaxDistance(max_dist, DS3D_IMMEDIATE);
+ sound3d->SetPosition(location.x, location.y, location.z, DS3D_IMMEDIATE);
+ sound3d->SetVelocity(velocity.x, velocity.y, velocity.z, DS3D_IMMEDIATE);
+ }
+
+#else
+
+ // if no buffer, nothing to do:
+ if (!buffer) {
+ moved = false;
+ return;
+ }
+
+ // Compute pan and volume from scratch:
+
+ if ((flags & LOC_3D) && creator) {
+ Vec3 loc = location;
+
+ SoundCardD3D* ears = (SoundCardD3D*) creator;
+ Camera& listener = ears->listener;
+ Vec3 ear_loc = listener.Pos(); ear_loc.SwapYZ();
+ Vec3 direction = loc - ear_loc;
+
+ loc.x = direction * listener.vrt();
+ loc.y = direction * listener.vup();
+ loc.z = direction * listener.vpn();
+
+ double pan = 10000;
+ if (loc.z != 0.0f) pan = fabs(1000.0f * loc.x / loc.z);
+ if (pan > 10000) pan = 10000;
+ if (loc.x < 0) pan = -pan;
+
+ if (volume > 0)
+ volume = 0;
+
+ double vol = volume;
+ double mind2 = min_dist * min_dist;
+ double maxd2 = max_dist * max_dist;
+ double d2 = (loc.x*loc.x) + (loc.y*loc.y) + (loc.z*loc.z);
+
+ if (d2 > maxd2)
+ vol = -10000;
+ else if (d2 > mind2)
+ vol -= (d2-mind2)/(maxd2-mind2) * (vol+10000);
+
+ // clamp volume to legal range:
+ if (vol < -10000) vol = -10000;
+ else if (vol > volume) vol = volume;
+
+ /***
+ Print("Localize: ears = (%f, %f, %f)\n", ear_loc.x, ear_loc.y, ear_loc.z);
+ Print(" world = (%f, %f, %f)\n", location.x, location.y, location.z);
+ Print(" view = (%f, %f, %f)\n", loc.x, loc.y, loc.z);
+ Print(" Pan=%f Volume=%f\n", pan, vol);
+ /***/
+
+ HRESULT hr = buffer->SetPan((LONG) pan);
+ if (!SUCCEEDED(hr)) {
+ char warn[512];
+ sprintf_s(warn, "Warning could not set pan on buffer to %f", pan);
+ SoundD3DError(warn, hr);
+ }
+
+ hr = buffer->SetVolume((LONG) vol);
+ if (!SUCCEEDED(hr)) {
+ char warn[512];
+ sprintf_s(warn, "Warning: could not set volume on buffer to %f", vol);
+ SoundD3DError(warn, hr);
+ }
+
+ // if not too far to hear...
+ if ((flags & DOPPLER) && (d2 < maxd2)) {
+ // COMPUTE DOPPLER SHIFT:
+ const float c = 10000.0f;
+
+ direction.Normalize();
+ float v_L = ears->velocity * direction;
+ float v_S = velocity * direction;
+
+ DWORD f_shift = wfex.nSamplesPerSec;
+
+ if (v_L != v_S) {
+ // towards listener:
+ if (v_S < 0)
+ f_shift = wfex.nSamplesPerSec + 20;
+ else
+ f_shift = wfex.nSamplesPerSec - 20;
+ }
+
+ // distance rolloff of high frequencies:
+ double dist = sqrt(d2);
+ DWORD roll_off = (DWORD) (80 * dist/max_dist);
+
+ f_shift -= roll_off;
+
+ if (f_shift < 100) f_shift = 100;
+ if (f_shift > 100000) f_shift = 100000;
+
+ hr = buffer->SetFrequency(f_shift);
+ if (!SUCCEEDED(hr)) {
+ char warn[512];
+ sprintf_s(warn, "Warning: could not set Doppler frequency on buffer to %d", f_shift); //-V576
+ SoundD3DError(warn, hr);
+ }
+ }
+ }
+ else {
+ buffer->SetPan((LONG) location.x);
+ buffer->SetVolume((LONG) volume);
+ }
+#endif
+
+ moved = false;
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+SoundD3D::Duplicate()
+{
+ Sound* sound = 0;
+
+ if (flags & RESOURCE) {
+ sound = Sound::Create(flags & ~RESOURCE, &wfex);
+
+ if (sound && !(flags & STREAMED)) {
+ sound->SetMinDistance(min_dist);
+ sound->SetMaxDistance(max_dist);
+
+ if (!buffer) {
+ sound->Load(data_len, data);
+ }
+
+ else {
+ SoundD3D* s3d = (SoundD3D*) sound;
+ soundcard->DuplicateSoundBuffer(buffer, &s3d->buffer);
+ sound->Rewind();
+ }
+ }
+ }
+
+ return sound;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::StreamFile(const char* name, DWORD offset)
+{
+ DWORD buf_size = wfex.nAvgBytesPerSec / 2;
+ DWORD safety_zone = buf_size * 2;
+
+ if (stream) {
+ delete[] transfer;
+ transfer = 0;
+ fclose(stream);
+ }
+
+ status = UNINITIALIZED;
+ stream_left = 0;
+ stream_offset = offset;
+
+ eos_written = false;
+ eos_latch = 0;
+ min_safety = safety_zone;
+ read_size = buf_size;
+
+ fopen_s(&stream, name, "rb");
+
+ // open the stream:
+ if (stream == 0) {
+ SoundD3DError("StreamFile: could not open stream", E_FAIL);
+ return E_FAIL;
+ }
+
+ // find the size of the file:
+ fseek(stream, 0, SEEK_END);
+ stream_left = ftell(stream) - offset;
+ fseek(stream, stream_offset, SEEK_SET);
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ read_size = stream_left;
+ }
+
+ HRESULT hr = AllocateBuffer(read_size + min_safety);
+
+ if (FAILED(hr))
+ return hr;
+
+ flags |= STREAMED;
+
+ // preload the buffer:
+ w = r = 0;
+ transfer = new(__FILE__,__LINE__) BYTE[read_size + 1024];
+
+ if (!transfer) {
+ hr = E_FAIL;
+ }
+
+ else {
+ ZeroMemory(transfer, read_size+1024);
+ StreamBlock();
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::StreamOggFile()
+{
+ DWORD buf_size = wfex.nAvgBytesPerSec / 2;
+ DWORD safety_zone = buf_size * 2;
+
+ if (stream) {
+ delete[] transfer;
+ fclose(stream);
+
+ transfer = 0;
+ stream = 0;
+ }
+
+ status = UNINITIALIZED;
+ stream_left = (DWORD) ov_pcm_total(ov_file,-1);
+ stream_offset = 0;
+
+ eos_written = false;
+ eos_latch = 0;
+ min_safety = safety_zone;
+ read_size = buf_size;
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ read_size = stream_left;
+ }
+
+ HRESULT hr = AllocateBuffer(read_size + min_safety);
+
+ if (FAILED(hr))
+ return hr;
+
+ flags |= STREAMED | OGGVORBIS;
+
+ // preload the buffer:
+ w = r = 0;
+ transfer = new(__FILE__,__LINE__) BYTE[read_size + 1024];
+
+ if (!transfer) {
+ hr = E_FAIL;
+ }
+
+ else {
+ ZeroMemory(transfer, read_size+1024);
+ StreamOggBlock();
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Load(DWORD bytes, BYTE* data)
+{
+ status = UNINITIALIZED;
+
+ HRESULT hr;
+
+ if (!buffer) {
+ hr = AllocateBuffer(bytes);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+
+ LPVOID dest1, dest2;
+ DWORD size1, size2;
+
+ hr = buffer->Lock(w, bytes, &dest1, &size1, &dest2, &size2, 0);
+
+ if (hr == DSERR_BUFFERLOST) {
+ buffer->Restore();
+ hr = buffer->Lock(w, bytes, &dest1, &size1, &dest2, &size2, 0);
+ }
+
+ if (SUCCEEDED(hr)) {
+ CopyMemory(dest1, data, size1);
+ if (dest2) {
+ CopyMemory(dest2, data + size1, size2);
+ }
+
+ if (flags & STREAMED)
+ w = (w + size1 + size2) % (read_size + min_safety);
+ else
+ w += size1 + size2;
+
+ hr = buffer->Unlock(dest1, size1, dest2, size2);
+ if (FAILED(hr)) {
+ SoundD3DError("Load: could not unlock buffer", hr);
+ }
+ }
+ else {
+ SoundD3DError("Load: could not lock buffer", hr);
+ }
+
+ if (SUCCEEDED(hr)) {
+ status = READY;
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::AllocateBuffer(DWORD bytes)
+{
+ HRESULT hr = S_OK;
+
+ if (!buffer) {
+ dsbd.dwBufferBytes = bytes;
+
+ if (soundcard) {
+ hr = soundcard->CreateSoundBuffer(&dsbd, &buffer, NULL);
+
+ if (FAILED(hr)) {
+ SoundD3DError("AllocateBuffer: could not create buffer", hr);
+
+ Print(" dsbd.dwSize = %8d\n", dsbd.dwSize);
+ Print(" dsbd.dwFlags = %08x\n", dsbd.dwFlags);
+ Print(" dsbd.dwBufferBytes = %8d\n", dsbd.dwBufferBytes);
+ Print(" dsbd.lpwfxFormat = %08x\n", dsbd.lpwfxFormat);
+
+ if (dsbd.lpwfxFormat) {
+ Print(" wfex.wBitsPerSample = %8d\n", dsbd.lpwfxFormat->wBitsPerSample);
+ Print(" wfex.nChannels = %8d\n", dsbd.lpwfxFormat->nChannels);
+ Print(" wfex.nSamplesPerSec = %8d\n", dsbd.lpwfxFormat->nSamplesPerSec);
+ }
+ }
+ }
+ else {
+ SoundD3DError("AllocateBuffer: soundcard is null", E_FAIL);
+ }
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Play()
+{
+ if (IsPlaying()) return S_OK;
+ if (!buffer) return E_FAIL;
+
+ HRESULT hr = E_FAIL;
+
+ if (IsDone())
+ hr = Rewind();
+
+ if (IsReady()) {
+ if (moved)
+ Localize();
+
+ if (flags & LOOP || flags & STREAMED)
+ hr = buffer->Play(0, 0, DSBPLAY_LOOPING);
+ else
+ hr = buffer->Play(0, 0, 0);
+
+ if (SUCCEEDED(hr))
+ status = PLAYING;
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Rewind()
+{
+ if (!buffer) return E_FAIL;
+
+ HRESULT hr = S_OK;
+
+ if (IsPlaying())
+ hr = Stop();
+
+ if (flags & STREAMED) {
+ RewindStream();
+ StreamBlock();
+ }
+
+ else {
+ hr = buffer->SetCurrentPosition(0);
+ }
+
+ if (SUCCEEDED(hr)) {
+ status = READY;
+ looped = 0;
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Pause()
+{
+ if (status == DONE)
+ return S_OK;
+
+ HRESULT hr = Stop();
+
+ if (SUCCEEDED(hr))
+ status = READY;
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Stop()
+{
+ if (!buffer)
+ return E_FAIL;
+
+ if (!IsPlaying()) {
+ status = DONE;
+ return S_OK;
+ }
+
+ status = DONE;
+ return buffer->Stop();
+}
+
+// +--------------------------------------------------------------------+
+
+double
+SoundD3D::GetTimeRemaining() const
+{
+ double time_left = -1;
+
+ if (IsPlaying() || IsReady()) {
+ time_left = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+ }
+
+ return time_left;
+}
+
+double
+SoundD3D::GetTimeElapsed() const
+{
+ double time_elapsed = 0;
+
+ if (IsPlaying()) {
+ time_elapsed = total_time - GetTimeRemaining();
+ }
+
+ return time_elapsed;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3D::SetVolume(long v)
+{
+ if (v > 0) v = 0;
+ else if (v < -10000) v = -10000;
+
+ volume = v;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+long
+SoundD3D::GetPan() const
+{
+ long p = 10000;
+
+ if (location.z) p = (long) fabs(location.x/location.z);
+ if (p > 10000) p = 10000;
+ if (location.x < 0) p = -p;
+
+ return p;
+}
+
+void
+SoundD3D::SetPan(long p)
+{
+ if (p > 10000) p = 10000;
+ if (p < -10000) p = -10000;
+
+ location.x = (float) p;
+ location.y = 0.0f;
+ location.z = 1.0f;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3D::SetLocation(const Vec3& l)
+{
+ location = l;
+ moved = true;
+}
+
+void
+SoundD3D::SetVelocity(const Vec3& v)
+{
+ velocity = v;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+SoundD3D::GetMinDistance() const
+{
+ return min_dist;
+}
+
+void
+SoundD3D::SetMinDistance(float f)
+{
+ min_dist = f;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+SoundD3D::GetMaxDistance() const
+{
+ return max_dist;
+}
+void
+SoundD3D::SetMaxDistance(float f)
+{
+ max_dist = f;
+ moved = true;
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3DError(const char* msg, HRESULT err)
+{
+ Print(" SoundD3D: %s. [%s]\n", msg, DSErrStr(err));
+}
+
+char*
+DSErrStr(HRESULT err)
+{
+ switch (err) {
+ case DS_OK: return "DS_OK";
+
+ case DSERR_ALLOCATED: return
+ "The call failed because resources (such as a priority level) "
+ "were already being used by another caller.";
+
+ case DSERR_CONTROLUNAVAIL: return
+ "The control (vol,pan,etc.) requested by the caller is not available.";
+
+ case DSERR_INVALIDPARAM: return
+ "An invalid parameter was passed to the returning function.";
+
+ case DSERR_INVALIDCALL: return
+ "This call is not valid for the current state of this object";
+
+ case DSERR_GENERIC: return
+ "An undetermined error occured inside the DirectSound subsystem";
+
+ case DSERR_PRIOLEVELNEEDED: return
+ "The caller does not have the priority level required for the function to succeed.";
+
+ case DSERR_OUTOFMEMORY: return
+ "Not enough free memory is available to complete the operation";
+
+ case DSERR_BADFORMAT: return
+ "The specified WAVE format is not supported";
+
+ case DSERR_UNSUPPORTED: return
+ "The function called is not supported at this time";
+
+ case DSERR_NODRIVER: return
+ "No sound driver is available for use";
+
+ case DSERR_ALREADYINITIALIZED: return
+ "This object is already initialized";
+
+ case DSERR_NOAGGREGATION: return
+ "This object does not support aggregation";
+
+ case DSERR_BUFFERLOST: return
+ "The buffer memory has been lost, and must be restored.";
+
+ case DSERR_OTHERAPPHASPRIO: return
+ "Another app has a higher priority level, preventing this call from succeeding.";
+
+ case DSERR_UNINITIALIZED: return
+ "This object has not been initialized.";
+
+#ifdef DIRECT_SOUND_3D
+ case DSERR_NOINTERFACE: return
+ "The requested COM interface is not available.";
+#endif
+
+ default: return "Unknown Error Code";
+ }
+
+ return "Internal Error";
+}
+