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