mirror of
https://github.com/ogoguel/activegs-ios.git
synced 2025-01-04 22:32:12 +00:00
663 lines
14 KiB
C++
663 lines
14 KiB
C++
/*
|
||
ActiveGS, Copyright 2004-2016 Olivier Goguel, https://github.com/ogoguel/ActiveGS
|
||
Based on Kegs, Copyright 2004 Kent Dickey, https://kegs.sourceforge.net
|
||
This code is covered by the GNU GPL licence
|
||
*/
|
||
|
||
#include "defc.h"
|
||
|
||
#if defined(DRIVER_IOS) || defined(DRIVER_OSX)
|
||
# include <AudioToolbox/AudioToolbox.h>
|
||
# import <OpenAL/alc.h>
|
||
# import <OpenAL/al.h>
|
||
# include <unistd.h>
|
||
#else
|
||
# include "../OpenAL/AL/alc.h"
|
||
# include "../OpenAL/AL/al.h"
|
||
#endif
|
||
|
||
#include "sound.h"
|
||
|
||
//extern int g_audio_rate;
|
||
|
||
#define AL_NUM_BUFFERS 8
|
||
#define AL_BUFFER_SIZE (SOUND_SHM_SAMP_SIZE*4*2)
|
||
ALuint source=0, buffers[AL_NUM_BUFFERS];
|
||
int sndQueues[AL_NUM_BUFFERS];
|
||
|
||
const ALenum format = AL_FORMAT_STEREO16;
|
||
unsigned char *al_tmp_buf[AL_NUM_BUFFERS][AL_BUFFER_SIZE];
|
||
ALCdevice *dev=NULL;
|
||
ALCcontext *ctx=NULL;
|
||
|
||
int nblog = 2000;
|
||
//#define SNDLOG(STR,VAL) if (nblog){outputInfo(STR,VAL); nblog--;}
|
||
//#define SNDLOG(STR,VAL) if (nblog){printf(STR,VAL); nblog--;}
|
||
#define SNDLOG(STR,VAL)
|
||
|
||
|
||
// http://www.subfurther.com/blog/?p=602
|
||
|
||
#if defined(DRIVER_IOS) || defined(DRIVER_OSX)
|
||
typedef ALvoid AL_APIENTRY (*alBufferDataStaticProcPtr) (const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq);
|
||
|
||
|
||
ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq)
|
||
{
|
||
|
||
static alBufferDataStaticProcPtr proc = NULL;
|
||
|
||
if (proc == NULL) {
|
||
proc = (alBufferDataStaticProcPtr) alGetProcAddress((const ALCchar*) "alBufferDataStatic");
|
||
}
|
||
|
||
if (proc)
|
||
proc(bid, format, data, size, freq);
|
||
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
void resetQueues()
|
||
{
|
||
|
||
for(int i=0;i<AL_NUM_BUFFERS;i++)
|
||
{
|
||
sndQueues[i]=0;
|
||
|
||
}
|
||
SNDLOG("resetQueues %d\n",0);
|
||
}
|
||
|
||
int getQueueFromBuffer(int _bid)
|
||
{
|
||
for(int i=0;i<AL_NUM_BUFFERS;i++)
|
||
{
|
||
if (buffers[i]==_bid)
|
||
return i;
|
||
}
|
||
SNDLOG("id %d not found\n",_bid);
|
||
|
||
return -1;
|
||
}
|
||
|
||
void freeQueue(int _id)
|
||
{
|
||
if (!sndQueues[_id])
|
||
{
|
||
SNDLOG("buffer id %d was not occupied\n",_id);
|
||
}
|
||
sndQueues[_id]=0;
|
||
}
|
||
|
||
int getAvailableQueue()
|
||
{
|
||
|
||
for(int i=0;i<AL_NUM_BUFFERS;i++)
|
||
{
|
||
if (!sndQueues[i])
|
||
{
|
||
sndQueues[i]=1;
|
||
return i;
|
||
}
|
||
}
|
||
|
||
SNDLOG("no available queue",0);
|
||
return -1;
|
||
}
|
||
|
||
int async_snd_send_audio(byte *ptr, int in_size);
|
||
|
||
int x_snd_send_audio(byte *ptr, int in_size)
|
||
{
|
||
|
||
return async_snd_send_audio(ptr,in_size);
|
||
|
||
}
|
||
|
||
|
||
void x_snd_child_init()
|
||
{
|
||
|
||
printf("x_snd_child_init");
|
||
|
||
if (!dev)
|
||
{
|
||
printf("err: async_sound_init not called\n");
|
||
return ;
|
||
}
|
||
set_audio_rate(g_sound.g_audio_rate);
|
||
}
|
||
|
||
void x_snd_init(word32 *shmaddr)
|
||
{
|
||
|
||
}
|
||
|
||
void x_snd_shutdown()
|
||
{
|
||
|
||
}
|
||
|
||
|
||
word32* x_sound_allocate(int size)
|
||
{
|
||
word32 *shmaddr;
|
||
shmaddr = (word32*)malloc(size);
|
||
memset(shmaddr, 0, size);
|
||
return shmaddr;
|
||
}
|
||
|
||
void x_sound_free(word32* _mem)
|
||
{
|
||
if (_mem)
|
||
free(_mem);
|
||
|
||
}
|
||
|
||
// ---------------------------------------------------------------------
|
||
|
||
void x_async_sound_init()
|
||
{
|
||
|
||
if (!g_sound.g_audio_enable)
|
||
{
|
||
printf("sound disabled : discarding openal driver\n");
|
||
return ;
|
||
}
|
||
|
||
if (dev)
|
||
{
|
||
printf("err: async_sound_init already initialized!");
|
||
return ;
|
||
}
|
||
dev = alcOpenDevice(NULL);
|
||
if(!dev)
|
||
{
|
||
fprintf(stderr, "Oops\n");
|
||
return ;
|
||
}
|
||
ctx = alcCreateContext(dev, NULL);
|
||
|
||
alcMakeContextCurrent(ctx);
|
||
if(!ctx)
|
||
{
|
||
fprintf(stderr, "Oops2\n");
|
||
return ;
|
||
}
|
||
|
||
alGenBuffers(AL_NUM_BUFFERS, buffers);
|
||
alGenSources(1, &source);
|
||
if(alGetError() != AL_NO_ERROR)
|
||
{
|
||
fprintf(stderr, "Error generating :(\n");
|
||
return ;
|
||
}
|
||
|
||
resetQueues();
|
||
|
||
x_preload_sounds();
|
||
}
|
||
|
||
|
||
void x_async_snd_shutdown()
|
||
{
|
||
|
||
if (!g_sound.g_audio_enable)
|
||
return ;
|
||
|
||
printf("async_snd_shutdown\n");
|
||
|
||
if (!dev)
|
||
{
|
||
printf("err: openal device not initialized\n");
|
||
return ;
|
||
}
|
||
alDeleteSources(1, &source);
|
||
source = 0;
|
||
alDeleteBuffers(AL_NUM_BUFFERS, buffers);
|
||
|
||
alcMakeContextCurrent(NULL);
|
||
alcDestroyContext(ctx);
|
||
ctx=NULL;
|
||
alcCloseDevice(dev);
|
||
dev=NULL;
|
||
|
||
}
|
||
|
||
int async_snd_send_audio(byte *ptr, int in_size)
|
||
{
|
||
|
||
|
||
int sizeref = in_size;
|
||
int nbw = 0;
|
||
// remplit les buffers avec la taille
|
||
while(in_size)
|
||
{
|
||
|
||
ALint val;
|
||
ALuint buffer = -1;
|
||
int ibuffer=1001;
|
||
|
||
int nb = MIN(AL_BUFFER_SIZE,in_size);
|
||
|
||
// trying to get an unprocess buffer
|
||
ALint processed=-1;
|
||
ALint queued=-1;
|
||
alGetSourcei (source, AL_BUFFERS_QUEUED, &queued);
|
||
SNDLOG("queued %d\n",queued);
|
||
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
|
||
SNDLOG("processed %d\n",processed);
|
||
|
||
// lib<69>re toute les queues
|
||
while(processed--)
|
||
{
|
||
alSourceUnqueueBuffers(source, 1, &buffer);
|
||
if(alGetError() != AL_NO_ERROR)
|
||
{
|
||
SNDLOG("#",0);
|
||
return sizeref;
|
||
}
|
||
ibuffer = getQueueFromBuffer(buffer);
|
||
freeQueue(ibuffer);
|
||
}
|
||
|
||
|
||
// r<>alloue un buffer
|
||
ibuffer = getAvailableQueue();
|
||
if (ibuffer<0)
|
||
{
|
||
SNDLOG("*",0);
|
||
return sizeref;
|
||
}
|
||
buffer = buffers[ibuffer];
|
||
|
||
SNDLOG("sendaudio: %d\n",in_size);
|
||
|
||
// on a assez de donnees pour remplir tout le buffer
|
||
|
||
memcpy(&al_tmp_buf[ibuffer][0],ptr+nbw,nb);
|
||
|
||
nbw += nb;
|
||
in_size -= nb;
|
||
|
||
|
||
// on peut l'envoyer
|
||
#if defined(DRIVER_IOS) || defined(DRIVER_OSX)
|
||
alBufferDataStaticProc
|
||
#else
|
||
alBufferData
|
||
#endif
|
||
(buffer, format, &al_tmp_buf[ibuffer][0], nb, g_sound.g_audio_rate);
|
||
|
||
if(alGetError() != AL_NO_ERROR)
|
||
{
|
||
printf("Error alBufferData :(%d)\n",ibuffer);
|
||
}
|
||
|
||
alSourceQueueBuffers(source, 1, &buffer);
|
||
if(alGetError() != AL_NO_ERROR)
|
||
{
|
||
printf( "Error alSourceQueueBuffers :(\n");
|
||
}
|
||
|
||
|
||
alGetSourcei(source, AL_SOURCE_STATE, &val);
|
||
if(alGetError() != AL_NO_ERROR)
|
||
{
|
||
printf( "Error alSourceQueueBuffers :(\n");
|
||
}
|
||
if(val != AL_PLAYING)
|
||
{
|
||
alSourcePlay(source);
|
||
if(alGetError() != AL_NO_ERROR)
|
||
{
|
||
printf( "Error alSourcePlay :(\n");
|
||
}
|
||
SNDLOG( "playSound\n",0);
|
||
|
||
}
|
||
}
|
||
return nbw;
|
||
}
|
||
|
||
|
||
|
||
OASound async_init_wav(const char* _file)
|
||
{
|
||
|
||
OASound sound;
|
||
memset(&sound,0,sizeof(OASound));
|
||
|
||
ALvoid* l_DataBuffer = NULL;
|
||
unsigned int l_FileSize = 0;
|
||
|
||
// OpenAL: first
|
||
if(x_load_wav(_file, (unsigned char**)&l_DataBuffer , l_FileSize,sound))
|
||
// if((l_DataBuffer != NULL) && (l_FileSize > 0))
|
||
{
|
||
if(l_DataBuffer == NULL)
|
||
{
|
||
printf("*** DataBuffer NULL!!! *** \n");
|
||
}
|
||
// grab a buffer ID from openAL
|
||
alGenBuffers(1, (ALuint*)&sound.m_BufferId);
|
||
|
||
ALenum l_error = alGetError();
|
||
|
||
if(l_error != AL_NO_ERROR)
|
||
{
|
||
printf("**** OpenAL ERROR buffer%i\n", l_error);
|
||
return sound;
|
||
}
|
||
else
|
||
{
|
||
printf("**** OpenAL OK buffer %i\n", sound.m_BufferId);
|
||
}
|
||
|
||
ALuint f;
|
||
if (sound.depth==16)
|
||
{
|
||
if (sound.nbchannel == 1)
|
||
f = AL_FORMAT_MONO16;
|
||
else
|
||
f = AL_FORMAT_STEREO16;
|
||
}
|
||
else
|
||
{
|
||
if (sound.nbchannel == 1)
|
||
f = AL_FORMAT_MONO8;
|
||
else
|
||
f = AL_FORMAT_STEREO8;
|
||
|
||
}
|
||
|
||
|
||
alBufferData(sound.m_BufferId, f, l_DataBuffer, l_FileSize, sound.frequency);
|
||
|
||
l_error = alGetError();
|
||
if(l_error != AL_NO_ERROR)
|
||
{
|
||
printf("**** OpenAL ERROR format\n");
|
||
return sound;
|
||
}
|
||
|
||
// create Source
|
||
alGenSources(1, (ALuint*)&sound.m_SourceId);
|
||
l_error = alGetError();
|
||
if(l_error != AL_NO_ERROR)
|
||
{
|
||
printf("**** OpenAL ERROR source\n");
|
||
return sound;
|
||
}
|
||
|
||
alSourcei(sound.m_SourceId, AL_BUFFER, sound.m_BufferId);
|
||
|
||
l_error = alGetError();
|
||
if(l_error != AL_NO_ERROR)
|
||
{
|
||
printf("**** OpenAL ERROR source buffer\n");
|
||
return sound;
|
||
}
|
||
|
||
// if loop:
|
||
//alSourcei(m_SourceId, AL_LOOPING, AL_TRUE);
|
||
|
||
alSourcef(sound.m_SourceId, AL_PITCH, 1.0f);
|
||
//l_error = alGetError();
|
||
|
||
//ASSERTC(l_error == AL_NO_ERROR,"**** OpenAL ERROR buffer\n");
|
||
//if(l_error != AL_NO_ERROR)
|
||
//{
|
||
// CONSOLEMSG("**** OpenAL ERROR properties\n");
|
||
// return FAILURE;
|
||
//}
|
||
|
||
alSourcef(sound.m_SourceId, AL_GAIN, 1.0f);
|
||
|
||
l_error = alGetError();
|
||
|
||
if(l_error != AL_NO_ERROR)
|
||
{
|
||
printf("**** OpenAL ERROR properties\n");
|
||
return sound;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
return sound;
|
||
}
|
||
|
||
if (l_DataBuffer)
|
||
{
|
||
free(l_DataBuffer);
|
||
l_DataBuffer = NULL;
|
||
}
|
||
|
||
//printf("**** OpenAL Resource load SUCCESS\n");
|
||
sound.successfullyLoaded = 1;
|
||
return sound;
|
||
}
|
||
|
||
int async_release_wav(OASound* _pSound)
|
||
{
|
||
if (!_pSound)
|
||
return 0;
|
||
|
||
|
||
if(_pSound->m_SourceId)
|
||
{
|
||
|
||
alDeleteSources(1, (ALuint*)&_pSound->m_SourceId);
|
||
_pSound->m_SourceId = 0;
|
||
|
||
}
|
||
if(_pSound->m_BufferId)
|
||
{
|
||
alDeleteBuffers(1, (ALuint*)&_pSound->m_BufferId);
|
||
_pSound->m_BufferId = 0;
|
||
}
|
||
_pSound->successfullyLoaded=0;
|
||
return 1;
|
||
}
|
||
|
||
|
||
// ****************************************************************************
|
||
//
|
||
// ****************************************************************************
|
||
int async_play_wav(OASound* _pSound, int _Loop, float _Attenuation, float _StartAt )
|
||
{
|
||
if (!_pSound)
|
||
return 0;
|
||
|
||
if (!_pSound->successfullyLoaded)
|
||
return 0;
|
||
|
||
|
||
if(!_pSound->m_SourceId)
|
||
return 0;
|
||
|
||
|
||
alSourcePlay(_pSound->m_SourceId);
|
||
|
||
int l_error = alGetError();
|
||
|
||
if(l_error != AL_NO_ERROR)
|
||
{
|
||
printf("**** OpenAL ERROR play %i\n", l_error);
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
// ****************************************************************************
|
||
//
|
||
// ****************************************************************************
|
||
int async_stop_wav(OASound* _pSound)
|
||
{
|
||
if (!_pSound)
|
||
return 0;
|
||
|
||
if (!_pSound->successfullyLoaded)
|
||
return 0;
|
||
|
||
if(!_pSound->m_SourceId)
|
||
return 0;
|
||
|
||
|
||
alSourceStop(_pSound->m_SourceId);
|
||
return 1;
|
||
}
|
||
|
||
void x_play_sound(enum_sound _hSound)
|
||
{
|
||
|
||
async_play_wav(&g_system_sounds[_hSound].sound,0,0,0);
|
||
|
||
}
|
||
|
||
int x_is_sound_playing(enum_sound _hSound)
|
||
{
|
||
OASound& sound = g_system_sounds[_hSound].sound;
|
||
if (sound.m_SourceId==0)
|
||
return 0;
|
||
ALint val;
|
||
alGetSourcei(sound.m_SourceId, AL_SOURCE_STATE, &val);
|
||
return (val == AL_PLAYING);
|
||
}
|
||
|
||
|
||
#if 1
|
||
|
||
#pragma push()
|
||
#pragma pack(1)
|
||
/*
|
||
* Struct to hold fmt subchunk data for WAVE files.
|
||
*/
|
||
struct WAVE_Format {
|
||
char subChunkID[4];
|
||
int subChunkSize;
|
||
short audioFormat;
|
||
short numChannels;
|
||
int sampleRate;
|
||
int byteRate;
|
||
short blockAlign;
|
||
short bitsPerSample;
|
||
} __attribute__((packed));
|
||
|
||
/*
|
||
* Struct to hold the data of the wave file
|
||
*/
|
||
struct WAVE_Data {
|
||
char subChunkID[4]; //should contain the word data
|
||
int subChunk2Size; //Stores the size of the data block
|
||
} __attribute__((packed));
|
||
|
||
struct RIFF_Header {
|
||
char chunkID[4];
|
||
int chunkSize;//size not including chunkSize or chunkID
|
||
char format[4];
|
||
} __attribute__((packed));
|
||
|
||
#pragma pop()
|
||
|
||
|
||
bool loadWavFile(const char* filename, unsigned char** buffer,
|
||
unsigned int* size, int* frequency,
|
||
int* depth,int* nbchannel) {
|
||
//Local Declarations
|
||
FILE* soundFile = NULL;
|
||
WAVE_Format wave_format;
|
||
RIFF_Header riff_header;
|
||
WAVE_Data wave_data;
|
||
unsigned char* data;
|
||
|
||
|
||
soundFile = fopen(filename, "rb");
|
||
if (!soundFile)
|
||
{
|
||
printf("could not open %s\n",filename);
|
||
return false;
|
||
}
|
||
|
||
// Read in the first chunk into the struct
|
||
fread(&riff_header, sizeof(RIFF_Header), 1, soundFile);
|
||
|
||
//check for RIFF and WAVE tag in memeory
|
||
if ((riff_header.chunkID[0] != 'R' ||
|
||
riff_header.chunkID[1] != 'I' ||
|
||
riff_header.chunkID[2] != 'F' ||
|
||
riff_header.chunkID[3] != 'F') ||
|
||
(riff_header.format[0] != 'W' ||
|
||
riff_header.format[1] != 'A' ||
|
||
riff_header.format[2] != 'V' ||
|
||
riff_header.format[3] != 'E'))
|
||
{
|
||
fclose(soundFile);
|
||
printf ("Invalid RIFF or WAVE Header in %s\n",filename);
|
||
return false;
|
||
}
|
||
|
||
//Read in the 2nd chunk for the wave info
|
||
fread(&wave_format, sizeof(WAVE_Format), 1, soundFile);
|
||
//check for fmt tag in memory
|
||
if (wave_format.subChunkID[0] != 'f' ||
|
||
wave_format.subChunkID[1] != 'm' ||
|
||
wave_format.subChunkID[2] != 't' ||
|
||
wave_format.subChunkID[3] != ' ')
|
||
{
|
||
fclose(soundFile);
|
||
printf ("Invalid Wave Format");
|
||
return false;
|
||
}
|
||
|
||
|
||
//check for extra parameters;
|
||
if (wave_format.subChunkSize > 16)
|
||
fseek(soundFile, sizeof(short), SEEK_CUR);
|
||
|
||
//Read in the the last byte of data before the sound file
|
||
fread(&wave_data, sizeof(WAVE_Data), 1, soundFile);
|
||
//check for data tag in memory
|
||
if (wave_data.subChunkID[0] != 'd' ||
|
||
wave_data.subChunkID[1] != 'a' ||
|
||
wave_data.subChunkID[2] != 't' ||
|
||
wave_data.subChunkID[3] != 'a')
|
||
{
|
||
|
||
fclose(soundFile);
|
||
printf ( "Invalid data header");
|
||
return false;
|
||
}
|
||
|
||
//Allocate memory for data
|
||
data = (unsigned char*)malloc(wave_data.subChunk2Size);
|
||
|
||
// Read in the sound data into the soundData variable
|
||
if (!fread(data, wave_data.subChunk2Size, 1, soundFile))
|
||
{
|
||
free(data);
|
||
fclose(soundFile);
|
||
printf ( "error loading WAVE data into struct!");
|
||
return false;
|
||
}
|
||
|
||
//Now we set the variables that we passed in with the
|
||
//data from the structs
|
||
*size = wave_data.subChunk2Size;
|
||
*frequency = wave_format.sampleRate;
|
||
*nbchannel = wave_format.numChannels ;
|
||
*depth = wave_format.bitsPerSample;
|
||
*buffer = data;
|
||
fclose(soundFile);
|
||
return true;
|
||
}
|
||
|
||
bool x_load_wav(const char* _Path, unsigned char** _outDataBuffer, unsigned int & _outFileSize, OASound & _sound )
|
||
{
|
||
|
||
return loadWavFile(_Path,_outDataBuffer,&_outFileSize,&_sound.frequency,&_sound.depth,&_sound.nbchannel);
|
||
}
|
||
|
||
#endif
|