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