6016b1b2355f4fbd0a9ece9cb658990001af3837
[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 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 #include "SND_SDLCDDevice.h"
44 #include "SoundDefines.h"
45
46 #include "SND_Utils.h"
47
48 #include <AL/al.h>
49 #include <AL/alc.h>
50 #include <AL/alut.h>
51
52 #include <stdio.h>
53 #include <stdlib.h>
54 #if defined(WIN32)
55 #include <io.h>
56 #else
57 #include <unistd.h>
58 #endif
59 #include <fcntl.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
204
205 SND_OpenALDevice::SND_OpenALDevice()
206 {
207     /* Removed the functionality for checking if noaudio was provided on */
208     /* the commandline. */
209         m_audio = true;
210         m_context = NULL;
211         m_buffersinitialized = false;
212         m_sourcesinitialized = false;
213
214         // let's check if we can get openal to initialize...
215         if (m_audio)
216         {
217 #ifdef OUDE_OPENAL
218                 m_audio = true;                 // openal_2.12
219                 alutInit(NULL, NULL);   // openal_2.12
220 #else
221                 m_audio = false;
222
223                 ALCdevice *dev = alcOpenDevice(NULL);
224                 if (dev) {
225                         m_context = alcCreateContext(dev, NULL);
226
227                         if (m_context) {
228                                 alcMakeContextCurrent(m_context);
229                                 m_audio = true;
230                         }
231                 }
232
233 #endif
234         }
235
236         // then try to generate some buffers
237         if (m_audio)
238         {
239                 // let openal generate its buffers
240                 alGenBuffers(NUM_BUFFERS, m_buffers);
241                 m_buffersinitialized = true;
242                 
243                 for (int i = 0; i < NUM_BUFFERS; i++)
244                 {
245                         if (!alIsBuffer(m_buffers[i]))
246                         {
247                                 //printf("\n\n  WARNING: OpenAL returned with an error. Continuing without audio.\n\n");
248                                 m_audio = false;
249                                 break;
250                         }
251                 }
252         }
253
254         // next: the sources
255         if (m_audio)
256         {
257 #ifdef OUDE_OPENAL
258                 ALenum alc_error = ALC_NO_ERROR;        // openal_2.12
259 #elif defined(_WIN32)
260                 // alcGetError has no arguments on windows
261                 ALenum alc_error = alcGetError();       // openal_2.14+
262 #else
263                 ALenum alc_error = alcGetError(NULL);   // openal_2.14+
264 #endif
265
266                 // let openal generate its sources
267                 if (alc_error == ALC_NO_ERROR)
268                 {
269                         alGenSources(NUM_SOURCES, m_sources);
270                         m_sourcesinitialized = true;
271                 }
272         }
273
274         // let's get us a wavecache
275         if (m_audio)
276         {
277                 m_wavecache = new SND_WaveCache();
278         }
279         
280         m_cdrom = new SND_SDLCDDevice();
281 }
282
283 void SND_OpenALDevice::UseCD(void) const
284 {
285         // only fmod has CD support, so only create it here
286         SND_CDObject::CreateSystem();
287
288 }
289
290 void SND_OpenALDevice::MakeCurrent() const
291 {
292 #ifdef WIN32
293         alcMakeContextCurrent(m_context);
294 #endif
295 }
296
297
298
299 SND_OpenALDevice::~SND_OpenALDevice()
300 {
301         if (m_context) {
302                 alcMakeContextCurrent(m_context);
303
304                 if (m_buffersinitialized)
305                         alDeleteBuffers(NUM_BUFFERS, m_buffers);
306
307                 if (m_sourcesinitialized)
308                         alDeleteSources(NUM_SOURCES, m_sources);
309         }
310         
311         // let's see if we used the cd. if not, just leave it alone
312         SND_CDObject* pCD = SND_CDObject::Instance();
313         
314         if (pCD)
315         {
316                 this->StopCD();
317                 SND_CDObject::DisposeSystem();
318         }
319         
320         if (m_cdrom)
321                 delete m_cdrom;
322 }
323
324
325
326 SND_WaveSlot* SND_OpenALDevice::LoadSample(const STR_String& name,
327                                                                                   void* memlocation,
328                                                                                   int size)
329 {
330         SND_WaveSlot* waveslot = NULL;
331         STR_String samplename = name;
332         
333         if (m_audio)
334         {
335                 /* create the waveslot */
336                 waveslot = m_wavecache->GetWaveSlot(samplename);
337
338                 /* do we support this sample? */
339                 if (SND_IsSampleValid(name, memlocation))
340                 {
341                         if (waveslot)
342                         {
343                                 int buffer = waveslot->GetBuffer();
344                                 void* data = NULL;
345                                 char loop = 'a';
346                                 int sampleformat, bitrate, numberofchannels;
347                                 ALenum al_error = alGetError();
348                                 
349 #ifdef OUDE_OPENAL
350                                 unsigned int samplerate, numberofsamples;               // openal_2.12
351 #else
352                                 int samplerate, numberofsamples, frequency;  // openal_2.14+
353 #endif
354                                 
355                                 /* load the sample from memory? */
356                                 if (size && memlocation)
357                                 {
358                                         waveslot->SetFileSize(size);
359                                         
360                                         /* what was (our) buffer? */
361                                         int buffer = waveslot->GetBuffer();
362                                         
363                                         /* get some info out of the sample */
364                                         SND_GetSampleInfo((signed char*)memlocation, waveslot);
365                                         numberofchannels = SND_GetNumberOfChannels(memlocation);
366                                         bitrate = SND_GetBitRate(memlocation);
367                                         
368                                         /* load the sample into openal */
369 #ifdef OUDE_OPENAL
370                                         alutLoadWAVMemory((char*)memlocation, &sampleformat, &data, &numberofsamples, &samplerate);                             //      openal_2.12
371 #else
372                                         alutLoadWAVMemory((signed char*)memlocation, &sampleformat, &data, &numberofsamples, &samplerate, &loop);//     openal_2.14+
373 #endif
374                                         /* put it in the buffer */
375                                         alBufferData(m_buffers[buffer], sampleformat, data, numberofsamples, samplerate);
376                                 }
377                                 /* or from file? */
378                                 else
379                                 {
380 #ifdef WIN32
381                                         alutLoadWAVFile((signed char*)samplename.Ptr(), &sampleformat, &data, &numberofsamples, &samplerate, &loop);
382 #else
383                                         alutLoadWAV((char*)samplename.Ptr(), &data,
384                                                 &sampleformat, &numberofsamples,
385                                                 &samplerate, &frequency);
386 #endif
387                                         /* put it in the buffer */
388                                         alBufferData(m_buffers[buffer], sampleformat, data, numberofsamples, samplerate);
389                                 }
390                                 
391                                 /* fill the waveslot with info */
392                                 al_error = alGetError();
393                                 if (al_error == AL_NO_ERROR && m_buffers[buffer])
394                                 {
395                                         waveslot->SetData(data);
396                                         waveslot->SetSampleFormat(sampleformat);
397                                         waveslot->SetNumberOfChannels(numberofchannels);
398                                         waveslot->SetSampleRate(samplerate);
399                                         waveslot->SetBitRate(bitrate);
400                                         waveslot->SetNumberOfSamples(numberofsamples);
401                                         
402                                         /* if the loading succeeded, mark the waveslot */
403                                         waveslot->SetLoaded(true);
404                                 }
405                                 else
406                                 {
407                                         /* or when it failed, free the waveslot */
408                                         m_wavecache->RemoveSample(waveslot->GetSampleName(), waveslot->GetBuffer());
409                                         waveslot = NULL;
410                                 }
411                                 
412                                 /* and free the original stuff (copy was made in openal) */
413                                 alutUnloadWAV(sampleformat, data, numberofsamples, samplerate);
414                         }
415                 }
416                 else
417                 {
418                         /* sample not supported, remove waveslot */
419                         m_wavecache->RemoveSample(waveslot->GetSampleName(), waveslot->GetBuffer());
420                         waveslot = NULL;
421                 }
422         }
423         return waveslot;
424 }
425
426
427
428 // listener's and general stuff //////////////////////////////////////////////////////
429
430
431
432 /* sets the global dopplervelocity */
433 void SND_OpenALDevice::SetDopplerVelocity(MT_Scalar dopplervelocity) const
434 {
435         alDopplerVelocity ((float)dopplervelocity);
436 }
437
438
439
440 /* sets the global dopplerfactor */
441 void SND_OpenALDevice::SetDopplerFactor(MT_Scalar dopplerfactor) const
442 {
443         alDopplerFactor ((float)dopplerfactor);
444 }
445
446
447
448 /* sets the global rolloff factor */
449 void SND_OpenALDevice::SetListenerRollOffFactor(MT_Scalar rollofffactor) const
450 {
451         // not implemented in openal
452 }
453
454
455
456 void SND_OpenALDevice::NextFrame() const
457 {
458         // CD
459         m_cdrom->NextFrame();
460         // not needed by openal
461 }
462
463
464
465 // set the gain for the listener
466 void SND_OpenALDevice::SetListenerGain(float gain) const
467 {
468         alListenerf (AL_GAIN, gain);
469 }
470
471
472
473 void SND_OpenALDevice::InitListener()
474 {
475         // initialize the listener with these values that won't change
476         // (as long as we can have only one listener)
477         // now we can superimpose all listeners on each other (for they
478         // have the same settings)
479         float lispos[3] = {0,0,0};
480         float lisvel[3] = {0,0,0};
481 #ifdef WIN32
482         float lisori[6] = {0,1,0,0,0,1};
483 #else
484         float lisori[6] = {0,0,1,0,-1,0};
485 #endif
486
487         alListenerfv(AL_POSITION, lispos);
488         alListenerfv(AL_VELOCITY, lisvel);
489         alListenerfv(AL_ORIENTATION, lisori);
490 }
491
492
493
494 // source playstate stuff ////////////////////////////////////////////////////////////
495
496
497
498 /* sets the buffer */
499 void SND_OpenALDevice::SetObjectBuffer(int id, unsigned int buffer)
500 {
501         alSourcei (m_sources[id], AL_BUFFER, m_buffers[buffer]);
502 }
503
504
505
506 // check if the sound's still playing
507 int SND_OpenALDevice::GetPlayState(int id)
508 {
509     int alstate = 0;
510         int result = 0;
511
512     alGetSourceiv(m_sources[id], AL_SOURCE_STATE, &alstate);
513
514         switch(alstate)
515         {
516         case AL_INITIAL:
517                 {
518                         result = SND_INITIAL;
519                         break;
520                 }
521         case AL_PLAYING:
522                 {
523                         result = SND_PLAYING;
524                         break;
525                 }
526         case AL_PAUSED:
527                 {
528                         result = SND_PAUSED;
529                         break;
530                 }
531         case AL_STOPPED:
532                 {
533                         result = SND_STOPPED;
534                         break;
535                 }
536         default:
537                 result = SND_UNKNOWN;
538         }
539
540     return result;
541 }
542
543
544
545 // make the source play
546 void SND_OpenALDevice::PlayObject(int id)
547 {
548         alSourcePlay(m_sources[id]);
549 }
550
551
552
553 // make the source stop
554 void SND_OpenALDevice::StopObject(int id) const
555 {
556         float obpos[3] = {0,0,0};
557         float obvel[3] = {0,0,0};
558
559         alSourcefv(m_sources[id], AL_POSITION, obpos);
560
561 #ifdef WIN32
562         alSourcefv(m_sources[id], AL_VELOCITY, obvel);
563 #endif
564
565         alSourcef(m_sources[id], AL_GAIN, 1.0);
566         alSourcef(m_sources[id], AL_PITCH, 1.0);
567         alSourcei(m_sources[id], AL_LOOPING, AL_FALSE);
568         alSourceStop(m_sources[id]);
569 }
570
571
572
573 // stop all sources
574 void SND_OpenALDevice::StopAllObjects()
575 {
576         alSourceStopv(NUM_SOURCES, m_sources);
577 }
578
579
580
581 // pause the source
582 void SND_OpenALDevice::PauseObject(int id) const
583 {
584         alSourcePause(m_sources[id]);
585 }
586
587
588
589 // source properties stuff ////////////////////////////////////////////////////////////
590
591
592
593 // give openal the object's pitch
594 void SND_OpenALDevice::SetObjectPitch(int id, MT_Scalar pitch) const
595 {
596         alSourcef (m_sources[id], AL_PITCH, (float)pitch);
597 }
598
599
600
601 // give openal the object's gain
602 void SND_OpenALDevice::SetObjectGain(int id, MT_Scalar gain) const
603 {
604         alSourcef (m_sources[id], AL_GAIN, (float)gain);
605 }
606
607
608
609 // give openal the object's looping
610 void SND_OpenALDevice::SetObjectLoop(int id, unsigned int loopmode) const
611 {
612         if (loopmode == SND_LOOP_OFF)
613         {
614                 //printf("%d - ", id);
615                 alSourcei (m_sources[id], AL_LOOPING, AL_FALSE);
616         }
617         else
618                 alSourcei (m_sources[id], AL_LOOPING, AL_TRUE);
619 }
620
621 void SND_OpenALDevice::SetObjectLoopPoints(int id, unsigned int loopstart, unsigned int loopend) const
622 {
623
624
625 }
626
627
628 void SND_OpenALDevice::SetObjectMinGain(int id, MT_Scalar mingain) const
629 {
630         alSourcef (m_sources[id], AL_MIN_GAIN, (float)mingain);
631 }
632
633
634
635 void SND_OpenALDevice::SetObjectMaxGain(int id, MT_Scalar maxgain) const
636 {
637         alSourcef (m_sources[id], AL_MAX_GAIN, (float)maxgain);
638 }
639
640
641
642 void SND_OpenALDevice::SetObjectRollOffFactor(int id, MT_Scalar rollofffactor) const
643 {
644         alSourcef (m_sources[id], AL_ROLLOFF_FACTOR, (float)rollofffactor);
645 }
646
647
648
649 void SND_OpenALDevice::SetObjectReferenceDistance(int id, MT_Scalar referencedistance) const
650 {
651         alSourcef (m_sources[id], AL_REFERENCE_DISTANCE, (float)referencedistance);
652 }
653
654
655
656 // give openal the object's position
657 void SND_OpenALDevice::ObjectIs2D(int id) const
658 {
659         float obpos[3] = {0,0,0};
660         float obvel[3] = {0,0,0};
661         
662         alSourcefv(m_sources[id], AL_POSITION, obpos);
663         alSourcefv(m_sources[id], AL_VELOCITY, obvel);
664 }
665
666
667
668 void SND_OpenALDevice::SetObjectTransform(int id,
669                                                                                   const MT_Vector3& position,
670                                                                                   const MT_Vector3& velocity,
671                                                                                   const MT_Matrix3x3& orientation,
672                                                                                   const MT_Vector3& lisposition,
673                                                                                   const MT_Scalar& rollofffactor) const 
674 {
675         float obpos[3];
676         float obvel[3];
677
678         obpos[0] = (float)position[0] * (float)rollofffactor;   //x (l/r)
679         obpos[1] = (float)position[1] * (float)rollofffactor;
680         obpos[2] = (float)position[2] * (float)rollofffactor;
681
682         alSourcefv(m_sources[id], AL_POSITION, obpos);
683
684 #ifdef WIN32
685         velocity.getValue(obvel);
686         alSourcefv(m_sources[id], AL_VELOCITY, obvel);
687 #endif
688
689 }
690
691 void SND_OpenALDevice::PlayCD(int track) const
692 {
693         m_cdrom->PlayCD(track);
694 }
695
696
697 void SND_OpenALDevice::PauseCD(bool pause) const
698 {
699         m_cdrom->PauseCD(pause);
700 }
701
702 void SND_OpenALDevice::StopCD() const
703 {
704         SND_CDObject* pCD = SND_CDObject::Instance();
705
706         if (pCD && pCD->GetUsed())
707         {
708                 m_cdrom->StopCD();
709         }
710 }
711
712 void SND_OpenALDevice::SetCDPlaymode(int playmode) const
713 {
714         m_cdrom->SetCDPlaymode(playmode);
715 }
716
717 void SND_OpenALDevice::SetCDGain(MT_Scalar gain) const
718 {
719         m_cdrom->SetCDGain(gain);
720 }