1/**********************************************************************************************************************************
2* OpenAL cross platform audio library
3* Copyright (C) 1999-2000 by authors.
4* Portions Copyright (C) 2004 by Apple Computer Inc., Copyright (C) 2012 by Apple Inc.
5* This library is free software; you can redistribute it and/or
6*  modify it under the terms of the GNU Library General Public
7*  License as published by the Free Software Foundation; either
8*  version 2.1 of the License, or (at your option) any later version.
9*
10* This library is distributed in the hope that it will be useful,
11*  but WITHOUT ANY WARRANTY; without even the implied warranty of
12*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13*  Library General Public License for more details.
14*
15* You should have received a copy of the GNU Library General Public
16*  License along with this library; if not, write to the
17*  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18*  Boston, MA  02111-1307, USA.
19* Or go to http://www.gnu.org/copyleft/lgpl.html
20**********************************************************************************************************************************/
21
22#include "oalOSX.h"
23
24#define		LOG_RING_BUFFER		0
25
26// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27#pragma mark ***** UTILITY *****
28// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
29// Use this for getting a unique token when creating  new contexts and devices
30uintptr_t	GetNewPtrToken (void)
31{
32	static	uintptr_t	currentToken = 24;
33	uintptr_t	returnedToken;
34
35	returnedToken = currentToken;
36	currentToken++;
37
38	return (returnedToken);
39}
40
41// Use this for getting a unique integer token
42ALuint	GetNewToken (void)
43{
44	static	ALuint	currentToken = 2400;
45	ALuint	returnedToken;
46
47	returnedToken = currentToken;
48	currentToken++;
49
50	return (returnedToken);
51}
52
53// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54const char		kPathDelimiter[] = "/";
55const UInt32	kPathDelimiterLength = 1;
56
57bool	IsRelativePath(const char* inPath)
58{
59	return inPath[0] != kPathDelimiter[0];
60}
61
62// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
63void	MakeAbsolutePath(const char* inRelativePath, char* outAbsolutePath, UInt32 inMaxAbsolutePathLength)
64{
65	//	get the path to the current working directory
66	getcwd(outAbsolutePath, inMaxAbsolutePathLength);
67	if ((strlen(outAbsolutePath) + kPathDelimiterLength) <= inMaxAbsolutePathLength - 1)
68	{
69		strcat(outAbsolutePath, kPathDelimiter);
70		if ((strlen(outAbsolutePath) + strlen(inRelativePath)) <= inMaxAbsolutePathLength - 1)
71		{
72			strcat(outAbsolutePath, inRelativePath);
73		}
74	}
75}
76
77// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
78UInt32	GetOALFormatFromASBD(CAStreamBasicDescription	&inASBD)
79{
80	switch (inASBD.mFormatID)
81	{
82		case kAudioFormatLinearPCM:
83			// NOTE: if float: check for extension
84
85			if (inASBD.mFormatFlags & kAudioFormatFlagIsFloat)
86			{
87				if (inASBD.NumberChannels() == 1 && inASBD.mBitsPerChannel == 32)
88				{
89					if(alIsExtensionPresent("AL_EXT_float32"))
90						return alGetEnumValue("AL_FORMAT_MONO_FLOAT32");
91				}
92				if (inASBD.NumberChannels() == 2 && inASBD.mBitsPerChannel == 32)
93				{
94					if(alIsExtensionPresent("AL_EXT_float32"))
95						return alGetEnumValue("AL_FORMAT_STEREO_FLOAT32");
96				}
97			}
98			else
99			{
100				if (inASBD.NumberChannels() == 1 && inASBD.mBitsPerChannel == 16)
101					return AL_FORMAT_MONO16;
102				else if (inASBD.NumberChannels() == 2 && inASBD.mBitsPerChannel == 16)
103					return AL_FORMAT_STEREO16;
104				else if (inASBD.NumberChannels() == 1 && inASBD.mBitsPerChannel == 8)
105					return AL_FORMAT_MONO8;
106				else if (inASBD.NumberChannels() == 2 && inASBD.mBitsPerChannel == 8)
107					return AL_FORMAT_STEREO8;
108			}
109			break;
110		default:
111			return (0);
112			break;
113	}
114	return (0);
115}
116
117// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
118const char* GetFormatString(UInt32 inToken)
119{
120	switch(inToken)
121	{
122		case AL_FORMAT_MONO16:
123			return "16-Bit/Mono";
124			break;
125		case AL_FORMAT_STEREO16:
126			return "16-Bit/Stereo";
127			break;
128		case AL_FORMAT_MONO8:
129			return "8-Bit/Mono";
130			break;
131		case AL_FORMAT_STEREO8:
132			return "8-Bit/Stereo";
133			break;
134	}
135	return "UNKNOWN FORMAT";
136}
137
138// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
139// For use with DebugMessages so errors are more readable
140const char* GetALAttributeString(UInt32 inToken)
141{
142	switch(inToken)
143	{
144		case AL_SOURCE_RELATIVE: return "AL_SOURCE_RELATIVE"; break;
145		case AL_CONE_INNER_ANGLE: return "AL_CONE_INNER_ANGLE"; break;
146		case AL_CONE_OUTER_ANGLE: return "AL_CONE_OUTER_ANGLE"; break;
147		case AL_CONE_OUTER_GAIN: return "AL_CONE_OUTER_GAIN"; break;
148		case AL_PITCH: return "AL_PITCH"; break;
149		case AL_POSITION: return "AL_POSITION"; break;
150		case AL_DIRECTION: return "AL_DIRECTION"; break;
151		case AL_VELOCITY: return "AL_VELOCITY"; break;
152		case AL_LOOPING: return "AL_LOOPING"; break;
153		case AL_BUFFER: return "AL_BUFFER"; break;
154		case AL_GAIN: return "AL_GAIN"; break;
155		case AL_MIN_GAIN: return "AL_MIN_GAIN"; break;
156		case AL_MAX_GAIN: return "AL_MAX_GAIN"; break;
157		case AL_ORIENTATION: return "AL_ORIENTATION"; break;
158		case AL_REFERENCE_DISTANCE: return "AL_REFERENCE_DISTANCE"; break;
159		case AL_ROLLOFF_FACTOR: return "AL_ROLLOFF_FACTOR"; break;
160		case AL_MAX_DISTANCE: return "AL_MAX_DISTANCE"; break;
161		case AL_SOURCE_STATE: return "AL_SOURCE_STATE"; break;
162		case AL_BUFFERS_QUEUED: return "AL_BUFFERS_QUEUED"; break;
163		case AL_BUFFERS_PROCESSED: return "AL_BUFFERS_PROCESSED"; break;
164		case AL_SEC_OFFSET: return "AL_SEC_OFFSET"; break;
165		case AL_SAMPLE_OFFSET: return "AL_SAMPLE_OFFSET"; break;
166		case AL_BYTE_OFFSET: return "AL_BYTE_OFFSET"; break;
167		case AL_SOURCE_TYPE: return "AL_SOURCE_TYPE"; break;
168		case AL_NONE: return "AL_NONE"; break;
169		case AL_INVERSE_DISTANCE: return "AL_INVERSE_DISTANCE"; break;
170		case AL_INVERSE_DISTANCE_CLAMPED: return "AL_INVERSE_DISTANCE_CLAMPED"; break;
171		case AL_LINEAR_DISTANCE: return "AL_LINEAR_DISTANCE"; break;
172		case AL_LINEAR_DISTANCE_CLAMPED: return "AL_LINEAR_DISTANCE_CLAMPED"; break;
173		case AL_EXPONENT_DISTANCE: return "AL_EXPONENT_DISTANCE"; break;
174		case AL_EXPONENT_DISTANCE_CLAMPED: return "AL_EXPONENT_DISTANCE_CLAMPED"; break;
175		case AL_INVALID_NAME: return "AL_INVALID_NAME"; break;
176	}
177	return "UNKNOWN ATTRIBUTE - WARNING WARNING WARNING";
178}
179
180const char* GetALCAttributeString(UInt32 inToken)
181{
182	switch(inToken)
183	{
184		case ALC_DEFAULT_DEVICE_SPECIFIER: return "ALC_DEFAULT_DEVICE_SPECIFIER"; break;
185		case ALC_DEVICE_SPECIFIER: return "ALC_DEVICE_SPECIFIER"; break;
186		case ALC_EXTENSIONS: return "ALC_EXTENSIONS"; break;
187		case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER: return "ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER"; break;
188		case ALC_CAPTURE_DEVICE_SPECIFIER: return "ALC_CAPTURE_DEVICE_SPECIFIER"; break;
189		case ALC_NO_ERROR: return "ALC_NO_ERROR"; break;
190		case ALC_INVALID_DEVICE: return "ALC_INVALID_DEVICE"; break;
191		case ALC_INVALID_CONTEXT: return "ALC_INVALID_CONTEXT"; break;
192		case ALC_INVALID_ENUM: return "ALC_INVALID_ENUM"; break;
193		case ALC_INVALID_VALUE: return "ALC_INVALID_VALUE"; break;
194
195		case ALC_ASA_REVERB_ON: return "ALC_ASA_REVERB_ON"; break;
196		case ALC_ASA_REVERB_ROOM_TYPE: return "ALC_ASA_REVERB_ROOM_TYPE"; break;
197		case ALC_ASA_REVERB_SEND_LEVEL: return "ALC_ASA_REVERB_SEND_LEVEL"; break;
198		case ALC_ASA_REVERB_GLOBAL_LEVEL: return "ALC_ASA_REVERB_GLOBAL_LEVEL"; break;
199		case ALC_ASA_OCCLUSION: return "ALC_ASA_OCCLUSION"; break;
200		case ALC_ASA_ROGER_BEEP_ENABLE: return "ALC_ASA_ROGER_BEEP_ENABLE"; break;
201		case ALC_ASA_ROGER_BEEP_ON: return "ALC_ASA_ROGER_BEEP_ON"; break;
202		case ALC_ASA_ROGER_BEEP_GAIN: return "ALC_ASA_ROGER_BEEP_GAIN"; break;
203		case ALC_ASA_ROGER_BEEP_SENSITIVITY: return "ALC_ASA_ROGER_BEEP_SENSITIVITY"; break;
204		case ALC_ASA_ROGER_BEEP_TYPE: return "ALC_ASA_ROGER_BEEP_TYPE"; break;
205		case ALC_ASA_ROGER_BEEP_PRESET: return "ALC_ASA_ROGER_BEEP_PRESET"; break;
206
207		case ALC_ASA_DISTORTION_ENABLE: return "ALC_ASA_DISTORTION_ENABLE"; break;
208		case ALC_ASA_DISTORTION_ON: return "ALC_ASA_DISTORTION_ON"; break;
209		case ALC_ASA_DISTORTION_MIX: return "ALC_ASA_DISTORTION_MIX"; break;
210		case ALC_ASA_DISTORTION_TYPE: return "ALC_ASA_DISTORTION_TYPE"; break;
211		case ALC_ASA_DISTORTION_PRESET: return "ALC_ASA_DISTORTION_PRESET"; break;
212	}
213	return "UNKNOWN ATTRIBUTE - WARNING WARNING WARNING";
214}
215
216// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
217bool IsValidRenderQuality (UInt32 inRenderQuality)
218{
219	switch (inRenderQuality)
220	{
221		case ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_HIGH:
222		case ALC_MAC_OSX_SPATIAL_RENDERING_QUALITY_LOW:
223			return (true);
224			break;
225
226		default:
227			return (false);
228			break;
229	}
230
231	return (false);
232}
233
234// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
235bool	IsFormatSupported(UInt32	inFormatID)
236{
237	switch(inFormatID)
238	{
239		case AL_FORMAT_MONO16:
240		case AL_FORMAT_STEREO16:
241		case AL_FORMAT_MONO8:
242		case AL_FORMAT_STEREO8:
243			return true;
244			break;
245		case AL_FORMAT_MONO_FLOAT32:
246		case AL_FORMAT_STEREO_FLOAT32:
247			return alIsExtensionPresent("AL_EXT_float32");
248			break;
249		default:
250			return false;
251			break;
252	}
253	return false;
254}
255
256// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
257OSStatus	FillInASBD(CAStreamBasicDescription &inASBD, UInt32	inFormatID, UInt32 inSampleRate)
258{
259	OSStatus	err = noErr;
260
261	switch (inFormatID)
262	{
263		case AL_FORMAT_STEREO16:
264			inASBD.mSampleRate = inSampleRate * 1.0;
265			inASBD.mFormatID = kAudioFormatLinearPCM;
266			inASBD.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
267			inASBD.mBytesPerPacket = 4;
268			inASBD.mBytesPerFrame = 4;
269			inASBD.mFramesPerPacket = 1;
270			inASBD.mBitsPerChannel = 16;
271			inASBD.mChannelsPerFrame = 2;
272			inASBD.mReserved = 0;
273			break;
274		case AL_FORMAT_MONO16:
275			inASBD.mSampleRate = inSampleRate * 1.0;
276			inASBD.mFormatID = kAudioFormatLinearPCM;
277			inASBD.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
278			inASBD.mBytesPerPacket = 2;
279			inASBD.mBytesPerFrame = 2;
280			inASBD.mFramesPerPacket = 1;
281			inASBD.mBitsPerChannel = 16;
282			inASBD.mChannelsPerFrame = 1;
283			inASBD.mReserved = 0;
284			break;
285		case AL_FORMAT_STEREO8:
286			inASBD.mSampleRate = inSampleRate * 1.0;
287			inASBD.mFormatID = kAudioFormatLinearPCM;
288			inASBD.mFormatFlags = kAudioFormatFlagIsPacked;
289			inASBD.mBytesPerPacket = 2;
290			inASBD.mBytesPerFrame = 2;
291			inASBD.mFramesPerPacket = 1;
292			inASBD.mBitsPerChannel = 8;
293			inASBD.mChannelsPerFrame = 2;
294			inASBD.mReserved = 0;
295			break;
296		case AL_FORMAT_MONO8 :
297			inASBD.mSampleRate = inSampleRate * 1.0;
298			inASBD.mFormatID = kAudioFormatLinearPCM;
299			inASBD.mFormatFlags = kAudioFormatFlagIsPacked;
300			inASBD.mBytesPerPacket = 1;
301			inASBD.mBytesPerFrame = 1;
302			inASBD.mFramesPerPacket = 1;
303			inASBD.mBitsPerChannel = 8;
304			inASBD.mChannelsPerFrame = 1;
305			inASBD.mReserved = 0;
306			break;
307		case AL_FORMAT_STEREO_FLOAT32 :
308			inASBD.mSampleRate = inSampleRate * 1.0;
309			inASBD.mFormatID = kAudioFormatLinearPCM;
310			inASBD.mFormatFlags = kLinearPCMFormatFlagIsFloat;
311			inASBD.mBytesPerPacket = 8;
312			inASBD.mBytesPerFrame = 8;
313			inASBD.mFramesPerPacket = 1;
314			inASBD.mBitsPerChannel = 32;
315			inASBD.mChannelsPerFrame = 2;
316			inASBD.mReserved = 0;
317			break;
318		case AL_FORMAT_MONO_FLOAT32 :
319			inASBD.mSampleRate = inSampleRate * 1.0;
320			inASBD.mFormatID = kAudioFormatLinearPCM;
321			inASBD.mFormatFlags = kLinearPCMFormatFlagIsFloat;
322			inASBD.mBytesPerPacket = 4;
323			inASBD.mBytesPerFrame = 4;
324			inASBD.mFramesPerPacket = 1;
325			inASBD.mBitsPerChannel = 32;
326			inASBD.mChannelsPerFrame = 1;
327			inASBD.mReserved = 0;
328			break;
329		default:
330			err = AL_INVALID_VALUE;
331			break;
332	}
333	return (err);
334}
335
336// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
337void	GetDefaultDeviceName(ALCchar*		outDeviceName, bool	isInput)
338{
339	UInt32		size = 0;
340	OSStatus	result = noErr;
341	UInt32		deviceProperty = isInput ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice;
342
343	try {
344		AudioDeviceID	defaultDevice = 0;
345		// Get the default output device
346		size = sizeof(defaultDevice);
347		result = AudioHardwareGetProperty(deviceProperty, &size, &defaultDevice);
348			THROW_RESULT
349
350		result = AudioDeviceGetPropertyInfo( defaultDevice, 0, false, kAudioDevicePropertyDeviceName, &size, NULL);
351			THROW_RESULT
352
353		if (size > maxLen)
354			throw -1;
355
356		size = maxLen;
357		result = AudioDeviceGetProperty(defaultDevice, 0, false, kAudioDevicePropertyDeviceName, &size, outDeviceName);
358			THROW_RESULT
359
360	} catch (...) {
361		outDeviceName[0] = '\0'; // failure case, make it a zero length string
362	}
363	return;
364}
365
366// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
367UInt32	CalculateNeededMixerBusses(const ALCint *attrList, UInt32 inDefaultBusCount)
368{
369	UInt32		monoSources = 0;
370	UInt32		stereoSources = 0;
371	UInt32		returnValue = inDefaultBusCount; // default case
372	ALCint*		currentAttribute = ( ALCint*) attrList;
373
374	// ATTRIBUTE LIST
375	if (attrList)
376	{
377		while (*currentAttribute != 0)
378		{
379			switch (*currentAttribute)
380			{
381				case ALC_MONO_SOURCES:
382					monoSources = currentAttribute[1];
383					break;
384				case ALC_STEREO_SOURCES:
385					stereoSources = currentAttribute[1];
386					break;
387				default:
388					break;
389			}
390			currentAttribute += 2;
391		}
392
393		if (monoSources == 0 && stereoSources == 0)
394			returnValue = inDefaultBusCount;
395		else if ((monoSources + stereoSources > kDefaultMaximumMixerBusCount) || (monoSources + stereoSources > inDefaultBusCount))
396			returnValue = monoSources + stereoSources;
397	}
398
399	return returnValue;
400}
401
402// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
403UInt32	GetDesiredRenderChannelsFor3DMixer(UInt32	inDeviceChannels)
404{
405    UInt32	returnValue = inDeviceChannels;
406
407	if ((Get3DMixerVersion() < k3DMixerVersion_2_0) && (returnValue == 4))
408    {
409        // quad did not work properly before version 2.0 of the 3DMixer, so just render to stereo
410        returnValue = 2;
411    }
412    else if (inDeviceChannels < 4)
413    {
414        // guard against the possibility of multi channel hw that has never been given a preferred channel layout
415        // Or, that a 3 channel layout was returned (which is unsupported by the 3DMixer)
416        returnValue = 2;
417    }
418    else if ((inDeviceChannels > 5) &&  (Get3DMixerVersion() < k3DMixerVersion_2_3))
419    {
420		// 3DMixer ver. 2.2 and below does not render to more than 5 channels
421		returnValue = 5;
422	}
423	else if (inDeviceChannels > 8)
424    {
425		// Current 3DMixer renders to maximum 8 channels
426		returnValue = 8;
427	}
428
429	return returnValue;
430}
431
432// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
433// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
434#pragma mark ***** DEPRECATED ALUT *****
435// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
436// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
437#ifdef __cplusplus
438extern "C" {
439#endif
440
441// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
442// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
443// alutLoadWAVMemory()
444// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
445// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
446
447#define ALUTAPI
448#define ALUTAPIENTRY
449
450ALUTAPI ALvoid	ALUTAPIENTRY alutInit(ALint *argc,ALbyte **argv);
451ALUTAPI ALvoid	ALUTAPIENTRY alutExit(void);
452ALUTAPI ALvoid	ALUTAPIENTRY alutLoadWAVFile(ALbyte *file,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq);
453ALUTAPI ALvoid  ALUTAPIENTRY alutLoadWAVMemory(ALbyte *memory,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq);
454ALUTAPI ALvoid  ALUTAPIENTRY alutUnloadWAV(ALenum format,ALvoid *data,ALsizei size,ALsizei freq);
455
456/*
457    alutLoadWAVMemory() existed in previous OAL implementations, and is provided for legacy purposes.
458    This is the same implementation already existing in the Open Source repository.
459*/
460
461typedef struct                                  /* WAV File-header */
462{
463  ALubyte  Id[4];
464  ALsizei  Size;
465  ALubyte  Type[4];
466} WAVFileHdr_Struct;
467
468typedef struct                                  /* WAV Fmt-header */
469{
470  ALushort Format;
471  ALushort Channels;
472  ALuint   SamplesPerSec;
473  ALuint   BytesPerSec;
474  ALushort BlockAlign;
475  ALushort BitsPerSample;
476} WAVFmtHdr_Struct;
477
478typedef struct									/* WAV FmtEx-header */
479{
480  ALushort Size;
481  ALushort SamplesPerBlock;
482} WAVFmtExHdr_Struct;
483
484typedef struct                                  /* WAV Smpl-header */
485{
486  ALuint   Manufacturer;
487  ALuint   Product;
488  ALuint   SamplePeriod;
489  ALuint   Note;
490  ALuint   FineTune;
491  ALuint   SMPTEFormat;
492  ALuint   SMPTEOffest;
493  ALuint   Loops;
494  ALuint   SamplerData;
495  struct
496  {
497    ALuint Identifier;
498    ALuint Type;
499    ALuint Start;
500    ALuint End;
501    ALuint Fraction;
502    ALuint Count;
503  }      Loop[1];
504} WAVSmplHdr_Struct;
505
506typedef struct                                  /* WAV Chunk-header */
507{
508  ALubyte  Id[4];
509  ALuint   Size;
510} WAVChunkHdr_Struct;
511
512void SwapWords(unsigned int *puint);
513void SwapBytes(unsigned short *pshort);
514
515// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
516void SwapWords(unsigned int *puint)
517{
518    unsigned int tempint;
519	char *pChar1, *pChar2;
520
521	tempint = *puint;
522	pChar2 = (char *)&tempint;
523	pChar1 = (char *)puint;
524
525	pChar1[0]=pChar2[3];
526	pChar1[1]=pChar2[2];
527	pChar1[2]=pChar2[1];
528	pChar1[3]=pChar2[0];
529}
530
531// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
532void SwapBytes(unsigned short *pshort)
533{
534    unsigned short tempshort;
535    char *pChar1, *pChar2;
536
537    tempshort = *pshort;
538    pChar2 = (char *)&tempshort;
539    pChar1 = (char *)pshort;
540
541    pChar1[0]=pChar2[1];
542    pChar1[1]=pChar2[0];
543}
544
545// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
546ALUTAPI ALvoid ALUTAPIENTRY alutLoadWAVMemory(ALbyte *memory,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq)
547{
548	WAVChunkHdr_Struct ChunkHdr;
549	WAVFmtExHdr_Struct FmtExHdr;
550	WAVFileHdr_Struct FileHdr;
551	WAVSmplHdr_Struct SmplHdr;
552	WAVFmtHdr_Struct FmtHdr;
553	ALbyte *Stream;
554
555	*format=AL_FORMAT_MONO16;
556	*data=NULL;
557	*size=0;
558	*freq=22050;
559	if (memory)
560	{
561		Stream=memory;
562		if (Stream)
563		{
564		    memcpy(&FileHdr,Stream,sizeof(WAVFileHdr_Struct));
565		    Stream+=sizeof(WAVFileHdr_Struct);
566			SwapWords((unsigned int*) &FileHdr.Size);
567			FileHdr.Size=((FileHdr.Size+1)&~1)-4;
568			while ((FileHdr.Size!=0)&&(memcpy(&ChunkHdr,Stream,sizeof(WAVChunkHdr_Struct))))
569			{
570				Stream+=sizeof(WAVChunkHdr_Struct);
571			    SwapWords(&ChunkHdr.Size);
572
573				if ((ChunkHdr.Id[0] == 'f') && (ChunkHdr.Id[1] == 'm') && (ChunkHdr.Id[2] == 't') && (ChunkHdr.Id[3] == ' '))
574				{
575					memcpy(&FmtHdr,Stream,sizeof(WAVFmtHdr_Struct));
576				    SwapBytes(&FmtHdr.Format);
577					if (FmtHdr.Format==0x0001)
578					{
579					    SwapBytes(&FmtHdr.Channels);
580					    SwapBytes(&FmtHdr.BitsPerSample);
581					    SwapWords(&FmtHdr.SamplesPerSec);
582					    SwapBytes(&FmtHdr.BlockAlign);
583
584						*format=(FmtHdr.Channels==1?
585								(FmtHdr.BitsPerSample==8?AL_FORMAT_MONO8:AL_FORMAT_MONO16):
586								(FmtHdr.BitsPerSample==8?AL_FORMAT_STEREO8:AL_FORMAT_STEREO16));
587						*freq=FmtHdr.SamplesPerSec;
588						Stream+=ChunkHdr.Size;
589					}
590					else
591					{
592						memcpy(&FmtExHdr,Stream,sizeof(WAVFmtExHdr_Struct));
593						Stream+=ChunkHdr.Size;
594					}
595				}
596				else if ((ChunkHdr.Id[0] == 'd') && (ChunkHdr.Id[1] == 'a') && (ChunkHdr.Id[2] == 't') && (ChunkHdr.Id[3] == 'a'))
597				{
598					if (FmtHdr.Format==0x0001)
599					{
600						*size=ChunkHdr.Size;
601                            if(*data == NULL){
602							*data=malloc(ChunkHdr.Size + 31);
603							memset(*data,0,ChunkHdr.Size+31);
604						}
605						else{
606							realloc(*data,ChunkHdr.Size + 31);
607							memset(*data,0,ChunkHdr.Size+31);
608						}
609						if (*data)
610						{
611							memcpy(*data,Stream,ChunkHdr.Size);
612						    memset(((char *)*data)+ChunkHdr.Size,0,31);
613							Stream+=ChunkHdr.Size;
614						    if (FmtHdr.BitsPerSample == 16)
615						    {
616						        for (UInt32 i = 0; i < (ChunkHdr.Size / 2); i++)
617						        {
618						        	SwapBytes(&(*(unsigned short **)data)[i]);
619						        }
620						    }
621						}
622					}
623					else if (FmtHdr.Format==0x0011)
624					{
625						//IMA ADPCM
626					}
627					else if (FmtHdr.Format==0x0055)
628					{
629						//MP3 WAVE
630					}
631				}
632				else if ((ChunkHdr.Id[0] == 's') && (ChunkHdr.Id[1] == 'm') && (ChunkHdr.Id[2] == 'p') && (ChunkHdr.Id[3] == 'l'))
633				{
634				   	memcpy(&SmplHdr,Stream,sizeof(WAVSmplHdr_Struct));
635					Stream+=ChunkHdr.Size;
636				}
637				else Stream+=ChunkHdr.Size;
638				Stream+=ChunkHdr.Size&1;
639				FileHdr.Size-=(((ChunkHdr.Size+1)&~1)+8);
640			}
641		}
642	}
643}
644
645// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
646ALUTAPI ALvoid ALUTAPIENTRY alutInit(ALint *argc,ALbyte **argv)
647{
648    ALCcontext *Context;
649    ALCdevice *Device;
650
651    Device=alcOpenDevice(NULL);  //Open device
652
653    if (Device != NULL) {
654        Context=alcCreateContext(Device,0);  //Create context
655
656	if (Context != NULL) {
657	    alcMakeContextCurrent(Context);  //Set active context
658	}
659    }
660}
661
662// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
663ALUTAPI ALvoid ALUTAPIENTRY alutExit(void)
664{
665    ALCcontext *Context;
666    ALCdevice *Device;
667
668    //Get active context
669    Context=alcGetCurrentContext();
670    //Get device for active context
671    Device=alcGetContextsDevice(Context);
672    //Release context
673    alcDestroyContext(Context);
674    //Close device
675    alcCloseDevice(Device);
676}
677
678// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
679ALUTAPI ALvoid ALUTAPIENTRY alutLoadWAVFile(ALbyte *file,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq)
680{
681	OSStatus		err = noErr;
682	AudioFileID		audioFile = 0;
683	FSRef			fsRef;
684
685	*data = NULL; // in case of failure, do not return some unitialized value as a bogus address
686
687	if (IsRelativePath(file))
688	{
689		char			absolutePath[256];
690		// we need to make a full path here so FSPathMakeRef() works properly
691		MakeAbsolutePath(file, absolutePath, 256);
692		// create an fsref from the file parameter
693		err = FSPathMakeRef ((const UInt8 *) absolutePath, &fsRef, NULL);
694	}
695	else
696		err = FSPathMakeRef ((const UInt8 *) file, &fsRef, NULL);
697
698	if (err == noErr)
699	{
700		err = AudioFileOpen(&fsRef, fsRdPerm, 0, &audioFile);
701		if (err == noErr)
702		{
703			UInt32							dataSize;
704			CAStreamBasicDescription		asbd;
705
706			dataSize = sizeof(CAStreamBasicDescription);
707			AudioFileGetProperty(audioFile, kAudioFilePropertyDataFormat, &dataSize, &asbd);
708
709			*format = GetOALFormatFromASBD(asbd);
710			if (IsFormatSupported(*format))
711			{
712				*freq = (UInt32) asbd.mSampleRate;
713
714				SInt64	audioDataSize = 0;
715				dataSize = sizeof(audioDataSize);
716				err = AudioFileGetProperty(audioFile, kAudioFilePropertyAudioDataByteCount, &dataSize, &audioDataSize);
717				if (err == noErr)
718				{
719					*size = audioDataSize;
720					*data = NULL;
721					*data = calloc(1, audioDataSize);
722					if (*data)
723					{
724						dataSize = audioDataSize;
725						/*err =*/ AudioFileReadBytes(audioFile, false, 0, &dataSize, *data);
726
727#if TARGET_RT_BIG_ENDIAN
728						// Only swap to big endian if running on a ppc mac
729						if ((asbd.mFormatID == kAudioFormatLinearPCM) && (asbd.mBitsPerChannel > 8))
730						{
731							// we just got 16 bit pcm data out of a WAVE file on a big endian platform, so endian swap the data
732							AudioConverterRef				converter;
733							CAStreamBasicDescription		outFormat = asbd;
734							void *							tempData = NULL;
735
736							// ste format to big endian
737							outFormat.mFormatFlags = kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
738							// make some place for converted data
739							tempData = calloc(1 , audioDataSize);
740
741							err = AudioConverterNew(&asbd, &outFormat, &converter);
742							if ((err == noErr) && (tempData != NULL))
743							{
744								UInt32		bufferSize = audioDataSize;
745								err = AudioConverterConvertBuffer(converter, audioDataSize, *data, &bufferSize, tempData);
746								if (err == noErr)
747									memcpy(*data, tempData, audioDataSize);
748								AudioConverterDispose(converter);
749							}
750							if (tempData) free (tempData);
751						}
752#endif // TARGET_RT_BIG_ENDIAN
753
754					}
755				}
756			}
757			/*err =*/ AudioFileClose(audioFile);
758		}
759	}
760    else
761        alSetError(err);
762}
763
764// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
765ALUTAPI ALvoid ALUTAPIENTRY alutUnloadWAV(ALenum format,ALvoid *data,ALsizei size,ALsizei freq)
766{
767	if (data)
768		free(data);
769}
770
771// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
772// AL_MAIN functions
773AL_API ALvoid AL_APIENTRY alInit(ALint *argc, ALubyte **argv)
774{
775}
776
777// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
778AL_API ALvoid AL_APIENTRY alExit(void)
779{
780}
781
782#ifdef __cplusplus
783}
784#endif
785
786// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
787// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
788#pragma mark ***** OALRingBuffer *****
789// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
790// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
791// count the leading zeroes in a word
792static __inline__ int CountLeadingZeroes(int arg) {
793
794#if TARGET_CPU_X86 || TARGET_CPU_X86_64
795	__asm__ volatile(
796				"bsrl %0, %0\n\t"
797				"movl $63, %%ecx\n\t"
798				"cmove %%ecx, %0\n\t"
799				"xorl $31, %0"
800				: "=r" (arg)
801				: "0" (arg)
802			);
803#elif TARGET_CPU_PPC || TARGET_CPU_PPC64
804	__asm__ volatile("cntlzw %0, %1" : "=r" (arg) : "r" (arg));
805#else
806	#error "ERROR - assembly instructions for counting leading zeroes not present"
807#endif
808         return arg;
809}
810
811// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
812// base 2 log of next power of two greater or equal to x
813inline UInt32 Log2Ceil(UInt32 x)
814{
815	return 32 - CountLeadingZeroes(x - 1);
816}
817
818// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
819// next power of two greater or equal to x
820inline UInt32 NextPowerOfTwo(UInt32 x)
821{
822	return 1L << Log2Ceil(x);
823}
824