// -*- C++ -*- /* * Simple MultimEdia LiTerator(SMELT) * by Chris Xiong 2015 * GFX implementation based on OpenAL * * WARNING: This library is in development and interfaces would be very * unstable. * */ #include "smelt_internal.hpp" static const char* SFX_OAL_SRCFN="smelt/glfw/sfx_oal.cpp"; #ifndef ENABLE_DUMB #ifdef ENABLE_OGG_SUPPORT struct oggdata{const BYTE *data;DWORD size,pos;}; static void* readVorbis(const BYTE *data,const DWORD size, ALsizei *decomp_size,ALenum *fmt,ALsizei *freq); #endif static void* readRiffWv(const BYTE *data,const DWORD size, ALsizei *decomp_size,ALenum *fmt,ALsizei *freq); SMSFX SMELT_IMPL::smSFXLoad(const char *path) { FILE *pFile;DWORD size,rsize; char *buff; pFile=fopen(path,"rb"); if(!pFile)return 0; fseek(pFile,0,SEEK_END);size=ftell(pFile);rewind(pFile); buff=(char*)malloc(sizeof(char)*size); if(!buff)return 0; rsize=fread(buff,1,size,pFile); if(rsize!=size)return 0; SMSFX ret=smSFXLoadFromMemory(buff,size); free(buff); fclose(pFile); return ret; } SMSFX SMELT_IMPL::smSFXLoadFromMemory(const char *ptr,DWORD size) { if(pOpenALDevice&&!mute) { #ifdef ENABLE_OGG_SUPPORT bool isOgg=size>4&&ptr[0]=='O'&&ptr[1]=='g'&&ptr[2]=='g'&&ptr[3]=='S'; #endif void *decompdata=NULL,*decomp=NULL; ALsizei decompsize=0,freq=0; ALenum fmt=AL_FORMAT_STEREO16; decompdata=readRiffWv((const BYTE*)ptr,size,&decompsize,&fmt,&freq); #ifdef ENABLE_OGG_SUPPORT if(!decompdata) { if(!isOgg)return 0; else decompdata=readVorbis((const BYTE*)ptr,size,&decompsize,&fmt,&freq); } #endif if(!decompdata)return 0; decomp=decompdata; ALuint buff=0;alGenBuffers(1,&buff); alBufferData(buff,fmt,decomp,decompsize,freq); free(decompdata);return(SMSFX)buff; } return 0; } SMCHN SMELT_IMPL::smSFXPlay(SMSFX fx,int vol,int pan,float pitch,bool loop) { if(pOpenALDevice) { ALuint src=getSource(); if(src) { if(vol<0)vol=0;if(vol>100)vol=100; if(pan<-100)pan=-100;if(pan>100)pan=100; alSourceStop(src); alSourcei(src,AL_BUFFER,(ALint)fx); alSourcef(src,AL_GAIN,((ALfloat)vol)/100.); alSourcef(src,AL_PITCH,pitch); alSource3f(src,AL_POSITION,((ALfloat)pan)/100.,.0,.0); alSourcei(src,AL_LOOPING,loop?AL_TRUE:AL_FALSE); alSourcePlay(src); } return src; } return 0; } float SMELT_IMPL::smSFXGetLengthf(SMSFX fx) { ALuint buff=(ALuint)fx; ALint freq; DWORD lend=smSFXGetLengthd(fx); alGetBufferi(buff,AL_FREQUENCY,&freq); float ret=lend/(float)freq; return ret; } DWORD SMELT_IMPL::smSFXGetLengthd(SMSFX fx) { if(pOpenALDevice) { ALint size_b,chnc,bit; ALuint buff=(ALuint)fx; alGetBufferi(buff,AL_SIZE,&size_b); alGetBufferi(buff,AL_CHANNELS,&chnc); alGetBufferi(buff,AL_BITS,&bit); DWORD ret=size_b*8/(chnc*bit); return ret; } return -1; } void SMELT_IMPL::smSFXSetLoopPoint(SMSFX fx,DWORD l,DWORD r) { if(pOpenALDevice) { ALint pt[2];pt[0]=l;pt[1]=r; alBufferiv((ALuint)fx,AL_LOOP_POINTS_SOFT,pt); } } void SMELT_IMPL::smSFXFree(SMSFX fx) { if(pOpenALDevice) { ALuint buff=(ALuint)fx; alDeleteBuffers(1,&buff); } } void SMELT_IMPL::smChannelVol(SMCHN chn,int vol) { if(pOpenALDevice) { if(vol<0)vol=0;if(vol>100)vol=100; alSourcef((ALuint)chn,AL_GAIN,((ALfloat)vol)/100.); } } void SMELT_IMPL::smChannelPan(SMCHN chn,int pan) { if(pOpenALDevice) { if(pan<-100)pan=-100;if(pan>100)pan=100; alSource3f((ALuint)chn,AL_POSITION,((ALfloat)pan)/100.,.0,.0); } } void SMELT_IMPL::smChannelPitch(SMCHN chn,float pitch) { if(pOpenALDevice)alSourcef((ALuint)chn,AL_PITCH,pitch); } void SMELT_IMPL::smChannelPause(SMCHN chn) { if(pOpenALDevice)alSourcePause((ALuint)chn); } void SMELT_IMPL::smChannelResume(SMCHN chn) { if(pOpenALDevice)alSourcePlay((ALuint)chn); } void SMELT_IMPL::smChannelStop(SMCHN chn) { if(pOpenALDevice)alSourceStop((ALuint)chn); } void SMELT_IMPL::smChannelPauseAll() { if(pOpenALDevice) { ALCcontext *ctx=alcGetCurrentContext(); alcSuspendContext(ctx); } } void SMELT_IMPL::smChannelResumeAll() { if(pOpenALDevice) { ALCcontext *ctx=alcGetCurrentContext(); alcProcessContext(ctx); } } void SMELT_IMPL::smChannelStopAll() { if(pOpenALDevice) { for(int i=0;isize-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 oggSeek(void *ds,ogg_int64_t offset,int whence) { oggdata *data=(oggdata*)ds; 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 long oggLocate(void *ds) { oggdata* data=(oggdata*)ds; return (long)data->pos; } static int oggClose(void *ds){return 0;} static ov_callbacks oggrt={oggRead,oggSeek,oggClose,oggLocate}; static void* readVorbis(const BYTE *data,const DWORD size, ALsizei *decomp_size,ALenum *fmt,ALsizei *freq) { oggdata adata={data,size,0}; OggVorbis_File vf; memset(&vf,0,sizeof(vf)); if(ov_open_callbacks(&adata,&vf,NULL,0,oggrt)==0) { int bs=0; vorbis_info *info=ov_info(&vf,-1); *decomp_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=1024*16; BYTE *ret=(ALubyte*)malloc(allocated); while((rc=ov_read(&vf,buf,sizeof(buf),0,2,1,&bs))!=0) { if(rc>0) { *decomp_size+=rc; if((unsigned)*decomp_size>=allocated) { allocated<<=1; ALubyte *tmp=(ALubyte*)realloc(ret,allocated); if(!tmp){free(ret);ret=NULL;break;} ret=tmp; } memcpy(ret+(*decomp_size-rc),buf,rc); } } ov_clear(&vf); return ret; } return NULL; } #endif static void* readRiffWv(const BYTE *data,const DWORD size, ALsizei *decomp_size,ALenum *fmt,ALsizei *freq) { if(data[0x0]!='R'||data[0x1]!='I'||data[0x2]!='F'||data[0x3]!='F')return NULL; if(data[0x8]!='W'||data[0x9]!='A'||data[0xA]!='V'||data[0xB]!='E')return NULL; if(data[0xC]!='f'||data[0xD]!='m'||data[0xE]!='t'||data[0xF]!=' ')return NULL; if(data[0x16]==2) { if(data[0x22]==16)*fmt=AL_FORMAT_STEREO16; else if(data[0x22]==8)*fmt=AL_FORMAT_STEREO8; else return NULL; } else if(data[0x16]==1) { if(data[0x22]==16)*fmt=AL_FORMAT_MONO16; else if(data[0x22]==8)*fmt=AL_FORMAT_MONO8; else return NULL; }else return NULL; *decomp_size=(ALsizei)(data[0x28]|(data[0x29]<<8L)|(data[0x2A]<<16L)|(data[0x2B]<<24L)); *freq=(ALsizei)(data[0x18]|(data[0x19]<<8L)|(data[0x1A]<<16L)|(data[0x1B]<<24L)); ALubyte *ret=(ALubyte*)malloc(*decomp_size); memcpy(ret,data+44,*decomp_size); return ret; } ALuint SMELT_IMPL::getSource() { for(int i=0;i=SRC_MAX)return 0; ALuint ret=0; alGenSources(1,&ret);if(!ret)return 0; src[scnt++]=ret;return ret; } bool SMELT_IMPL::initOAL() { if(pOpenALDevice)return true; scnt=0;memset(src,0,sizeof(src)); smLog("%s:" SLINE ": Initializing OpenAL...\n",SFX_OAL_SRCFN); ALCdevice *dev=alcOpenDevice(NULL); if(!dev) { smLog("%s:" SLINE ": alcOpenDevice() failed.\n",SFX_OAL_SRCFN); return mute=true; } ALint caps[]={ALC_FREQUENCY,44100,0}; ALCcontext *ctx=alcCreateContext(dev,caps); if(!ctx) { smLog("%s:" SLINE ": alcCreateContext() failed.\n",SFX_OAL_SRCFN); return mute=true; } alcMakeContextCurrent(ctx);alcProcessContext(ctx); smLog("%s:" SLINE ": Done OpenAL initialization\n",SFX_OAL_SRCFN); smLog("%s:" SLINE ": AL_VENDOR: %s\n",SFX_OAL_SRCFN,(char*)alGetString(AL_VENDOR)); smLog("%s:" SLINE ": AL_RENDERER: %s\n",SFX_OAL_SRCFN,(char*)alGetString(AL_RENDERER)); smLog("%s:" SLINE ": AL_VERSION: %s\n",SFX_OAL_SRCFN,(char*)alGetString(AL_VERSION)); const char* ext=(const char*)alGetString(AL_EXTENSIONS); lpp=strstr(ext,"AL_SOFT_loop_points")!=NULL; if(!lpp)smLog("%s:" SLINE ": Warning: loop points not supported. Please recompile with OpenAL Soft.\n",SFX_OAL_SRCFN); pOpenALDevice=(void*)dev; return true; } void SMELT_IMPL::finiOAL() { if(pOpenALDevice) { for(int i=0;i