#ifndef DEPENDENCY_AUDIO_OUT //Stubs: void snd_mainloop(){return;} void snd_init(){return;} void snd_un_init(){return;} #else #ifdef QB64_BACKSLASH_FILESYSTEM #include "AL\\al.h" #include "AL\\alc.h" #include #include #include #include #include #else #include "AL/al.h" #include "AL/alc.h" #include #include #include #include #include #endif //forward refs (with no struct dependencies) void sndsetup(); void sndclose_now(int32 handle); void sub__sndstop(int32 handle); void sub__sndplay(int32); uint8 *soundwave(double frequency,double length,double volume,double fadein,double fadeout); void qb64_generatesound(double f,double l,uint8 wait); void qb64_internal_sndraw(uint8* data,int32 bytes,int32 block); double func__sndrawlen(int32 handle,int32 passed); //global variables int32 qb64_sndraw_lock=0; int32 qb64_internal_sndraw_handle=0; int64 qb64_internal_sndraw_lastcall=0; int64 qb64_internal_sndraw_prepad=0; int64 qb64_internal_sndraw_postpad=0; int32 soundwave_bytes=0; int32 snd_frequency=44100; int32 snd_buffer_size=16384; int32 snd_allow_internal=0;//set this flag before calling snd_... commands with an internal sound uint8 *soundwave(double frequency,double length,double volume,double fadein,double fadeout){ //this creates 16bit signed stereo data sndsetup(); static uint8 *data; static int32 i; static int16 x,lastx; static int16* sp; static double samples_per_second; samples_per_second=snd_frequency; //calculate total number of samples required static double samples; static int32 samplesi; samples=length*samples_per_second; samplesi=samples; if (!samplesi) samplesi=1; soundwave_bytes=samplesi*4; data=(uint8*)malloc(soundwave_bytes); sp=(int16*)data; static int32 direction; direction=1; static double value; value=0; static double volume_multiplier; volume_multiplier=volume*32767.0; static int32 waveend; waveend=0; static double gradient; //frequency*4.0*length is the total distance value will travel (+1,-2,+1[repeated]) //samples is the number of steps to do this in if (samples) gradient=(frequency*4.0*length)/samples; else gradient=0;//avoid division by 0 lastx=1;//set to 1 to avoid passing initial comparison for (i=0;i0){ if (lastx<=0){ waveend=i; } } lastx=x; if (direction){ if ((value+=gradient)>=1.0){direction=0; value=2.0-value;} }else{ if ((value-=gradient)<=-1.0){direction=1; value=-2.0-value;} } }//i if (waveend) soundwave_bytes=waveend*4; return (uint8*)data; } int32 wavesize(double length){ static int32 samples; samples=length*(double)snd_frequency; if (samples==0) samples=1; return samples*4; } void sub_sound(double frequency,double lengthinclockticks){ sndsetup(); if (new_error) return; //note: there are 18.2 clock ticks per second if ((frequency<37.0)&&(frequency!=0)) goto error; if (frequency>32767.0) goto error; if (lengthinclockticks<0.0) goto error; if (lengthinclockticks>65535.0) goto error; if (lengthinclockticks==0.0) return; qb64_generatesound(frequency,lengthinclockticks/18.2,1); return; error: error(5); } struct snd_sequence_struct{ uint16 *data; int32 data_size; uint8 channels;//note: more than 2 channels may be supported in the future uint16 *data_left; int32 data_left_size; uint16 *data_right; int32 data_right_size; //origins of data (only relevent before src) uint8 endian;//0=native, 1=little(Windows, x86), 2=big(Motorola, Xilinx Microblaze, IBM POWER) uint8 is_unsigned;//1=unsigned, 0=signed(most common) int32 sample_rate;//eg. 11025, 22100 int32 bits_per_sample;//eg. 8, 16 int32 references;//number of SND handles dependent on this }; list *snd_sequences=list_new(sizeof(snd_sequence_struct)); struct snd_struct{ uint8 internal;//1=internal uint8 type;//1=RAW, 2=SEQUENCE //sequence snd_sequence_struct *seq; //----part specific variables---- ALuint al_seq_left_buffer; ALuint al_seq_right_buffer; ALenum al_seq_format; ALsizei al_seq_freq; ALboolean al_seq_loop; ALuint al_seq_left_source; ALuint al_seq_right_source; //------------------------------- float volume; uint8 volume_update; uint8 close; uint8 limit_state;//0=off, 1=awaiting start[duration has been set], 2=waiting for stop point double limit_duration; int64 limit_stop_point; //locks uint8 setpos_lock_release; uint8 setpos_update; float setpos; float bal_left_x,bal_left_y,bal_left_z; float bal_right_x,bal_right_y,bal_right_z; uint8 bal_update; //usage of buffer depends heavily on type uint8 *buffer; int32 buffer_size; ptrszint *stream_buffer;//pointers to buffers int32 stream_buffer_last; int32 stream_buffer_start; int32 stream_buffer_next; ALuint al_source; ALuint *al_buffers;//[4] uint8 *al_buffer_state;//[4] 0=never used, 1=processing, 2=processed int32 *al_buffer_index;//[4] int64 raw_close_time; //The maximum number of buffers on iOS and on OS X is 1024. //The maximum number of sources is 32 on iOS and 256 on OS X. //therefore: inactive sources should be de-initialized & buffers should be 4 uint8 state; }; #define SND_STATE_STOPPED 0 #define SND_STATE_PLAYING 1 #define SND_STATE_PAUSED 2 list *snd_handles=list_new(sizeof(snd_struct)); void sndsetup(){ static int32 sndsetup_called=0; if (!sndsetup_called){ sndsetup_called=1; //... } //scan through all sounds and close marked ones, performed here to avoid thread issues static int32 list_index; for (list_index=1;list_index<=snd_handles->indexes;list_index++){ static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,list_index); if (snd){ if (snd->close==2){ sndclose_now(list_index); } }//snd }//list_index }//sndsetup void sub_beep(){ sndsetup(); qb64_generatesound(783.99,0.2,0); sub__delay(0.25); } ALCdevice *dev; ALCcontext *ctx; struct stat statbuf; void snd_un_init(){ alcCloseDevice(dev); return; } int32 snd_init_done=0; void snd_init(){ if (!snd_init_done){ dev = alcOpenDevice(NULL); if(!dev) exit(111); ctx = alcCreateContext(dev, NULL); alcMakeContextCurrent(ctx); if(!ctx) exit(222); alListener3f(AL_POSITION, 0, 0, 0); alListener3f(AL_VELOCITY, 0, 0, 0); alListener3f(AL_ORIENTATION, 0, 0, -1);//facing 'forward' on rhs co-ordinate system alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); } snd_init_done=1; } //OPENAL int32 snd_raw_channel=0; int32 func__sndopenraw(){ static int32 handle; handle=list_add(snd_handles); static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); snd->internal=0; snd->type=1; snd->buffer=(uint8*)malloc(snd_buffer_size); snd->buffer_size=0; snd->stream_buffer_last=65536; snd->stream_buffer=(ptrszint*)malloc(sizeof(ptrszint)*(snd->stream_buffer_last+1));//range: 1-65536 (0 ignored) snd->stream_buffer_start=0; snd->stream_buffer_next=1; alGenSources(1,&snd->al_source); //if(alGetError()!=AL_NO_ERROR) return 0; snd->al_buffers=(ALuint*)malloc(sizeof(ALuint)*4); alGenBuffers(4,snd->al_buffers); //if(alGetError()!=AL_NO_ERROR) return 0; snd->al_buffer_state=(uint8*)calloc(4,1); snd->al_buffer_index=(int32*)calloc(4,4); return handle; } void sub__sndraw(float left,float right,int32 handle,int32 passed){ if (passed&2){ if (handle==0) return;//note: this would be an invalid handle }else{ if (!snd_raw_channel) snd_raw_channel=func__sndopenraw(); handle=snd_raw_channel; } static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd) goto error; if (snd->internal) goto error; if (snd->type!=1) goto error; while (qb64_sndraw_lock) Sleep(0); qb64_sndraw_lock=1; if (handle==qb64_internal_sndraw_handle) qb64_internal_sndraw_lastcall=GetTicks(); static int16 sample_left; sample_left=left*32767.0; static int16 sample_right; if (passed&1) sample_right=right*32767.0; else sample_right=sample_left; //add sample if (snd->buffer_sizebuffer+snd->buffer_size)=sample_left; snd->buffer_size+=2; *(int16*)(snd->buffer+snd->buffer_size)=sample_right; snd->buffer_size+=2; } if (snd->buffer_size==snd_buffer_size){ //detach buffer static uint8 *buffer; buffer=snd->buffer; //create new buffer snd->buffer=(uint8*)malloc(snd_buffer_size); snd->buffer_size=0; //attach detached buffer to stream (or discard it) static int32 p,p2; p=snd->stream_buffer_next; p2=p+1; if (p2>snd->stream_buffer_last) p2=1; if (p2==snd->stream_buffer_start){free(buffer); qb64_sndraw_lock=0; return;}//all buffers are full! (quietly ignore this buffer) snd->stream_buffer[p]=(ptrszint)buffer; snd->stream_buffer_next=p2; if (!snd->stream_buffer_start) snd->stream_buffer_start=1; } qb64_sndraw_lock=0; return; error: error(5); return; } void snd_mainloop(){ static int64 t; t=-1; //scan through all sounds int32 list_index; for (list_index = 1; list_index<=snd_handles->indexes; list_index++) { snd_struct *snd = (snd_struct*)list_get(snd_handles, list_index); if (!snd) continue; if (snd->type == 2){ if (snd->limit_state == 2){ if (t==-1) t = GetTicks(); if (t >= snd->limit_stop_point) { snd->limit_state=0; sub__sndstop(list_index); } }//limit_state==2 if (snd->close == 1){ //directly poll to check the sound's state ALint al_state; alGetSourcei(snd->al_seq_left_source,AL_SOURCE_STATE,&al_state); if (al_state==AL_INITIAL) snd->state=SND_STATE_STOPPED; if (al_state==AL_STOPPED) snd->state=SND_STATE_STOPPED; if (al_state==AL_PLAYING) snd->state=SND_STATE_PLAYING; if (al_state==AL_PAUSED) snd->state=SND_STATE_PAUSED; if (snd->state!=SND_STATE_PLAYING) snd->close=2; }//snd->close==1 }//2 if (snd->type==1){//RAW if (snd->close!=2){ if (snd->stream_buffer_start){ static int32 repeat; do{ repeat=0; //internal sndraw post padding //note: without post padding the final, incomplete buffer of sound data would not be played if (list_index==qb64_internal_sndraw_handle){//internal sound raw if (snd->stream_buffer_start==snd->stream_buffer_next){//on last source buffer if (snd->buffer_size>0){//partial size if (GetTicks()>(qb64_internal_sndraw_lastcall+20)){//no input received for last 0.02 seconds if (!qb64_sndraw_lock){//lock (or skip) qb64_sndraw_lock=1; if (qb64_internal_sndraw_postpad){//post-pad allowed qb64_internal_sndraw_postpad=0; while (snd->buffer_sizebuffer+snd->buffer_size)=0; snd->buffer_size+=2; *(int16*)(snd->buffer+snd->buffer_size)=0; snd->buffer_size+=2; } //detach buffer static uint8 *buffer; buffer=snd->buffer; //create new buffer snd->buffer=(uint8*)calloc(snd_buffer_size,1); snd->buffer_size=0; //attach detached buffer to stream (or discard it) static int32 p,p2; p=snd->stream_buffer_next; p2=p+1; if (p2>snd->stream_buffer_last) p2=1; if (p2==snd->stream_buffer_start){ free(buffer); //all buffers are full! (quietly ignore this buffer) }else{ snd->stream_buffer[p]=(ptrszint)buffer; snd->stream_buffer_next=p2; } //next sound command to prepad if necessary to begin sound qb64_internal_sndraw_prepad=1; //unlock qb64_sndraw_lock=0; }//post-pad allowed }//lock (or skip) }//no input received for last x seconds }//partial size }//on last source buffer }//internal sound raw if (snd->stream_buffer_start!=snd->stream_buffer_next){ static int32 p,p2; static int32 i,i2; p=snd->stream_buffer_start; p2=p+1; if (p2>snd->stream_buffer_last) p2=1; //unqueue processed buffers (if any) static ALint buffers_processed; static ALuint buffers[4]; alGetSourcei(snd->al_source, AL_BUFFERS_PROCESSED, &buffers_processed); if (buffers_processed){ alSourceUnqueueBuffers(snd->al_source, buffers_processed, &buffers[0]); //free associated data for (i2=0;i2al_buffers[i]){ free((void*)snd->stream_buffer[snd->al_buffer_index[i]]); snd->al_buffer_state[i]=2;//"processed" } } } } //check for uninitiated buffers for (i=0;i<=3;i++){ if (snd->al_buffer_state[i]==0){ snd->al_buffer_state[i]=1;//"processing" snd->al_buffer_index[i]=p; static ALuint frequency; static ALenum format; frequency=snd_frequency; format=AL_FORMAT_STEREO16; alBufferData(snd->al_buffers[i], format, (void*)snd->stream_buffer[p], snd_buffer_size, frequency); alSourceQueueBuffers(snd->al_source, 1, &snd->al_buffers[i]); static ALint al_state; alGetSourcei(snd->al_source,AL_SOURCE_STATE,&al_state); if (al_state!=AL_PLAYING){ alSourcePlay(snd->al_source); } goto gotbuffer; } } //check for finished buffers for (i=0;i<=3;i++){ if (snd->al_buffer_state[i]==2){//"processed" static ALuint buffer; static ALuint frequency; static ALenum format; frequency=snd_frequency; format=AL_FORMAT_STEREO16; alBufferData(snd->al_buffers[i], format, (void*)snd->stream_buffer[p], snd_buffer_size, frequency); alSourceQueueBuffers(snd->al_source, 1, &snd->al_buffers[i]); static ALint al_state; alGetSourcei(snd->al_source,AL_SOURCE_STATE,&al_state); if (al_state!=AL_PLAYING){ alSourcePlay(snd->al_source); } snd->al_buffer_index[i]=p; snd->al_buffer_state[i]=1;//"processing" goto gotbuffer; } } i=-1; gotbuffer: if (i!=-1){ repeat=1; snd->stream_buffer_start=p2; } }//queued buffer exists }while(repeat); }//started }//close!=2 //close raw? if (snd->close==1){ if (t==-1) t=GetTicks(); if (t>(snd->raw_close_time+3000)){ static ALint al_state; alGetSourcei(snd->al_source,AL_SOURCE_STATE,&al_state); if (al_state==AL_INITIAL) snd->state=SND_STATE_STOPPED; if (al_state==AL_STOPPED) snd->state=SND_STATE_STOPPED; if (al_state==AL_PLAYING) snd->state=SND_STATE_PLAYING; if (al_state==AL_PAUSED) snd->state=SND_STATE_PAUSED; if (snd->state!=SND_STATE_PLAYING){//not playing //note: hardware interface parts closed here, handles closed in sndclose_now if (snd->al_source){ alDeleteSources(1,&snd->al_source); snd->al_source=0; } static int32 i; for (i=0;i<=3;i++){ if (snd->al_buffers[i]) alDeleteBuffers(1,&snd->al_buffers[i]); } //remove the buffers //1)remove 4 AL buffers free(snd->al_buffers); free(snd->al_buffer_index); free(snd->al_buffer_state); //2)remove build buffer free(snd->buffer); //3)remove the 65536 pointers to potential buffers free(snd->stream_buffer); snd->close=2; }//not playing }//time>3 secs }//sndclose==1 }//RAW }//list_index loop } int32 sndupdate_dont_free_resources=0; void sndupdate(snd_struct *snd){ if (snd->type==2){//seq type static snd_sequence_struct *seq; seq=snd->seq; if (snd->al_seq_left_source){ //update state info static ALint al_state; alGetSourcei(snd->al_seq_left_source,AL_SOURCE_STATE,&al_state); //ref: Each source can be in one of four possible execution states: AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED if (al_state==AL_INITIAL) snd->state=SND_STATE_STOPPED; if (al_state==AL_STOPPED) snd->state=SND_STATE_STOPPED; if (al_state==AL_PLAYING) snd->state=SND_STATE_PLAYING; if (al_state==AL_PAUSED) snd->state=SND_STATE_PAUSED; if (snd->state==SND_STATE_STOPPED){ if (!sndupdate_dont_free_resources){ if (snd->setpos_lock_release) goto no_release; //###agressively free OpenAL resources (buffers & sources are very limited on some platforms)### alDeleteSources(1,&snd->al_seq_left_source); snd->al_seq_left_source=0; alDeleteBuffers(1,&snd->al_seq_left_buffer); snd->al_seq_left_buffer=0; if (snd->al_seq_right_source) { alDeleteSources(1, &snd->al_seq_right_source); snd->al_seq_right_source = 0; alDeleteBuffers(1, &snd->al_seq_right_buffer); snd->al_seq_right_buffer = 0; } //flag updates snd->volume_update=1; snd->bal_update=1; no_release:; } } if (snd->limit_state==2){ if (snd->state!=SND_STATE_PLAYING) snd->limit_state=0;//disable limit } if (snd->al_seq_left_source){//still valid? if (snd->bal_update){ snd->bal_update=0; alSource3f(snd->al_seq_left_source, AL_POSITION, snd->bal_left_x, snd->bal_left_y, snd->bal_left_z); if (snd->al_seq_right_source) { alSource3f(snd->al_seq_right_source, AL_POSITION, snd->bal_right_x, snd->bal_right_y, snd->bal_right_z); } /* OpenAL -- like OpenGL -- uses a right-handed Cartesian coordinate system (RHS), where in a frontal default view X (thumb) points right, Y (index finger) points up, and Z (middle finger) points towards the viewer/camera. To switch from a left handed coordinate system (LHS) to a right handed coordinate systems, flip the sign on the Z coordinate. */ /* ref: old bal system code & notes x distance values go from left(negative) to right(positive). y distance values go from below(negative) to above(positive). z distance values go from behind(negative) to in front(positive). d=sqrt(x*x+y*y+z*z); if (d<1) d=0; if (d>1000) d=1000; d=1000-d; d=d/1000.0; stream_volume_mult1=d; -------------------- snd[i].posx=x; snd[i].posy=y; snd[i].posz=z; d=atan2(x,z)*57.295779513; if (d<0) d=360+d; i2=d+0.5; if (i2==360) i2=0;//angle d2=sqrt(x*x+y*y+z*z);//distance if (d2<1) d2=1; if (d2>999.9) d2=999.9; i3=d2/3.90625; */ }//snd->bal_update if (snd->volume_update){ snd->volume_update=0; alSourcef(snd->al_seq_left_source, AL_GAIN, snd->volume); if (snd->al_seq_right_source) { alSourcef(snd->al_seq_right_source, AL_GAIN, snd->volume); } }//snd->volume_update if (snd->setpos_lock_release){ if (snd->state!=SND_STATE_STOPPED){ snd->setpos_lock_release=0; } } if (snd->setpos_update){ snd->setpos_update=0; alSourcef(snd->al_seq_left_source,AL_SEC_OFFSET,snd->setpos); if (snd->al_seq_right_source) { alSourcef(snd->al_seq_right_source, AL_SEC_OFFSET, snd->setpos); } snd->setpos_lock_release=1; } }//snd->al_seq_source still valid? }//snd->al_seq_source }//2 /* if (snd->type==1){ static ALint al_state; alGetSourcei(snd->al_source,AL_SOURCE_STATE,&al_state); //ref: Each source can be in one of four possible execution states: AL_INITIAL, AL_PLAYING, AL_PAUSED, AL_STOPPED if (al_state==AL_INITIAL) snd->state=SND_STATE_STOPPED; if (al_state==AL_STOPPED) snd->state=SND_STATE_STOPPED; if (al_state==AL_PLAYING) snd->state=SND_STATE_PLAYING; if (al_state==AL_PAUSED) snd->state=SND_STATE_PAUSED; } */ }//sndupdate int32 func__sndcopy(int32 handle){ if (new_error) return 0; sndsetup(); if (handle == 0) return 0;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd || snd->internal) { error(5); return 0; } if (snd->type == 2){ sndupdate(snd); //increment seq references snd->seq->references++; snd_struct *snd_source = snd; //create new snd handle int32 handle = list_add(snd_handles); snd_struct *snd = (snd_struct *)list_get(snd_handles, handle); //import all data memcpy (snd, snd_source, sizeof(snd_struct)); //adjust data snd->al_seq_left_buffer = 0;//no buffer snd->al_seq_left_source = 0;//no source snd->al_seq_right_buffer = 0; snd->al_seq_right_source = 0; snd->volume_update = 1; snd->state = 0; return handle; }//2 error(5); return 0; } int32 sub__sndvol_error=0; void sub__sndvol(int32 handle, float volume){ if (new_error) return; sndsetup(); sub__sndvol_error = 0; if (handle == 0) return;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); sub__sndvol_error = 1; if (!snd || snd->internal) { error(5); return; } snd->volume = volume; snd->volume_update = 1; sndupdate(snd); sub__sndvol_error = 0; } void sub__sndpause(int32 handle) { if (new_error) return; sndsetup(); if (handle == 0) return;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd || snd->internal) { error(5); return; } if (snd->type == 2){ sndupdate(snd); if (snd->al_seq_left_source && snd->state == SND_STATE_PLAYING) { const ALuint sources[2] = {snd->al_seq_left_source, snd->al_seq_right_source}; alSourcePausev(snd->al_seq_right_source ? 2 : 1, sources); snd->state = SND_STATE_PAUSED; if (snd->limit_state == 2) { snd->limit_state = 0; } } return; } error(5); } void sub__sndstop(int32 handle){ if (new_error) return; sndsetup(); if (handle == 0) return;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd || snd->internal) { error(5); return; } if (snd->type == 2){ sndupdate(snd); if (snd->al_seq_left_source && snd->state != SND_STATE_STOPPED) { const ALuint sources[2] = {snd->al_seq_left_source, snd->al_seq_right_source}; alSourceStopv(snd->al_seq_right_source ? 2 : 1, sources); if (snd->limit_state == 2) { snd->limit_state = 0; //disable limit } } return; }//2 error(5); } int32 sndplay_loop=0; void sub__sndplay(int32 handle){ if (new_error) return; sndsetup(); if (handle == 0) return;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd){ error(5); return; } if (!snd_allow_internal){ if (snd->internal){ error(5); return; } } if (snd->type == 2){ snd_sequence_struct *seq; seq=snd->seq; if (!snd->al_seq_left_buffer){ alGenBuffers(1, &snd->al_seq_left_buffer); if (!snd->al_seq_right_buffer && seq->data_right) { alGenBuffers(1, &snd->al_seq_right_buffer); } snd->al_seq_freq=snd_frequency; snd->al_seq_format = AL_FORMAT_MONO16; alBufferData(snd->al_seq_left_buffer, snd->al_seq_format, seq->data_left, seq->data_left_size, snd_frequency); if (seq->data_right) { alBufferData(snd->al_seq_right_buffer, snd->al_seq_format, seq->data_right, seq->data_right_size, snd_frequency); } } if (!snd->al_seq_left_source){ alGenSources(1, &snd->al_seq_left_source); alSourcef(snd->al_seq_left_source,AL_REFERENCE_DISTANCE,0.01); alSourcef(snd->al_seq_left_source,AL_MAX_DISTANCE,10); alSourcef(snd->al_seq_left_source,AL_ROLLOFF_FACTOR,1); alSourcei(snd->al_seq_left_source,AL_BUFFER,snd->al_seq_left_buffer); } if (!snd->al_seq_right_source && seq->data_right) { alGenSources(1, &snd->al_seq_right_source); alSourcef(snd->al_seq_right_source,AL_REFERENCE_DISTANCE,0.01); alSourcef(snd->al_seq_right_source,AL_MAX_DISTANCE,10); alSourcef(snd->al_seq_right_source,AL_ROLLOFF_FACTOR,1); alSourcei(snd->al_seq_right_source,AL_BUFFER,snd->al_seq_right_buffer); } sndupdate_dont_free_resources=1; sndupdate(snd); if (snd->state==SND_STATE_PLAYING){ sub__sndstop(handle); sndupdate(snd); } sndupdate_dont_free_resources=0; alSourcei(snd->al_seq_left_source, AL_LOOPING, sndplay_loop ? AL_TRUE : AL_FALSE); alSourcei(snd->al_seq_right_source, AL_LOOPING, sndplay_loop ? AL_TRUE : AL_FALSE); const ALuint sources[2] = {snd->al_seq_left_source, snd->al_seq_right_source}; alSourcePlayv(snd->al_seq_right_source ? 2 : 1, sources); snd->state=SND_STATE_PLAYING; if (snd->limit_state==2) snd->limit_state=0;//disable limit if (snd->limit_state==1){ snd->limit_stop_point=GetTicks()+(snd->limit_duration*1000.0); snd->limit_state=2; } return; }//2 error(5); }//sndplay void sub__sndloop(int32 handle){ sndplay_loop=1; sub__sndplay(handle);//call SNDPLAY with loop option set sndplay_loop=0; }//sndloop int32 func__sndpaused(int32 handle){ if (new_error) return 0; sndsetup(); if (handle==0) return 0;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd || snd->internal){ error(5); return 0; } sndupdate(snd); if (snd->state == SND_STATE_PAUSED) return -1; return 0; } int32 func__sndplaying(int32 handle){ if (new_error) return 0; sndsetup(); if (handle==0) return 0;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd || snd->internal){ error(5); return 0; } sndupdate(snd); if (snd->state == SND_STATE_PLAYING) return -1; return 0; } double func__sndlen(int32 handle){ if (new_error) return 0; sndsetup(); if (handle==0) return 0;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd || snd->internal){error(5); return 0;} if (snd->type==2){ snd_sequence_struct *seq = snd->seq; int32 samples = seq->data_size / seq->channels / (seq->bits_per_sample / 8); double seconds = samples/(double)seq->sample_rate; return seconds; }//2 error(5); return 0; } void sub__sndbal(int32 handle, double x, double y, double z, int32 channel, int32 passed){ if (new_error) return; sndsetup(); if (handle == 0) return;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd || snd->internal || channel < 1 || channel > 2){ error(5); return; } if (snd->type != 2) return; if (!(passed & 8)) { channel = 1; } if (channel == 1) { if (passed & 1) snd->bal_left_x = x / 100; if (passed & 2) snd->bal_left_y = y / 100; if (passed & 4) snd->bal_left_z = z / 100; } else if (channel == 2) { if (passed & 1) snd->bal_right_x = x / 100; if (passed & 2) snd->bal_right_y = y / 100; if (passed & 4) snd->bal_right_z = z / 100; } snd->bal_update = 1; sndupdate(snd); } double func__sndgetpos(int32 handle){ if (new_error) return 0; sndsetup(); if (handle==0) return 0;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd || snd->internal) { error(5); return 0; } if (snd->type==2){ if (snd->al_seq_left_source){ float seconds; alGetSourcef(snd->al_seq_left_source, AL_SEC_OFFSET, &seconds); return seconds; } return 0; } error(5); return 0; }//getpos void sub__sndsetpos(int32 handle,double seconds){ if (new_error) return; sndsetup(); if (handle == 0) return;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd || snd->internal || seconds < 0) { error(5); return; } if (snd->type == 2){ snd->setpos = seconds; snd->setpos_update = 1; sndupdate(snd); return; } error(5); }//setpos void sub__sndlimit(int32 handle,double limit){ if (new_error) return; sndsetup(); if (handle==0) return;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd || snd->internal || limit < 0) { error(5); return; } if (snd->type == 2){ if (limit == 0){ snd->limit_state = 0; return; } sndupdate(snd); if (snd->state == SND_STATE_PLAYING){ //begin count immediately snd->limit_stop_point = GetTicks() + (limit * 1000.0); snd->limit_state = 2; }else{ //begin after play is called snd->limit_duration = limit; snd->limit_state=1; } return; } error(5); } //note: this is an internal command void sndclose_now(int32 handle){ snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (snd->type==2){ //remove OpenAL content if (snd->al_seq_left_source){ alDeleteSources(1,&snd->al_seq_left_source); snd->al_seq_left_source=0; alDeleteBuffers(1,&snd->al_seq_left_buffer); snd->al_seq_left_buffer=0; } if (snd->al_seq_right_source){ alDeleteSources(1,&snd->al_seq_right_source); snd->al_seq_right_source=0; alDeleteBuffers(1,&snd->al_seq_right_buffer); snd->al_seq_right_buffer=0; } //remove sound handle list_remove(snd_handles,handle); //remove seq if (snd->seq->references > 1){ snd->seq->references--; }else{ if (snd->seq->data) free(snd->seq->data); if (snd->seq->data_left != snd->seq->data) free(snd->seq->data_left); if (snd->seq->data_right) free(snd->seq->data_right); list_remove(snd_sequences, list_get_index(snd_sequences, snd->seq)); } }//2 if (snd->type==1){ list_remove(snd_handles,handle); } }//sndclose_now void sub__sndclose(int32 handle){ if (new_error) return; sndsetup(); if (handle==0) return;//default response snd_struct *snd = (snd_struct*)list_get(snd_handles, handle); if (!snd || snd->internal) { error(5); return; } sndupdate(snd); snd->internal = 1;//switch to internal, no more commands related to this sound can be accepted if (snd->type == 2){ if (snd->state==SND_STATE_PLAYING){ snd->close=1;//close when finished playing }else{ sndclose_now(handle); } return; } snd->close=1;//raw snd->raw_close_time=GetTicks(); }//sndclose //"macros" void sub__sndplaycopy(int32 handle,double volume,int32 passed){ if (new_error) return; sndsetup(); int32 handle2; handle2=func__sndcopy(handle); if (!handle2) return;//an error has already happened if (passed){ sub__sndvol(handle2,volume); if (sub__sndvol_error){ sub__sndclose(handle2); return; } } sub__sndplay(handle2); sub__sndclose(handle2); } //uint8 *soundwave(double frequency,double length,double volume,double fadein,double fadeout,uint8 *data); //uint8 *soundwavesilence(double length,uint8 *data); int32 func_play(int32 ignore){ return 0; } /* Formats: A[#|+|-][0-64] 0-64 is like temp. Lnumber, 0 is whatever the current default is */ void sub_play(qbs *str){ sndsetup(); static uint8 *b,*wave,*wave2,*wave3; static double d; static int32 i,bytes_left,a,x,x2,x3,x4,x5,wave_bytes,wave_base; static int32 o=4; static double t=120;//quarter notes per minute (120/60=2 per second) static double l=4; static double pause=1.0/8.0;//ML 0.0, MN 1.0/8.0, MS 1.0/4.0 static double length,length2;//derived from l and t static double frequency; static double mb=0; static double v=50; static int32 n;//the semitone-intervaled note to be played static int32 n_changed;//+,#,- applied? static int64 number; static int32 number_entered; static int32 followup;//1=play note static int32 playit; static uint32 handle=NULL; static int32 fullstops=0; b=str->chr; bytes_left=str->len; wave=NULL; wave_bytes=0; n_changed=0; n=0; number_entered=0; number=0; followup=0; length=1.0/(t/60.0)*(4.0/l); playit=0; wave_base=0;//point at which new sounds will be inserted next_byte: if ((bytes_left--)||followup){ if (bytes_left<0){i=32; goto follow_up;} i=*b++; if (i==32) goto next_byte; if (i>=97&&i<=122) a=i-32; else a=i; if (i==61){//= (+VARPTR$) if (fullstops){error(5); return;} if (number_entered){error(5); return;} number_entered=2; //VARPTR$ reference /* 'BYTE=1 'INTEGER=2 'STRING=3 SUB-STRINGS must use "X"+VARPTR$(string$) 'SINGLE=4 'INT64=5 'FLOAT=6 'DOUBLE=8 'LONG=20 'BIT=64+n */ if (bytes_left<3){error(5); return;} i=*b++; bytes_left--;//read type byte x=*(uint16*)b; b+=2; bytes_left-=2;//read offset within DBLOCK //note: allowable _BIT type variables in VARPTR$ are all at a byte offset and are all // padded until the next byte d=0; switch(i){ case 1: d=*(char*)(dblock+x); break; case (1+128): d=*(uint8*)(dblock+x); break; case 2: d=*(int16*)(dblock+x); break; case (2+128): d=*(uint16*)(dblock+x); break; case 4: d=*(float*)(dblock+x); break; case 5: d=*(int64*)(dblock+x); break; case (5+128): d=*(int64*)(dblock+x); //unsigned conversion is unsupported! break; case 6: d=*(long double*)(dblock+x); break; case 8: d=*(double*)(dblock+x); break; case 20: d=*(int32*)(dblock+x); break; case (20+128): d=*(uint32*)(dblock+x); break; default: //bit type? if ((i&64)==0){error(5); return;} x2=i&63; if (x2>56){error(5); return;}//valid number of bits? //create a mask static int64 i64num,mask,i64x; mask=(((int64)1)<2147483647.0||d<-2147483648.0){error(5); return;}//out of range value! number=qbr_double_to_long(d); goto next_byte; } //read in a number if ((i>=48)&&(i<=57)){ if (fullstops||(number_entered==2)){error(5); return;} if (!number_entered){number=0; number_entered=1;} number=number*10+i-48; goto next_byte; } //read fullstops if (i==46){ if (followup!=7&&followup!=1&&followup!=4){error(5); return;} fullstops++; goto next_byte; } follow_up: if (followup==8){//V... if (!number_entered){error(5); return;} number_entered=0; if (number>100){error(5); return;} v=number; followup=0; if (bytes_left<0) goto done; }//8 if (followup==7){//P... if (number_entered){ number_entered=0; if (number<1||number>64){error(5); return;} length2=1.0/(t/60.0)*(4.0/((double)number)); }else{ length2=length; } d=length2; for (x=1;x<=fullstops;x++){d/=2.0; length2=length2+d;} fullstops=0; soundwave_bytes=wavesize(length2); if (!wave){ //create buffer wave=(uint8*)calloc(soundwave_bytes,1); wave_bytes=soundwave_bytes; wave_base=0; }else{ //increase buffer? if ((wave_base+soundwave_bytes)>wave_bytes){ wave=(uint8*)realloc(wave,wave_base+soundwave_bytes); memset(wave+wave_base,0,wave_base+soundwave_bytes-wave_bytes); wave_bytes=wave_base+soundwave_bytes; } } if (i!=44){ wave_base+=soundwave_bytes; } playit=1; followup=0; if (i==44) goto next_byte; if (bytes_left<0) goto done; }//7 if (followup==6){//T... if (!number_entered){error(5); return;} number_entered=0; if (number<32||number>255){number=120;} t=number; length=1.0/(t/60.0)*(4.0/l); followup=0; if (bytes_left<0) goto done; }//6 if (followup==5){//M... if (number_entered){error(5); return;} switch(a){ case 76://L pause=0; break; case 78://N pause=1.0/8.0; break; case 83://S pause=1.0/4.0; break; case 66://MB if (!mb){ mb=1; if (playit){ playit=0; qb64_internal_sndraw(wave,wave_bytes,1); } wave=NULL; } break; case 70://MF if (mb){ mb=0; //preceding MB content incorporated into MF block } break; default: error(5); return; } followup=0; goto next_byte; }//5 if (followup==4){//N... if (!number_entered){error(5); return;} number_entered=0; if (number>84){error(5); return;} n=-33+number; goto followup1; followup=0; if (bytes_left<0) goto done; }//4 if (followup==3){//O... if (!number_entered){error(5); return;} number_entered=0; if (number>6){error(5); return;} o=number; followup=0; if (bytes_left<0) goto done; }//3 if (followup==2){//L... if (!number_entered){error(5); return;} number_entered=0; if (number<1||number>64){error(5); return;} l=number; length=1.0/(t/60.0)*(4.0/l); followup=0; if (bytes_left<0) goto done; }//2 if (followup==1){//A-G... if (i==45){//- if (n_changed||number_entered){error(5); return;} n_changed=1; n--; goto next_byte; } if (i==43||i==35){//+,# if (n_changed||number_entered){error(5); return;} n_changed=1; n++; goto next_byte; } followup1: if (number_entered){ number_entered=0; if (number<0||number>64){error(5); return;} if (!number) length2=length; else length2=1.0/(t/60.0)*(4.0/((double)number)); }else{ length2=length; }//number_entered d=length2; for (x=1;x<=fullstops;x++){d/=2.0; length2=length2+d;} fullstops=0; //frequency=(2^(note/12))*440 frequency=pow(2.0,((double)n)/12.0)*440.0; //create wave wave2=soundwave(frequency,length2*(1.0-pause),v/100.0,NULL,NULL); if (pause>0){ wave2=(uint8*)realloc(wave2,soundwave_bytes+wavesize(length2*pause)); memset(wave2+soundwave_bytes,0,wavesize(length2*pause)); soundwave_bytes+=wavesize(length2*pause); } if (!wave){ //adopt buffer wave=wave2; wave_bytes=soundwave_bytes; wave_base=0; }else{ //mix required? if (wave_base==wave_bytes) x=0; else x=1; //increase buffer? if ((wave_base+soundwave_bytes)>wave_bytes){ wave=(uint8*)realloc(wave,wave_base+soundwave_bytes); memset(wave+wave_base,0,wave_base+soundwave_bytes-wave_bytes); wave_bytes=wave_base+soundwave_bytes; } //mix or copy if (x){ //mix static int16 *sp,*sp2; sp=(int16*)(wave+wave_base); sp2=(int16*)wave2; x2=soundwave_bytes/2; for (x=0;x32767) x4=32767; if (x4<-32767) x4=-32767; *sp++=x4; }//x }else{ //copy memcpy(wave+wave_base,wave2,soundwave_bytes); }//x free(wave2); } if (i!=44){ wave_base+=soundwave_bytes; } playit=1; n_changed=0; followup=0; if (i==44) goto next_byte; if (bytes_left<0) goto done; }//1 if (a>=65&&a<=71){ //modify a to represent a semitonal note (n) interval switch(a){ //[c][ ][d][ ][e][f][ ][g][ ][a][ ][b] // 0 1 2 3 4 5 6 7 8 9 0 1 case 65: n=9; break; case 66: n=11; break; case 67: n=0; break; case 68: n=2; break; case 69: n=4; break; case 70: n=5; break; case 71: n=7; break; } n=n+(o-2)*12-9; followup=1; goto next_byte; }//a if (a==76){//L followup=2; goto next_byte; } if (a==77){//M followup=5; goto next_byte; } if (a==78){//N followup=4; goto next_byte; } if (a==79){//O followup=3; goto next_byte; } if (a==84){//T followup=6; goto next_byte; } if (a==60){//< o--; if (o<0) o=0; goto next_byte; } if (a==62){//> o++; if (o>6) o=6; goto next_byte; } if (a==80){//P followup=7; goto next_byte; } if (a==86){//V followup=8; goto next_byte; } error(5); return; }//bytes_left done: if (number_entered||followup){error(5); return;}//unhandled data if (playit){ if (mb){ qb64_internal_sndraw(wave,wave_bytes,0); }else{ qb64_internal_sndraw(wave,wave_bytes,1); } }//playit } int32 func__sndrate(){ return snd_frequency; } void qb64_generatesound(double f,double l,uint8 block){ sndsetup(); static uint8* data; data=soundwave(f,l,1,0,0); qb64_internal_sndraw(data,soundwave_bytes,block); } void qb64_internal_sndraw(uint8* data,int32 bytes,int32 block){//data required in 16bit stereo at native frequency, data is freed sndsetup(); static int32 i; if (qb64_internal_sndraw_handle==0){ qb64_internal_sndraw_handle=func__sndopenraw(); qb64_internal_sndraw_prepad=1; } int64 buffered_ms; if (block){ buffered_ms=func__sndrawlen(qb64_internal_sndraw_handle,1)*1000.0; buffered_ms=((double)buffered_ms)*0.95;//take 95% of actual length to allow time for processing of new content buffered_ms-=250;//allow for latency (call frequency and pre/post pad) if (buffered_ms<0) buffered_ms=0; } if (qb64_internal_sndraw_prepad){ qb64_internal_sndraw_prepad=0; //pad initial buffer so that first sound is played immediately static int32 snd_buffer_size_samples; snd_buffer_size_samples=snd_buffer_size/2/2; static int32 n; n=snd_buffer_size_samples-(bytes/2/2); if (n>0){ for (i=0;i0){ sub__delay(((double)length_ms+(double)buffered_ms)/1000.0); } } } double func__sndrawlen(int32 handle,int32 passed){ if (passed){ if (handle==0) return 0; }else{ if (!snd_raw_channel) return 0; handle=snd_raw_channel; } static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd) goto error; if (snd->internal) goto error; if (snd->type!=1) goto error; if (!snd->stream_buffer_start) return 0; //count buffered source buffers static int32 source_buffers; source_buffers=0; static int32 i; i=snd->stream_buffer_start; while(i!=snd->stream_buffer_next){ source_buffers++; i++; if (i>snd->stream_buffer_last) i=1; } //count dest buffers static int32 dest_buffers; dest_buffers=0; for (i=0;i<=3;i++){ if (snd->al_buffer_state[i]==1) dest_buffers++; } static double result; result = ((double)((dest_buffers+source_buffers)*(snd_buffer_size/2/2)))/(double)snd_frequency; if (result < .375) result = 0; //hack to reenable _SNDRAWLEN, which gets stuck at .3715192763764172 return result; error: error(5); return 0; } void sub__sndrawdone(int32 handle,int32 passed){ if (passed){ if (handle==0) return; }else{ if (!snd_raw_channel) return; handle=snd_raw_channel; } static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd) goto error; if (snd->internal) goto error; if (snd->type!=1) goto error; if (snd->buffer_size>0){//partial size if (!qb64_sndraw_lock){//lock (or skip) qb64_sndraw_lock=1; while (snd->buffer_sizebuffer+snd->buffer_size)=0; snd->buffer_size+=2; *(int16*)(snd->buffer+snd->buffer_size)=0; snd->buffer_size+=2; } //detach buffer static uint8 *buffer; buffer=snd->buffer; //create new buffer snd->buffer=(uint8*)calloc(snd_buffer_size,1); snd->buffer_size=0; //attach detached buffer to stream (or discard it) static int32 p,p2; p=snd->stream_buffer_next; p2=p+1; if (p2>snd->stream_buffer_last) p2=1; if (p2==snd->stream_buffer_start){ free(buffer); //all buffers are full! (quietly ignore this buffer) }else{ snd->stream_buffer[p]=(ptrszint)buffer; snd->stream_buffer_next=p2; if (!snd->stream_buffer_start) snd->stream_buffer_start=1; } //unlock qb64_sndraw_lock=0; }//lock (or skip) }//partial size return; error: error(5); return; } #endif