Panda3D
milesAudioSequence.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 milesAudioSequence.cxx
10  * @author drose
11  * @date 2007-07-31
12  */
13 
14 #include "milesAudioSequence.h"
15 
16 #ifdef HAVE_RAD_MSS //[
17 
18 #include "milesAudioManager.h"
19 
20 
21 TypeHandle MilesAudioSequence::_type_handle;
22 
23 #undef miles_audio_debug
24 
25 #ifndef NDEBUG //[
26 #define miles_audio_debug(x) \
27  audio_debug("MilesAudioSequence \""<<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 MilesAudioSequence::
36 MilesAudioSequence(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("MilesAudioSequence(manager=0x"<<(void*)&manager
43  <<", sd=0x"<<(void*)sd<<", file_name="<<file_name<<")");
44 
45  _sequence = 0;
46  _sequence_index = 0;
47 }
48 
49 /**
50  *
51  */
52 MilesAudioSequence::
53 ~MilesAudioSequence() {
54  miles_audio_debug("~MilesAudioSequence()");
55  cleanup();
56  _manager->release_sound(this);
57  miles_audio_debug("~MilesAudioSequence() done");
58 }
59 
60 /**
61  *
62  */
63 void MilesAudioSequence::
64 play() {
65  miles_audio_debug("play()");
66  if (_active) {
67  stop();
68 
69  if (_sd->_raw_data.empty()) {
70  milesAudio_cat.warning()
71  << "Could not play " << _file_name << ": no data\n";
72  } else {
73  _manager->starting_sound(this);
74  nassertv(_sequence == 0);
75 
76  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
77  if (!mgr->get_sequence(_sequence, _sequence_index, this)){
78  milesAudio_cat.warning()
79  << "Could not play " << _file_name << ": too many open sequences\n";
80  _sequence = 0;
81  } else {
82  AIL_init_sequence(_sequence, &_sd->_raw_data[0], 0);
83  AIL_set_sequence_user_data(_sequence, 0, (SINTa)this);
84  AIL_register_sequence_callback(_sequence, finish_callback);
85 
86  set_volume(_volume);
87  set_play_rate(_play_rate);
88  AIL_set_sequence_loop_count(_sequence, _loop_count);
89 
90  if (_got_start_time) {
91  do_set_time(_start_time);
92  AIL_resume_sequence(_sequence);
93  } else {
94  AIL_start_sequence(_sequence);
95  }
96  }
97 
98  _got_start_time = false;
99  }
100  } else {
101  // In case _loop_count gets set to forever (zero):
102  audio_debug(" paused "<<_file_name );
103  _paused = true;
104  }
105 }
106 
107 /**
108  *
109  */
110 void MilesAudioSequence::
111 stop() {
112  miles_audio_debug("stop()");
113  _manager->stopping_sound(this);
114  // The _paused flag should not be cleared here. _paused is not like the
115  // Pause button on a cddvd player. It is used as a flag to say that it was
116  // looping when it was set inactive. There is no need to make this
117  // symmetrical with play(). set_active() is the 'owner' of _paused. play()
118  // accesses _paused to help in the situation where someone calls play on an
119  // inactive sound().
120 
121  if (_sequence != 0) {
122  AIL_end_sequence(_sequence);
123 
124  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
125  mgr->release_sequence(_sequence_index, this);
126 
127  _sequence = 0;
128  _sequence_index = 0;
129  }
130 }
131 
132 /**
133  *
134  */
135 PN_stdfloat MilesAudioSequence::
136 get_time() const {
137  if (_sequence == 0) {
138  if (_got_start_time) {
139  return _start_time;
140  }
141  return 0.0f;
142  }
143 
144  S32 current_ms;
145  AIL_sequence_ms_position(_sequence, nullptr, &current_ms);
146  PN_stdfloat time = PN_stdfloat(current_ms * 0.001f);
147 
148  return time;
149 }
150 
151 /**
152  *
153  */
154 void MilesAudioSequence::
155 set_volume(PN_stdfloat volume) {
156  miles_audio_debug("set_volume(volume="<<volume<<")");
157 
158  // Set the volume even if our volume is not changing, because the
159  // MilesAudioManager will call set_volume() when *its* volume changes.
160 
161  // Set the volume:
162  _volume = volume;
163 
164  if (_sequence != 0) {
165  volume *= _manager->get_volume();
166 
167  // Change to Miles volume, range 0 to 127:
168  S32 milesVolume = (S32)(volume * 127.0f);
169  milesVolume = std::min(milesVolume, 127);
170  milesVolume = std::max(milesVolume, 0);
171 
172  AIL_set_sequence_volume(_sequence, milesVolume, 0);
173  }
174 }
175 
176 /**
177  *
178  */
179 void MilesAudioSequence::
180 set_balance(PN_stdfloat balance_right) {
181  miles_audio_debug("set_balance(balance_right="<<balance_right<<")");
182  _balance = balance_right;
183 
184  // Balance has no effect on a MIDI file.
185 }
186 
187 /**
188  *
189  */
190 void MilesAudioSequence::
191 set_play_rate(PN_stdfloat play_rate) {
192  miles_audio_debug("set_play_rate(play_rate="<<play_rate<<")");
193 
194  // Set the play_rate:
195  _play_rate = play_rate;
196 
197  if (_sequence != 0) {
198  play_rate *= _manager->get_play_rate();
199 
200  S32 percent = (S32)(play_rate * 100.0f);
201  AIL_set_sequence_tempo(_sequence, percent, 0);
202  }
203 }
204 
205 /**
206  *
207  */
208 PN_stdfloat MilesAudioSequence::
209 length() const {
210  if (_sequence == 0) {
211  // The MIDI file hasn't been started yet. See if the length is cached in
212  // the SoundData.
213  if (!_sd->_has_length) {
214  // It isn't cached, so load the sequence temporarily to determine its
215  // length.
216  ((MilesAudioSequence *)this)->determine_length();
217  }
218 
219  return _sd->get_length();
220  }
221 
222  // The MIDI file has already been started, so we can ask it directly.
223  S32 length_ms;
224  AIL_sequence_ms_position(_sequence, &length_ms, nullptr);
225  PN_stdfloat time = (PN_stdfloat)length_ms * 0.001f;
226  return time;
227 }
228 
229 /**
230  *
231  */
232 AudioSound::SoundStatus MilesAudioSequence::
233 status() const {
234  if (_sequence == 0) {
235  return AudioSound::READY;
236  }
237  switch (AIL_sequence_status(_sequence)) {
238  case SEQ_DONE:
239  case SEQ_STOPPED:
240  case SEQ_FREE:
241  return AudioSound::READY;
242 
243  case SEQ_PLAYING:
244  case SEQ_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 MilesAudioSequence::
257 cleanup() {
258  stop();
259 }
260 
261 /**
262  * Called by the GlobalMilesManager when it is detected that this particular
263  * sound has already stopped, and its sequence handle will be recycled.
264  */
265 void MilesAudioSequence::
266 internal_stop() {
267  _sequence = 0;
268  _sequence_index = 0;
269 }
270 
271 /**
272  * This callback is made by Miles (possibly in a sub-thread) when the sequence
273  * finishes.
274  */
275 void AILCALLBACK MilesAudioSequence::
276 finish_callback(HSEQUENCE sequence) {
277  MilesAudioSequence *self = (MilesAudioSequence *)AIL_sequence_user_data(sequence, 0);
278  if (milesAudio_cat.is_debug()) {
279  milesAudio_cat.debug()
280  << "finished " << *self << "\n";
281  }
282  self->_manager->_sounds_finished = true;
283 }
284 
285 /**
286  * Sets the start time of an already allocated stream.
287  */
288 void MilesAudioSequence::
289 do_set_time(PN_stdfloat time) {
290  miles_audio_debug("do_set_time(time="<<time<<")");
291 
292  nassertv(_sequence != 0);
293 
294  S32 time_ms = (S32)(1000.0f * time);
295 
296  // Ensure we don't inadvertently run off the end of the sound.
297  S32 length_ms;
298  AIL_sequence_ms_position(_sequence, &length_ms, nullptr);
299  time_ms = std::min(time_ms, length_ms);
300 
301  AIL_set_sequence_ms_position(_sequence, time_ms);
302 }
303 
304 
305 /**
306  * Temporarily loads the sequence to determine its length. Stores the result
307  * on the _sd.
308  */
309 void MilesAudioSequence::
310 determine_length() {
311  nassertv(_sequence == 0);
312 
313  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
314  if (!mgr->get_sequence(_sequence, _sequence_index, this)){
315  milesAudio_cat.warning()
316  << "Could not determine length of " << _file_name << ": too many open sequences\n";
317  _sequence = 0;
318  } else {
319  AIL_init_sequence(_sequence, &_sd->_raw_data[0], 0);
320  S32 length_ms;
321  AIL_sequence_ms_position(_sequence, &length_ms, nullptr);
322  PN_stdfloat time = (PN_stdfloat)length_ms * 0.001f;
323  mgr->release_sequence(_sequence_index, this);
324  _sequence = 0;
325  _sequence_index = 0;
326 
327  _sd->set_length(time);
328  }
329 }
330 
331 #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.