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