Panda3D
fmodAudioSound.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file fmodAudioSound.cxx
10  * @author cort
11  * @date 2003-01-22
12  * @author ben
13  * @date 2003-10-22
14  * Prior system by: cary
15  * @author Stan Rosenbaum "Staque" - Spring 2006
16  */
17 
18 #include "pandabase.h"
19 #include "dcast.h"
20 
21 // Panda Headers
22 #include "config_audio.h"
23 #include "config_fmodAudio.h"
24 #include "fmodAudioSound.h"
25 #include "string_utils.h"
26 #include "subfileInfo.h"
27 #include "reMutexHolder.h"
28 #include "virtualFileSystem.h"
29 #include "vector_uchar.h"
30 
31 using std::istream;
32 using std::string;
33 
34 TypeHandle FmodAudioSound::_type_handle;
35 
36 /**
37  * Constructor All sound will DEFAULT load as a 2D sound unless otherwise
38  * specified.
39  */
40 
42 FmodAudioSound(AudioManager *manager, VirtualFile *file, bool positional) {
43  ReMutexHolder holder(FmodAudioManager::_lock);
44  audio_debug("FmodAudioSound::FmodAudioSound() Creating new sound, filename: "
45  << file->get_original_filename());
46 
47  _active = manager->get_active();
48  _paused = false;
49  _start_time = 0.0;
50 
51  // Local Variables that are needed.
52  FMOD_RESULT result;
53 
54  // Inits 3D Attributes
55  _location.x = 0;
56  _location.y = 0;
57  _location.z = 0;
58 
59  _velocity.x = 0;
60  _velocity.y = 0;
61  _velocity.z = 0;
62 
63  _min_dist = 1.0;
64  _max_dist = 1000000000.0;
65 
66  // Play Rate Variable
67  _playrate = 1;
68 
69  // These set the Speaker Levels to a default if you are using a MultiChannel
70  // Setup.
71  for (int i=0; i<AudioManager::SPK_COUNT; i++) {
72  _mix[i] = 1.0;
73  }
74 
75  // Assign the values we need
76  FmodAudioManager *fmanager;
77  DCAST_INTO_V(fmanager, manager);
78  _manager = fmanager;
79 
80  _channel = 0;
81  _file_name = file->get_original_filename();
82  _file_name.set_binary();
83 
84  // Get the Speaker Mode [Important for later on.]
85  result = _manager->_system->getSpeakerMode( &_speakermode );
86  fmod_audio_errcheck("_system->getSpeakerMode()", result);
87 
88  {
89  bool preload = (fmod_audio_preload_threshold < 0) || (file->get_file_size() < fmod_audio_preload_threshold);
90  int flags = FMOD_SOFTWARE;
91  flags |= positional ? FMOD_3D : FMOD_2D;
92 
93  FMOD_CREATESOUNDEXINFO sound_info;
94  memset(&sound_info, 0, sizeof(sound_info));
95  sound_info.cbsize = sizeof(sound_info);
96 
97  string ext = downcase(_file_name.get_extension());
98  if (ext == "mid") {
99  // Get the MIDI parameters.
100  memcpy(&sound_info, &_manager->_midi_info, sizeof(sound_info));
101  if (sound_info.dlsname != nullptr) {
102  audio_debug("Using DLS file " << sound_info.dlsname);
103  }
104  }
105 
106  const char *name_or_data = _file_name.c_str();
107  string os_filename;
108 
109  vector_uchar mem_buffer;
110  SubfileInfo info;
111  if (preload) {
112  // Pre-read the file right now, and pass it in as a memory buffer. This
113  // avoids threading issues completely, because all of the reading
114  // happens right here.
115  file->read_file(mem_buffer, true);
116  sound_info.length = mem_buffer.size();
117  if (mem_buffer.size() != 0) {
118  name_or_data = (const char *)&mem_buffer[0];
119  }
120  flags |= FMOD_OPENMEMORY;
121  if (fmodAudio_cat.is_debug()) {
122  fmodAudio_cat.debug()
123  << "Reading " << _file_name << " into memory (" << sound_info.length
124  << " bytes)\n";
125  }
126  result =
127  _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound);
128  }
129  else {
130  result = FMOD_ERR_FILE_BAD;
131 
132  if (file->get_system_info(info)) {
133  // The file exists on disk (or it's part of a multifile that exists on
134  // disk), so we can have FMod read the file directly. This is also
135  // safe, because FMod uses its own IO operations that don't involve
136  // Panda, so this can safely happen in an FMod thread.
137  os_filename = info.get_filename().to_os_specific();
138  name_or_data = os_filename.c_str();
139  sound_info.fileoffset = (unsigned int)info.get_start();
140  sound_info.length = (unsigned int)info.get_size();
141  flags |= FMOD_CREATESTREAM;
142  if (fmodAudio_cat.is_debug()) {
143  fmodAudio_cat.debug()
144  << "Streaming " << _file_name << " from disk (" << name_or_data
145  << ", " << sound_info.fileoffset << ", " << sound_info.length << ")\n";
146  }
147 
148  result =
149  _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound);
150  }
151 
152  // If FMOD can't directly read the file (eg. if Panda is locking it for
153  // write, or it's compressed) we have to use the callback interface.
154  if (result == FMOD_ERR_FILE_BAD) {
155  #if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS)
156  // Otherwise, if the Panda threading system is compiled in, we can
157  // assign callbacks to read the file through the VFS.
158  name_or_data = (const char *)file;
159  sound_info.fileoffset = 0;
160  sound_info.length = (unsigned int)info.get_size();
161  sound_info.useropen = open_callback;
162  sound_info.userclose = close_callback;
163  sound_info.userread = read_callback;
164  sound_info.userseek = seek_callback;
165  flags |= FMOD_CREATESTREAM;
166  if (fmodAudio_cat.is_debug()) {
167  fmodAudio_cat.debug()
168  << "Streaming " << _file_name << " from disk using callbacks\n";
169  }
170  result =
171  _manager->_system->createSound(name_or_data, flags, &sound_info, &_sound);
172 
173  #else // HAVE_THREADS && !SIMPLE_THREADS
174  // Without threads, we can't safely read this file.
175  name_or_data = "";
176 
177  fmodAudio_cat.warning()
178  << "Cannot stream " << _file_name << "; file is not literally on disk.\n";
179  #endif
180  }
181  }
182  }
183 
184  if (result != FMOD_OK) {
185  audio_error("createSound(" << _file_name << "): " << FMOD_ErrorString(result));
186 
187  // We couldn't load the sound file. Create a blank sound record instead.
188  FMOD_CREATESOUNDEXINFO sound_info;
189  memset(&sound_info, 0, sizeof(sound_info));
190  char blank_data[100];
191  memset(blank_data, 0, sizeof(blank_data));
192  sound_info.cbsize = sizeof(sound_info);
193  sound_info.length = sizeof(blank_data);
194  sound_info.numchannels = 1;
195  sound_info.defaultfrequency = 8000;
196  sound_info.format = FMOD_SOUND_FORMAT_PCM16;
197  int flags = FMOD_SOFTWARE | FMOD_OPENMEMORY | FMOD_OPENRAW;
198 
199  result = _manager->_system->createSound( blank_data, flags, &sound_info, &_sound);
200  fmod_audio_errcheck("createSound (blank)", result);
201  }
202 
203  // Some WAV files contain a loop bit. This is not handled consistently.
204  // Override it.
205  _sound->setLoopCount(1);
206  _sound->setMode(FMOD_LOOP_OFF);
207 
208  // This is just to collect the defaults of the sound, so we don't Have to
209  // query FMOD everytime for the info. It is also important we get the
210  // '_sampleFrequency' variable here, for the 'set_play_rate()' and
211  // 'get_play_rate()' methods later;
212 
213  result = _sound->getDefaults( &_sampleFrequency, &_volume , &_balance, &_priority);
214  fmod_audio_errcheck("_sound->getDefaults()", result);
215 }
216 
217 
218 /**
219  * DESTRUCTOR!!!
220  */
222 ~FmodAudioSound() {
223  ReMutexHolder holder(FmodAudioManager::_lock);
224  FMOD_RESULT result;
225 
226  // Remove me from table of all sounds.
227  _manager->_all_sounds.erase(this);
228 
229  // The Release Sound
230  result = _sound->release();
231  fmod_audio_errcheck("_sound->release()", result);
232 }
233 
234 
235 /**
236  * Plays a sound.
237  */
239 play() {
240  start_playing();
241 }
242 
243 /**
244  * Stop a sound
245  */
247 stop() {
248  ReMutexHolder holder(FmodAudioManager::_lock);
249  FMOD_RESULT result;
250 
251  if (_channel != 0) {
252  result =_channel->stop();
253  if (result == FMOD_ERR_INVALID_HANDLE || result == FMOD_ERR_CHANNEL_STOLEN) {
254  _channel = 0;
255  } else if (result == FMOD_OK) {
256  _self_ref.clear();
257  } else {
258  fmod_audio_errcheck("_channel->stop()", result);
259  }
260  }
261  _start_time = 0.0;
262  _paused = false;
263 }
264 
265 
266 /**
267  * Turns looping on and off
268  */
270 set_loop(bool loop) {
271  if (loop) {
272  set_loop_count(0);
273  } else {
274  set_loop_count(1);
275  }
276 }
277 
278 /**
279  * Returns whether looping is on or off
280  */
282 get_loop() const {
283  if (get_loop_count() == 1) {
284  return false;
285  } else {
286  return true;
287  }
288 }
289 
290 /**
291  *
292  * Panda uses 0 to mean loop forever. Fmod uses negative numbers to mean loop
293  * forever. (0 means don't loop, 1 means play twice, etc. We must convert!
294  */
296 set_loop_count(unsigned long loop_count) {
297  ReMutexHolder holder(FmodAudioManager::_lock);
298  audio_debug("FmodAudioSound::set_loop_count() Setting the sound's loop count to: " << loop_count);
299 
300  // LOCALS
301  FMOD_RESULT result;
302 
303  if (loop_count == 0) {
304  result = _sound->setLoopCount( -1 );
305  fmod_audio_errcheck("_sound->setLoopCount()", result);
306  result =_sound->setMode(FMOD_LOOP_NORMAL);
307  fmod_audio_errcheck("_sound->setMode()", result);
308  } else if (loop_count == 1) {
309  result = _sound->setLoopCount( 1 );
310  fmod_audio_errcheck("_sound->setLoopCount()", result);
311  result =_sound->setMode(FMOD_LOOP_OFF);
312  fmod_audio_errcheck("_sound->setMode()", result);
313  } else {
314  result = _sound->setLoopCount( loop_count );
315  fmod_audio_errcheck("_sound->setLoopCount()", result);
316  result =_sound->setMode(FMOD_LOOP_NORMAL);
317  fmod_audio_errcheck("_sound->setMode()", result);
318  }
319 
320  audio_debug("FmodAudioSound::set_loop_count() Sound's loop count should be set to: " << loop_count);
321 }
322 
323 /**
324  * Return how many times a sound will loop.
325  */
326 unsigned long FmodAudioSound::
327 get_loop_count() const {
328  ReMutexHolder holder(FmodAudioManager::_lock);
329  FMOD_RESULT result;
330  int loop_count;
331 
332  result = _sound->getLoopCount( &loop_count );
333  fmod_audio_errcheck("_sound->getLoopCount()", result);
334 
335  if (loop_count <= 0) {
336  return 0;
337  } else {
338  return (unsigned long)loop_count;
339  }
340 }
341 
342 /**
343  * Sets the time at which the next play() operation will begin. If we are
344  * already playing, skips to that time immediatey.
345  */
347 set_time(PN_stdfloat start_time) {
348  ReMutexHolder holder(FmodAudioManager::_lock);
349  _start_time = start_time;
350 
351  if (status() == PLAYING) {
352  // Already playing; skip to the indicated time.
353  start_playing();
354  }
355 }
356 
357 /**
358  * Gets the play position within the sound
359  */
360 PN_stdfloat FmodAudioSound::
361 get_time() const {
362  ReMutexHolder holder(FmodAudioManager::_lock);
363  FMOD_RESULT result;
364  unsigned int current_time;
365 
366  if (_channel == 0) {
367  return 0.0f;
368  }
369 
370  result = _channel->getPosition( &current_time , FMOD_TIMEUNIT_MS );
371  if (result == FMOD_ERR_INVALID_HANDLE || result == FMOD_ERR_CHANNEL_STOLEN) {
372  return 0.0f;
373  }
374  fmod_audio_errcheck("_channel->getPosition()", result);
375 
376  return ((double)current_time) / 1000.0;
377 }
378 
379 /**
380  * 0.0 to 1.0 scale of volume converted to Fmod's internal 0.0 to 255.0 scale.
381  */
383 set_volume(PN_stdfloat vol) {
384  ReMutexHolder holder(FmodAudioManager::_lock);
385  _volume = vol;
386  set_volume_on_channel();
387 }
388 
389 /**
390  * Gets the current volume of a sound. 1 is Max. O is Min.
391  */
392 PN_stdfloat FmodAudioSound::
393 get_volume() const {
394  return _volume;
395 }
396 
397 /**
398  * Starts the sound playing at _start_time.
399  */
400 void FmodAudioSound::
401 start_playing() {
402  ReMutexHolder holder(FmodAudioManager::_lock);
403  FMOD_RESULT result;
404 
405  if (!_active) {
406  _paused = true;
407  return;
408  }
409 
410  int startTime = (int)(_start_time * 1000);
411 
412  if (_channel != 0) {
413  // try backing up current sound.
414  result = _channel->setPosition( startTime , FMOD_TIMEUNIT_MS );
415  if (result == FMOD_ERR_INVALID_HANDLE || result == FMOD_ERR_CHANNEL_STOLEN) {
416  _channel = 0;
417 
418  } else {
419  fmod_audio_errcheck("_channel->setPosition()", result);
420 
421  bool playing;
422  result = _channel->isPlaying(&playing);
423  fmod_audio_errcheck("_channel->isPlaying()", result);
424  if (result != FMOD_OK || !playing) {
425  _channel = 0;
426  }
427  }
428  }
429 
430  if (_channel == 0) {
431  result = _manager->_system->playSound(FMOD_CHANNEL_FREE, _sound, true, &_channel);
432  fmod_audio_errcheck("_system->playSound()", result);
433  result = _channel->setChannelGroup(_manager->_channelgroup);
434  fmod_audio_errcheck("_channel->setChannelGroup()", result);
435  result = _channel->setUserData(this);
436  fmod_audio_errcheck("_channel->setUserData()", result);
437  result = _channel->setCallback(sound_end_callback);
438  fmod_audio_errcheck("_channel->setCallback()", result);
439  result = _channel->setPosition( startTime , FMOD_TIMEUNIT_MS );
440  fmod_audio_errcheck("_channel->setPosition()", result);
441 
442  set_volume_on_channel();
443  set_play_rate_on_channel();
444  set_speaker_mix_or_balance_on_channel();
445  // add_dsp_on_channel();
446  set_3d_attributes_on_channel();
447 
448  result = _channel->setPaused(false);
449  fmod_audio_errcheck("_channel->setPaused()", result);
450 
451  _self_ref = this;
452  }
453 }
454 
455 /**
456  * Set the volume on a prepared Sound channel.
457  */
458 void FmodAudioSound::
459 set_volume_on_channel() {
460  ReMutexHolder holder(FmodAudioManager::_lock);
461  FMOD_RESULT result;
462 
463  if (_channel != 0) {
464  result = _channel->setVolume( _volume );
465  if (result == FMOD_ERR_INVALID_HANDLE || result == FMOD_ERR_CHANNEL_STOLEN) {
466  _channel = 0;
467  } else {
468  fmod_audio_errcheck("_channel->setVolume()", result);
469  }
470  }
471 }
472 
473 /**
474  * -1.0 to 1.0 scale
475  */
477 set_balance(PN_stdfloat bal) {
478  ReMutexHolder holder(FmodAudioManager::_lock);
479  _balance = bal;
480  set_speaker_mix_or_balance_on_channel();
481 }
482 
483 /**
484  * -1.0 to 1.0 scale -1 should be all the way left. 1 is all the way to the
485  * right.
486  */
487 PN_stdfloat FmodAudioSound::
488 get_balance() const {
489  return _balance;
490 }
491 
492 /**
493  * Sets the speed at which a sound plays back. The rate is a multiple of the
494  * sound, normal playback speed. IE 2 would play back 2 times fast, 3 would
495  * play 3 times, and so on. This can also be set to a negative number so a
496  * sound plays backwards. But rememeber if the sound is not playing, you must
497  * set the sound's time to its end to hear a song play backwards.
498  */
500 set_play_rate(PN_stdfloat rate) {
501  ReMutexHolder holder(FmodAudioManager::_lock);
502  _playrate = rate;
503  set_play_rate_on_channel();
504 }
505 
506 /**
507  *
508  */
509 PN_stdfloat FmodAudioSound::
510 get_play_rate() const {
511  return _playrate;
512 }
513 
514 /**
515  * Set the play rate on a prepared Sound channel.
516  */
517 void FmodAudioSound::
518 set_play_rate_on_channel() {
519  ReMutexHolder holder(FmodAudioManager::_lock);
520  FMOD_RESULT result;
521  PN_stdfloat frequency = _sampleFrequency * _playrate;
522 
523  if (_channel != 0) {
524  result = _channel->setFrequency( frequency );
525  if (result == FMOD_ERR_INVALID_HANDLE || result == FMOD_ERR_CHANNEL_STOLEN) {
526  _channel = 0;
527  } else {
528  fmod_audio_errcheck("_channel->setFrequency()", result);
529  }
530  }
531 }
532 
533 /**
534  * Get name of sound file
535  */
536 const string& FmodAudioSound::
537 get_name() const {
538  return _file_name;
539 }
540 
541 /**
542  * Get length FMOD returns the time in MS so we have to convert to seconds.
543  */
544 PN_stdfloat FmodAudioSound::
545 length() const {
546  ReMutexHolder holder(FmodAudioManager::_lock);
547  FMOD_RESULT result;
548  unsigned int length;
549 
550  result = _sound->getLength( &length, FMOD_TIMEUNIT_MS );
551  fmod_audio_errcheck("_sound->getLength()", result);
552 
553  return ((double)length) / 1000.0;
554 }
555 
556 /**
557  * Set position and velocity of this sound NOW LISTEN UP!!! THIS IS IMPORTANT!
558  * Both Panda3D and FMOD use a left handed coordinate system. But there is a
559  * major difference! In Panda3D the Y-Axis is going into the Screen and the
560  * Z-Axis is going up. In FMOD the Y-Axis is going up and the Z-Axis is going
561  * into the screen. The solution is simple, we just flip the Y and Z axis, as
562  * we move coordinates from Panda to FMOD and back. What does did mean to
563  * average Panda user? Nothing, they shouldn't notice anyway. But if you
564  * decide to do any 3D audio work in here you have to keep it in mind. I told
565  * you, so you can't say I didn't.
566  */
568 set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz) {
569  ReMutexHolder holder(FmodAudioManager::_lock);
570  _location.x = px;
571  _location.y = pz;
572  _location.z = py;
573 
574  _velocity.x = vx;
575  _velocity.y = vz;
576  _velocity.z = vy;
577 
578  set_3d_attributes_on_channel();
579 }
580 
581 /**
582  *
583  */
584 void FmodAudioSound::
585 set_3d_attributes_on_channel() {
586  ReMutexHolder holder(FmodAudioManager::_lock);
587  FMOD_RESULT result;
588  FMOD_MODE soundMode;
589 
590  result = _sound->getMode(&soundMode);
591  fmod_audio_errcheck("_sound->getMode()", result);
592 
593  if ((_channel != 0) && (soundMode & FMOD_3D)) {
594  result = _channel->set3DAttributes( &_location, &_velocity );
595  if (result == FMOD_ERR_INVALID_HANDLE || result == FMOD_ERR_CHANNEL_STOLEN) {
596  _channel = 0;
597  } else {
598  fmod_audio_errcheck("_channel->set3DAttributes()", result);
599  }
600  }
601 }
602 
603 /**
604  * Get position and velocity of this sound Currently unimplemented. Get the
605  * attributes of the attached object.
606  */
608 get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz) {
609  audio_error("get3dAttributes: Currently unimplemented. Get the attributes of the attached object.");
610 }
611 
612 /**
613  * Set the distance that this sound begins to fall off. Also affects the rate
614  * it falls off.
615  */
617 set_3d_min_distance(PN_stdfloat dist) {
618  ReMutexHolder holder(FmodAudioManager::_lock);
619  FMOD_RESULT result;
620 
621  _min_dist = dist;
622 
623  result = _sound->set3DMinMaxDistance( dist, _max_dist );
624  fmod_audio_errcheck("_sound->set3DMinMaxDistance()", result);
625 }
626 
627 /**
628  * Get the distance that this sound begins to fall off
629  */
630 PN_stdfloat FmodAudioSound::
631 get_3d_min_distance() const {
632  return _min_dist;
633 }
634 
635 /**
636  * Set the distance that this sound stops falling off
637  */
639 set_3d_max_distance(PN_stdfloat dist) {
640  ReMutexHolder holder(FmodAudioManager::_lock);
641  FMOD_RESULT result;
642 
643  _max_dist = dist;
644 
645  result = _sound->set3DMinMaxDistance( _min_dist, dist );
646  fmod_audio_errcheck("_sound->set3DMinMaxDistance()", result);
647 }
648 
649 /**
650  * Get the distance that this sound stops falling off
651  */
652 PN_stdfloat FmodAudioSound::
653 get_3d_max_distance() const {
654  return _max_dist;
655 }
656 
657 /**
658  * In Multichannel Speaker systems [like Surround].
659  *
660  * Speakers which don't exist in some systems will simply be ignored. But I
661  * haven't been able to test this yet, so I am jsut letting you know.
662  *
663  * BTW This will also work in Stereo speaker systems, but since PANDA/FMOD has
664  * a balance [pan] function what is the point?
665  */
666 PN_stdfloat FmodAudioSound::
667 get_speaker_mix(int speaker) {
668  ReMutexHolder holder(FmodAudioManager::_lock);
669  if (_channel == 0) {
670  return 0.0;
671  }
672 
673  FMOD_RESULT result;
674  float frontleft;
675  float frontright;
676  float center;
677  float sub;
678  float backleft;
679  float backright;
680  float sideleft;
681  float sideright;
682 
683  result = _channel->getSpeakerMix( &frontleft, &frontright, &center, &sub, &backleft, &backright, &sideleft, &sideright );
684  fmod_audio_errcheck("_channel->getSpeakerMix()", result);
685 
686  switch(speaker) {
687  case AudioManager::SPK_frontleft: return frontleft;
688  case AudioManager::SPK_frontright: return frontright;
689  case AudioManager::SPK_center: return center;
690  case AudioManager::SPK_sub: return sub;
691  case AudioManager::SPK_backleft: return backleft;
692  case AudioManager::SPK_backright: return backright;
693  case AudioManager::SPK_sideleft: return sideleft;
694  case AudioManager::SPK_sideright: return sideright;
695  default: return 0.0;
696  }
697 }
698 
699 /**
700  * This sets the speaker mix for Surround Sound sytems. It required 8
701  * parameters which match up to the following:
702  *
703  * * 1 = Front Left * 2 = Front Right * 3 = Center * 4 = Subwoofer * 5 = Back
704  * Left * 6 = Back Right * 7 = Side Left * 8 = Side Right
705  *
706  */
708 set_speaker_mix(PN_stdfloat frontleft, PN_stdfloat frontright, PN_stdfloat center, PN_stdfloat sub, PN_stdfloat backleft, PN_stdfloat backright, PN_stdfloat sideleft, PN_stdfloat sideright) {
709  ReMutexHolder holder(FmodAudioManager::_lock);
710  _mix[AudioManager::SPK_frontleft] = frontleft;
711  _mix[AudioManager::SPK_frontright] = frontright;
712  _mix[AudioManager::SPK_center] = center;
713  _mix[AudioManager::SPK_sub] = sub;
714  _mix[AudioManager::SPK_backleft] = backleft;
715  _mix[AudioManager::SPK_backright] = backright;
716  _mix[AudioManager::SPK_sideleft] = sideleft;
717  _mix[AudioManager::SPK_sideright] = sideright;
718 
719  set_speaker_mix_or_balance_on_channel();
720 }
721 
722 /**
723  * This is simply a safety catch. If you are using a Stero speaker setup
724  * Panda will only pay attention to 'set_balance()' command when setting
725  * speaker balances. Other wise it will use 'set_speaker_mix'. I put this in,
726  * because other wise you end up with a sitation, where 'set_speaker_mix()' or
727  * 'set_balace()' will override any previous speaker balance setups. It all
728  * depends on which was called last.
729  */
730 void FmodAudioSound::
731 set_speaker_mix_or_balance_on_channel() {
732  ReMutexHolder holder(FmodAudioManager::_lock);
733  FMOD_RESULT result;
734  FMOD_MODE soundMode;
735 
736  result = _sound->getMode(&soundMode);
737  fmod_audio_errcheck("_sound->getMode()", result);
738 
739  if ((_channel != 0) && (( soundMode & FMOD_3D ) == 0)) {
740  if ( _speakermode == FMOD_SPEAKERMODE_STEREO ) {
741  result = _channel->setPan( _balance );
742  } else {
743  result = _channel->setSpeakerMix( _mix[AudioManager::SPK_frontleft],
744  _mix[AudioManager::SPK_frontright],
745  _mix[AudioManager::SPK_center],
746  _mix[AudioManager::SPK_sub],
747  _mix[AudioManager::SPK_backleft],
748  _mix[AudioManager::SPK_backright],
749  _mix[AudioManager::SPK_sideleft],
750  _mix[AudioManager::SPK_sideright]
751  );
752  }
753  if (result == FMOD_ERR_INVALID_HANDLE || result == FMOD_ERR_CHANNEL_STOLEN) {
754  _channel = 0;
755  } else {
756  fmod_audio_errcheck("_channel->setSpeakerMix()/setPan()", result);
757  }
758  }
759 }
760 
761 /**
762  * Sets the priority of a sound. This is what FMOD uses to determine is a
763  * sound will play if all the other real channels have been used up.
764  */
765 int FmodAudioSound::
766 get_priority() {
767  audio_debug("FmodAudioSound::get_priority()");
768  return _priority;
769 }
770 
771 /**
772  * Sets the Sound Priority [Whether is will be played over other sound when
773  * real audio channels become short.
774  */
775 void FmodAudioSound::
776 set_priority(int priority) {
777  ReMutexHolder holder(FmodAudioManager::_lock);
778 
779  audio_debug("FmodAudioSound::set_priority()");
780 
781  FMOD_RESULT result;
782 
783  _priority = priority;
784 
785  result = _sound->setDefaults( _sampleFrequency, _volume , _balance, _priority);
786  fmod_audio_errcheck("_sound->setDefaults()", result);
787 }
788 
789 /**
790  * Get status of the sound.
791  */
792 AudioSound::SoundStatus FmodAudioSound::
793 status() const {
794  ReMutexHolder holder(FmodAudioManager::_lock);
795  FMOD_RESULT result;
796  bool playingState;
797 
798  if ( _channel == 0 ) {
799  return READY;
800  }
801 
802  result = _channel->isPlaying( &playingState );
803  if ((result == FMOD_OK) && (playingState == true)) {
804  return PLAYING;
805  } else {
806  return READY;
807  }
808 }
809 
810 /**
811  * Sets whether the sound is marked "active". By default, the active flag
812  * true for all sounds. If the active flag is set to false for any particular
813  * sound, the sound will not be heard.
814  */
816 set_active(bool active) {
817  ReMutexHolder holder(FmodAudioManager::_lock);
818  if (_active != active) {
819  _active = active;
820  if (_active) {
821  // ...activate the sound.
822  if (_paused && get_loop_count()==0) {
823  // ...this sound was looping when it was paused.
824  _paused = false;
825  play();
826  }
827 
828  } else {
829  // ...deactivate the sound.
830  if (status() == PLAYING) {
831  PN_stdfloat time = get_time();
832  stop();
833  if (get_loop_count() == 0) {
834  // ...we're pausing a looping sound.
835  _paused = true;
836  _start_time = time;
837  }
838  }
839  }
840  }
841 }
842 
843 
844 /**
845  * Returns whether the sound has been marked "active".
846  */
848 get_active() const {
849  return _active;
850 }
851 
852 /**
853  * Not implemented.
854  */
856 finished() {
857  audio_error("finished: not implemented under FMOD-EX");
858 }
859 
860 /**
861  * NOT USED ANYMORE!!! Assign a string for the finished event to be referenced
862  * by in python by an accept method
863  *
864  */
866 set_finished_event(const string& event) {
867  audio_error("set_finished_event: not implemented under FMOD-EX");
868 }
869 
870 /**
871  * NOT USED ANYMORE!!! Return the string the finished event is referenced by
872  *
873 
874  *
875  */
876 const string& FmodAudioSound::
877 get_finished_event() const {
878  audio_error("get_finished_event: not implemented under FMOD-EX");
879  return _finished_event;
880 }
881 
882 /**
883  * When fmod finishes playing a sound, decrements the reference count of the
884  * associated FmodAudioSound.
885  */
886 FMOD_RESULT F_CALLBACK FmodAudioSound::
887 sound_end_callback(FMOD_CHANNEL * channel,
888  FMOD_CHANNEL_CALLBACKTYPE type,
889  void *commanddata1,
890  void *commanddata2) {
891  // Fortunately, this callback is made synchronously rather than
892  // asynchronously (it is triggered during System::update()), so we don't
893  // have to worry about thread-related issues here.
894  if (type == FMOD_CHANNEL_CALLBACKTYPE_END) {
895  FMOD::Channel *fc = (FMOD::Channel *)channel;
896  void *userdata = nullptr;
897  FMOD_RESULT result = fc->getUserData(&userdata);
898  fmod_audio_errcheck("channel->getUserData()", result);
899  FmodAudioSound *fsound = (FmodAudioSound*)userdata;
900  fsound->_self_ref = fsound;
901  }
902  return FMOD_OK;
903 }
904 
905 /**
906  * A hook into Panda's virtual file system.
907  */
908 FMOD_RESULT F_CALLBACK FmodAudioSound::
909 open_callback(const char *name, int, unsigned int *file_size,
910  void **handle, void **user_data) {
911  // We actually pass in the VirtualFile pointer as the "name".
912  VirtualFile *file = (VirtualFile *)name;
913  if (file == nullptr) {
914  return FMOD_ERR_FILE_NOTFOUND;
915  }
916  if (fmodAudio_cat.is_spam()) {
917  fmodAudio_cat.spam()
918  << "open_callback(" << *file << ")\n";
919  }
920 
921  istream *str = file->open_read_file(true);
922 
923  (*file_size) = file->get_file_size(str);
924  (*handle) = (void *)str;
925  (*user_data) = (void *)file;
926 
927  // Explicitly ref the VirtualFile since we're storing it in a void pointer
928  // instead of a PT(VirtualFile).
929  file->ref();
930 
931  return FMOD_OK;
932 }
933 
934 /**
935  * A hook into Panda's virtual file system.
936  */
937 FMOD_RESULT F_CALLBACK FmodAudioSound::
938 close_callback(void *handle, void *user_data) {
939  VirtualFile *file = (VirtualFile *)user_data;
940  if (fmodAudio_cat.is_spam()) {
941  fmodAudio_cat.spam()
942  << "close_callback(" << *file << ")\n";
943  }
944 
946 
947  istream *str = (istream *)handle;
948  vfs->close_read_file(str);
949 
950  // Explicitly unref the VirtualFile pointer.
951  unref_delete(file);
952 
953  return FMOD_OK;
954 }
955 
956 /**
957  * A hook into Panda's virtual file system.
958  */
959 FMOD_RESULT F_CALLBACK FmodAudioSound::
960 read_callback(void *handle, void *buffer, unsigned int size_bytes,
961  unsigned int *bytes_read, void *user_data) {
962  VirtualFile *file = (VirtualFile *)user_data;
963  if (fmodAudio_cat.is_spam()) {
964  fmodAudio_cat.spam()
965  << "read_callback(" << *file << ", " << size_bytes << ")\n";
966  }
967 
968  istream *str = (istream *)handle;
969  str->read((char *)buffer, size_bytes);
970  (*bytes_read) = str->gcount();
971 
972  // We can't yield here, since this callback is made within a sub-thread--an
973  // OS-level sub-thread spawned by FMod, not a Panda thread. But we will
974  // only execute this code in the true-threads case anyway.
975  // thread_consider_yield();
976 
977  if (str->eof()) {
978  if ((*bytes_read) == 0) {
979  return FMOD_ERR_FILE_EOF;
980  } else {
981  // Report the EOF next time.
982  return FMOD_OK;
983  }
984  } if (str->fail()) {
985  return FMOD_ERR_FILE_BAD;
986  } else {
987  return FMOD_OK;
988  }
989 }
990 
991 /**
992  * A hook into Panda's virtual file system.
993  */
994 FMOD_RESULT F_CALLBACK FmodAudioSound::
995 seek_callback(void *handle, unsigned int pos, void *user_data) {
996  VirtualFile *file = (VirtualFile *)user_data;
997  if (fmodAudio_cat.is_spam()) {
998  fmodAudio_cat.spam()
999  << "seek_callback(" << *file << ", " << pos << ")\n";
1000  }
1001 
1002  istream *str = (istream *)handle;
1003  str->clear();
1004  str->seekg(pos);
1005 
1006  if (str->fail() && !str->eof()) {
1007  return FMOD_ERR_FILE_COULDNOTSEEK;
1008  } else {
1009  return FMOD_OK;
1010  }
1011 }
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:414
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
void set_active(bool active=true)
Sets whether the sound is marked "active".
PN_stdfloat get_3d_max_distance() const
Get the distance that this sound stops falling off.
void set_3d_min_distance(PN_stdfloat dist)
Set the distance that this sound begins to fall off.
void set_time(PN_stdfloat start_time=0.0)
Sets the time at which the next play() operation will begin.
void set_3d_max_distance(PN_stdfloat dist)
Set the distance that this sound stops falling off.
PN_stdfloat get_balance() const
-1.0 to 1.0 scale -1 should be all the way left.
PN_stdfloat get_time() const
Gets the play position within the sound.
void set_loop(bool loop=true)
Turns looping on and off.
void finished()
Not implemented.
PN_stdfloat length() const
Get length FMOD returns the time in MS so we have to convert to seconds.
void set_loop_count(unsigned long loop_count=1)
Panda uses 0 to mean loop forever.
virtual PN_stdfloat get_speaker_mix(int speaker)
In Multichannel Speaker systems [like Surround].
AudioSound::SoundStatus status() const
Get status of the sound.
void get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz)
Get position and velocity of this sound Currently unimplemented.
PN_stdfloat get_3d_min_distance() const
Get the distance that this sound begins to fall off.
void set_volume(PN_stdfloat volume=1.0)
0.0 to 1.0 scale of volume converted to Fmod's internal 0.0 to 255.0 scale.
const std::string & get_name() const
Get name of sound file.
virtual void set_speaker_mix(PN_stdfloat frontleft, PN_stdfloat frontright, PN_stdfloat center, PN_stdfloat sub, PN_stdfloat backleft, PN_stdfloat backright, PN_stdfloat sideleft, PN_stdfloat sideright)
This sets the speaker mix for Surround Sound sytems.
const std::string & get_finished_event() const
NOT USED ANYMORE!!! Return the string the finished event is referenced by.
PN_stdfloat get_volume() const
Gets the current volume of a sound.
void play()
Plays a sound.
~FmodAudioSound()
DESTRUCTOR!!!
void set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz)
Set position and velocity of this sound NOW LISTEN UP!!! THIS IS IMPORTANT! Both Panda3D and FMOD use...
void stop()
Stop a sound.
unsigned long get_loop_count() const
Return how many times a sound will loop.
void set_finished_event(const std::string &event)
NOT USED ANYMORE!!! Assign a string for the finished event to be referenced by in python by an accept...
bool get_active() const
Returns whether the sound has been marked "active".
FmodAudioSound(AudioManager *manager, VirtualFile *file, bool positional)
Constructor All sound will DEFAULT load as a 2D sound unless otherwise specified.
void set_balance(PN_stdfloat balance_right=0.0)
-1.0 to 1.0 scale
void set_play_rate(PN_stdfloat play_rate=1.0f)
Sets the speed at which a sound plays back.
bool get_loop() const
Returns whether looping is on or off.
Similar to MutexHolder, but for a reentrant mutex.
Definition: reMutexHolder.h:25
void ref() const
Explicitly increments the reference count.
This class records a particular byte sub-range within an existing file on disk.
Definition: subfileInfo.h:26
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
A hierarchy of directories and files that appears to be one continuous file system,...
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
virtual std::istream * open_read_file(bool auto_unwrap) const
Opens the file for reading.
virtual bool get_system_info(SubfileInfo &info)
Populates the SubfileInfo structure with the data representing where the file actually resides on dis...
const Filename & get_original_filename() const
Returns the original filename as it was used to locate this VirtualFile.
Definition: virtualFile.I:27
virtual std::streamsize get_file_size(std::istream *stream) const
Returns the current size on disk (or wherever it is) of the already-open file.
std::string read_file(bool auto_unwrap) const
Returns the entire contents of the file as a string.
Definition: virtualFile.I:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void unref_delete(RefCountType *ptr)
This global helper function will unref the given ReferenceCount object, and if the reference count re...
string downcase(const string &s)
Returns the input string with all uppercase letters converted to lowercase.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.