Panda3D
Loading...
Searching...
No Matches
globalMilesManager.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 globalMilesManager.cxx
10 * @author drose
11 * @date 2007-07-26
12 */
13
14#include "globalMilesManager.h"
15
16#ifdef HAVE_RAD_MSS //[
17
18#include "lightMutexHolder.h"
19#include "milesAudioManager.h"
20#include "milesAudioSample.h"
21#include "milesAudioSequence.h"
22
23#ifdef WIN32
24// For midiOutReset()
25#include <windows.h>
26#include <mmsystem.h>
27#endif
28
29using std::istream;
30using std::string;
31
32GlobalMilesManager *GlobalMilesManager::_global_ptr;
33
34/**
35 *
36 */
37GlobalMilesManager::
38GlobalMilesManager() :
39 _managers_lock("GlobalMilesManager::_managers_lock"),
40 _samples_lock("GlobalMilesManager::_samples_lock"),
41 _sequences_lock("GlobalMilesManager::_sequences_lock")
42{
43 _digital_driver = 0;
44 _midi_driver = 0;
45 _dls_device = 0;
46 _dls_file = 0;
47 _is_open = false;
48}
49
50/**
51 * Records a new MilesAudioManager in the world. This will open the Miles API
52 * when the first audio manager is added.
53 */
54void GlobalMilesManager::
55add_manager(MilesAudioManager *manager) {
56 LightMutexHolder holder(_managers_lock);
57 _managers.insert(manager);
58 if (!_is_open) {
59 open_api();
60 }
61}
62
63/**
64 * Records that a MilesAudioManager is destructing. This will clsoe the Miles
65 * API when the last audio manager is removed.
66 */
67void GlobalMilesManager::
68remove_manager(MilesAudioManager *manager) {
69 LightMutexHolder holder(_managers_lock);
70 _managers.erase(manager);
71 if (_managers.empty() && _is_open) {
72 close_api();
73 }
74}
75
76/**
77 * Calls cleanup() on all MilesAudioManagers, to cause a clean shutdown.
78 */
79void GlobalMilesManager::
80cleanup() {
81 LightMutexHolder holder(_managers_lock);
82 Managers::iterator mi;
83 for (mi = _managers.begin(); mi != _managers.end(); ++mi) {
84 (*mi)->cleanup();
85 }
86}
87
88/**
89 * Gets a sample handle from the global pool for the digital output device, to
90 * be used with the indicated AudioSound.
91 *
92 * If successful, sets the sample handle and the index (which should later be
93 * used to release the sample) and returns true. If unsuccessful (because
94 * there are no more available handles), returns false.
95 *
96 * This is a very limited resource; you should only get a sample just before
97 * playing a sound.
98 */
99bool GlobalMilesManager::
100get_sample(HSAMPLE &sample, size_t &index, MilesAudioSample *sound) {
101 LightMutexHolder holder(_samples_lock);
102
103 for (size_t i = 0; i < _samples.size(); ++i) {
104 SampleData &smp = _samples[i];
105 if (AIL_sample_status(smp._sample) == SMP_DONE) {
106 if (smp._sound != nullptr) {
107 // Tell the last sound that was using this sample that it's done now.
108 smp._sound->internal_stop();
109 }
110 smp._sound = sound;
111 sample = smp._sample;
112 index = i;
113 return true;
114 }
115 }
116
117 // No more already-allocated samples; get a new one from the system.
118 sample = AIL_allocate_sample_handle(_digital_driver);
119 if (sample == 0) {
120 return false;
121 }
122
123 AIL_init_sample(sample, DIG_F_STEREO_16, 0);
124 index = _samples.size();
125
126 SampleData smp;
127 smp._sound = sound;
128 smp._sample = sample;
129 _samples.push_back(smp);
130 return true;
131}
132
133/**
134 * Indicates that the indicated AudioSound no longer needs this sample.
135 */
136void GlobalMilesManager::
137release_sample(size_t index, MilesAudioSample *sound) {
138 LightMutexHolder holder(_samples_lock);
139 nassertv(index < _samples.size());
140
141 SampleData &smp = _samples[index];
142 if (smp._sound == sound) {
143 smp._sound = nullptr;
144 }
145}
146
147/**
148 * Gets a sequence handle from the global pool for the digital output device,
149 * to be used with the indicated AudioSound.
150 *
151 * If successful, sets the sequence handle and the index (which should later
152 * be used to release the sequence) and returns true. If unsuccessful
153 * (because there are no more available handles), returns false.
154 *
155 * This is a very limited resource; you should only get a sequence just before
156 * playing a sound.
157 */
158bool GlobalMilesManager::
159get_sequence(HSEQUENCE &sequence, size_t &index, MilesAudioSequence *sound) {
160 LightMutexHolder holder(_sequences_lock);
161
162 for (size_t i = 0; i < _sequences.size(); ++i) {
163 SequenceData &seq = _sequences[i];
164 if (AIL_sequence_status(seq._sequence) == SEQ_DONE) {
165 if (seq._sound != nullptr) {
166 // Tell the last sound that was using this sequence that it's done
167 // now.
168 seq._sound->internal_stop();
169 }
170 seq._sound = sound;
171 sequence = seq._sequence;
172 index = i;
173 return true;
174 }
175 }
176
177 // No more already-allocated sequences; get a new one from the system.
178 sequence = AIL_allocate_sequence_handle(_midi_driver);
179 if (sequence == 0) {
180 return false;
181 }
182
183 index = _sequences.size();
184
185 SequenceData seq;
186 seq._sound = sound;
187 seq._sequence = sequence;
188 _sequences.push_back(seq);
189 return true;
190}
191
192/**
193 * Indicates that the indicated AudioSound no longer needs this sequence.
194 */
195void GlobalMilesManager::
196release_sequence(size_t index, MilesAudioSequence *sound) {
197 LightMutexHolder holder(_sequences_lock);
198 nassertv(index < _sequences.size());
199
200 SequenceData &seq = _sequences[index];
201 if (seq._sound == sound) {
202 seq._sound = nullptr;
203 }
204}
205
206/**
207 * Sometimes Miles seems to leave midi notes hanging, even after stop is
208 * called, so call this method to perform an explicit reset using winMM.dll
209 * calls, just to ensure silence.
210 */
211void GlobalMilesManager::
212force_midi_reset() {
213 if (!miles_audio_force_midi_reset) {
214 audio_debug("MilesAudioManager::skipping force_midi_reset");
215 return;
216 }
217 audio_debug("MilesAudioManager::force_midi_reset");
218
219#ifdef WIN32
220 if ((_midi_driver!=nullptr) && (_midi_driver->deviceid != MIDI_nullptr_DRIVER) && (_midi_driver->hMidiOut != nullptr)) {
221 audio_debug("MilesAudioManager::calling midiOutReset");
222 midiOutReset(_midi_driver->hMidiOut);
223 }
224#endif
225}
226
227/**
228 * Returns the pointer to the one GlobalMilesManager object.
229 */
230GlobalMilesManager *GlobalMilesManager::
231get_global_ptr() {
232 if (_global_ptr == nullptr) {
233 _global_ptr = new GlobalMilesManager;
234 }
235 return _global_ptr;
236}
237
238/**
239 * Called internally to initialize the Miles API.
240 */
241void GlobalMilesManager::
242open_api() {
243 audio_debug("GlobalMilesManager::open_api()")
244 nassertv(!_is_open);
245
246 bool use_digital = (audio_play_wave || audio_play_mp3);
247 if (audio_play_midi && audio_software_midi) {
248 use_digital = true;
249 }
250
251#ifdef IS_OSX
252 audio_software_midi = true;
253#endif
254
255 audio_debug(" use_digital="<<use_digital);
256 audio_debug(" audio_play_midi="<<audio_play_midi);
257 audio_debug(" audio_software_midi="<<audio_software_midi);
258 audio_debug(" audio_output_rate="<<audio_output_rate);
259 audio_debug(" audio_output_bits="<<audio_output_bits);
260 audio_debug(" audio_output_channels="<<audio_output_channels);
261 audio_debug(" audio_software_midi="<<audio_software_midi);
262
263#if !defined(NDEBUG) && defined(AIL_MSS_version) //[
264 char version[8];
265 AIL_MSS_version(version, 8);
266 audio_debug(" Mss32.dll Version: "<<version);
267#endif //]
268
269 if (!AIL_startup()) {
270 milesAudio_cat.warning()
271 << "Miles Sound System already initialized!\n";
272 }
273
274 AIL_set_file_callbacks(open_callback, close_callback,
275 seek_callback, read_callback);
276
277 if (use_digital) {
278 _digital_driver =
279 AIL_open_digital_driver(audio_output_rate, audio_output_bits,
280 audio_output_channels, 0);
281 }
282
283 if (audio_play_midi) {
284 if (audio_software_midi) {
285 _midi_driver = AIL_open_XMIDI_driver(AIL_OPEN_XMIDI_NULL_DRIVER);
286
287 // Load the downloadable sounds file:
288 _dls_device = AIL_DLS_open(_midi_driver, _digital_driver, nullptr, 0,
289 audio_output_rate, audio_output_bits,
290 audio_output_channels);
291
293
295 vfs->resolve_filename(dls_pathname, get_model_path());
296
297 _dls_data.clear();
298 PT(VirtualFile) file = vfs->get_file(dls_pathname);
299 if (file == nullptr) {
300 milesAudio_cat.warning()
301 << "DLS file does not exist: " << dls_pathname << "\n";
302
303 } else if (!file->read_file(_dls_data, true)) {
304 milesAudio_cat.warning()
305 << "Could not read DLS file: " << dls_pathname << "\n";
306
307 } else if (_dls_data.empty()) {
308 milesAudio_cat.warning()
309 << "DLS file is empty: " << dls_pathname << "\n";
310
311 } else {
312 _dls_file = AIL_DLS_load_memory(_dls_device, &_dls_data[0], 0);
313 }
314
315 if (_dls_file == 0) {
316 audio_error(" Could not get DLS file, switching to hardware MIDI.");
317 AIL_DLS_close(_dls_device, 0);
318 _dls_device = 0;
319 AIL_close_XMIDI_driver(_midi_driver);
320 _midi_driver = AIL_open_XMIDI_driver(0);
321
322 } else {
323 audio_info(" using Miles software midi");
324 }
325 } else {
326 _midi_driver = AIL_open_XMIDI_driver(0);
327 audio_info(" using Miles hardware midi");
328 }
329 }
330
331 _is_open = true;
332}
333
334/**
335 * Called internally to shut down the Miles API.
336 */
337void GlobalMilesManager::
338close_api() {
339 audio_debug("GlobalMilesManager::close_api()")
340 nassertv(_is_open);
341
342 Samples::iterator si;
343 for (si = _samples.begin(); si != _samples.end(); ++si) {
344 SampleData &smp = (*si);
345 AIL_release_sample_handle(smp._sample);
346 }
347 _samples.clear();
348
349 Sequences::iterator qi;
350 for (qi = _sequences.begin(); qi != _sequences.end(); ++qi) {
351 SequenceData &smp = (*qi);
352 AIL_release_sequence_handle(smp._sequence);
353 }
354 _sequences.clear();
355
356 if (_dls_file != 0) {
357 AIL_DLS_unload(_dls_device, _dls_file);
358 _dls_file = 0;
359 }
360
361 if (_dls_device != 0) {
362 AIL_DLS_close(_dls_device, 0);
363 _dls_device = 0;
364 }
365
366 if (_midi_driver != 0) {
367 AIL_close_XMIDI_driver(_midi_driver);
368 _midi_driver = 0;
369 }
370
371 if (_digital_driver != 0) {
372 AIL_close_digital_driver(_digital_driver);
373 _digital_driver = 0;
374 }
375
376 AIL_shutdown();
377
378 _is_open = false;
379}
380
381/**
382 * This callback function is given to Miles to handle file I/O via the Panda
383 * VFS. It's only used to implemented streaming audio files, since in all
384 * other cases we open files directly.
385 */
386U32 AILCALLBACK GlobalMilesManager::
387open_callback(char const *filename, UINTa *file_handle) {
389 istream *strm = vfs->open_read_file(Filename::binary_filename(string(filename)), true);
390 if (strm == nullptr) {
391 // Failure.
392 return 0;
393 }
394 // Success.
395 (*file_handle) = (UINTa)strm;
396 return 1;
397}
398
399/**
400 * This callback function is given to Miles to handle file I/O via the Panda
401 * VFS.
402 */
403void AILCALLBACK GlobalMilesManager::
404close_callback(UINTa file_handle) {
405 istream *strm = (istream *)file_handle;
407 vfs->close_read_file(strm);
408}
409
410/**
411 * This callback function is given to Miles to handle file I/O via the Panda
412 * VFS.
413 */
414S32 AILCALLBACK GlobalMilesManager::
415seek_callback(UINTa file_handle, S32 offset, U32 type) {
416 istream *strm = (istream *)file_handle;
417 strm->clear();
418 switch (type) {
419 case AIL_FILE_SEEK_BEGIN:
420 strm->seekg(offset, std::ios::beg);
421 break;
422
423 case AIL_FILE_SEEK_CURRENT:
424 strm->seekg(offset, std::ios::cur);
425 break;
426
427 case AIL_FILE_SEEK_END:
428 strm->seekg(offset, std::ios::end);
429 break;
430 }
431
432 return strm->tellg();
433}
434
435/**
436 * This callback function is given to Miles to handle file I/O via the Panda
437 * VFS.
438 */
439U32 AILCALLBACK GlobalMilesManager::
440read_callback(UINTa file_handle, void *buffer, U32 bytes) {
441 istream *strm = (istream *)file_handle;
442 strm->read((char *)buffer, bytes);
443 return strm->gcount();
444}
445
446#endif //]
get_dls_pathname
Returns the full pathname to the DLS file, as specified by the Config.prc file, or the default for th...
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
Similar to MutexHolder, but for a light mutex.
A hierarchy of directories and files that appears to be one continuous file system,...
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
std::istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read,...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
The abstract base class for a file or directory within the VirtualFileSystem.
Definition virtualFile.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.