Fixed a stupid bug when exporting meshes with empty material slots.
[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 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 #ifdef AL_VERSION_1_1
235                 alcMakeContextCurrent((ALCcontext*)m_context);
236 #else
237                         alcMakeContextCurrent(m_context);
238 #endif
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 #ifdef AL_VERSION_1_1
347                 alcDestroyContext((ALCcontext*)m_context);
348 #else
349                 alcDestroyContext(m_context);
350 #endif
351                 m_context = NULL;
352         }
353         
354 #ifdef __linux__
355         // restore the signal state above.
356         signal(SIGHUP, SIG_DFL);
357 #endif  
358         // let's see if we used the cd. if not, just leave it alone
359         SND_CDObject* pCD = SND_CDObject::Instance();
360         
361         if (pCD)
362         {
363                 this->StopCD();
364                 SND_CDObject::DisposeSystem();
365         }
366 #ifndef __APPLE__
367         if (m_cdrom)
368                 delete m_cdrom;
369 #endif
370 #ifdef OUDE_OPENAL
371         if (m_audio)
372                 alutExit();
373 #else
374         if (m_device)
375                 alcCloseDevice((ALCdevice*) m_device);
376 #endif
377 }
378
379
380
381 SND_WaveSlot* SND_OpenALDevice::LoadSample(const STR_String& name,
382                                                                                   void* memlocation,
383                                                                                   int size)
384 {
385         SND_WaveSlot* waveslot = NULL;
386         STR_String samplename = name;
387         
388         if (m_audio)
389         {
390                 /* create the waveslot */
391                 waveslot = m_wavecache->GetWaveSlot(samplename);
392
393                 /* do we support this sample? */
394                 if (SND_IsSampleValid(name, memlocation))
395                 {
396                         if (waveslot)
397                         {
398                                 int buffer = waveslot->GetBuffer();
399                                 void* data = NULL;
400 #ifndef __APPLE__
401                                 char loop = 'a';
402 #endif
403                                 int sampleformat, bitrate, numberofchannels;
404                                 ALenum al_error = alGetError();
405                                 
406 #ifdef OUDE_OPENAL
407                                 unsigned int samplerate, numberofsamples;               // openal_2.12
408 #else
409                                 int samplerate, numberofsamples;  // openal_2.14+
410 #endif
411                                 
412                                 /* Give them some safe defaults just incase */
413                                 bitrate = numberofchannels = 0;
414
415                                 /* load the sample from memory? */
416                                 if (size && memlocation)
417                                 {
418                                         waveslot->SetFileSize(size);
419                                         
420                                         /* what was (our) buffer? */
421                                         int buffer = waveslot->GetBuffer();
422                                         
423                                         /* get some info out of the sample */
424                                         SND_GetSampleInfo((signed char*)memlocation, waveslot);
425                                         numberofchannels = SND_GetNumberOfChannels(memlocation);
426                                         bitrate = SND_GetBitRate(memlocation);
427                                         
428                                         /* load the sample into openal */
429 #if defined(OUDE_OPENAL) || defined (__APPLE__)
430                                         alutLoadWAVMemory((char*)memlocation, &sampleformat, &data, &numberofsamples, &samplerate);                             //      openal_2.12
431 #else
432 #ifdef AL_VERSION_1_1                                   
433                                         alutLoadWAVMemory((ALbyte*)memlocation, &sampleformat, &data, &numberofsamples, &samplerate, &loop);//  openal_2.14+
434 #else
435                                          alutLoadWAVMemory((signed char*)memlocation, &sampleformat, &data, &numberofsamples, &samplerate, &loop);//  openal_2.14+
436                                          
437 #endif 
438 #endif
439                                         /* put it in the buffer */
440                                         alBufferData(m_buffers[buffer], sampleformat, data, numberofsamples, samplerate);
441                                 }
442                                 /* or from file? */
443                                 else
444                                 {
445 #ifdef __APPLE__
446                                         alutLoadWAVFile((ALbyte *)samplename.Ptr(), &sampleformat, &data, &numberofsamples, &samplerate);
447 #else
448                                         alutLoadWAVFile((ALbyte *)samplename.Ptr(), &sampleformat, &data, &numberofsamples, &samplerate, &loop);
449 #endif
450                                         /* put it in the buffer */
451                                         alBufferData(m_buffers[buffer], sampleformat, data, numberofsamples, samplerate);
452                                 }
453                                 
454                                 /* fill the waveslot with info */
455                                 al_error = alGetError();
456                                 if (al_error == AL_NO_ERROR && m_buffers[buffer])
457                                 {
458                                         waveslot->SetData(data);
459                                         waveslot->SetSampleFormat(sampleformat);
460                                         waveslot->SetNumberOfChannels(numberofchannels);
461                                         waveslot->SetSampleRate(samplerate);
462                                         waveslot->SetBitRate(bitrate);
463                                         waveslot->SetNumberOfSamples(numberofsamples);
464                                         
465                                         /* if the loading succeeded, mark the waveslot */
466                                         waveslot->SetLoaded(true);
467                                 }
468                                 else
469                                 {
470                                         /* or when it failed, free the waveslot */
471                                         m_wavecache->RemoveSample(waveslot->GetSampleName(), waveslot->GetBuffer());
472                                         waveslot = NULL;
473                                 }
474                                 
475                                 /* and free the original stuff (copy was made in openal) */
476                                 alutUnloadWAV(sampleformat, data, numberofsamples, samplerate);
477                         }
478                 }
479                 else
480                 {
481                         /* sample not supported, remove waveslot */
482                         m_wavecache->RemoveSample(waveslot->GetSampleName(), waveslot->GetBuffer());
483                         waveslot = NULL;
484                 }
485         }
486         return waveslot;
487 }
488
489
490
491 // listener's and general stuff //////////////////////////////////////////////////////
492
493
494
495 /* sets the global dopplervelocity */
496 void SND_OpenALDevice::SetDopplerVelocity(MT_Scalar dopplervelocity) const
497 {
498         alDopplerVelocity ((float)dopplervelocity);
499 }
500
501
502
503 /* sets the global dopplerfactor */
504 void SND_OpenALDevice::SetDopplerFactor(MT_Scalar dopplerfactor) const
505 {
506         alDopplerFactor ((float)dopplerfactor);
507 }
508
509
510
511 /* sets the global rolloff factor */
512 void SND_OpenALDevice::SetListenerRollOffFactor(MT_Scalar rollofffactor) const
513 {
514         // not implemented in openal
515 }
516
517
518
519 void SND_OpenALDevice::NextFrame() const
520 {
521         // CD
522 #ifndef __APPLE__
523         m_cdrom->NextFrame();
524 #endif
525         // not needed by openal
526 }
527
528
529
530 // set the gain for the listener
531 void SND_OpenALDevice::SetListenerGain(float gain) const
532 {
533         alListenerf (AL_GAIN, gain);
534 }
535
536
537
538 void SND_OpenALDevice::InitListener()
539 {
540         // initialize the listener with these values that won't change
541         // (as long as we can have only one listener)
542         // now we can superimpose all listeners on each other (for they
543         // have the same settings)
544         float lispos[3] = {0,0,0};
545         float lisvel[3] = {0,0,0};
546 #ifdef WIN32
547         float lisori[6] = {0,1,0,0,0,1};
548 #else
549         float lisori[6] = {0,0,1,0,-1,0};
550 #endif
551
552         alListenerfv(AL_POSITION, lispos);
553         alListenerfv(AL_VELOCITY, lisvel);
554         alListenerfv(AL_ORIENTATION, lisori);
555 }
556
557
558
559 // source playstate stuff ////////////////////////////////////////////////////////////
560
561
562
563 /* sets the buffer */
564 void SND_OpenALDevice::SetObjectBuffer(int id, unsigned int buffer)
565 {
566         alSourcei (m_sources[id], AL_BUFFER, m_buffers[buffer]);
567 }
568
569
570
571 // check if the sound's still playing
572 int SND_OpenALDevice::GetPlayState(int id)
573 {
574     int alstate = 0;
575         int result = 0;
576
577 #ifdef __APPLE__
578         alGetSourcei(m_sources[id], AL_SOURCE_STATE, &alstate);
579 #else
580     alGetSourceiv(m_sources[id], AL_SOURCE_STATE, &alstate);
581 #endif
582         
583         switch(alstate)
584         {
585         case AL_INITIAL:
586                 {
587                         result = SND_INITIAL;
588                         break;
589                 }
590         case AL_PLAYING:
591                 {
592                         result = SND_PLAYING;
593                         break;
594                 }
595         case AL_PAUSED:
596                 {
597                         result = SND_PAUSED;
598                         break;
599                 }
600         case AL_STOPPED:
601                 {
602                         result = SND_STOPPED;
603                         break;
604                 }
605         default:
606                 result = SND_UNKNOWN;
607         }
608
609     return result;
610 }
611
612
613
614 // make the source play
615 void SND_OpenALDevice::PlayObject(int id)
616 {
617         alSourcePlay(m_sources[id]);
618 }
619
620
621
622 // make the source stop
623 void SND_OpenALDevice::StopObject(int id) const
624 {
625         float obpos[3] = {0,0,0};
626         float obvel[3] = {0,0,0};
627
628         alSourcefv(m_sources[id], AL_POSITION, obpos);
629         alSourcefv(m_sources[id], AL_VELOCITY, obvel);
630
631         alSourcef(m_sources[id], AL_GAIN, 1.0);
632         alSourcef(m_sources[id], AL_PITCH, 1.0);
633         alSourcei(m_sources[id], AL_LOOPING, AL_FALSE);
634         alSourceStop(m_sources[id]);
635 }
636
637
638
639 // stop all sources
640 void SND_OpenALDevice::StopAllObjects()
641 {
642         alSourceStopv(NUM_SOURCES, m_sources);
643 }
644
645
646
647 // pause the source
648 void SND_OpenALDevice::PauseObject(int id) const
649 {
650         alSourcePause(m_sources[id]);
651 }
652
653
654
655 // source properties stuff ////////////////////////////////////////////////////////////
656
657
658
659 // give openal the object's pitch
660 void SND_OpenALDevice::SetObjectPitch(int id, MT_Scalar pitch) const
661 {
662         alSourcef (m_sources[id], AL_PITCH, (float)pitch);
663 }
664
665
666
667 // give openal the object's gain
668 void SND_OpenALDevice::SetObjectGain(int id, MT_Scalar gain) const
669 {
670         alSourcef (m_sources[id], AL_GAIN, (float)gain);
671 }
672
673
674
675 // give openal the object's looping
676 void SND_OpenALDevice::SetObjectLoop(int id, unsigned int loopmode) const
677 {
678         if (loopmode == SND_LOOP_OFF)
679         {
680                 //printf("%d - ", id);
681                 alSourcei (m_sources[id], AL_LOOPING, AL_FALSE);
682         }
683         else
684                 alSourcei (m_sources[id], AL_LOOPING, AL_TRUE);
685 }
686
687 void SND_OpenALDevice::SetObjectLoopPoints(int id, unsigned int loopstart, unsigned int loopend) const
688 {
689
690
691 }
692
693
694 void SND_OpenALDevice::SetObjectMinGain(int id, MT_Scalar mingain) const
695 {
696         alSourcef (m_sources[id], AL_MIN_GAIN, (float)mingain);
697 }
698
699
700
701 void SND_OpenALDevice::SetObjectMaxGain(int id, MT_Scalar maxgain) const
702 {
703         alSourcef (m_sources[id], AL_MAX_GAIN, (float)maxgain);
704 }
705
706
707
708 void SND_OpenALDevice::SetObjectRollOffFactor(int id, MT_Scalar rollofffactor) const
709 {
710         alSourcef (m_sources[id], AL_ROLLOFF_FACTOR, (float)rollofffactor);
711 }
712
713
714
715 void SND_OpenALDevice::SetObjectReferenceDistance(int id, MT_Scalar referencedistance) const
716 {
717         alSourcef (m_sources[id], AL_REFERENCE_DISTANCE, (float)referencedistance);
718 }
719
720
721
722 // give openal the object's position
723 void SND_OpenALDevice::ObjectIs2D(int id) const
724 {
725         float obpos[3] = {0,0,0};
726         float obvel[3] = {0,0,0};
727         
728         alSourcefv(m_sources[id], AL_POSITION, obpos);
729         alSourcefv(m_sources[id], AL_VELOCITY, obvel);
730 }
731
732
733
734 void SND_OpenALDevice::SetObjectTransform(int id,
735                                                                                   const MT_Vector3& position,
736                                                                                   const MT_Vector3& velocity,
737                                                                                   const MT_Matrix3x3& orientation,
738                                                                                   const MT_Vector3& lisposition,
739                                                                                   const MT_Scalar& rollofffactor) const 
740 {
741         float obpos[3];
742         float obvel[3];
743
744         obpos[0] = (float)position[0] * (float)rollofffactor;   //x (l/r)
745         obpos[1] = (float)position[1] * (float)rollofffactor;
746         obpos[2] = (float)position[2] * (float)rollofffactor;
747
748         alSourcefv(m_sources[id], AL_POSITION, obpos);
749
750         velocity.getValue(obvel);
751         alSourcefv(m_sources[id], AL_VELOCITY, obvel);
752 }
753
754 void SND_OpenALDevice::PlayCD(int track) const
755 {
756 #ifndef __APPLE__
757         m_cdrom->PlayCD(track);
758 #endif
759 }
760
761
762 void SND_OpenALDevice::PauseCD(bool pause) const
763 {
764 #ifndef __APPLE__
765         m_cdrom->PauseCD(pause);
766 #endif
767 }
768
769 void SND_OpenALDevice::StopCD() const
770 {
771 #ifndef __APPLE__
772         SND_CDObject* pCD = SND_CDObject::Instance();
773
774         if (pCD && pCD->GetUsed())
775         {
776                 m_cdrom->StopCD();
777         }
778 #endif
779 }
780
781 void SND_OpenALDevice::SetCDPlaymode(int playmode) const
782 {
783 #ifndef __APPLE__
784         m_cdrom->SetCDPlaymode(playmode);
785 #endif
786 }
787
788 void SND_OpenALDevice::SetCDGain(MT_Scalar gain) const
789 {
790 #ifndef __APPLE__
791         m_cdrom->SetCDGain(gain);
792 #endif
793 }