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