Panda3D
Loading...
Searching...
No Matches
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
20TypeHandle 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 */
34MilesAudioStream::
35MilesAudioStream(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 */
47MilesAudioStream::
48~MilesAudioStream() {
49 miles_audio_debug("~MilesAudioStream()");
50 cleanup();
51
52 miles_audio_debug("~MilesAudioStream() done");
53}
54
55/**
56 *
57 */
58void MilesAudioStream::
59play() {
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 */
119void MilesAudioStream::
120stop() {
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 */
145PN_stdfloat MilesAudioStream::
146get_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 */
164void MilesAudioStream::
165set_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 */
189void MilesAudioStream::
190set_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 */
200void MilesAudioStream::
201set_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 */
220PN_stdfloat MilesAudioStream::
221length() 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 */
240AudioSound::SoundStatus MilesAudioStream::
241status() 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 */
261void MilesAudioStream::
262cleanup() {
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 */
279void AILCALLBACK MilesAudioStream::
280finish_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 */
295void MilesAudioStream::
296do_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 //]
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.