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