66950e6ef928e8febd685a796e38a52322a3bca0
[blender.git] / intern / SoundSystem / openal / SND_OpenALDevice.cpp
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  * SND_OpenALDevice derived from SND_IAudioDevice
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #ifdef WIN32
39 #pragma warning (disable:4786) // get rid of stupid stl-visual compiler debug warning
40 #endif //WIN32
41
42 #include "SND_OpenALDevice.h"
43 #ifndef __APPLE__
44 #include "SND_SDLCDDevice.h"
45 #endif
46 #include "SoundDefines.h"
47
48 #include "SND_Utils.h"
49
50 #include <AL/al.h>
51 #include <AL/alc.h>
52 #include <AL/alut.h>
53
54 #include <stdio.h>
55 #include <stdlib.h>
56 #if defined(WIN32)
57 #include <io.h>
58 #else
59 #include <unistd.h>
60 #endif
61 #include <fcntl.h>
62
63 #include <signal.h>
64
65 /* untill openal gets unified we need this hack for non-windows systems */
66 #if !defined(WIN32) && !defined(ALC_MAJOR_VERSION)
67
68 #include <malloc.h>
69
70 ALvoid alutLoadWAVMemory(ALbyte *memory,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq,ALboolean *loop);
71 ALvoid alutUnloadWAV(ALenum format,ALvoid *data,ALsizei size,ALsizei freq);
72
73 typedef struct                                  /* WAV File-header */
74 {
75   ALubyte  Id[4];
76   ALsizei  Size;
77   ALubyte  Type[4];
78 } WAVFileHdr_Struct;
79
80 typedef struct                                  /* WAV Fmt-header */
81 {
82   ALushort Format;                              
83   ALushort Channels;
84   ALuint   SamplesPerSec;
85   ALuint   BytesPerSec;
86   ALushort BlockAlign;
87   ALushort BitsPerSample;
88 } WAVFmtHdr_Struct;
89
90 typedef struct                                                                  /* WAV FmtEx-header */
91 {
92   ALushort Size;
93   ALushort SamplesPerBlock;
94 } WAVFmtExHdr_Struct;
95
96 typedef struct                                  /* WAV Smpl-header */
97 {
98   ALuint   Manufacturer;
99   ALuint   Product;
100   ALuint   SamplePeriod;                          
101   ALuint   Note;                                  
102   ALuint   FineTune;                              
103   ALuint   SMPTEFormat;
104   ALuint   SMPTEOffest;
105   ALuint   Loops;
106   ALuint   SamplerData;
107   struct
108   {
109     ALuint Identifier;
110     ALuint Type;
111     ALuint Start;
112     ALuint End;
113     ALuint Fraction;
114     ALuint Count;
115   }      Loop[1];
116 } WAVSmplHdr_Struct;
117
118 typedef struct                                  /* WAV Chunk-header */
119 {
120   ALubyte  Id[4];
121   ALuint   Size;
122 } WAVChunkHdr_Struct;
123
124 ALvoid alutLoadWAVMemory(ALbyte *memory,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq,ALboolean *loop)
125 {
126         WAVChunkHdr_Struct ChunkHdr;
127         WAVFmtExHdr_Struct FmtExHdr;
128         WAVFileHdr_Struct FileHdr;
129         WAVSmplHdr_Struct SmplHdr;
130         WAVFmtHdr_Struct FmtHdr;
131         ALbyte *Stream;
132         
133         *format=AL_FORMAT_MONO16;
134         *data=NULL;
135         *size=0;
136         *freq=22050;
137         *loop=AL_FALSE;
138         if (memory)
139         {
140                 Stream=memory;
141                 if (Stream)
142                 {
143                         memcpy(&FileHdr,Stream,sizeof(WAVFileHdr_Struct));
144                         Stream+=sizeof(WAVFileHdr_Struct);
145                         FileHdr.Size=((FileHdr.Size+1)&~1)-4;
146                         while ((FileHdr.Size!=0)&&(memcpy(&ChunkHdr,Stream,sizeof(WAVChunkHdr_Struct))))
147                         {
148                                 Stream+=sizeof(WAVChunkHdr_Struct);
149                                 if (!memcmp(ChunkHdr.Id,"fmt ",4))
150                                 {
151                                         memcpy(&FmtHdr,Stream,sizeof(WAVFmtHdr_Struct));
152                                         if (FmtHdr.Format==0x0001)
153                                         {
154                                                 *format=(FmtHdr.Channels==1?
155                                                                 (FmtHdr.BitsPerSample==8?AL_FORMAT_MONO8:AL_FORMAT_MONO16):
156                                                                 (FmtHdr.BitsPerSample==8?AL_FORMAT_STEREO8:AL_FORMAT_STEREO16));
157                                                 *freq=FmtHdr.SamplesPerSec;
158                                                 Stream+=ChunkHdr.Size;
159                                         } 
160                                         else
161                                         {
162                                                 memcpy(&FmtExHdr,Stream,sizeof(WAVFmtExHdr_Struct));
163                                                 Stream+=ChunkHdr.Size;
164                                         }
165                                 }
166                                 else if (!memcmp(ChunkHdr.Id,"data",4))
167                                 {
168                                         if (FmtHdr.Format==0x0001)
169                                         {
170                                                 *size=ChunkHdr.Size;
171                                                 *data=malloc(ChunkHdr.Size+31);
172                                                 if (*data) memcpy(*data,Stream,ChunkHdr.Size);
173                                                 memset(((char *)*data)+ChunkHdr.Size,0,31);
174                                                 Stream+=ChunkHdr.Size;
175                                         }
176                                         else if (FmtHdr.Format==0x0011)
177                                         {
178                                                 //IMA ADPCM
179                                         }
180                                         else if (FmtHdr.Format==0x0055)
181                                         {
182                                                 //MP3 WAVE
183                                         }
184                                 }
185                                 else if (!memcmp(ChunkHdr.Id,"smpl",4))
186                                 {
187                                         memcpy(&SmplHdr,Stream,sizeof(WAVSmplHdr_Struct));
188                                         *loop = (SmplHdr.Loops ? AL_TRUE : AL_FALSE);
189                                         Stream+=ChunkHdr.Size;
190                                 }
191                                 else Stream+=ChunkHdr.Size;
192                                 Stream+=ChunkHdr.Size&1;
193                                 FileHdr.Size-=(((ChunkHdr.Size+1)&~1)+8);
194                         }
195                 }
196         }
197 }
198
199 ALvoid alutUnloadWAV(ALenum format,ALvoid *data,ALsizei size,ALsizei freq)
200 {
201         if (data)
202                 free(data);
203 }
204
205 #endif /* WIN32 */
206
207 #ifdef __APPLE__
208 #define OUDE_OPENAL 1
209 #endif
210
211
212 SND_OpenALDevice::SND_OpenALDevice()
213         : SND_AudioDevice(),
214           m_context(NULL),
215           m_device(NULL)
216 {
217     /* Removed the functionality for checking if noaudio was provided on */
218     /* the commandline. */
219         m_audio = true;
220         m_context = NULL;
221         m_buffersinitialized = false;
222         m_sourcesinitialized = false;
223
224         // let's check if we can get openal to initialize...
225         if (m_audio)
226         {
227 #ifdef OUDE_OPENAL
228                 m_audio = true;                 // openal_2.12
229                 alutInit(NULL, NULL);   // openal_2.12
230 #else
231                 m_audio = false;
232
233                 ALCdevice *dev = alcOpenDevice(NULL);
234                 if (dev) {
235                         m_context = alcCreateContext(dev, NULL);
236
237                         if (m_context) {
238                                 alcMakeContextCurrent(m_context);
239                                 m_audio = true;
240                                 m_device = dev;
241 #ifdef __linux__
242                                 /*
243                                 *   SIGHUP Hack:
244                                 *
245                                 *   On Linux, alcDestroyContext generates a SIGHUP (Hangup) when killing the OpenAL
246                                 *   mixer thread, which kills Blender.
247                                 *  
248                                 *   So we set the signal to ignore....
249                                 *
250                                 *   TODO: check if this applies to other platforms.
251                                 *
252                                 */
253                                 signal(SIGHUP, SIG_IGN);
254 #endif
255                         }
256                 }
257
258 #endif
259         }
260
261         // then try to generate some buffers
262         if (m_audio)
263         {
264                 // let openal generate its buffers
265                 alGenBuffers(NUM_BUFFERS, m_buffers);
266                 m_buffersinitialized = true;
267                 
268                 for (int i = 0; i < NUM_BUFFERS; i++)
269                 {
270                         if (!alIsBuffer(m_buffers[i]))
271                         {
272                                 //printf("\n\n  WARNING: OpenAL returned with an error. Continuing without audio.\n\n");
273                                 m_audio = false;
274                                 break;
275                         }
276                 }
277         }
278
279         // next: the sources
280         if (m_audio)
281         {
282 #ifdef OUDE_OPENAL
283                 ALenum alc_error = ALC_NO_ERROR;        // openal_2.12
284 #elif defined(_WIN32)
285                 // alcGetError has no arguments on windows
286                 ALenum alc_error = alcGetError();       // openal_2.14+
287 #else
288                 ALenum alc_error = alcGetError(NULL);   // openal_2.14+
289 #endif
290
291                 // let openal generate its sources
292                 if (alc_error == ALC_NO_ERROR)
293                 {
294                         alGenSources(NUM_SOURCES, m_sources);
295                         m_sourcesinitialized = true;
296                 }
297         }
298
299         // let's get us a wavecache
300         if (m_audio)
301         {
302                 m_wavecache = new SND_WaveCache();
303         }
304 #ifndef __APPLE__       
305         m_cdrom = new SND_SDLCDDevice();
306 #endif
307 }
308
309 void SND_OpenALDevice::UseCD(void) const
310 {
311         // only fmod has CD support, so only create it here
312         SND_CDObject::CreateSystem();
313
314 }
315
316 void SND_OpenALDevice::MakeCurrent() const
317 {
318 #ifdef WIN32
319         alcMakeContextCurrent(m_context);
320 #endif
321 }
322
323
324
325 SND_OpenALDevice::~SND_OpenALDevice()
326 {
327         MakeCurrent();
328         
329         if (m_buffersinitialized)
330         {
331                 alDeleteBuffers(NUM_BUFFERS, m_buffers);
332                 m_buffersinitialized = false;
333         }
334         
335         if (m_sourcesinitialized)
336         {
337                 for (int i = 0; i < NUM_SOURCES; i++)
338                         alSourceStop(m_sources[i]);
339                 
340                 alDeleteSources(NUM_SOURCES, m_sources);
341                 m_sourcesinitialized = false;
342         }
343         
344         if (m_context) {
345                 MakeCurrent();
346                 alcDestroyContext(m_context);
347                 m_context = NULL;
348         }
349         
350 #ifdef __linux__
351         // restore the signal state above.
352         signal(SIGHUP, SIG_DFL);
353 #endif  
354         // let's see if we used the cd. if not, just leave it alone
355         SND_CDObject* pCD = SND_CDObject::Instance();
356         
357         if (pCD)
358         {
359                 this->StopCD();
360                 SND_CDObject::DisposeSystem();
361         }
362 #ifndef __APPLE__
363         if (m_cdrom)
364                 delete m_cdrom;
365 #endif
366 #ifdef OUDE_OPENAL
367         if (m_audio)
368                 alutExit();
369 #else
370         if (m_device)
371                 alcCloseDevice((ALCdevice*) m_device);
372 #endif
373 }
374
375
376
377 SND_WaveSlot* SND_OpenALDevice::LoadSample(const STR_String& name,
378                                                                                   void* memlocation,
379                                                                                   int size)
380 {
381         SND_WaveSlot* waveslot = NULL;
382         STR_String samplename = name;
383         
384         if (m_audio)
385         {
386                 /* create the waveslot */
387                 waveslot = m_wavecache->GetWaveSlot(samplename);
388
389                 /* do we support this sample? */
390                 if (SND_IsSampleValid(name, memlocation))
391                 {
392                         if (waveslot)
393                         {
394                                 int buffer = waveslot->GetBuffer();
395                                 void* data = NULL;
396 #ifndef __APPLE__
397                                 char loop = 'a';
398 #endif
399                                 int sampleformat, bitrate, numberofchannels;
400                                 ALenum al_error = alGetError();
401                                 
402 #ifdef OUDE_OPENAL
403                                 unsigned int samplerate, numberofsamples;               // openal_2.12
404 #else
405                                 int samplerate, numberofsamples, frequency;  // openal_2.14+
406 #endif
407                                 
408                                 /* Give them some safe defaults just incase */
409                                 bitrate = numberofchannels = 0;
410
411                                 /* load the sample from memory? */
412                                 if (size && memlocation)
413                                 {
414                                         waveslot->SetFileSize(size);
415                                         
416                                         /* what was (our) buffer? */
417                                         int buffer = waveslot->GetBuffer();
418                                         
419                                         /* get some info out of the sample */
420                                         SND_GetSampleInfo((signed char*)memlocation, waveslot);
421                                         numberofchannels = SND_GetNumberOfChannels(memlocation);
422                                         bitrate = SND_GetBitRate(memlocation);
423                                         
424                                         /* load the sample into openal */
425 #if defined(OUDE_OPENAL) || defined (__APPLE__)
426                                         alutLoadWAVMemory((char*)memlocation, &sampleformat, &data, &numberofsamples, &samplerate);                             //      openal_2.12
427 #else
428                                         alutLoadWAVMemory((signed char*)memlocation, &sampleformat, &data, &numberofsamples, &samplerate, &loop);//     openal_2.14+
429 #endif
430                                         /* put it in the buffer */
431                                         alBufferData(m_buffers[buffer], sampleformat, data, numberofsamples, samplerate);
432                                 }
433                                 /* or from file? */
434                                 else
435                                 {
436 #ifdef __APPLE__
437                                         alutLoadWAVFile((signed char*)samplename.Ptr(), &sampleformat, &data, &numberofsamples, &samplerate);
438 #else
439                                         alutLoadWAVFile((signed char*)samplename.Ptr(), &sampleformat, &data, &numberofsamples, &samplerate, &loop);
440 #endif
441                                         /* put it in the buffer */
442                                         alBufferData(m_buffers[buffer], sampleformat, data, numberofsamples, samplerate);
443                                 }
444                                 
445                                 /* fill the waveslot with info */
446                                 al_error = alGetError();
447                                 if (al_error == AL_NO_ERROR && m_buffers[buffer])
448                                 {
449                                         waveslot->SetData(data);
450                                         waveslot->SetSampleFormat(sampleformat);
451                                         waveslot->SetNumberOfChannels(numberofchannels);
452                                         waveslot->SetSampleRate(samplerate);
453                                         waveslot->SetBitRate(bitrate);
454                                         waveslot->SetNumberOfSamples(numberofsamples);
455                                         
456                                         /* if the loading succeeded, mark the waveslot */
457                                         waveslot->SetLoaded(true);
458                                 }
459                                 else
460                                 {
461                                         /* or when it failed, free the waveslot */
462                                         m_wavecache->RemoveSample(waveslot->GetSampleName(), waveslot->GetBuffer());
463                                         waveslot = NULL;
464                                 }
465                                 
466                                 /* and free the original stuff (copy was made in openal) */
467                                 alutUnloadWAV(sampleformat, data, numberofsamples, samplerate);
468                         }
469                 }
470                 else
471                 {
472                         /* sample not supported, remove waveslot */
473                         m_wavecache->RemoveSample(waveslot->GetSampleName(), waveslot->GetBuffer());
474                         waveslot = NULL;
475                 }
476         }
477         return waveslot;
478 }
479
480
481
482 // listener's and general stuff //////////////////////////////////////////////////////
483
484
485
486 /* sets the global dopplervelocity */
487 void SND_OpenALDevice::SetDopplerVelocity(MT_Scalar dopplervelocity) const
488 {
489         alDopplerVelocity ((float)dopplervelocity);
490 }
491
492
493
494 /* sets the global dopplerfactor */
495 void SND_OpenALDevice::SetDopplerFactor(MT_Scalar dopplerfactor) const
496 {
497         alDopplerFactor ((float)dopplerfactor);
498 }
499
500
501
502 /* sets the global rolloff factor */
503 void SND_OpenALDevice::SetListenerRollOffFactor(MT_Scalar rollofffactor) const
504 {
505         // not implemented in openal
506 }
507
508
509
510 void SND_OpenALDevice::NextFrame() const
511 {
512         // CD
513 #ifndef __APPLE__
514         m_cdrom->NextFrame();
515 #endif
516         // not needed by openal
517 }
518
519
520
521 // set the gain for the listener
522 void SND_OpenALDevice::SetListenerGain(float gain) const
523 {
524         alListenerf (AL_GAIN, gain);
525 }
526
527
528
529 void SND_OpenALDevice::InitListener()
530 {
531         // initialize the listener with these values that won't change
532         // (as long as we can have only one listener)
533         // now we can superimpose all listeners on each other (for they
534         // have the same settings)
535         float lispos[3] = {0,0,0};
536         float lisvel[3] = {0,0,0};
537 #ifdef WIN32
538         float lisori[6] = {0,1,0,0,0,1};
539 #else
540         float lisori[6] = {0,0,1,0,-1,0};
541 #endif
542
543         alListenerfv(AL_POSITION, lispos);
544         alListenerfv(AL_VELOCITY, lisvel);
545         alListenerfv(AL_ORIENTATION, lisori);
546 }
547
548
549
550 // source playstate stuff ////////////////////////////////////////////////////////////
551
552
553
554 /* sets the buffer */
555 void SND_OpenALDevice::SetObjectBuffer(int id, unsigned int buffer)
556 {
557         alSourcei (m_sources[id], AL_BUFFER, m_buffers[buffer]);
558 }
559
560
561
562 // check if the sound's still playing
563 int SND_OpenALDevice::GetPlayState(int id)
564 {
565     int alstate = 0;
566         int result = 0;
567
568 #ifdef __APPLE__
569         alGetSourcei(m_sources[id], AL_SOURCE_STATE, &alstate);
570 #else
571     alGetSourceiv(m_sources[id], AL_SOURCE_STATE, &alstate);
572 #endif
573         
574         switch(alstate)
575         {
576         case AL_INITIAL:
577                 {
578                         result = SND_INITIAL;
579                         break;
580                 }
581         case AL_PLAYING:
582                 {
583                         result = SND_PLAYING;
584                         break;
585                 }
586         case AL_PAUSED:
587                 {
588                         result = SND_PAUSED;
589                         break;
590                 }
591         case AL_STOPPED:
592                 {
593                         result = SND_STOPPED;
594                         break;
595                 }
596         default:
597                 result = SND_UNKNOWN;
598         }
599
600     return result;
601 }
602
603
604
605 // make the source play
606 void SND_OpenALDevice::PlayObject(int id)
607 {
608         alSourcePlay(m_sources[id]);
609 }
610
611
612
613 // make the source stop
614 void SND_OpenALDevice::StopObject(int id) const
615 {
616         float obpos[3] = {0,0,0};
617         float obvel[3] = {0,0,0};
618
619         alSourcefv(m_sources[id], AL_POSITION, obpos);
620         alSourcefv(m_sources[id], AL_VELOCITY, obvel);
621
622         alSourcef(m_sources[id], AL_GAIN, 1.0);
623         alSourcef(m_sources[id], AL_PITCH, 1.0);
624         alSourcei(m_sources[id], AL_LOOPING, AL_FALSE);
625         alSourceStop(m_sources[id]);
626 }
627
628
629
630 // stop all sources
631 void SND_OpenALDevice::StopAllObjects()
632 {
633         alSourceStopv(NUM_SOURCES, m_sources);
634 }
635
636
637
638 // pause the source
639 void SND_OpenALDevice::PauseObject(int id) const
640 {
641         alSourcePause(m_sources[id]);
642 }
643
644
645
646 // source properties stuff ////////////////////////////////////////////////////////////
647
648
649
650 // give openal the object's pitch
651 void SND_OpenALDevice::SetObjectPitch(int id, MT_Scalar pitch) const
652 {
653         alSourcef (m_sources[id], AL_PITCH, (float)pitch);
654 }
655
656
657
658 // give openal the object's gain
659 void SND_OpenALDevice::SetObjectGain(int id, MT_Scalar gain) const
660 {
661         alSourcef (m_sources[id], AL_GAIN, (float)gain);
662 }
663
664
665
666 // give openal the object's looping
667 void SND_OpenALDevice::SetObjectLoop(int id, unsigned int loopmode) const
668 {
669         if (loopmode == SND_LOOP_OFF)
670         {
671                 //printf("%d - ", id);
672                 alSourcei (m_sources[id], AL_LOOPING, AL_FALSE);
673         }
674         else
675                 alSourcei (m_sources[id], AL_LOOPING, AL_TRUE);
676 }
677
678 void SND_OpenALDevice::SetObjectLoopPoints(int id, unsigned int loopstart, unsigned int loopend) const
679 {
680
681
682 }
683
684
685 void SND_OpenALDevice::SetObjectMinGain(int id, MT_Scalar mingain) const
686 {
687         alSourcef (m_sources[id], AL_MIN_GAIN, (float)mingain);
688 }
689
690
691
692 void SND_OpenALDevice::SetObjectMaxGain(int id, MT_Scalar maxgain) const
693 {
694         alSourcef (m_sources[id], AL_MAX_GAIN, (float)maxgain);
695 }
696
697
698
699 void SND_OpenALDevice::SetObjectRollOffFactor(int id, MT_Scalar rollofffactor) const
700 {
701         alSourcef (m_sources[id], AL_ROLLOFF_FACTOR, (float)rollofffactor);
702 }
703
704
705
706 void SND_OpenALDevice::SetObjectReferenceDistance(int id, MT_Scalar referencedistance) const
707 {
708         alSourcef (m_sources[id], AL_REFERENCE_DISTANCE, (float)referencedistance);
709 }
710
711
712
713 // give openal the object's position
714 void SND_OpenALDevice::ObjectIs2D(int id) const
715 {
716         float obpos[3] = {0,0,0};
717         float obvel[3] = {0,0,0};
718         
719         alSourcefv(m_sources[id], AL_POSITION, obpos);
720         alSourcefv(m_sources[id], AL_VELOCITY, obvel);
721 }
722
723
724
725 void SND_OpenALDevice::SetObjectTransform(int id,
726                                                                                   const MT_Vector3& position,
727                                                                                   const MT_Vector3& velocity,
728                                                                                   const MT_Matrix3x3& orientation,
729                                                                                   const MT_Vector3& lisposition,
730                                                                                   const MT_Scalar& rollofffactor) const 
731 {
732         float obpos[3];
733         float obvel[3];
734
735         obpos[0] = (float)position[0] * (float)rollofffactor;   //x (l/r)
736         obpos[1] = (float)position[1] * (float)rollofffactor;
737         obpos[2] = (float)position[2] * (float)rollofffactor;
738
739         alSourcefv(m_sources[id], AL_POSITION, obpos);
740
741         velocity.getValue(obvel);
742         alSourcefv(m_sources[id], AL_VELOCITY, obvel);
743 }
744
745 void SND_OpenALDevice::PlayCD(int track) const
746 {
747 #ifndef __APPLE__
748         m_cdrom->PlayCD(track);
749 #endif
750 }
751
752
753 void SND_OpenALDevice::PauseCD(bool pause) const
754 {
755 #ifndef __APPLE__
756         m_cdrom->PauseCD(pause);
757 #endif
758 }
759
760 void SND_OpenALDevice::StopCD() const
761 {
762 #ifndef __APPLE__
763         SND_CDObject* pCD = SND_CDObject::Instance();
764
765         if (pCD && pCD->GetUsed())
766         {
767                 m_cdrom->StopCD();
768         }
769 #endif
770 }
771
772 void SND_OpenALDevice::SetCDPlaymode(int playmode) const
773 {
774 #ifndef __APPLE__
775         m_cdrom->SetCDPlaymode(playmode);
776 #endif
777 }
778
779 void SND_OpenALDevice::SetCDGain(MT_Scalar gain) const
780 {
781 #ifndef __APPLE__
782         m_cdrom->SetCDGain(gain);
783 #endif
784 }