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