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
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
milesAudioManager.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
milesAudioSequence.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.