Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
SoundD3D.cpp
Go to the documentation of this file.
1 /* Project nGenEx
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: nGenEx.lib
6  FILE: SoundD3D.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  DirectSound and DirectSound3D (sound card) class
13 */
14 
15 //#define INITGUID
16 #include <objbase.h>
17 #include <cguid.h>
18 #include <mmsystem.h>
19 #include <dsound.h>
20 
21 
22 #include "MemDebug.h"
23 #include "SoundD3D.h"
24 #include "Game.h"
25 
26 #ifdef DIRECT_SOUND_3D
27 #include "ia3d.h"
28 #endif
29 
30 // +--------------------------------------------------------------------+
31 
32 void Print(const char* msg, ...);
33 char* DSErrStr(HRESULT dserr);
34 void SoundD3DError(const char* msg, HRESULT dserr);
35 
36 static int DS3D_report_errors = 1;
37 
38 #ifndef RELEASE
39 #define RELEASE(x) if (x) { x->Release(); x=NULL; }
40 #endif
41 
42 // +====================================================================+
43 // | SOUND CARD D3D
44 // +====================================================================+
45 
47 : soundcard(0), primary(0)
48 {
49  HRESULT err = 0;
50  status = SC_ERROR;
51 
52  // 1) Get interface to DirectSound object:
53 
54 #ifdef DIRECT_SOUND_3D
55  CoInitialize(NULL);
56  err = CoCreateInstance(CLSID_A3d, NULL, CLSCTX_INPROC_SERVER,
57  IID_IDirectSound, (VOID **)&soundcard);
58 
59  if (SUCCEEDED(err)) {
60  soundcard->Initialize(NULL);
61  SoundD3DError("Initialized Aureal3D Sound", 0);
62  }
63  else {
64  SoundD3DError("Could not initialize Aureal3D Sound", err);
65  SoundD3DError("Proceding with standard DirectSoundCreate", 0);
66 #endif
67 
68  err = DirectSoundCreate(0, &soundcard, 0);
69  if (FAILED(err)) {
70  SoundD3DError("Could not create DirectSound object.", err);
71  soundcard = 0;
72  primary = 0;
73  return;
74  }
75 
76 #ifdef DIRECT_SOUND_3D
77  }
78 #endif
79 
80  // 2) Set the cooperative level:
81  err = soundcard->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
82  if (FAILED(err)) {
83  SoundD3DError("Could not set cooperative level.", err);
85  return;
86  }
87 
88  // Prepare to initialize the primary buffer:
89  DSCAPS caps;
90  memset(&caps, 0, sizeof(caps));
91  caps.dwSize = sizeof(caps);
92 
93  err = soundcard->GetCaps(&caps);
94  if (FAILED(err)) {
95  SoundD3DError("Could not get soundcard caps.", err);
97  return;
98  }
99 
100  if (caps.dwFlags & DSCAPS_EMULDRIVER)
101  Print(" WARNING: using DirectSound emulated drivers\n");
102 
103  memset(&dsbd, 0, sizeof(dsbd));
104  dsbd.dwSize = sizeof(dsbd);
105 
106 #ifdef DIRECT_SOUND_3D
107  int use_ds3d = true;
108 
109  // 3) Set up the primary buffer (try to use DS3D):
110  dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D;
111 
112  err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
113  if (err == DSERR_CONTROLUNAVAIL) {
114  use_ds3d = false;
115 
116  // try again, without using DS3D
117  dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLDEFAULT;
118 
119  err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
120  if (FAILED(err)) {
121  SoundD3DError("Could not initialize primary buffer", err);
123  return;
124  }
125  else {
126  Print(" WARNING: DirectSound3D is not available, simulating instead\n");
127  }
128  }
129 
130  // 4) Set up the listener:
131  if (primary && use_ds3d) {
132  err = primary->QueryInterface(IID_IDirectSound3DListener, (void**)&listener);
133  if (FAILED(err)) {
134  SoundD3DError("Could not get listener interface", err);
135  }
136  else {
137  listener->SetPosition(0.0f, 0.0f, 0.0f, DS3D_IMMEDIATE);
138  listener->SetOrientation(0.0f, 0.0f, 1.0f,
139  0.0f, 1.0f, 0.0f, DS3D_IMMEDIATE);
140  }
141  }
142 
143 #else
144  // 3) Set up the primary buffer:
145  dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
146 
147  err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
148  if (FAILED(err)) {
149  SoundD3DError("Could not initialize primary buffer", err);
151  return;
152  }
153 #endif
154 
155  // 5) Set primary buffer parameters to 16 bit STEREO at 44kHz
156  SetFormat(16, 2, 44100);
157 
158  // read back the result format and display to the log file:
159  GetFormat(0);
160  ShowFormat();
161 
162  status = SC_OK;
163 }
164 
165 // +--------------------------------------------------------------------+
166 
168 {
169  ListIter<Sound> iter = sounds;
170  while (++iter) {
171  Sound* s = iter.value();
172  s->Stop();
173  }
174 
175  sounds.destroy();
176 
177 #ifdef DIRECT_SOUND_3D
178  RELEASE(listener);
179 #endif
180 
181  RELEASE(primary);
183 
184  Print(" SoundCardD3D: shutdown\n");
185 
187 }
188 
189 // +--------------------------------------------------------------------+
190 
191 bool
192 SoundCardD3D::SetFormat(int bits, int channels, int hertz)
193 {
194  if (!soundcard) return false;
195 
196  DSCAPS caps;
197  memset(&caps, 0, sizeof(caps));
198  caps.dwSize = sizeof(caps);
199  soundcard->GetCaps(&caps);
200 
201  if (!(caps.dwFlags & DSCAPS_PRIMARY16BIT)) bits = 8;
202  if (!(caps.dwFlags & DSCAPS_PRIMARYSTEREO)) channels = 1;
203 
204  memset(&wfex, 0, sizeof(wfex));
205 
206  wfex.wFormatTag = WAVE_FORMAT_PCM;
207  wfex.nChannels = channels;
208  wfex.nSamplesPerSec = hertz;
209  wfex.nBlockAlign = (channels * bits) / 8;
210  wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
211  wfex.wBitsPerSample = bits;
212 
213  return SetFormat(&wfex);
214 }
215 
216 // +--------------------------------------------------------------------+
217 
218 bool
219 SoundCardD3D::SetFormat(LPWAVEFORMATEX format)
220 {
221  HRESULT err = E_FAIL;
222 
223  if (primary)
224  err = primary->SetFormat(format);
225 
226  return SUCCEEDED(err);
227 }
228 
229 // +--------------------------------------------------------------------+
230 
231 bool
232 SoundCardD3D::GetFormat(LPWAVEFORMATEX format)
233 {
234  if (!format) format = &wfex;
235 
236  HRESULT err = E_FAIL;
237 
238  if (primary)
239  err = primary->GetFormat(format, sizeof(WAVEFORMATEX), 0);
240 
241  return SUCCEEDED(err);
242 }
243 
244 // +--------------------------------------------------------------------+
245 
246 void
248 {
249  Print(" SoundCardD3D Primary Buffer Format:\n");
250  Print(" bits: %d\n", wfex.wBitsPerSample);
251  Print(" chls: %d\n", wfex.nChannels);
252  Print(" rate: %d\n\n", wfex.nSamplesPerSec);
253 }
254 
255 // +--------------------------------------------------------------------+
256 
257 Sound*
258 SoundCardD3D::CreateSound(DWORD flags, LPWAVEFORMATEX format)
259 {
260  if (!soundcard) return 0;
261 
262  Sound* result = new(__FILE__,__LINE__) SoundD3D(soundcard, flags, format);
263  if (result) AddSound(result);
264  return result;
265 }
266 
267 // +--------------------------------------------------------------------+
268 
269 Sound*
270 SoundCardD3D::CreateSound(DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data)
271 {
272  if (!soundcard) return 0;
273  Sound* result = new(__FILE__,__LINE__) SoundD3D(soundcard, flags, format, len, data);
274 
275  if (flags & (Sound::STREAMED | Sound::OGGVORBIS)) {
276  if (result)
277  AddSound(result);
278  }
279 
280  return result;
281 }
282 
283 // +--------------------------------------------------------------------+
284 
285 void
286 SoundCardD3D::SetListener(const Camera& cam, const Vec3& vel)
287 {
288  Point pos = cam.Pos();
289 
290 #ifdef DIRECT_SOUND_3D
291  listener->SetPosition((float) pos.x, (float) pos.z, (float) pos.y, DS3D_IMMEDIATE);
292  listener->SetOrientation((float) cam.vpn().x, (float) cam.vpn().y, (float) cam.vpn().z,
293  (float) cam.vup().x, (float) cam.vup().y, (float) cam.vup().z,
294  DS3D_IMMEDIATE);
295  listener->SetVelocity(vel.x, vel.y, vel.z, DS3D_IMMEDIATE);
296 #else
297  listener.Clone(cam);
298  listener.MoveTo(pos.x, pos.z, pos.y);
299  velocity = vel;
300 #endif
301 }
302 
303 // +--------------------------------------------------------------------+
304 
305 bool
307 {
308  AutoThreadSync a(sync);
309 
310  ListIter<Sound> iter = sounds;
311  while (++iter) {
312  Sound* s = iter.value();
313 
314  if ((s->GetFlags() & Sound::INTERFACE) == 0)
315  s->Pause();
316  }
317 
318  return true;
319 }
320 
321 // +--------------------------------------------------------------------+
322 
323 bool
325 {
326  AutoThreadSync a(sync);
327 
328  ListIter<Sound> iter = sounds;
329  while (++iter) {
330  Sound* s = iter.value();
331 
332  if (s->IsReady())
333  s->Play();
334  }
335 
336  return true;
337 }
338 
339 // +--------------------------------------------------------------------+
340 
341 bool
343 {
344  AutoThreadSync a(sync);
345 
346  DWORD ok_sounds = (Sound::INTERFACE | Sound::OGGVORBIS | Sound::RESOURCE);
347 
348  ListIter<Sound> iter = sounds;
349  while (++iter) {
350  Sound* s = iter.value();
351 
352  if ((s->GetFlags() & ok_sounds) == 0)
353  s->Stop();
354  }
355 
356  return true;
357 }
358 
359 // +====================================================================+
360 // | SOUND D3D
361 // +====================================================================+
362 
363 SoundD3D::SoundD3D(LPDIRECTSOUND card, DWORD flag_req, LPWAVEFORMATEX format)
364 : soundcard(card), buffer(0), min_dist(1.0f), max_dist(100000.0f),
365 stream(0), stream_left(0), min_safety(0), read_size(0), transfer(0), w(0), r(0),
366 stream_offset(0), data_len(0), data(0), moved(false), eos_written(false), eos_latch(0),
367 ov_file(0), total_time(0)
368 {
369  flags = flag_req;
370 
371  CopyMemory(&wfex, format, sizeof(wfex));
372  ZeroMemory(&dsbd, sizeof(dsbd));
373 
374  dsbd.dwSize = sizeof(dsbd);
375  dsbd.dwFlags = DSBCAPS_CTRLVOLUME /* | DSBCAPS_GETCURRENTPOSITION2 */;
376  dsbd.lpwfxFormat = &wfex;
377 
378 #ifdef DIRECT_SOUND_3D
379  sound3d = 0;
380  if (flags & LOCALIZED)
381  if (flags & LOC_3D)
382  dsbd.dwFlags |= DSBCAPS_CTRL3D;
383  else
384  dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
385 #else
386  dsbd.dwFlags |= DSBCAPS_LOCSOFTWARE;
387 
388  if (flags & LOCALIZED)
389  dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
390 #endif
391 
392 }
393 
394 // +--------------------------------------------------------------------+
395 // SOUND RESOURCE CONSTRUCTOR:
396 // (Now also used to create Ogg Vorbis streaming sound objects)
397 
398 SoundD3D::SoundD3D(LPDIRECTSOUND card, DWORD flag_req, LPWAVEFORMATEX format, DWORD len, LPBYTE pData)
399 : soundcard(card), buffer(0), min_dist(1.0f), max_dist(100000.0f),
400 stream(0), stream_left(0), min_safety(0), read_size(0), transfer(0), w(0), r(0),
401 stream_offset(0), data_len(0), data(0), moved(false), eos_written(false), eos_latch(0),
402 ov_file(0)
403 {
404  flags = flag_req;
405 
406  if (!(flags & (STREAMED | OGGVORBIS)))
407  flags = flag_req | RESOURCE;
408 
409  CopyMemory(&wfex, format, sizeof(wfex));
410  ZeroMemory(&dsbd, sizeof(dsbd));
411 
412  dsbd.dwSize = sizeof(dsbd);
413  dsbd.dwFlags = DSBCAPS_CTRLVOLUME /* | DSBCAPS_GETCURRENTPOSITION2 */;
414  dsbd.lpwfxFormat = &wfex;
415 
416 #ifdef DIRECT_SOUND_3D
417  sound3d = 0;
418  if (flags & LOCALIZED)
419  if (flags & LOC_3D)
420  dsbd.dwFlags |= DSBCAPS_CTRL3D;
421  else
422  dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
423 #else
424  if (flags & LOCALIZED)
425  dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
426 #endif
427 
428  if (len) {
429  // If this is an OGG VORBIS streaming sound,
430  // the parameter that normally points to the actual data
431  // is used to pass in an Ogg Vorbis file structure instead:
432 
433  if (flags & OGGVORBIS) {
434  ov_file = (OggVorbis_File*) pData;
435  StreamOggFile();
436  }
437 
438  else {
439  data_len = len;
440  data = new(__FILE__,__LINE__) BYTE[len];
441 
442  if (!data) {
443  data_len = 0;
444  }
445 
446  else {
447  CopyMemory(data, pData, data_len);
448  Load(data_len, data);
449  }
450  }
451  }
452 }
453 
454 // +--------------------------------------------------------------------+
455 
457 {
458  delete [] data;
459  delete [] transfer;
460 
461  if (ov_file) {
462  ov_clear(ov_file);
463  delete ov_file;
464  }
465 
466  else if (stream) {
467  fclose(stream);
468  }
469 
470  RELEASE(buffer);
471 
472 #ifdef DIRECT_SOUND_3D
473  RELEASE(sound3d);
474 #endif
475 }
476 
477 // +--------------------------------------------------------------------+
478 
479 void
481 {
482  if (!buffer || status != PLAYING) return; // nothing to do
483 
484  DWORD dstat;
485  HRESULT hr = buffer->GetStatus(&dstat);
486  if (FAILED(hr)) {
487  SoundD3DError("Update: GetStatus failed", hr);
488  return;
489  }
490 
491  AutoThreadSync a(sync);
492 
493  if (sound_check) sound_check->Update(this);
494 
495  if (!Game::Paused() || flags & STREAMED) {
496  // see if we are done:
497  if (!(dstat & DSBSTATUS_PLAYING)) {
498  status = DONE;
499  buffer->Stop();
500  return;
501  }
502 
503  // if not done, see if we need to change location params:
504  if (moved) {
505  Localize();
506  }
507  }
508 
509  // if not done, see if we need to load more data
510  // into the streaming buffer:
511  if (flags & STREAMED) {
512  buffer->GetCurrentPosition(&r, 0);
513 
514  DWORD data_left;
515  if (w > r)
516  data_left = w - r;
517  else
518  data_left = w + (read_size + min_safety) - r;
519 
520  // getting low, fill 'er up:
521  if (eos_written || data_left <= min_safety) {
522  StreamBlock();
523 
524  if (stream_left == 0) {
525  // if this is the end of a looping stream,
526  if (flags & LOOP) {
527  RewindStream();
528  looped++;
529  }
530  else {
531  if (!eos_written) {
532  eos_written = true;
533  eos_latch = 3;
534  }
535 
536  else if (--eos_latch == 0) {
537  status = DONE;
538  buffer->Stop();
539  }
540  }
541  }
542 
543  status = PLAYING;
544  }
545  }
546 }
547 
548 void
550 {
551  if (flags & OGGVORBIS) {
552  StreamOggBlock();
553  return;
554  }
555 
556  if (!stream || !stream_left)
557  return;
558 
559  if (stream_left < read_size) {
560  if (stream_left > 0) {
561  fread(transfer, stream_left, 1, stream);
563  stream_left = 0;
564  }
565  }
566 
567  else if (read_size > 0) {
568  fread(transfer, read_size, 1, stream);
571  }
572 }
573 
575 
576 void
578 {
579  int current_bitstream;
580  DWORD bytes_read = 0;
581  long retval = 0;
582  char* p = (char*) transfer;
583 
584  while (stream_left && bytes_read < read_size) {
585  retval = ov_read(ov_file, p, read_size-bytes_read, 0,2,1, &current_bitstream);
586 
587  if (retval == 0) {
588  /* EOF */
589  stream_left = 0;
590  }
591  else if (retval < 0) {
592  /* error in the stream. Not a problem, just reporting it in
593  case the app cares. In this case, we don't. */
595  }
596  else {
597  /* we don't bother dealing with sample rate changes, etc, but you'll have to ??? */
598  bytes_read += retval;
599  stream_left -= retval;
600  p += retval;
601  }
602  }
603 
604  if (bytes_read)
605  Load(bytes_read, transfer);
606 }
607 
608 void
610 {
611  if (flags & OGGVORBIS) {
612  RewindOggStream();
613  return;
614  }
615 
616  if (!stream || !buffer)
617  return;
618 
619  // rewind the stream and keep going...
620  eos_written = false;
621  eos_latch = 0;
622  read_size = wfex.nAvgBytesPerSec / 2;
623 
624  // find the size of the file:
625  fseek(stream, 0, SEEK_END);
626  stream_left = ftell(stream) - stream_offset;
627  fseek(stream, stream_offset, SEEK_SET);
628 
629  total_time = (double) stream_left /
630  (double) wfex.nAvgBytesPerSec;
631 
632  if (stream_left < read_size) {
633  status = DONE;
634  buffer->Stop();
635  }
636 }
637 
638 void
640 {
641  if (!ov_file || !buffer)
642  return;
643 
644  // rewind the stream and keep going...
645  eos_written = false;
646  eos_latch = 0;
647  read_size = wfex.nAvgBytesPerSec / 2;
648 
649  // set the stream pointer back to the beginning:
650  ov_pcm_seek(ov_file, 0);
651 
652  // find the size of the file:
653  stream_left = (DWORD) ov_pcm_total(ov_file,-1);
654  stream_offset = 0;
655 
656  total_time = (double) stream_left /
657  (double) wfex.nAvgBytesPerSec;
658 
659  if (stream_left < read_size) {
660  status = DONE;
661  buffer->Stop();
662  }
663 }
664 
665 void
667 {
668 #ifdef DIRECT_SOUND_3D
669  if (sound3d) {
670  sound3d->SetMinDistance(min_dist, DS3D_IMMEDIATE);
671  sound3d->SetMaxDistance(max_dist, DS3D_IMMEDIATE);
672  sound3d->SetPosition(location.x, location.y, location.z, DS3D_IMMEDIATE);
673  sound3d->SetVelocity(velocity.x, velocity.y, velocity.z, DS3D_IMMEDIATE);
674  }
675 
676 #else
677 
678  // if no buffer, nothing to do:
679  if (!buffer) {
680  moved = false;
681  return;
682  }
683 
684  // Compute pan and volume from scratch:
685 
686  if ((flags & LOC_3D) && creator) {
687  Vec3 loc = location;
688 
689  SoundCardD3D* ears = (SoundCardD3D*) creator;
690  Camera& listener = ears->listener;
691  Vec3 ear_loc = listener.Pos(); ear_loc.SwapYZ();
692  Vec3 direction = loc - ear_loc;
693 
694  loc.x = direction * listener.vrt();
695  loc.y = direction * listener.vup();
696  loc.z = direction * listener.vpn();
697 
698  double pan = 10000;
699  if (loc.z != 0.0f) pan = fabs(1000.0f * loc.x / loc.z);
700  if (pan > 10000) pan = 10000;
701  if (loc.x < 0) pan = -pan;
702 
703  if (volume > 0)
704  volume = 0;
705 
706  double vol = volume;
707  double mind2 = min_dist * min_dist;
708  double maxd2 = max_dist * max_dist;
709  double d2 = (loc.x*loc.x) + (loc.y*loc.y) + (loc.z*loc.z);
710 
711  if (d2 > maxd2)
712  vol = -10000;
713  else if (d2 > mind2)
714  vol -= (d2-mind2)/(maxd2-mind2) * (vol+10000);
715 
716  // clamp volume to legal range:
717  if (vol < -10000) vol = -10000;
718  else if (vol > volume) vol = volume;
719 
720  /***
721  Print("Localize: ears = (%f, %f, %f)\n", ear_loc.x, ear_loc.y, ear_loc.z);
722  Print(" world = (%f, %f, %f)\n", location.x, location.y, location.z);
723  Print(" view = (%f, %f, %f)\n", loc.x, loc.y, loc.z);
724  Print(" Pan=%f Volume=%f\n", pan, vol);
725  /***/
726 
727  HRESULT hr = buffer->SetPan((LONG) pan);
728  if (!SUCCEEDED(hr)) {
729  char warn[512];
730  sprintf_s(warn, "Warning could not set pan on buffer to %f", pan);
731  SoundD3DError(warn, hr);
732  }
733 
734  hr = buffer->SetVolume((LONG) vol);
735  if (!SUCCEEDED(hr)) {
736  char warn[512];
737  sprintf_s(warn, "Warning: could not set volume on buffer to %f", vol);
738  SoundD3DError(warn, hr);
739  }
740 
741  // if not too far to hear...
742  if ((flags & DOPPLER) && (d2 < maxd2)) {
743  // COMPUTE DOPPLER SHIFT:
744  const float c = 10000.0f;
745 
746  direction.Normalize();
747  float v_L = ears->velocity * direction;
748  float v_S = velocity * direction;
749 
750  DWORD f_shift = wfex.nSamplesPerSec;
751 
752  if (v_L != v_S) {
753  // towards listener:
754  if (v_S < 0)
755  f_shift = wfex.nSamplesPerSec + 20;
756  else
757  f_shift = wfex.nSamplesPerSec - 20;
758  }
759 
760  // distance rolloff of high frequencies:
761  double dist = sqrt(d2);
762  DWORD roll_off = (DWORD) (80 * dist/max_dist);
763 
764  f_shift -= roll_off;
765 
766  if (f_shift < 100) f_shift = 100;
767  if (f_shift > 100000) f_shift = 100000;
768 
769  hr = buffer->SetFrequency(f_shift);
770  if (!SUCCEEDED(hr)) {
771  char warn[512];
772  sprintf_s(warn, "Warning: could not set Doppler frequency on buffer to %d", f_shift);
773  SoundD3DError(warn, hr);
774  }
775  }
776  }
777  else {
778  buffer->SetPan((LONG) location.x);
779  buffer->SetVolume((LONG) volume);
780  }
781 #endif
782 
783  moved = false;
784 }
785 
786 // +--------------------------------------------------------------------+
787 
788 Sound*
790 {
791  Sound* sound = 0;
792 
793  if (flags & RESOURCE) {
794  sound = Sound::Create(flags & ~RESOURCE, &wfex);
795 
796  if (sound && !(flags & STREAMED)) {
797  sound->SetMinDistance(min_dist);
798  sound->SetMaxDistance(max_dist);
799 
800  if (!buffer) {
801  sound->Load(data_len, data);
802  }
803 
804  else {
805  SoundD3D* s3d = (SoundD3D*) sound;
806  soundcard->DuplicateSoundBuffer(buffer, &s3d->buffer);
807  sound->Rewind();
808  }
809  }
810  }
811 
812  return sound;
813 }
814 
815 // +--------------------------------------------------------------------+
816 
817 HRESULT
818 SoundD3D::StreamFile(const char* name, DWORD offset)
819 {
820  DWORD buf_size = wfex.nAvgBytesPerSec / 2;
821  DWORD safety_zone = buf_size * 2;
822 
823  if (stream) {
824  delete[] transfer;
825  transfer = 0;
826  fclose(stream);
827  }
828 
830  stream_left = 0;
831  stream_offset = offset;
832 
833  eos_written = false;
834  eos_latch = 0;
835  min_safety = safety_zone;
836  read_size = buf_size;
837 
838  fopen_s(&stream, name, "rb");
839 
840  // open the stream:
841  if (stream == 0) {
842  SoundD3DError("StreamFile: could not open stream", E_FAIL);
843  return E_FAIL;
844  }
845 
846  // find the size of the file:
847  fseek(stream, 0, SEEK_END);
848  stream_left = ftell(stream) - offset;
849  fseek(stream, stream_offset, SEEK_SET);
850 
851  total_time = (double) stream_left /
852  (double) wfex.nAvgBytesPerSec;
853 
854  if (stream_left < read_size) {
856  }
857 
858  HRESULT hr = AllocateBuffer(read_size + min_safety);
859 
860  if (FAILED(hr))
861  return hr;
862 
863  flags |= STREAMED;
864 
865  // preload the buffer:
866  w = r = 0;
867  transfer = new(__FILE__,__LINE__) BYTE[read_size + 1024];
868 
869  if (!transfer) {
870  hr = E_FAIL;
871  }
872 
873  else {
874  ZeroMemory(transfer, read_size+1024);
875  StreamBlock();
876  }
877 
878  return hr;
879 }
880 
881 // +--------------------------------------------------------------------+
882 
883 HRESULT
885 {
886  DWORD buf_size = wfex.nAvgBytesPerSec / 2;
887  DWORD safety_zone = buf_size * 2;
888 
889  if (stream) {
890  delete[] transfer;
891  fclose(stream);
892 
893  transfer = 0;
894  stream = 0;
895  }
896 
898  stream_left = (DWORD) ov_pcm_total(ov_file,-1);
899  stream_offset = 0;
900 
901  eos_written = false;
902  eos_latch = 0;
903  min_safety = safety_zone;
904  read_size = buf_size;
905 
906  total_time = (double) stream_left /
907  (double) wfex.nAvgBytesPerSec;
908 
909  if (stream_left < read_size) {
911  }
912 
913  HRESULT hr = AllocateBuffer(read_size + min_safety);
914 
915  if (FAILED(hr))
916  return hr;
917 
918  flags |= STREAMED | OGGVORBIS;
919 
920  // preload the buffer:
921  w = r = 0;
922  transfer = new(__FILE__,__LINE__) BYTE[read_size + 1024];
923 
924  if (!transfer) {
925  hr = E_FAIL;
926  }
927 
928  else {
929  ZeroMemory(transfer, read_size+1024);
930  StreamOggBlock();
931  }
932 
933  return hr;
934 }
935 
936 // +--------------------------------------------------------------------+
937 
938 HRESULT
939 SoundD3D::Load(DWORD bytes, BYTE* data)
940 {
942 
943  HRESULT hr;
944 
945  if (!buffer) {
946  hr = AllocateBuffer(bytes);
947  if (FAILED(hr)) {
948  return hr;
949  }
950  }
951 
952  LPVOID dest1, dest2;
953  DWORD size1, size2;
954 
955  hr = buffer->Lock(w, bytes, &dest1, &size1, &dest2, &size2, 0);
956 
957  if (hr == DSERR_BUFFERLOST) {
958  buffer->Restore();
959  hr = buffer->Lock(w, bytes, &dest1, &size1, &dest2, &size2, 0);
960  }
961 
962  if (SUCCEEDED(hr)) {
963  CopyMemory(dest1, data, size1);
964  if (dest2) {
965  CopyMemory(dest2, data + size1, size2);
966  }
967 
968  if (flags & STREAMED)
969  w = (w + size1 + size2) % (read_size + min_safety);
970  else
971  w += size1 + size2;
972 
973  hr = buffer->Unlock(dest1, size1, dest2, size2);
974  if (FAILED(hr)) {
975  SoundD3DError("Load: could not unlock buffer", hr);
976  }
977  }
978  else {
979  SoundD3DError("Load: could not lock buffer", hr);
980  }
981 
982  if (SUCCEEDED(hr)) {
983  status = READY;
984  }
985 
986  return hr;
987 }
988 
989 // +--------------------------------------------------------------------+
990 
991 HRESULT
993 {
994  HRESULT hr = S_OK;
995 
996  if (!buffer) {
997  dsbd.dwBufferBytes = bytes;
998 
999  if (soundcard) {
1000  hr = soundcard->CreateSoundBuffer(&dsbd, &buffer, NULL);
1001 
1002  if (FAILED(hr)) {
1003  SoundD3DError("AllocateBuffer: could not create buffer", hr);
1004 
1005  Print(" dsbd.dwSize = %8d\n", dsbd.dwSize);
1006  Print(" dsbd.dwFlags = %08x\n", dsbd.dwFlags);
1007  Print(" dsbd.dwBufferBytes = %8d\n", dsbd.dwBufferBytes);
1008  Print(" dsbd.lpwfxFormat = %08x\n", dsbd.lpwfxFormat);
1009 
1010  if (dsbd.lpwfxFormat) {
1011  Print(" wfex.wBitsPerSample = %8d\n", dsbd.lpwfxFormat->wBitsPerSample);
1012  Print(" wfex.nChannels = %8d\n", dsbd.lpwfxFormat->nChannels);
1013  Print(" wfex.nSamplesPerSec = %8d\n", dsbd.lpwfxFormat->nSamplesPerSec);
1014  }
1015  }
1016  }
1017  else {
1018  SoundD3DError("AllocateBuffer: soundcard is null", E_FAIL);
1019  }
1020  }
1021 
1022  return hr;
1023 }
1024 
1025 // +--------------------------------------------------------------------+
1026 
1027 HRESULT
1029 {
1030  if (IsPlaying()) return S_OK;
1031  if (!buffer) return E_FAIL;
1032 
1033  HRESULT hr = E_FAIL;
1034 
1035  if (IsDone())
1036  hr = Rewind();
1037 
1038  if (IsReady()) {
1039  if (moved)
1040  Localize();
1041 
1042  if (flags & LOOP || flags & STREAMED)
1043  hr = buffer->Play(0, 0, DSBPLAY_LOOPING);
1044  else
1045  hr = buffer->Play(0, 0, 0);
1046 
1047  if (SUCCEEDED(hr))
1048  status = PLAYING;
1049  }
1050 
1051  return hr;
1052 }
1053 
1054 // +--------------------------------------------------------------------+
1055 
1056 HRESULT
1058 {
1059  if (!buffer) return E_FAIL;
1060 
1061  HRESULT hr = S_OK;
1062 
1063  if (IsPlaying())
1064  hr = Stop();
1065 
1066  if (flags & STREAMED) {
1067  RewindStream();
1068  StreamBlock();
1069  }
1070 
1071  else {
1072  hr = buffer->SetCurrentPosition(0);
1073  }
1074 
1075  if (SUCCEEDED(hr)) {
1076  status = READY;
1077  looped = 0;
1078  }
1079 
1080  return hr;
1081 }
1082 
1083 // +--------------------------------------------------------------------+
1084 
1085 HRESULT
1087 {
1088  if (status == DONE)
1089  return S_OK;
1090 
1091  HRESULT hr = Stop();
1092 
1093  if (SUCCEEDED(hr))
1094  status = READY;
1095 
1096  return hr;
1097 }
1098 
1099 // +--------------------------------------------------------------------+
1100 
1101 HRESULT
1103 {
1104  if (!buffer)
1105  return E_FAIL;
1106 
1107  if (!IsPlaying()) {
1108  status = DONE;
1109  return S_OK;
1110  }
1111 
1112  status = DONE;
1113  return buffer->Stop();
1114 }
1115 
1116 // +--------------------------------------------------------------------+
1117 
1118 double
1120 {
1121  double time_left = -1;
1122 
1123  if (IsPlaying() || IsReady()) {
1124  time_left = (double) stream_left /
1125  (double) wfex.nAvgBytesPerSec;
1126  }
1127 
1128  return time_left;
1129 }
1130 
1131 double
1133 {
1134  double time_elapsed = 0;
1135 
1136  if (IsPlaying()) {
1137  time_elapsed = total_time - GetTimeRemaining();
1138  }
1139 
1140  return time_elapsed;
1141 }
1142 
1143 // +--------------------------------------------------------------------+
1144 
1145 void
1147 {
1148  if (v > 0) v = 0;
1149  else if (v < -10000) v = -10000;
1150 
1151  volume = v;
1152  moved = true;
1153 }
1154 
1155 // +--------------------------------------------------------------------+
1156 
1157 long
1159 {
1160  long p = 10000;
1161 
1162  if (location.z) p = (long) fabs(location.x/location.z);
1163  if (p > 10000) p = 10000;
1164  if (location.x < 0) p = -p;
1165 
1166  return p;
1167 }
1168 
1169 void
1171 {
1172  if (p > 10000) p = 10000;
1173  if (p < -10000) p = -10000;
1174 
1175  location.x = (float) p;
1176  location.y = 0.0f;
1177  location.z = 1.0f;
1178  moved = true;
1179 }
1180 
1181 // +--------------------------------------------------------------------+
1182 
1183 void
1185 {
1186  location = l;
1187  moved = true;
1188 }
1189 
1190 void
1192 {
1193  velocity = v;
1194  moved = true;
1195 }
1196 
1197 // +--------------------------------------------------------------------+
1198 
1199 float
1201 {
1202  return min_dist;
1203 }
1204 
1205 void
1207 {
1208  min_dist = f;
1209  moved = true;
1210 }
1211 
1212 // +--------------------------------------------------------------------+
1213 
1214 float
1216 {
1217  return max_dist;
1218 }
1219 void
1221 {
1222  max_dist = f;
1223  moved = true;
1224 }
1225 
1226 
1227 // +--------------------------------------------------------------------+
1228 
1229 void
1230 SoundD3DError(const char* msg, HRESULT err)
1231 {
1232  Print(" SoundD3D: %s. [%s]\n", msg, DSErrStr(err));
1233 }
1234 
1235 char*
1236 DSErrStr(HRESULT err)
1237 {
1238  switch (err) {
1239  case DS_OK: return "DS_OK";
1240 
1241  case DSERR_ALLOCATED: return
1242  "The call failed because resources (such as a priority level) "
1243  "were already being used by another caller.";
1244 
1245  case DSERR_CONTROLUNAVAIL: return
1246  "The control (vol,pan,etc.) requested by the caller is not available.";
1247 
1248  case DSERR_INVALIDPARAM: return
1249  "An invalid parameter was passed to the returning function.";
1250 
1251  case DSERR_INVALIDCALL: return
1252  "This call is not valid for the current state of this object";
1253 
1254  case DSERR_GENERIC: return
1255  "An undetermined error occured inside the DirectSound subsystem";
1256 
1257  case DSERR_PRIOLEVELNEEDED: return
1258  "The caller does not have the priority level required for the function to succeed.";
1259 
1260  case DSERR_OUTOFMEMORY: return
1261  "Not enough free memory is available to complete the operation";
1262 
1263  case DSERR_BADFORMAT: return
1264  "The specified WAVE format is not supported";
1265 
1266  case DSERR_UNSUPPORTED: return
1267  "The function called is not supported at this time";
1268 
1269  case DSERR_NODRIVER: return
1270  "No sound driver is available for use";
1271 
1272  case DSERR_ALREADYINITIALIZED: return
1273  "This object is already initialized";
1274 
1275  case DSERR_NOAGGREGATION: return
1276  "This object does not support aggregation";
1277 
1278  case DSERR_BUFFERLOST: return
1279  "The buffer memory has been lost, and must be restored.";
1280 
1281  case DSERR_OTHERAPPHASPRIO: return
1282  "Another app has a higher priority level, preventing this call from succeeding.";
1283 
1284  case DSERR_UNINITIALIZED: return
1285  "This object has not been initialized.";
1286 
1287 #ifdef DIRECT_SOUND_3D
1288  case DSERR_NOINTERFACE: return
1289  "The requested COM interface is not available.";
1290 #endif
1291 
1292  default: return "Unknown Error Code";
1293  }
1294 
1295  return "Internal Error";
1296 }
1297