aboutsummaryrefslogtreecommitdiff
path: root/archive/hge/sound.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'archive/hge/sound.cpp')
-rw-r--r--archive/hge/sound.cpp531
1 files changed, 531 insertions, 0 deletions
diff --git a/archive/hge/sound.cpp b/archive/hge/sound.cpp
new file mode 100644
index 0000000..9024652
--- /dev/null
+++ b/archive/hge/sound.cpp
@@ -0,0 +1,531 @@
+/*
+** Haaf's Game Engine 1.8
+** Copyright (C) 2003-2007, Relish Games
+** hge.relishgames.com
+**
+** Core functions implementation: audio routines
+*/
+
+
+// This is just enough to get Hammerfight working without using libBA$$.
+// If you want a full HGE audio implementation, you should either use the
+// code in sound_libbass.cpp (and maybe pay for a BA$$ licen$e), or improve
+// this code.
+// Well, this code is now improved by Chris Xiong, adding several new interfaces.
+// (Such as seeking in sample...)
+// Channel functions are fully supported now. However music and streaming are
+// still not supported. Some APIs changed for OpenAL is different from BA$$.
+
+#include "hge_impl.h"
+
+#include "AL/al.h"
+#include "AL/alc.h"
+#include "AL/alext.h"
+#include "ogg/ogg.h"
+#include "vorbis/vorbisfile.h"
+static const char* SOUND_SRC_FN="hge/sound.cpp";
+struct oggcbdata
+{
+ const BYTE *data;
+ DWORD size;
+ DWORD pos;
+};
+
+static size_t oggcb_read(void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+ oggcbdata *data = (oggcbdata *) datasource;
+ const DWORD avail = data->size - data->pos;
+ size_t want = nmemb * size;
+ if (want > avail)
+ want = avail - (avail % size);
+ if (want > 0)
+ {
+ memcpy(ptr, data->data + data->pos, want);
+ data->pos += want;
+ }
+ return want / size;
+}
+
+static int oggcb_seek(void *datasource, ogg_int64_t offset, int whence)
+{
+ oggcbdata *data = (oggcbdata *) datasource;
+ ogg_int64_t pos = 0;
+ switch (whence)
+ {
+ case SEEK_SET: pos = offset; break;
+ case SEEK_CUR: pos = ((ogg_int64_t) data->pos) + offset; break;
+ case SEEK_END: pos = ((ogg_int64_t) data->size) + offset; break;
+ default: return -1;
+ }
+
+ if ( (pos < 0) || (pos > ((ogg_int64_t) data->size)) )
+ return -1;
+
+ data->pos = (DWORD) pos;
+ return 0;
+}
+
+static int oggcb_close(void *datasource)
+{
+ return 0;
+}
+
+static long oggcb_tell(void *datasource)
+{
+ oggcbdata *data = (oggcbdata *) datasource;
+ return (long) data->pos;
+}
+
+static ov_callbacks oggcb = { oggcb_read, oggcb_seek, oggcb_close, oggcb_tell };
+
+static void *decompress_vorbis(const BYTE *data, const DWORD size, ALsizei *decompressed_size, ALenum *fmt, ALsizei *freq)
+{
+#ifdef __POWERPC__
+ const int bigendian = 1;
+#else
+ const int bigendian = 0;
+#endif
+
+ oggcbdata cbdata = { data, size, 0 };
+ OggVorbis_File vf;
+ memset(&vf, '\0', sizeof (vf));
+ if (ov_open_callbacks(&cbdata, &vf, NULL, 0, oggcb) == 0)
+ {
+ int bitstream = 0;
+ vorbis_info *info = ov_info(&vf, -1);
+
+ *decompressed_size = 0;
+ *fmt = (info->channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
+ *freq = info->rate;
+
+ if ((info->channels != 1) && (info->channels != 2))
+ {
+ ov_clear(&vf);
+ return NULL;
+ }
+
+ char buf[1024 * 16];
+ long rc = 0;
+ size_t allocated = 64 * 1024;
+ BYTE *retval = (ALubyte *) malloc(allocated);
+ while ( (rc = ov_read(&vf, buf, sizeof (buf), bigendian, 2, 1, &bitstream)) != 0 )
+ {
+ if (rc > 0)
+ {
+ *decompressed_size += rc;
+ if ((unsigned)*decompressed_size >= allocated)
+ {
+ allocated *= 2;
+ ALubyte *tmp = (ALubyte *) realloc(retval, allocated);
+ if (tmp == NULL)
+ {
+ free(retval);
+ retval = NULL;
+ break;
+ }
+ retval = tmp;
+ }
+ memcpy(retval + (*decompressed_size - rc), buf, rc);
+ }
+ }
+ ov_clear(&vf);
+ return retval;
+ }
+
+ return NULL;
+}
+
+#define MAX_SIDS 128
+static int sidcount = 0;
+static ALuint sids[MAX_SIDS];
+
+static ALuint get_source()
+{
+ for (int i = 0; i < sidcount; i++)
+ {
+ ALint state = AL_PLAYING;
+ alGetSourceiv(sids[i], AL_SOURCE_STATE, &state);
+ if ((state != AL_PLAYING) && (state != AL_PAUSED))
+ return sids[i];
+ }
+ if (sidcount >= MAX_SIDS)
+ return 0;
+
+ ALuint sid = 0;
+ alGenSources(1, &sid);
+ if (sid == 0)
+ return 0;
+ sids[sidcount++] = sid;
+ return sid;
+}
+
+
+HEFFECT CALL HGE_Impl::Effect_Load(const char *filename, DWORD size)
+{
+ DWORD _size;
+ void *data;
+
+ if(hOpenAL)
+ {
+ if(bSilent) return 1;
+
+ if(size) { data=(void *)filename; _size=size; }
+ else
+ {
+ data=Resource_Load(filename, &_size);
+ if(!data) return 0;
+ }
+
+ const BYTE *magic = (const BYTE *) data;
+ const bool isOgg = ( (_size > 4) &&
+ (magic[0] == 'O') && (magic[1] == 'g') &&
+ (magic[2] == 'g') && (magic[3] == 'S') );
+ if (!isOgg)
+ {
+ if(!size) Resource_Free(data);
+ return 0;
+ }
+ void *allocation_decompressed = NULL;
+ void *decompressed = NULL;
+ ALsizei decompressed_size = 0;
+ ALsizei freq = 0;
+ ALenum fmt = AL_FORMAT_STEREO16;
+ if (isOgg)
+ {
+ /*if (alIsExtensionPresent((const ALchar *) "AL_EXT_vorbis"))//useless
+ {
+ fmt = alGetEnumValue((const ALchar *) "AL_FORMAT_VORBIS_EXT");
+ decompressed = data;
+ decompressed_size = _size;
+ }
+ else
+ {*/
+ allocation_decompressed = decompress_vorbis((const BYTE *) data, _size, &decompressed_size, &fmt, &freq);
+ decompressed = allocation_decompressed;
+ //}
+ }
+
+ ALuint bid = 0;
+ alGenBuffers(1, &bid);
+ alBufferData(bid, fmt, decompressed, decompressed_size, freq);
+ free(allocation_decompressed); // not delete[] !
+ if(!size) Resource_Free(data);
+ return (HEFFECT) bid;
+ }
+ else return 0;
+}
+
+HCHANNEL CALL HGE_Impl::Effect_Play(HEFFECT eff)
+{
+ return Effect_PlayEx(eff, 1.0f, 0, 1.0f, false);
+}
+
+HCHANNEL CALL HGE_Impl::Effect_PlayEx(HEFFECT eff, float volume, float pan, float pitch, bool loop)
+{
+ if(hOpenAL)
+ {
+ const ALuint sid = get_source(); // find an unused sid, or generate a new one.
+ if (sid != 0)
+ {
+ if (volume < 0) volume = 0; else if (volume > 1.0) volume = 1.0;
+ if (pan < -1.0) pan = -1.0; else if (pan > 1.0) pan = 1.0;
+ alSourceStop(sid);
+ alSourcei(sid, AL_BUFFER, (ALint) eff);
+ alSourcef(sid, AL_GAIN, volume);
+ alSourcef(sid, AL_PITCH, pitch);
+ alSource3f(sid, AL_POSITION, pan, 0.0f, 0.0f);
+ alSourcei(sid, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
+ alSourcePlay(sid);
+ }
+ return sid;
+ }
+ else return 0;
+}
+
+void CALL HGE_Impl::Effect_Free(HEFFECT eff)
+{
+ if(hOpenAL)
+ {
+ ALuint bid = (ALuint) eff;
+ alDeleteBuffers(1, &bid);
+ }
+}
+//Castrate!!
+HMUSIC CALL HGE_Impl::Music_Load(const char *filename, DWORD size){return 0;}
+
+HCHANNEL CALL HGE_Impl::Music_Play(HMUSIC mus, bool loop, int volume, int order, int row){return 0;}
+
+void CALL HGE_Impl::Music_Free(HMUSIC mus){}
+
+void CALL HGE_Impl::Music_SetAmplification(HMUSIC music, int ampl){}
+
+int CALL HGE_Impl::Music_GetAmplification(HMUSIC music){return -1;}
+
+int CALL HGE_Impl::Music_GetLength(HMUSIC music){return -1;}
+
+void CALL HGE_Impl::Music_SetPos(HMUSIC music, int order, int row){}
+
+bool CALL HGE_Impl::Music_GetPos(HMUSIC music, int *order, int *row){return false;}
+
+void CALL HGE_Impl::Music_SetInstrVolume(HMUSIC music, int instr, int volume){}
+
+int CALL HGE_Impl::Music_GetInstrVolume(HMUSIC music, int instr){return -1;}
+
+void CALL HGE_Impl::Music_SetChannelVolume(HMUSIC music, int channel, int volume){}
+
+int CALL HGE_Impl::Music_GetChannelVolume(HMUSIC music, int channel){return -1;}
+
+HSTREAM CALL HGE_Impl::Stream_Load(const char *filename, DWORD size){return 0;}
+
+void CALL HGE_Impl::Stream_Free(HSTREAM stream){}
+
+HCHANNEL CALL HGE_Impl::Stream_Play(HSTREAM stream, bool loop, int volume){return 0;}
+
+void CALL HGE_Impl::Channel_SetPanning(HCHANNEL chn, float pan)
+{
+ if(pan>1.0)pan=1.0;
+ if(pan<-1.0)pan=-1.0;
+ if(hOpenAL)
+ {
+ alSource3f((ALuint) chn, AL_POSITION, pan, 0.0f, 0.0f);
+ }
+}
+
+void CALL HGE_Impl::Channel_SetVolume(HCHANNEL chn, float volume)
+{
+ if(hOpenAL)
+ {
+ if (volume < 0) volume = 0; else if (volume > 1.0f) volume = 1.0f;
+ alSourcef((ALuint) chn, AL_GAIN, volume);
+ }
+}
+
+void CALL HGE_Impl::Channel_SetPitch(HCHANNEL chn, float pitch)
+{
+ if(hOpenAL)
+ {
+ alSourcef((ALuint) chn, AL_PITCH, pitch);
+ }
+}
+
+void CALL HGE_Impl::Channel_Pause(HCHANNEL chn)
+{
+ if(hOpenAL)
+ {
+ alSourcePause((ALuint) chn);
+ }
+}
+
+void CALL HGE_Impl::Channel_Resume(HCHANNEL chn)
+{
+ if(hOpenAL)
+ {
+ alSourcePlay((ALuint) chn);
+ }
+}
+
+void CALL HGE_Impl::Channel_Stop(HCHANNEL chn)
+{
+ if(hOpenAL)
+ {
+ alSourceStop((ALuint) chn);
+ }
+}
+
+void CALL HGE_Impl::Channel_PauseAll()
+{
+ if(hOpenAL)
+ {
+ ALCcontext *ctx = alcGetCurrentContext();
+ alcSuspendContext(ctx);
+ }
+}
+
+void CALL HGE_Impl::Channel_ResumeAll()
+{
+ if(hOpenAL)
+ {
+ ALCcontext *ctx = alcGetCurrentContext();
+ alcProcessContext(ctx);
+ }
+}
+
+void CALL HGE_Impl::Channel_StopAll()
+{
+ if(hOpenAL)
+ {
+ for (int i = 0; i < sidcount; i++)
+ alSourceStop(sids[i]);
+ }
+}
+
+bool CALL HGE_Impl::Channel_IsPlaying(HCHANNEL chn)
+{
+ if(hOpenAL)
+ {
+ ALint state = AL_STOPPED;
+ alGetSourceiv((ALuint) chn, AL_SOURCE_STATE, &state);
+ return state == AL_PLAYING;
+ }
+ else return false;
+}
+//The following features are ported to OpenAL by Chris
+float CALL HGE_Impl::Channel_GetLength(HCHANNEL chn)
+//WARNING!!:In OpenAL We pass HEFFECT insteat HCHANNEL in!
+//This should be fixed.
+{
+ //Well, you developers should know this "by heart".
+ if (hOpenAL)
+ {
+ ALint sizeInBytes;
+ ALint channels;
+ ALint bits;
+ ALuint bufferID=chn;
+ alGetBufferi(bufferID, AL_SIZE, &sizeInBytes);
+ alGetBufferi(bufferID, AL_CHANNELS, &channels);
+ alGetBufferi(bufferID, AL_BITS, &bits);
+ int lengthInSamples = sizeInBytes * 8 / (channels * bits);
+ ALint frequency;
+ alGetBufferi(bufferID, AL_FREQUENCY, &frequency);
+ float durationInSeconds = (float)lengthInSamples / (float)frequency;
+ return durationInSeconds;
+ }
+ return -1;
+}
+
+float CALL HGE_Impl::Channel_GetPos(HCHANNEL chn)
+{
+ if (hOpenAL)
+ {
+ ALfloat res;
+ alGetSourcef((ALuint)chn,AL_SEC_OFFSET,&res);
+ return (float)res;
+ }
+ else return -1.0f;
+}
+
+void CALL HGE_Impl::Channel_SetPos(HCHANNEL chn, float fSeconds)
+{
+ if (hOpenAL)
+ {
+ alSourcef((ALuint)chn,AL_SEC_OFFSET,(ALfloat)fSeconds);
+ }
+}
+
+int CALL HGE_Impl::Channel_GetPos_BySample(HCHANNEL chn)
+{
+ if (hOpenAL)
+ {
+ ALint res;
+ alGetSourcei((ALuint)chn,AL_SAMPLE_OFFSET,&res);
+ return (int)res;
+ }
+ else return -1;
+}
+
+void CALL HGE_Impl::Channel_SetPos_BySample(HCHANNEL chn, int iSample)
+{
+ if (hOpenAL)
+ {
+ alSourcei((ALuint)chn,AL_SAMPLE_OFFSET,(ALint)iSample);
+ }
+}
+
+void CALL HGE_Impl::Channel_SlideTo(HCHANNEL channel, float time, int volume, int pan, float pitch){}
+
+bool CALL HGE_Impl::Channel_IsSliding(HCHANNEL channel){return false;}
+
+
+//////// Implementation ////////
+
+
+bool HGE_Impl::_SoundInit()
+{
+ if(!bUseSound || hOpenAL) return true;
+
+ bSilent=false;
+
+ sidcount = 0;
+ memset(sids, '\0', sizeof (sids));
+
+ System_Log("%s: Starting OpenAL init",SOUND_SRC_FN);
+
+ ALCdevice *dev = alcOpenDevice(NULL);
+ if (!dev)
+ {
+ System_Log("%s: alcOpenDevice(NULL) failed, using no sound",SOUND_SRC_FN);
+ bSilent=true;
+ return true;
+ }
+
+ ALint caps[] = { ALC_FREQUENCY, nSampleRate, 0 };
+ ALCcontext *ctx = alcCreateContext(dev, caps);
+ if (!ctx)
+ {
+ alcCloseDevice(dev);
+ System_Log("%s: alcCreateContext(NULL) failed, using no sound",SOUND_SRC_FN);
+ bSilent=true;
+ return true;
+ }
+
+ alcMakeContextCurrent(ctx);
+ alcProcessContext(ctx);
+
+ System_Log("%s: OpenAL initialized successfully.",SOUND_SRC_FN);
+ System_Log("%s: AL_VENDOR: %s",SOUND_SRC_FN, (char *) alGetString(AL_VENDOR));
+ System_Log("%s: AL_RENDERER: %s",SOUND_SRC_FN, (char *) alGetString(AL_RENDERER));
+ System_Log("%s: AL_VERSION: %s",SOUND_SRC_FN,(char *) alGetString(AL_VERSION));
+ System_Log("%s: AL_EXTENSIONS: %s",SOUND_SRC_FN,(char *) alGetString(AL_EXTENSIONS));
+
+ hOpenAL = (void *) 0x1; // something non-NULL (!!! FIXME: this should eventually be a library handle).
+
+ _SetFXVolume(nFXVolume);
+ //_SetMusVolume(nMusVolume);
+ //_SetStreamVolume(nStreamVolume);
+
+ return true;
+}
+
+void HGE_Impl::_SoundDone()
+{
+ CStreamList *stmItem=streams, *stmNext;
+
+ if(hOpenAL)
+ {
+ for (int i = 0; i < sidcount; i++)
+ alSourceStop(sids[i]);
+ alDeleteSources(sidcount, sids);
+ sidcount = 0;
+ memset(sids, '\0', sizeof (sids));
+
+ ALCcontext *ctx = alcGetCurrentContext();
+ ALCdevice *dev = alcGetContextsDevice(ctx);
+ alcMakeContextCurrent(NULL);
+ alcSuspendContext(ctx);
+ alcDestroyContext(ctx);
+ alcCloseDevice(dev);
+
+ hOpenAL=0;
+
+ while(stmItem)
+ {
+ stmNext=stmItem->next;
+ Resource_Free(stmItem->data);
+ delete stmItem;
+ stmItem=stmNext;
+ }
+ streams=0;
+ }
+}
+
+void HGE_Impl::_SetMusVolume(int vol){}
+
+void HGE_Impl::_SetStreamVolume(int vol){}
+
+void HGE_Impl::_SetFXVolume(int vol)
+{
+ if(hOpenAL)
+ {
+ alListenerf(AL_GAIN, ((ALfloat) vol) / 100.0f);
+ }
+}