Panda3D
milesAudioStream.cxx
1 // Filename: milesAudioStream.cxx
2 // Created by: drose (26Jul07)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "milesAudioStream.h"
16 #ifdef HAVE_RAD_MSS //[
17 
18 #include "milesAudioManager.h"
19 #include "pnotify.h"
20 
21 TypeHandle MilesAudioStream::_type_handle;
22 
23 #undef miles_audio_debug
24 
25 #ifndef NDEBUG //[
26 #define miles_audio_debug(x) \
27  audio_debug("MilesAudioStream \""<<get_name()<<"\" "<< x )
28 #else //][
29 #define miles_audio_debug(x) ((void)0)
30 #endif //]
31 
32 ////////////////////////////////////////////////////////////////////
33 // Function: MilesAudioStream::Constructor
34 // Access: Private
35 // Description:
36 ////////////////////////////////////////////////////////////////////
37 MilesAudioStream::
38 MilesAudioStream(MilesAudioManager *manager, const string &file_name,
39  const Filename &path) :
40  MilesAudioSound(manager, file_name),
41  _path(path)
42 {
43  _stream = 0;
44  _got_length = false;
45 }
46 
47 ////////////////////////////////////////////////////////////////////
48 // Function: MilesAudioStream::Destructor
49 // Access: Public, Virtual
50 // Description:
51 ////////////////////////////////////////////////////////////////////
52 MilesAudioStream::
53 ~MilesAudioStream() {
54  miles_audio_debug("~MilesAudioStream()");
55  cleanup();
56 
57  miles_audio_debug("~MilesAudioStream() done");
58 }
59 
60 ////////////////////////////////////////////////////////////////////
61 // Function: MilesAudioStream::play
62 // Access: Public, Virtual
63 // Description:
64 ////////////////////////////////////////////////////////////////////
65 void MilesAudioStream::
66 play() {
67  miles_audio_debug("play()");
68  if (_active) {
69 
70  _manager->starting_sound(this);
71 
72  if (_stream == 0) {
73  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
74  _stream = AIL_open_stream(mgr->_digital_driver, _path.c_str(), 0);
75  if (_stream == 0) {
76  milesAudio_cat.warning()
77  << "Could not play " << _file_name << ": too many open streams\n";
78  return;
79  }
80  AIL_set_stream_user_data(_stream, 0, (SINTa)this);
81  AIL_register_stream_callback(_stream, finish_callback);
82 
83  } else {
84  // We already had the stream open. Keep it open; just restart
85  // it.
86  AIL_pause_stream(_stream, 1);
87  _manager->stop_service_stream(_stream);
88  }
89 
90  // Start playing:
91  nassertv(_stream != 0);
92  HSAMPLE sample = AIL_stream_sample_handle(_stream);
93  nassertv(sample != 0);
94 
95  _original_playback_rate = AIL_sample_playback_rate(sample);
96  set_volume(_volume);
97  set_play_rate(_play_rate);
98 
99  AIL_set_stream_loop_count(_stream, _loop_count);
100 
101  AIL_start_stream(_stream);
102  if (_got_start_time) {
103  // There's no AIL_resume_stream(), so we start in the middle by
104  // starting normally, then immediately skipping to the middle.
105  do_set_time(_start_time);
106  }
107 
108  if (miles_audio_panda_threads) {
109  AIL_auto_service_stream(_stream, 0);
110  _manager->start_service_stream(_stream);
111  } else {
112  AIL_auto_service_stream(_stream, 1);
113  }
114 
115  _got_start_time = false;
116 
117  } else {
118  // In case _loop_count gets set to forever (zero):
119  audio_debug(" paused "<<_file_name );
120  _paused = true;
121  }
122 }
123 
124 ////////////////////////////////////////////////////////////////////
125 // Function: MilesAudioStream::stop
126 // Access: Public, Virtual
127 // Description:
128 ////////////////////////////////////////////////////////////////////
129 void MilesAudioStream::
130 stop() {
131  if (_manager == (MilesAudioManager *)NULL) {
132  return;
133  }
134  miles_audio_debug("stop()");
135  _manager->stopping_sound(this);
136 
137  // The _paused flag should not be cleared here. _paused is not like
138  // the Pause button on a cd/dvd player. It is used as a flag to say
139  // that it was looping when it was set inactive. There is no need to
140  // make this symmetrical with play(). set_active() is the 'owner' of
141  // _paused. play() accesses _paused to help in the situation where
142  // someone calls play on an inactive sound().
143  if (_stream != 0) {
144  _manager->stop_service_stream(_stream);
145 
146  AIL_pause_stream(_stream, 1);
147  AIL_close_stream(_stream);
148  _stream = 0;
149  }
150 }
151 
152 ////////////////////////////////////////////////////////////////////
153 // Function: MilesAudioStream::
154 // Access: Public, Virtual
155 // Description:
156 ////////////////////////////////////////////////////////////////////
157 PN_stdfloat MilesAudioStream::
158 get_time() const {
159  if (_stream == 0) {
160  if (_got_start_time) {
161  return _start_time;
162  }
163  return 0.0f;
164  }
165 
166  S32 current_ms;
167  AIL_stream_ms_position(_stream, NULL, &current_ms);
168  PN_stdfloat time = PN_stdfloat(current_ms * 0.001f);
169 
170  return time;
171 }
172 
173 ////////////////////////////////////////////////////////////////////
174 // Function: MilesAudioStream::
175 // Access: Public, Virtual
176 // Description:
177 ////////////////////////////////////////////////////////////////////
178 void MilesAudioStream::
179 set_volume(PN_stdfloat volume) {
180  _volume = volume;
181 
182  if (_stream != 0) {
183  HSAMPLE sample = AIL_stream_sample_handle(_stream);
184  nassertv(sample != 0);
185 
186  volume *= _manager->get_volume();
187 
188  // Change to Miles volume, range 0 to 1.0:
189  F32 milesVolume = volume;
190  milesVolume = min(milesVolume, 1.0f);
191  milesVolume = max(milesVolume, 0.0f);
192 
193  // Convert balance of -1.0..1.0 to 0-1.0:
194  F32 milesBalance = (F32)((_balance + 1.0f) * 0.5f);
195 
196  AIL_set_sample_volume_pan(sample, milesVolume, milesBalance);
197  }
198 }
199 
200 ////////////////////////////////////////////////////////////////////
201 // Function: MilesAudioStream::set_balance
202 // Access: Public, Virtual
203 // Description:
204 ////////////////////////////////////////////////////////////////////
205 void MilesAudioStream::
206 set_balance(PN_stdfloat balance_right) {
207  _balance = balance_right;
208 
209  // Call set_volume to effect the change:
210  set_volume(_volume);
211 }
212 
213 ////////////////////////////////////////////////////////////////////
214 // Function: MilesAudioStream::set_play_rate
215 // Access: Public, Virtual
216 // Description:
217 ////////////////////////////////////////////////////////////////////
218 void MilesAudioStream::
219 set_play_rate(PN_stdfloat play_rate) {
220  _play_rate = play_rate;
221 
222  if (_stream != 0) {
223  HSAMPLE sample = AIL_stream_sample_handle(_stream);
224  nassertv(sample != 0);
225 
226  play_rate *= _manager->get_play_rate();
227 
228  // wave and mp3 use sample rate (e.g. 44100)
229  S32 speed = (S32)(play_rate * (PN_stdfloat)_original_playback_rate);
230  AIL_set_sample_playback_rate(sample, speed);
231  audio_debug(" play_rate for this wav or mp3 is now " << speed);
232  }
233 }
234 
235 ////////////////////////////////////////////////////////////////////
236 // Function: MilesAudioStream::length
237 // Access: Public, Virtual
238 // Description:
239 ////////////////////////////////////////////////////////////////////
240 PN_stdfloat MilesAudioStream::
241 length() const {
242  if (!_got_length) {
243  if (_stream == 0) {
244  GlobalMilesManager *mgr = GlobalMilesManager::get_global_ptr();
245  ((MilesAudioStream *)this)->_stream = AIL_open_stream(mgr->_digital_driver, _path.c_str(), 0);
246  }
247 
248  S32 length_ms;
249  AIL_stream_ms_position(_stream, &length_ms, NULL);
250  _length = (PN_stdfloat)length_ms * 0.001f;
251  _got_length = true;
252  }
253 
254  return _length;
255 }
256 
257 ////////////////////////////////////////////////////////////////////
258 // Function: MilesAudioStream::status
259 // Access: Public, Virtual
260 // Description:
261 ////////////////////////////////////////////////////////////////////
262 AudioSound::SoundStatus MilesAudioStream::
263 status() const {
264  if (!_stream) {
265  return AudioSound::READY;
266  }
267 
268  switch (AIL_stream_status(_stream)) {
269  case SMP_STOPPED:
270  case SMP_DONE:
271  return AudioSound::READY;
272  case SMP_PLAYING:
273  case SMP_PLAYINGBUTRELEASED:
274  return AudioSound::PLAYING;
275  default:
276  return AudioSound::BAD;
277  }
278 }
279 
280 ////////////////////////////////////////////////////////////////////
281 // Function: MilesAudioStream::cleanup
282 // Access: Public, Virtual
283 // Description: Called to release any resources associated with the
284 // sound.
285 ////////////////////////////////////////////////////////////////////
286 void MilesAudioStream::
287 cleanup() {
288  if (_stream) {
289  stop();
290  }
291  set_active(false);
292  nassertv(_stream == 0);
293 
294  if (_manager != (MilesAudioManager *)NULL) {
295  _manager->release_sound(this);
296  _manager = NULL;
297  }
298 }
299 
300 ////////////////////////////////////////////////////////////////////
301 // Function: MilesAudioStream::finish_callback
302 // Access: Private, Static
303 // Description: This callback is made by Miles (possibly in a
304 // sub-thread) when the stream finishes.
305 ////////////////////////////////////////////////////////////////////
306 void AILCALLBACK MilesAudioStream::
307 finish_callback(HSTREAM stream) {
308  MilesAudioStream *self = (MilesAudioStream *)AIL_stream_user_data(stream, 0);
309  if (milesAudio_cat.is_debug()) {
310  milesAudio_cat.debug()
311  << "finished " << *self << "\n";
312  }
313  if (self->_manager == (MilesAudioManager *)NULL) {
314  return;
315  }
316  self->_manager->_sounds_finished = true;
317 }
318 
319 ////////////////////////////////////////////////////////////////////
320 // Function: MilesAudioStream::do_set_time
321 // Access: Private
322 // Description: Sets the start time of an already allocated stream.
323 ////////////////////////////////////////////////////////////////////
324 void MilesAudioStream::
325 do_set_time(PN_stdfloat time) {
326  nassertv(_stream != 0);
327 
328  S32 time_ms = (S32)(1000.0f * time);
329 
330  // Ensure we don't inadvertently run off the end of the sound.
331  S32 length_ms;
332  AIL_stream_ms_position(_stream, &length_ms, NULL);
333  time_ms = min(time_ms, length_ms);
334 
335  AIL_set_stream_ms_position(_stream, time_ms);
336 }
337 
338 
339 #endif //]
void set_volume(PN_stdfloat volume=1.0)
0.0 to 1.0 scale of volume converted to Fmod&#39;s internal 0.0 to 255.0 scale.
void stop()
Stop a sound.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
void set_play_rate(PN_stdfloat play_rate=1.0f)
Sets the speed at which a sound plays back.
void set_active(bool active=true)
Sets whether the sound is marked "active".
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85