Panda3D
Loading...
Searching...
No Matches
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
21TypeHandle 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 */
35MilesAudioSample::
36MilesAudioSample(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 */
53MilesAudioSample::
54~MilesAudioSample() {
55 miles_audio_debug("~MilesAudioSample()");
56 cleanup();
57 miles_audio_debug("~MilesAudioSample() done");
58}
59
60/**
61 *
62 */
63void MilesAudioSample::
64play() {
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 */
113void MilesAudioSample::
114stop() {
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 */
145PN_stdfloat MilesAudioSample::
146get_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 */
164void MilesAudioSample::
165set_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 */
192void MilesAudioSample::
193set_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 */
204void MilesAudioSample::
205set_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 */
224PN_stdfloat MilesAudioSample::
225length() const {
226 return _sd->get_length();
227}
228
229/**
230 *
231 */
232AudioSound::SoundStatus MilesAudioSample::
233status() 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 */
256void MilesAudioSample::
257cleanup() {
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 */
271void MilesAudioSample::
272output(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 */
283void 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 */
297void 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 */
320void 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 */
339PN_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 */
357void 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 */
376PN_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 */
407PN_stdfloat MilesAudioSample::
408get_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 */
449void MilesAudioSample::
450set_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 */
486void MilesAudioSample::
487internal_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 */
496void AILCALLBACK MilesAudioSample::
497finish_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 */
512void MilesAudioSample::
513do_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 //]
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.