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, even though the files may originate from several different sources that may not be related to the actual OS&#39;s 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&#39;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.