Panda3D
Loading...
Searching...
No Matches
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
31using std::istream;
32using std::string;
33
34TypeHandle FmodAudioSound::_type_handle;
35
36/**
37 * Constructor All sound will DEFAULT load as a 2D sound unless otherwise
38 * specified.
39 */
40
42FmodAudioSound(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 */
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 */
239play() {
240 start_playing();
241}
242
243/**
244 * Stop a sound
245 */
247stop() {
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 */
270set_loop(bool loop) {
271 if (loop) {
273 } else {
275 }
276}
277
278/**
279 * Returns whether looping is on or off
280 */
282get_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 */
296set_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 */
326unsigned long FmodAudioSound::
327get_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 */
347set_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 */
361get_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 */
383set_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 */
393get_volume() const {
394 return _volume;
395}
396
397/**
398 * Starts the sound playing at _start_time.
399 */
400void FmodAudioSound::
401start_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 */
458void FmodAudioSound::
459set_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 */
477set_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 */
488get_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 */
500set_play_rate(PN_stdfloat rate) {
501 ReMutexHolder holder(FmodAudioManager::_lock);
502 _playrate = rate;
503 set_play_rate_on_channel();
504}
505
506/**
507 *
508 */
509PN_stdfloat FmodAudioSound::
510get_play_rate() const {
511 return _playrate;
512}
513
514/**
515 * Set the play rate on a prepared Sound channel.
516 */
517void FmodAudioSound::
518set_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 */
536const string& FmodAudioSound::
537get_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 */
545length() 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 */
568set_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 */
584void FmodAudioSound::
585set_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 */
608get_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 */
617set_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 */
631get_3d_min_distance() const {
632 return _min_dist;
633}
634
635/**
636 * Set the distance that this sound stops falling off
637 */
639set_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 */
653get_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 */
667get_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 */
708set_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 */
730void FmodAudioSound::
731set_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 */
765int FmodAudioSound::
766get_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 */
775void FmodAudioSound::
776set_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 */
792AudioSound::SoundStatus FmodAudioSound::
793status() 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 */
816set_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 */
848get_active() const {
849 return _active;
850}
851
852/**
853 * Not implemented.
854 */
856finished() {
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 */
866set_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 */
876const string& FmodAudioSound::
877get_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 */
886FMOD_RESULT F_CALLBACK FmodAudioSound::
887sound_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 */
908FMOD_RESULT F_CALLBACK FmodAudioSound::
909open_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 */
937FMOD_RESULT F_CALLBACK FmodAudioSound::
938close_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 */
959FMOD_RESULT F_CALLBACK FmodAudioSound::
960read_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 */
994FMOD_RESULT F_CALLBACK FmodAudioSound::
995seek_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.
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.