#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{ uint8 *data; int32 data_size; uint8 channels;//note: more than 2 channels may be supported in the future uint8 *data_stereo;//note: built/linked-to when available/required, otherwise NULL int32 data_stereo_size; uint8 *data_mono;//note: built/linked-to when available/required, otherwise NULL int32 data_mono_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 uint8 streamed;//1(NOT SYNC) or 0(SYNC) //sequence snd_sequence_struct *seq; //----part specific variables---- ALuint al_seq_buffer; ALenum al_seq_format; ALsizei al_seq_size; ALsizei al_seq_freq; ALboolean al_seq_loop; ALvoid* al_seq_data; ALuint al_seq_source; //------------------------------- float volume; uint8 volume_update; //----part specific variables---- float al_volume; //------------------------------- uint8 use_mono; 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_x,bal_y,bal_z; uint8 bal_update; //----part specific variables---- float al_bal_x,al_bal_y,al_bal_z; //------------------------------- //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 capability; uint8 state; //old stuff below...sigh! uint8 free; uint8 playing; uint8 paused; float volume_mult1; float posx; float posy; float posz; uint32 start_time; uint32 paused_time; uint8 looping; uint32 limit; double len; }; #define SND_STATE_STOPPED 0 #define SND_STATE_PLAYING 1 #define SND_STATE_PAUSED 2 list *snd_handles=list_new(sizeof(snd_struct)); int32 snd_stream_handle=0;//only one sound can be streamed 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 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->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 static ALint al_state; alGetSourcei(snd->al_seq_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 }//snd }//list_index loop } #define SND_CAPABILITY_SYNC 1 #define SND_CAPABILITY_VOL 2 #define SND_CAPABILITY_PAUSE 4 #define SND_CAPABILITY_LEN 8 #define SND_CAPABILITY_SETPOS 16 /* struct snd_struct{ uint32 handle; Mix_Chunk *sample; uint8 free; uint8 playing; uint8 paused; float volume; float volume_mult1; float posx; float posy; float posz; uint32 start_time; uint32 paused_time; uint8 looping; uint32 limit; double len; uint8 capability; }; snd_struct snd[AUDIO_CHANNELS]; Mix_Music *stream=NULL; int32 stream_free=0; int32 stream_loaded=0; int32 stream_playing=0; uint32 snd_free_handle=2; int32 stream_type=0; int32 stream_paused=0; float stream_volume=1; float stream_volume_mult1=1; uint32 stream_start_time=0; uint32 stream_paused_time=0; double stream_setpos=0; int32 stream_looping=0; double stream_limit=0; int32 stream_limited=0; double stream_len=-1; uint8 stream_capability; void snd_check(){ sndsetup(); static int32 i,i2,i3; //check stream if (stream_loaded){ if (stream_free){ //still playing? if (stream_playing&&(!stream_looping)) if (!Mix_PlayingMusic()) stream_playing=0; if (!stream_playing){ Mix_FreeMusic(stream); stream=NULL; } } } //check samples for (i=0;ialen)/22050.0/2.0/2.0; snd[i].handle=snd_free_handle; snd_free_handle++; if (snd_free_handle==0) snd_free_handle=2; snd[i].capability=SND_CAPABILITY_SYNC; return snd[i].handle; } } return 0;//no free indexes } */ 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_source){ //update state info static ALint al_state; alGetSourcei(snd->al_seq_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_source); snd->al_seq_source=0; alDeleteBuffers(1,&snd->al_seq_buffer); snd->al_seq_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_source){//still valid? if (snd->bal_update){ snd->bal_update=0; if (snd->bal_y||snd->bal_z){ snd->use_mono=1; } if ((!snd->use_mono)||(!seq->data_mono)){//2D repositioning if (seq->data_stereo){ //note: OpenAL would ignore our call so use a volume change to simulate distance static double d; d=sqrt(snd->bal_x*snd->bal_x+snd->bal_y*snd->bal_y+snd->bal_z*snd->bal_z); if (d<1) d=0; if (d>1000) d=1000; d=1000-d; d=d/1000.0;//'d' is now a scaling factor from 0 to 1 d=d*((double)snd->volume); alSourcef(snd->al_seq_source,AL_GAIN,d); snd->volume_update=0;//cancel possible volume update goto skip_bal_change; } } //rescale our audio co-ordinate system (-1000[-1] to [1]1000) to match OpenAL(-10[-0.01] to [0.01]10) snd->al_bal_x=snd->bal_x/100.0; snd->al_bal_y=snd->bal_y/100.0; snd->al_bal_z=-(snd->bal_z/100.0); alSource3f(snd->al_seq_source, AL_POSITION, snd->al_bal_x, snd->al_bal_y, snd->al_bal_z); skip_bal_change:; /* 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; snd->al_volume=snd->volume; if (snd->bal_x&&((!snd->use_mono)||(!seq->data_mono))){ if (seq->data_stereo){ static double d; d=sqrt(snd->al_bal_x*snd->al_bal_x+snd->al_bal_y*snd->al_bal_y+snd->al_bal_z*snd->al_bal_z); if (d<1) d=0; if (d>1000) d=1000; d=1000-d; d=d/1000.0;//'d' is now a scaling factor from 0 to 1 d=d*((double)snd->volume); alSourcef(snd->al_seq_source,AL_GAIN,d); goto skip_regular_vol_change; } } alSourcef(snd->al_seq_source,AL_GAIN,snd->al_volume); skip_regular_vol_change:; }//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_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 static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd){error(5); return 0;} if (snd->internal){error(5); return 0;} if (snd->type==2){ if (snd->streamed){error(5); return 0;} sndupdate(snd); static snd_sequence_struct *seq; seq=snd->seq; //increment seq references seq->references++; static snd_struct *snd_source; snd_source=snd; //create new snd handle static int32 handle; handle=list_add(snd_handles); static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); //import all data memcpy (snd,snd_source,sizeof(snd_struct)); //adjust data snd->al_seq_buffer=0;//no buffer snd->al_seq_source=0;//no source snd->volume_update=1; snd->state=0; return handle; }//2 error(5); return 0; }//sndcopy 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 static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); sub__sndvol_error=1; if (!snd){error(5); return;} if (snd->internal){error(5); return;} if (!(snd->capability&SND_CAPABILITY_VOL)){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 static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd){error(5); return;} if (!(snd->capability&SND_CAPABILITY_PAUSE)){error(5); return;} if (snd->internal){error(5); return;} if (snd->type==2){ static snd_sequence_struct *seq; seq=snd->seq; sndupdate(snd); if (snd->al_seq_source){ if (snd->state==SND_STATE_PLAYING){ alSourcePause(snd->al_seq_source); snd->state=SND_STATE_PAUSED; if (snd->limit_state==2) snd->limit_state=0;//disable limit } }//source return; }//2 error(5); }//sndpause void sub__sndstop(int32 handle){ if (new_error) return; sndsetup(); if (handle==0) return;//default response static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd){error(5); return;} if (snd->internal){error(5); return;} if (snd->type==2){ static snd_sequence_struct *seq; seq=snd->seq; sndupdate(snd); if (snd->al_seq_source){ if (snd->state!=SND_STATE_STOPPED){ alSourceStop(snd->al_seq_source); snd->state=SND_STATE_STOPPED; if (snd->limit_state==2) snd->limit_state=0;//disable limit } }//source return; }//2 error(5); }//sndstop void snd_seq_buildmono(snd_sequence_struct* seq){ if (!seq->data_mono){ //we know: its stereo, its 16-bit //our plan: average out every 2nd sample static int32 samples; samples=seq->data_stereo_size/4; static int16 *src; static int16 *dst; src=(int16*)seq->data_stereo; dst=(int16*)malloc(samples*2); static int32 i; for (i=0;idata_mono=(uint8*)dst; seq->data_mono_size=samples*2; }//no mono }//snd_seq_buildmono int32 sndplay_loop=0; void sub__sndplay(int32 handle){ if (new_error) return; sndsetup(); if (handle==0) return;//default response static snd_struct *snd; 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){ static snd_sequence_struct *seq; seq=snd->seq; switch_to_mono: if (!snd->al_seq_buffer){ alGenBuffers(1,&snd->al_seq_buffer); snd->al_seq_freq=snd_frequency; static int32 mono; mono=0; if (!seq->data_stereo) mono=1; if (seq->data_mono) mono=1; if (mono){ snd->al_seq_data=(ALvoid*)seq->data_mono; snd->al_seq_size=seq->data_mono_size; snd->al_seq_format=AL_FORMAT_MONO16; }else{ snd->al_seq_data=(ALvoid*)seq->data_stereo; snd->al_seq_size=seq->data_stereo_size; snd->al_seq_format=AL_FORMAT_STEREO16; } /* ref: #define AL_FORMAT_MONO8 0x1100 #define AL_FORMAT_MONO16 0x1101 #define AL_FORMAT_STEREO8 0x1102 #define AL_FORMAT_STEREO16 0x1103 */ alBufferData(snd->al_seq_buffer,snd->al_seq_format,snd->al_seq_data,snd->al_seq_size,snd->al_seq_freq); } if (!snd->al_seq_source){ alGenSources(1,&snd->al_seq_source); alSourcef(snd->al_seq_source,AL_REFERENCE_DISTANCE,0.01); alSourcef(snd->al_seq_source,AL_MAX_DISTANCE,10); alSourcef(snd->al_seq_source,AL_ROLLOFF_FACTOR,1); alSourcei(snd->al_seq_source,AL_BUFFER,snd->al_seq_buffer); } sndupdate_dont_free_resources=1; sndupdate(snd); if (snd->state==SND_STATE_PLAYING){ sub__sndstop(handle); sndupdate(snd); } sndupdate_dont_free_resources=0; if (snd->state==SND_STATE_STOPPED){ if (snd->use_mono){ if (!seq->data_mono){//does a mono version exist? snd_seq_buildmono(seq);//create a mono version sndupdate(snd);//trigger destruction of current buffer, source and the update of states goto switch_to_mono; }//create mono }//needs mono }//stopped if (sndplay_loop){ alSourcei(snd->al_seq_source, AL_LOOPING, AL_TRUE); }else{ alSourcei(snd->al_seq_source, AL_LOOPING, AL_FALSE); } alSourcePlay(snd->al_seq_source); 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 static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd){error(5); return 0;} if (snd->internal){error(5); return 0;} sndupdate(snd); if (snd->state==SND_STATE_PAUSED) return -1; return 0; }//sndpaused int32 func__sndplaying(int32 handle){ if (new_error) return 0; sndsetup(); if (handle==0) return 0;//default response static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd){error(5); return 0;} if (snd->internal){error(5); return 0;} sndupdate(snd); if (snd->state==SND_STATE_PLAYING) return -1; return 0; }//func__sndplaying double func__sndlen(int32 handle){ if (new_error) return 0; sndsetup(); if (handle==0) return 0;//default response static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd){error(5); return 0;} if (!(snd->capability&SND_CAPABILITY_LEN)){error(5); return 0;} if (snd->internal){error(5); return 0;} if (snd->type==2){ static snd_sequence_struct *seq; seq=snd->seq; static int32 samples; samples=seq->data_size/seq->channels/(seq->bits_per_sample/8); static double seconds; seconds=((double)samples)/((double)seq->sample_rate); return seconds; }//2 error(5); return 0; } void sub__sndbal(int32 handle,double x,double y,double z,int32 passed){ //note: any optional parameter not passed is assumed to be 0 (which is what NULL equates to) if (new_error) return; sndsetup(); if (handle==0) return;//default response static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd){error(5); return;} if (!(snd->capability&SND_CAPABILITY_VOL)){error(5); return;} if (snd->internal){error(5); return;} if (snd->type==2){ static snd_sequence_struct *seq; seq=snd->seq; if (passed&(2+4)){ if ((y==0)&&(z==0)) z=0.001;//trigger 3D sound placement } snd->bal_x=x; snd->bal_y=y; snd->bal_z=z; snd->bal_update=1; sndupdate(snd); }//2 //... }//sndbal double func__sndgetpos(int32 handle){ if (new_error) return 0; sndsetup(); if (handle==0) return 0;//default response static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd){error(5); return 0;} if (snd->internal){error(5); return 0;} if (snd->type==2){ if (snd->al_seq_source){ static float seconds; alGetSourcef(snd->al_seq_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; if (seconds<0){error(5); return;} sndsetup(); if (handle==0) return;//default response static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd){error(5); return;} if (!(snd->capability&SND_CAPABILITY_SETPOS)){error(5); return;} if (snd->internal){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; if (limit<0){error(5); return;} sndsetup(); if (handle==0) return;//default response static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd){error(5); return;} if (snd->internal){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); return; } //note: this is an internal command void sndclose_now(int32 handle){ static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (snd->streamed) snd_stream_handle=0; if (snd->type==2){ //remove OpenAL content if (snd->al_seq_source){ alDeleteSources(1,&snd->al_seq_source); snd->al_seq_source=0; alDeleteBuffers(1,&snd->al_seq_buffer); snd->al_seq_buffer=0; } static snd_sequence_struct *seq; seq=snd->seq; //remove sound handle list_remove(snd_handles,handle); //remove seq if (seq->references>1){ seq->references--; }else{ free(seq->data); if (seq->data_stereo){ if (seq->data_stereo!=seq->data) free(seq->data_stereo); } if (seq->data_mono){ if (seq->data_mono!=seq->data) free(seq->data_mono); } static int32 seq_handle; seq_handle=list_get_index(snd_sequences,seq); list_remove(snd_sequences,seq_handle); } }//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 static snd_struct *snd; snd=(snd_struct*)list_get(snd_handles,handle); if (!snd){error(5); return;} if (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++; } return ((double)((dest_buffers+source_buffers)*(snd_buffer_size/2/2)))/(double)snd_frequency; 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