Panda3D
milesAudioSample.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 milesAudioSample.cxx
10  * @author skyler
11  * @date 2001-06-06
12  */
13 
14 #include "milesAudioSample.h"
15 
16 #ifdef HAVE_RAD_MSS //[
17 
18 #include "milesAudioManager.h"
19 
20 
21 TypeHandle MilesAudioSample::_type_handle;
22 
23 #undef miles_audio_debug
24 
25 #ifndef NDEBUG //[
26 #define miles_audio_debug(x) \
27  audio_debug("MilesAudioSample \""<<get_name()<<"\" "<< x )
28 #else //][
29 #define miles_audio_debug(x) ((void)0)
30 #endif //]
31 
32 /**
33  * This constructor is called only by the MilesAudioManager.
34  */
35 MilesAudioSample::
36 MilesAudioSample(MilesAudioManager *manager, MilesAudioManager::SoundData *sd,
37  const std::string &file_name) :
38  MilesAudioSound(manager, file_name),
39  _sd(sd)
40 {
41  nassertv(sd != nullptr);
42  audio_debug("MilesAudioSample(manager=0x"<<(void*)&manager
43  <<", sd=0x"<<(void*)sd<<", file_name="<<file_name<<")");
44 
45  _sample = 0;
46  _sample_index = 0;
47  _original_playback_rate = 1.0f;
48 }
49 
50 /**
51  *
52  */
53 MilesAudioSample::
54 ~MilesAudioSample() {
55  miles_audio_debug("~MilesAudioSample()");
56  cleanup();
57  miles_audio_debug("~MilesAudioSample() done");
58 }
59 
60 /**
61  *
62  */
63 void MilesAudioSample::
64 play() {
65  miles_audio_debug("play()");
66  if (_active) {
67  if (_sd->_raw_data.empty()) {
68  milesAudio_cat.warning()
69  << "Could not play " << _file_name << ": no data\n";
70  } else {
71  stop();
72  _manager->starting_sound(this);
73 
74  nassertv(_sample == 0);
75 
76  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
77  if (!mgr->get_sample(_sample, _sample_index, this)){
78  milesAudio_cat.warning()
79  << "Could not play " << _file_name << ": too many open samples\n";
80  _sample = 0;
81  } else {
82  AIL_set_named_sample_file(_sample, _sd->_basename.c_str(),
83  &_sd->_raw_data[0], _sd->_raw_data.size(),
84  0);
85  _original_playback_rate = AIL_sample_playback_rate(_sample);
86  AIL_set_sample_user_data(_sample, 0, (SINTa)this);
87  AIL_register_EOS_callback(_sample, finish_callback);
88 
89  set_volume(_volume);
90  set_play_rate(_play_rate);
91  AIL_set_sample_loop_count(_sample, _loop_count);
92 
93  if (_got_start_time) {
94  do_set_time(_start_time);
95  AIL_resume_sample(_sample);
96  } else {
97  AIL_start_sample(_sample);
98  }
99  }
100 
101  _got_start_time = false;
102  }
103  } else {
104  // In case _loop_count gets set to forever (zero):
105  audio_debug(" paused "<<_file_name );
106  _paused = true;
107  }
108 }
109 
110 /**
111  *
112  */
113 void MilesAudioSample::
114 stop() {
115  if (_manager == nullptr) {
116  return;
117  }
118 
119  miles_audio_debug("stop()");
120  _manager->stopping_sound(this);
121  // The _paused flag should not be cleared here. _paused is not like the
122  // Pause button on a cddvd player. It is used as a flag to say that it was
123  // looping when it was set inactive. There is no need to make this
124  // symmetrical with play(). set_active() is the 'owner' of _paused. play()
125  // accesses _paused to help in the situation where someone calls play on an
126  // inactive sound().
127 
128  // it fixes audio bug, I don't understand the reasoning of the above comment
129  _paused = false;
130 
131  if (_sample != 0) {
132  AIL_end_sample(_sample);
133 
134  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
135  mgr->release_sample(_sample_index, this);
136 
137  _sample = 0;
138  _sample_index = 0;
139  }
140 }
141 
142 /**
143  *
144  */
145 PN_stdfloat MilesAudioSample::
146 get_time() const {
147  if (_sample == 0) {
148  if (_got_start_time) {
149  return _start_time;
150  }
151  return 0.0f;
152  }
153 
154  S32 current_ms;
155  AIL_sample_ms_position(_sample, nullptr, &current_ms);
156  PN_stdfloat time = PN_stdfloat(current_ms * 0.001f);
157 
158  return time;
159 }
160 
161 /**
162  *
163  */
164 void MilesAudioSample::
165 set_volume(PN_stdfloat volume) {
166  miles_audio_debug("set_volume(volume="<<volume<<")");
167 
168  // Set the volume even if our volume is not changing, because the
169  // MilesAudioManager will call set_volume() when *its* volume changes.
170 
171  // Set the volume:
172  _volume = volume;
173 
174  if (_sample != 0) {
175  volume *= _manager->get_volume();
176 
177  // Change to Miles volume, range 0 to 1.0:
178  F32 milesVolume = volume;
179  milesVolume = std::min(milesVolume, 1.0f);
180  milesVolume = std::max(milesVolume, 0.0f);
181 
182  // Convert balance of -1.0..1.0 to 0-1.0:
183  F32 milesBalance = (F32)((_balance + 1.0f) * 0.5f);
184 
185  AIL_set_sample_volume_pan(_sample, milesVolume, milesBalance);
186  }
187 }
188 
189 /**
190  *
191  */
192 void MilesAudioSample::
193 set_balance(PN_stdfloat balance_right) {
194  miles_audio_debug("set_balance(balance_right="<<balance_right<<")");
195  _balance = balance_right;
196 
197  // Call set_volume to effect the change:
198  set_volume(_volume);
199 }
200 
201 /**
202  *
203  */
204 void MilesAudioSample::
205 set_play_rate(PN_stdfloat play_rate) {
206  miles_audio_debug("set_play_rate(play_rate="<<play_rate<<")");
207 
208  // Set the play_rate:
209  _play_rate = play_rate;
210 
211  if (_sample != 0) {
212  play_rate *= _manager->get_play_rate();
213 
214  // wave and mp3 use sample rate (e.g. 44100)
215  S32 speed = (S32)(play_rate * (PN_stdfloat)_original_playback_rate);
216  AIL_set_sample_playback_rate(_sample, speed);
217  audio_debug(" play_rate for this wav or mp3 is now " << speed);
218  }
219 }
220 
221 /**
222  *
223  */
224 PN_stdfloat MilesAudioSample::
225 length() const {
226  return _sd->get_length();
227 }
228 
229 /**
230  *
231  */
232 AudioSound::SoundStatus MilesAudioSample::
233 status() const {
234  if (_sample == 0) {
235  return AudioSound::READY;
236  }
237  switch (AIL_sample_status(_sample)) {
238  case SMP_DONE:
239  case SMP_STOPPED:
240  case SMP_FREE:
241  return AudioSound::READY;
242 
243  case SMP_PLAYING:
244  case SMP_PLAYINGBUTRELEASED:
245  return AudioSound::PLAYING;
246 
247  default:
248  return AudioSound::BAD;
249  }
250 }
251 
252 /**
253  * Stops the sound from playing and releases any associated resources, in
254  * preparation for releasing the sound or shutting down the sound system.
255  */
256 void MilesAudioSample::
257 cleanup() {
258  stop();
259  set_active(false);
260  nassertv(_sample == 0);
261 
262  if (_manager != nullptr) {
263  _manager->release_sound(this);
264  _manager = nullptr;
265  }
266 }
267 
268 /**
269  *
270  */
271 void MilesAudioSample::
272 output(std::ostream &out) const {
273  out << get_type() << " " << get_name() << " " << status();
274  if (!_sd.is_null()) {
275  out << " " << (_sd->_raw_data.size() + 1023) / 1024 << "K";
276  }
277 }
278 
279 /**
280  * Set position and velocity of this sound. Note that Y and Z are switched to
281  * translate from Miles's coordinate system.
282  */
283 void MilesAudioSample::set_3d_attributes(PN_stdfloat px, PN_stdfloat py, PN_stdfloat pz, PN_stdfloat vx, PN_stdfloat vy, PN_stdfloat vz) {
284  audio_debug("MilesAudioSample::set_3d_attributes() Setting a sound's 3D Coordinates.");
285 
286  if(_sample != 0) {
287  AIL_set_sample_3D_position(_sample, px, pz, py);
288  AIL_set_sample_3D_velocity_vector(_sample, vx, vz, vy);
289  } else {
290  audio_warning("_sample == 0 in MilesAudioSample::set_3d_attributes().");
291  }
292 }
293 
294 /**
295  * Get position and velocity of this sound.
296  */
297 void MilesAudioSample::get_3d_attributes(PN_stdfloat *px, PN_stdfloat *py, PN_stdfloat *pz, PN_stdfloat *vx, PN_stdfloat *vy, PN_stdfloat *vz) {
298  audio_debug("MilesAudioSample::get_3d_attributes().");
299 
300  if(_sample != 0) {
301  float lpx, lpy, lpz, lvx, lvy, lvz;
302  AIL_sample_3D_position(_sample, &lpx, &lpz, &lpy);
303  AIL_sample_3D_velocity(_sample, &lvx, &lvz, &lvy);
304  *px = lpx;
305  *py = lpy;
306  *pz = lpz;
307  *vx = lvx;
308  *vy = lvy;
309  *vz = lvz;
310  } else {
311  audio_warning("_sample == 0 in MilesAudioSample::get_3d_attributes().");
312  }
313 }
314 
315 /**
316  * Set the distance that this sound begins to fall off. With Miles's default
317  * falloff behavior, when the distance between the sound and the listener is
318  * doubled, the volume is halved, and vice versa.
319  */
320 void MilesAudioSample::set_3d_min_distance(PN_stdfloat dist) {
321  audio_debug("MilesAudioSample::set_3d_min_distance() Setting the sound's 3D min distance ( min= " << dist << " ) ");
322 
323  if(_sample != 0) {
324  // Implementation is awkward, since Miles gets and sets min and max
325  // distances in a single operation.
326  float max_dist;
327  int auto_3D_wet_atten;
328  AIL_sample_3D_distances(_sample, &max_dist, nullptr, &auto_3D_wet_atten);
329 
330  AIL_set_sample_3D_distances(_sample, max_dist, dist, auto_3D_wet_atten);
331  } else {
332  audio_warning("_sample == 0 in MilesAudioSample::set_3d_min_distance().");
333  }
334 }
335 
336 /**
337  * Get the distance that this sound begins to fall off.
338  */
339 PN_stdfloat MilesAudioSample::get_3d_min_distance() const {
340  audio_debug("MilesAudioSample::get_3d_min_distance() ");
341 
342  if(_sample != 0) {
343  float min_dist;
344  AIL_sample_3D_distances(_sample, nullptr, &min_dist, nullptr);
345  return (PN_stdfloat)min_dist;
346  } else {
347  audio_warning("_sample == 0 in MilesAudioSample::get_3d_min_distance().");
348  return -1.0;
349  }
350 }
351 
352 /**
353  * Set the distance at which this sound is clipped to silence. Note that this
354  * value does not affect the rate at which the sound falls off, but only the
355  * distance at which it gets clipped.
356  */
357 void MilesAudioSample::set_3d_max_distance(PN_stdfloat dist) {
358  audio_debug("MilesAudioSample::set_3d_max_distance() Setting the sound's 3D max distance ( max= " << dist << " ) ");
359 
360  if(_sample != 0) {
361  // Implementation is awkward, since Miles gets and sets min and max
362  // distances in a single operation.
363  float min_dist;
364  int auto_3D_wet_atten;
365  AIL_sample_3D_distances(_sample, nullptr, &min_dist, &auto_3D_wet_atten);
366 
367  AIL_set_sample_3D_distances(_sample, dist, min_dist, auto_3D_wet_atten);
368  } else {
369  audio_warning("_sample == 0 in MilesAudioSample::set_3d_max_distance().");
370  }
371 }
372 
373 /**
374  * Get the distance at which this sound is clipped to silence.
375  */
376 PN_stdfloat MilesAudioSample::get_3d_max_distance() const {
377  audio_debug("MilesAudioSample::get_3d_max_distance() ");
378 
379  if(_sample != 0) {
380  float max_dist;
381  AIL_sample_3D_distances(_sample, &max_dist, nullptr, nullptr);
382  return (PN_stdfloat)max_dist;
383  } else {
384  audio_warning("_sample == 0 in MilesAudioSample::get_3d_max_distance().");
385  return -1.0;
386  }
387 }
388 
389 /**
390  * Get the level of a particular logical channel (speaker). "index" specifies
391  * which speaker in an array of all the logical channels currently in use to
392  * retrieve the level of.
393  *
394  * For instance, in a standard 4.0 channel setup, speakers are setup as
395  * [frontLeft, frontRight, backLeft, backRight]. Thus, get_speaker_level(2)
396  * will retrieve the level of the backLeft speaker.
397  *
398  * The order in which speakers appear in the array for standard speaker setups
399  * is defined to be:
400  *
401  * FRONT_LEFT FRONT_RIGHT FRONT_CENTER LOW_FREQUENCY (sub woofer) BACK_LEFT
402  * BACK_RIGHT FRONT_LEFT_OF_CENTER FRONT_RIGHT_OF_CENTER BACK_CENTER SIDE_LEFT
403  * SIDE_RIGHT TOP_CENTER TOP_FRONT_LEFT TOP_FRONT_CENTER TOP_FRONT_RIGHT
404  * TOP_BACK_LEFT TOP_BACK_CENTER TOP_BACK_RIGHT
405  *
406  */
407 PN_stdfloat MilesAudioSample::
408 get_speaker_level(int index) {
409  audio_debug("MilesAudioSample::get_speaker_level(" << index << ")");
410 
411  if(_sample != 0) {
412  int numLevels;
413  float *levels = AIL_sample_channel_levels(_sample, &numLevels);
414 
415  if(index < numLevels) {
416  return (PN_stdfloat)levels[index];
417  } else {
418  audio_error("index out of range in MilesAudioSample::get_speaker_level. numLevels: " << numLevels);
419  return -1.0;
420  }
421  } else {
422  audio_warning("Warning: MilesAudioSample::get_speaker_level only works for sounds that are currently playing");
423  return -1.0;
424  }
425 }
426 
427 /**
428  * Set the output levels on the logical channels (speakers) for this sound.
429  * Values should be in the range 0.0 to 1.0. Levels for up to nine channels
430  * may be specified. As soon as a level is reached that falls outside the
431  * range 0.0 to 1.0, the levels specified up to that point will be sent and
432  * all other levels will be ignored.
433  *
434  * The user must know what the current speaker setup is in order to know which
435  * level corresponds to which speaker.
436  *
437  * This method will have no effect if 3D attributes have been set for this
438  * sound.
439  *
440  * The order in which speakers appear in the array for standard speaker setups
441  * is defined to be:
442  *
443  * FRONT_LEFT FRONT_RIGHT FRONT_CENTER LOW_FREQUENCY (sub woofer) BACK_LEFT
444  * BACK_RIGHT FRONT_LEFT_OF_CENTER FRONT_RIGHT_OF_CENTER BACK_CENTER SIDE_LEFT
445  * SIDE_RIGHT TOP_CENTER TOP_FRONT_LEFT TOP_FRONT_CENTER TOP_FRONT_RIGHT
446  * TOP_BACK_LEFT TOP_BACK_CENTER TOP_BACK_RIGHT
447  *
448  */
449 void MilesAudioSample::
450 set_speaker_levels(PN_stdfloat level1, PN_stdfloat level2, PN_stdfloat level3, PN_stdfloat level4, PN_stdfloat level5, PN_stdfloat level6, PN_stdfloat level7, PN_stdfloat level8, PN_stdfloat level9) {
451  audio_debug("MilesAudioSample::set_speaker_levels()");
452 
453  if(_sample != 0) {
454  float levels[9] = {level1, level2, level3, level4, level5, level6, level7, level8, level9};
455 
456  if((level1 < 0.0) || (level1 > 1.0)) {
457  audio_error("No valid levels specified in MilesAudioSample::set_speaker_levels().");
458  } else if((level2 < 0.0) || (level2 > 1.0)) {
459  AIL_set_sample_channel_levels(_sample, levels, 1);
460  } else if((level3 < 0.0) || (level3 > 1.0)) {
461  AIL_set_sample_channel_levels(_sample, levels, 2);
462  } else if((level4 < 0.0) || (level4 > 1.0)) {
463  AIL_set_sample_channel_levels(_sample, levels, 3);
464  } else if((level5 < 0.0) || (level5 > 1.0)) {
465  AIL_set_sample_channel_levels(_sample, levels, 4);
466  } else if((level6 < 0.0) || (level6 > 1.0)) {
467  AIL_set_sample_channel_levels(_sample, levels, 5);
468  } else if((level7 < 0.0) || (level7 > 1.0)) {
469  AIL_set_sample_channel_levels(_sample, levels, 6);
470  } else if((level8 < 0.0) || (level8 > 1.0)) {
471  AIL_set_sample_channel_levels(_sample, levels, 7);
472  } else if((level9 < 0.0) || (level9 > 1.0)) {
473  AIL_set_sample_channel_levels(_sample, levels, 8);
474  } else {
475  AIL_set_sample_channel_levels(_sample, levels, 9);
476  }
477  } else {
478  audio_warning("Warning: MilesAudioSample::set_speaker_levels only works for sounds that are currently playing");
479  }
480 }
481 
482 /**
483  * Called by the GlobalMilesManager when it is detected that this particular
484  * sound has already stopped, and its sample handle will be recycled.
485  */
486 void MilesAudioSample::
487 internal_stop() {
488  _sample = 0;
489  _sample_index = 0;
490 }
491 
492 /**
493  * This callback is made by Miles (possibly in a sub-thread) when the sample
494  * finishes.
495  */
496 void AILCALLBACK MilesAudioSample::
497 finish_callback(HSAMPLE sample) {
498  MilesAudioSample *self = (MilesAudioSample *)AIL_sample_user_data(sample, 0);
499  if (milesAudio_cat.is_debug()) {
500  milesAudio_cat.debug()
501  << "finished " << *self << "\n";
502  }
503  if (self->_manager == nullptr) {
504  return;
505  }
506  self->_manager->_sounds_finished = true;
507 }
508 
509 /**
510  * Sets the start time of an already allocated sample.
511  */
512 void MilesAudioSample::
513 do_set_time(PN_stdfloat time) {
514  miles_audio_debug("do_set_time(time="<<time<<")");
515  nassertv(_sample != 0);
516 
517  // Ensure we don't inadvertently run off the end of the sound.
518  PN_stdfloat max_time = length();
519  if (time > max_time) {
520  milesAudio_cat.warning()
521  << "set_time(" << time << ") requested for sound of length "
522  << max_time << "\n";
523  time = max_time;
524  }
525 
526  S32 time_ms = (S32)(1000.0f * time);
527  AIL_set_sample_ms_position(_sample, time_ms);
528 }
529 
530 #endif //]
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.