| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590 |
- // freeaudio.cpp
- // 2007.02.15 new dsound device, readpointer and tidy up SA
- // 2006.05.26 overflow handling for winmmdevice waveOutGetPosition
- // 2006.04.26 winmmdevice doubles latency for windows98
- // 2006.04.26 fixed recycling of autostopped channels
- // 2006.01.17 fixed recycling of stopped channels
- // 2005.11.22 fixed freepool sounds not resetting parameters - thanks to Fetze
- // 2005.09.13 new ALSA code for Linux - thanks to Craig Kiesau
- // 2005.08.05 new single looped buffer with timer callback for win32mmdevice
- // 2005.07.04 reduced audio latency
- // 2005.06.17 fixed memory leak in sound allocation
- // 2005.05.23 removed output transitions for queued/paused sounds
- // 2005.04.05 added AudioOutputUnitStop call for apple close
- // 2005.01.07 fixed up audio hardware failure handling
- // 2004.08.16 fixed channelstatus, allocchannel now always returns unique handle
- // 2004.06.04 added linux drivers
- // 2004.03.19 new ,loop parameter support in loadsound(file,looping)
- // 2004.03.02 dynamic sounds release temp channel when recycled SA
- // 2004.02.06 initial single file release SA
- #include "freeaudio.h"
- #include <stdlib.h>
- #include <stdio.h>
- #include <memory.h>
- //static audiodevice *audio=0;
- mixer::mixer(int bufflen):output(0)
- {
- int i;
- size=bufflen;
- playlist=0;
- // freelist=0;
- mbuff=new int[bufflen];
- for (i=0;i<size;i++) mbuff[i]=0;
- freq=0;channels=0;
- }
- sound *mixer::allocsound(output *out)
- {
- sound *s;
- s=freelist.pull(); //getfreesound();
- if (s){
- if (io && s->channel){
- io->freechannel(s->channel);
- s->channel=0;
- }
- }else{
- s=new sound(this);
- }
- if (out){
- s->resetrate(out->getrate(1));
- s->resetvolume(out->getvolume(1));
- s->resetpan(out->getpan(1));
- s->resetdepth(out->getdepth(1));
- }else{
- s->resetrate(65536);
- s->resetvolume(VOLUMEONE);
- s->resetpan(0);
- s->resetdepth(0);
- }
- return s;
- }
- sound *mixer::play(sample *sam,output *out,int state)
- {
- sound *s;
- if (!sam) return 0;
- if (sam->channels==0) return 0;
- if (freq==0) return 0;
-
- s=allocsound(out);
- sam->refcount++;
- s->status=state;
- s->samp=sam;
- s->pos64=0;
- s->mix64=0;
- s->starttime=bbMilliSecs();
- startlist.push(s);
- return s;
- }
- sound::sound(output *p):output(p){
- status=STOPPED;
- next=0;
- resetrate(65536);
- resetvolume(VOLUMEONE);
- resetpan(0);
- resetdepth(0);
- }
- void sound::flush(){
- if (status==STREAMING && samp){
- int readpos=samp->writepos;
- mix64=((i64)(readpos*samp->channels))<<32;
- pos64=((i64)((readpos%samp->samples)*samp->channels))<<32;
- }
- }
- // mix audio stream at rate,amplitude with linear interpolation
- #define blend(a,b,c) (a+((((b)-(a))*((((int)(c))>>16)&65535))>>16))
- #define ublend(a,b,c) ( ((a-128)<<8) + ((((b)-(a))*((((int)(c))>>16)&65535))>>8) )
- /*
- int ublend(u8 a,u8 b,int c)
- {
- int a0=a-128;
- int b0=b-128;
- return blend(a0,b0,c);
- }
- */
- int sound::mix(int *b,int size) //returns 0=ok 1=releasechannel
- {
- short *s;
- u8 *c;
- int t,n;
- int count;
- short pan,depth;
- short right,left;
- int amp0,amp1;
- i64 freq0,freq1;
- i64 len64,freq64;
- int p;
- if (status==STOPPED) return 1;
- if (status&PAUSED) return -2;
- if (status==FREE) return -1;
- count=size/2;
-
- if (delay)
- {
- if (delay>=count)
- {
- delay-=count;
- return 0;
- }
- if (delay>0)
- {
- count-=delay;
- b+=delay*2;
- }
- delay=0;
- }
-
- freq0=((i64)samp->freq*getrate(0))/44100;
- freq1=((i64)samp->freq*getrate(1))/44100;
- n=(freq0*count)>>16;
- int readpos=mix64>>32;
- if ((status&STREAMING) && (readpos+n>samp->writepos)) return 0;
- amp0=getvolume(0);
- amp1=getvolume(1);
- pan=getpan(1);
- depth=getdepth(1);
- len64=((i64)(samp->samples*samp->channels))<<32;
- if ((amp0==amp1) && (freq0==freq1))
- {
- freq64=(freq0*samp->channels)<<16;
- if (amp1==0) //muted
- {
- currentvolume=0;
- pos64+=freq64*count;
- mix64+=freq64*count;
- while (pos64>=len64)
- {
- if (status&LOOPING) pos64-=len64;else return 1;
- }
- return 0;
- }
- right=left=amp0;
- if (pan>0) left=(left*(4096-pan))>>12;
- if (pan<0) right=(right*(4096+pan))>>12;
- if (depth<0) right=-right;
- if (samp->bits==8)
- {
- c=(u8 *)samp->data;
- if (samp->channels==1)
- {
- while (count-->0)
- {
- p=(int)(pos64>>32);
- t=ublend(c[p],c[p+1],pos64);
- mix64+=freq64;
- pos64+=freq64;
- *b+++=(t*left)>>8;
- *b+++=(t*right)>>8;
- while (pos64>=len64)
- {
- if (status&LOOPING) pos64-=len64;else return 1;
- }
- }
- }
- else
- {
- while (count-->0)
- {
- p=(int)(pos64>>32)&-2;
- *b+++=(ublend(c[p+0],c[p+2],pos64>>1)*left)>>8;
- *b+++=(ublend(c[p+1],c[p+3],pos64>>1)*right)>>8;
- pos64+=freq64;
- mix64+=freq64;
- while (pos64>=len64)
- {
- if (status&LOOPING) pos64-=len64;else return 1;
- }
- }
- }
- }
- else
- {
- s=(short *)samp->data;
- if (samp->channels==1)
- {
- while (count-->0)
- {
- p=(int)(pos64>>32);
- t=blend(s[p],s[p+1],pos64);
- mix64+=freq64;
- pos64+=freq64;
- *b+++=(t*left)>>8;
- *b+++=(t*right)>>8;
- while (pos64>=len64)
- {
- if (status&LOOPING)
- {
- pos64-=len64;
- }
- else
- {
- return 1;
- }
- }
- }
- }
- else
- {
- while (count-->0)
- {
- p=(int)(pos64>>32)&-2;
- *b+++=(blend(s[p+0],s[p+2],pos64>>1)*left)>>8;
- *b+++=(blend(s[p+1],s[p+3],pos64>>1)*right)>>8;
- mix64+=freq64;
- pos64+=freq64;
- while (pos64>=len64)
- {
- if (status&LOOPING) pos64-=len64;else return 1;
- }
- }
- }
- }
- return 0;
- }
- else //dynamice volume|frequency
- {
- int v1,v2,vd;
- i64 f1,f2,fd;
- v1=amp0<<8;
- v2=amp1<<8;
- vd=(v2-v1)/count;
- f1=(freq0*samp->channels)<<16;
- f2=(freq1*samp->channels)<<16;
- fd=(f2-f1)/count;
-
- currentvolume=targetvolume;
- currentrate=targetrate;
-
- if (samp->bits==8)
- {
- c=(u8 *)samp->data;
- if (samp->channels==1)
- {
- while (count-->0)
- {
- left=right=v1>>8;
- if (pan>0) left=(left*(4096-pan))>>12;
- if (pan<0) right=(right*(4096+pan))>>12;
- p=(int)(pos64>>32);
- t=ublend(c[p],c[p+1],pos64);
- *b+++=(left*t)>>8;
- *b+++=(right*t)>>8;
- v1+=vd;
- pos64+=f1;
- mix64+=f1;
- f1+=fd;
- while (pos64>=len64)
- {
- if (status&LOOPING) pos64-=len64;else return 1;
- }
- }
- }
- else
- {
- while (count-->0)
- {
- left=right=v1>>8;
- if (pan>0) left=(left*(4096-pan))>>12;
- if (pan<0) right=(right*(4096+pan))>>12;
- if (depth<0) right=-right;
- p=(int)(pos64>>32)&-2;
- *b+++=(ublend(c[p+0],c[p+2],pos64>>1)*left)>>8;
- *b+++=(ublend(c[p+1],c[p+3],pos64>>1)*right)>>8;
- v1+=vd;
- pos64+=f1;
- mix64+=f1;
- f1+=fd;
- while (pos64>=len64)
- {
- if (status&LOOPING) pos64-=len64;else return 1;
- }
- }
- }
- }
- else
- {
- s=(short *)samp->data;
- if (samp->channels==1)
- {
- while (count-->0)
- {
- left=right=v1>>8;
- if (pan>0) left=(left*(4096-pan))>>12;
- if (pan<0) right=(right*(4096+pan))>>12;
- if (depth<0) right=-right;
- p=(int)(pos64>>32);
- t=blend(s[p],s[p+1],pos64);
- *b+++=(t*left)>>8;
- *b+++=(t*right)>>8;
- v1+=vd;
- mix64+=f1;
- pos64+=f1;
- f1+=fd;
- while (pos64>=len64)
- {
- if (status&LOOPING) pos64-=len64;else return 1;
- }
- }
- }
- else
- {
- while (count-->0)
- {
- left=right=v1>>8;
- if (pan>0) left=(left*(4096-pan))>>12;
- if (pan<0) right=(right*(4096+pan))>>12;
- if (depth<0) right=-right;
- p=(int)(pos64>>32)&-2;
- *b+++=(blend(s[p+0],s[p+2],pos64>>1)*left)>>8;
- *b+++=(blend(s[p+1],s[p+3],pos64>>1)*right)>>8;
- v1+=vd;
- mix64+=f1;
- pos64+=f1;
- f1+=fd;
- while (pos64>=len64)
- {
- if (status&LOOPING) pos64-=len64;else return 1;
- }
- }
- }
- }
- return 0;
- }
- }
- void mixer::releaseall()
- {
- sound *s;
-
- while (s=playlist)
- {
- playlist=s->next;
- releasesound(s);
- }
- }
- void mixer::releasesound(sound *s) // should only be called by mix
- {
- s->samp->free();
- s->samp=0;
- freelist.push(s);
- }
- void mixer::mix(int count)
- {
- sound *sptr,*tptr,**lptr;
-
- while (sptr=startlist.pull())
- {
- sptr->next=playlist;
- playlist=sptr;
- int skip=20+sptr->starttime-bbMilliSecs();
- if (skip<0) skip=0;
- skip=(skip*freq)/1000;
- sptr->delay=skip;
- }
- sptr=playlist;
- lptr=&playlist;
- while (sptr)
- {
- int res=sptr->mix(mbuff,count); //,count
- switch (res)
- {
- case 0:
- lptr=&sptr->next;
- sptr=sptr->next;
- break;
- case 1: //STOP
- if (sptr->recycle){
- sptr->status=output::FREE; //dynamic
- tptr=sptr;
- *lptr=sptr=sptr->next;
- releasesound(tptr);
- }else{
- sptr->status=output::STOPPED;
- lptr=&sptr->next;
- sptr=sptr->next;
- }
- break;
- case -1: //FREE
- tptr=sptr;
- *lptr=sptr=sptr->next;
- releasesound(tptr);
- break;
- case -2: //PAUSED
- lptr=&sptr->next;
- sptr=sptr->next;
- break;
- }
- }
- }
- void mixer::mix8(u8 *abuff,int count)
- {
- int i,j;
- int *tptr;
- if (count==0) count=size;
- mix(count);
- tptr=mbuff;
- for (i=0;i<count;i++)
- {
- j=((*tptr)>>16)+0x80;
- if (j & 0xffffff00) {if (j<0) j=0;else j=0xff;}
- *abuff++=j;
- *tptr++=0;
- }
- }
- void mixer::mix16(short *d,int count)
- {
- int *tptr;
- if (count==0) count=size;
- mix(count);
- tptr=mbuff;
- for (int i=0;i<count;i++)
- {
- *d++=BND(*tptr>>4,-32768,32767);
- *tptr++=0;
- }
- }
- void mixer::mix16s(short *dd,int count)
- {
- int *d,*tptr;
- if (count==0) count=size;
- mix(count);
- d=(int*)dd;
- tptr=mbuff;
- for (int i=0;i<count;i++) //ll;i++)
- {
- int t=*tptr>>8;
- int p=*d;
- short s1=p;
- u32 p1=BND(s1+t,-32768,32767);
- u32 p2=BND((p>>16)+t,-32768,32767);
- *d++=(p1&0xffff)+(p2<<16);
- *tptr++=0;
- }
- }
- int sample::init(int n,int f,int c,int b,void *_data)
- {
- freq=f;
- channels=c;
- bits=b;
- refcount=1;
- if (b<8) b=8; //single bit handler
- samplesize=channels*b/8;
- samples=n;//size/samplesize;
- sizebytes=n*samplesize;
- if (_data){
- capacity=0;
- data=_data;
- }else{
- capacity=sizebytes+samplesize;
- data=malloc(capacity);
- }
- buffer=data;
- writepos=0;
- return 0;
- };
- void sample::setloop(int l)
- {
- char *p;
-
- loop=l;
- p=(char*)data;
- if (loop)
- memcpy(p+sizebytes,p,samplesize);
- else
- memcpy(p+sizebytes,p+sizebytes-samplesize,samplesize);
- }
- void sample::free()
- {
- if (--refcount==0)
- {
- if (capacity) ::free(data);
- delete this;
- }
- }
- void sample::write(int n)
- {
- writepos+=n;
- buffer=(char *)data+(writepos%samples)*samplesize;
- }
- int sample::write( void *src,int samples,int readpos)
- {
- int n,t,nn; //,bytes;
- t=0;
- while (samples)
- {
- n=buffersize(readpos);
- if (n==0) break;
- if (n>samples) n=samples;
- nn=n*samplesize;
- memcpy(buffer,src,nn);
- src=(char*)src+nn;
- samples-=n;t+=n;write(n);
- }
- return t;
- }
- int sample::buffersize(int readpos)
- {
- int inuse,rpos,wpos;
- inuse=writepos-readpos;
- if (inuse>=samples) return 0; //buffer full!
- wpos=writepos%samples;
- rpos=readpos%samples;
- if (rpos>wpos) return rpos-wpos;
- return samples-wpos;
- }
- int sample::peek(int t)
- {
- int s;
- if (t<0||t>=samples) return 0;
- switch (samplesize)
- {
- case 1:
- s=((u8*)data)[t]<<8;
- break;
- case 2:
- if (channels==1) s=((u16*)data)[t];else s=((u8*)data)[t*2]<<8;
- break;
- case 4:
- s=((u16*)data)[t*2];
- break;
- }
- return s-32768;
- }
|