diff --git a/src/hardware/mixer.cpp b/src/hardware/mixer.cpp index 36ad2ecd..ec6af869 100644 --- a/src/hardware/mixer.cpp +++ b/src/hardware/mixer.cpp @@ -9,7 +9,7 @@ * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * GNU Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software @@ -28,6 +28,7 @@ #include "SDL.h" #include "mem.h" +#include "pic.h" #include "dosbox.h" #include "mixer.h" #include "timer.h" @@ -38,34 +39,14 @@ #include "hardware.h" #include "programs.h" -#define MIXER_MAXCHAN 8 -#define MIXER_BUFSIZE (16*1024) #define MIXER_SSIZE 4 -#define MIXER_SHIFT 16 +#define MIXER_SHIFT 14 #define MIXER_REMAIN ((1<MAX_AUDIO) ? (Bit16s)MAX_AUDIO : (SAMPhandler)(((Bit8u*)&mixer.temp)+sizeof(mixer.temp.TYPE[0]),sample_toread); \ - Bitu sample_index=(1 << MIXER_SHIFT) - chan->sample_left; \ - for (Bitu mix=0;mix> MIXER_SHIFT;sample_index+=chan->sample_add; \ - mixer.work[mix][0]+=chan->vol_mul[LCHAN]*MAKE_##TYPE( LCHAN ); \ - mixer.work[mix][1]+=chan->vol_mul[RCHAN]*MAKE_##TYPE( RCHAN ); \ - } \ - chan->remain=*(Bitu *)&mixer.temp.TYPE[sample_index>>MIXER_SHIFT]; \ - chan->sample_left=sample_total-sample_index; \ - break; \ -} - struct MIXER_Channel { double vol_main[2]; Bits vol_mul[2]; @@ -90,19 +71,11 @@ static Bit8u wavheader[]={ }; static struct { - struct { - Bit16s data[MIXER_BUFSIZE][2]; - Bitu read,write; - } out; Bit32s work[MIXER_BUFSIZE][2]; - union { - Bit16s m16[MIXER_BUFSIZE][1]; - Bit16s s16[MIXER_BUFSIZE][2]; - Bit8u m8[MIXER_BUFSIZE][1]; - Bit8u s8[MIXER_BUFSIZE][2]; - } temp; - double mastervol[2]; - MIXER_Channel * channels; + Bitu pos,done; + Bitu needed,min_needed; + float mastervol[2]; + MixerChannel * channels; bool nosound; Bitu freq; Bitu blocksize; @@ -115,24 +88,22 @@ static struct { } wave; } mixer; -MIXER_Channel * MIXER_AddChannel(MIXER_MixHandler handler,Bitu freq,char * name) { -//TODO Find a free channel - MIXER_Channel * chan=new MIXER_Channel; - if (!chan) return 0; - chan->playing=false; - chan->mode=MIXER_16STEREO; +Bit8u MixTemp[MIXER_BUFSIZE]; + +MixerChannel * MIXER_AddChannel(MIXER_Handler handler,Bitu freq,char * name) { + MixerChannel * chan=new MixerChannel(); chan->handler=handler; chan->name=name; - chan->sample_add=(freq<sample_left=0; + chan->SetFreq(freq); chan->next=mixer.channels; + chan->SetVolume(1,1); + chan->enabled=false; mixer.channels=chan; - MIXER_SetVolume(chan,1,1); return chan; } -MIXER_Channel * MIXER_FindChannel(const char * name) { - MIXER_Channel * chan=mixer.channels; +MixerChannel * MIXER_FindChannel(const char * name) { + MixerChannel * chan=mixer.channels; while (chan) { if (!strcasecmp(chan->name,name)) break; chan=chan->next; @@ -140,129 +111,212 @@ MIXER_Channel * MIXER_FindChannel(const char * name) { return chan; } -void MIXER_SetFreq(MIXER_Channel * chan,Bitu freq) { - if (!chan) return; - chan->freq=freq; - chan->sample_add=(freq<mode=mode; +void MixerChannel::UpdateVolume(void) { + volmul[0]=(Bits)((1 << MIXER_VOLSHIFT)*volmain[0]*mixer.mastervol[0]); + volmul[1]=(Bits)((1 << MIXER_VOLSHIFT)*volmain[1]*mixer.mastervol[1]); } -void MIXER_UpdateVolume(MIXER_Channel * chan) { - chan->vol_mul[0]=(Bits)((1 << MIXER_VOLSHIFT)*chan->vol_main[0]*mixer.mastervol[0]); - chan->vol_mul[1]=(Bits)((1 << MIXER_VOLSHIFT)*chan->vol_main[1]*mixer.mastervol[1]); +void MixerChannel::SetVolume(float _left,float _right) { + volmain[0]=_left; + volmain[1]=_right; + UpdateVolume(); } -void MIXER_SetVolume(MIXER_Channel * chan,float left,float right) { - if (!chan) return; - if (left>=0) chan->vol_main[0]=left; - if (right>=0) chan->vol_main[1]=right; - MIXER_UpdateVolume(chan); +void MixerChannel::Enable(bool _yesno) { + if (_yesno==enabled) return; + enabled=_yesno; + if (enabled) { + freq_index=MIXER_REMAIN; + SDL_LockAudio(); + if (doneplaying=enable; +void MixerChannel::SetFreq(Bitu _freq) { + freq_add=(_freq<done) { + Bitu todo=needed-done; + todo*=freq_add; + if (todo & MIXER_REMAIN) { + todo=(todo >> MIXER_SHIFT) + 1; + } else { + todo=(todo >> MIXER_SHIFT); + } + handler(todo); + } +} + +void MixerChannel::AddSilence(void) { + if (done +INLINE void MixerChannel::AddSamples(Bitu len,void * data) { + Bits diff[2]; + Bit8u * data8=(Bit8u*)data; + Bit16s * data16=(Bit16s*)data; + Bitu mixpos=mixer.pos+done; + freq_index&=MIXER_REMAIN; + Bitu pos=0; + goto thestart; + while (1) { + Bitu new_pos=freq_index >> MIXER_SHIFT; + if (pos=len) return; + if (_8bits) { + if (stereo) { + diff[0]=(((Bit8s)(data8[pos*2+0] ^ 0x80)) << 8)-last[0]; + diff[1]=(((Bit8s)(data8[pos*2+1] ^ 0x80)) << 8)-last[1]; + } else { + diff[0]=(((Bit8s)(data8[pos] ^ 0x80)) << 8)-last[0]; + } + } else { + if (stereo) { + diff[0]=data16[pos*2+0]-last[0]; + diff[1]=data16[pos*2+1]-last[1]; + } else { + diff[0]=data16[pos]-last[0]; + } + } + } + Bits diff_mul=freq_index & MIXER_REMAIN; + freq_index+=freq_add; + mixpos&=MIXER_BUFMASK; + Bits sample=last[0]+((diff[0]*diff_mul) >> MIXER_SHIFT); + mixer.work[mixpos][0]+=sample*volmul[0]; + if (stereo) sample=last[1]+((diff[1]*diff_mul) >> MIXER_SHIFT); + mixer.work[mixpos][1]+=sample*volmul[1]; + mixpos++;done++; + } +} + +void MixerChannel::AddStretched(Bitu len,Bit16s * data) { + if (done>=needed) { + LOG_MSG("Can't add, buffer full"); + return; + } + Bitu outlen=needed-done;Bits diff; + freq_index=0; + Bitu temp_add=(len << MIXER_SHIFT)/outlen; + Bitu mixpos=mixer.pos+done;done=needed; + Bitu pos=0; + diff=data[0]-last[0]; + while (outlen--) { + Bitu new_pos=freq_index >> MIXER_SHIFT; + if (pos> MIXER_SHIFT); + mixer.work[mixpos][0]+=sample*volmul[0]; + mixer.work[mixpos][1]+=sample*volmul[1]; + mixpos++; + } +}; + +void MixerChannel::AddSamples_m8(Bitu len,Bit8u * data) { + AddSamples(len,data); +} +void MixerChannel::AddSamples_s8(Bitu len,Bit8u * data) { + AddSamples(len,data); +} +void MixerChannel::AddSamples_m16(Bitu len,Bit16s * data) { + AddSamples(len,data); +} +void MixerChannel::AddSamples_s16(Bitu len,Bit16s * data) { + AddSamples(len,data); +} + +void MixerChannel::FillUp(void) { + SDL_LockAudio(); + if (!enabled || doneMIXER_BUFSIZE) samples=MIXER_BUFSIZE; - MIXER_Channel * chan=mixer.channels; +static void MIXER_MixData(Bitu needed) { + MixerChannel * chan=mixer.channels; while (chan) { - if (chan->playing) { - /* This should always allocate 1 extra sample */ - Bitu sample_total=(samples*chan->sample_add)-chan->sample_left; - Bitu sample_toread=sample_total >> MIXER_SHIFT; - if (sample_total & MIXER_REMAIN) sample_toread++; - sample_total=(sample_toread+1)<remain; - switch (chan->mode) { - case MIXER_8MONO: - MIX_NORMAL(m8,0,0); - break; - case MIXER_8STEREO: - MIX_NORMAL(m8,0,1); - break; - case MIXER_16MONO: - MIX_NORMAL(m16,0,0); - break; - case MIXER_16STEREO: - MIX_NORMAL(s16,0,1); - break; - default: - E_Exit("MIXER:Illegal sound mode %2X",chan->mode); - break; - } - } + chan->Mix(needed); chan=chan->next; } - Bitu index=mixer.out.write; - for (Bitu read=0;read> MIXER_VOLSHIFT; - mixer.out.data[index][0]=MIXER_CLIP(temp); - mixer.work[read][0]=0; - temp=mixer.work[read][1] >> MIXER_VOLSHIFT; - mixer.out.data[index][1]=MIXER_CLIP(temp); - mixer.work[read][1]=0; - index=(index+1)&(MIXER_BUFSIZE-1); - } - mixer.out.write=index; - if (mixer.wave.handle) { - index=(MIXER_BUFSIZE+mixer.out.write-samples); - while (samples--) { - index=index&(MIXER_BUFSIZE-1); - mixer.wave.buf[mixer.wave.used][0]=mixer.out.data[index][0]; - mixer.wave.buf[mixer.wave.used][1]=mixer.out.data[index][1]; - index++;mixer.wave.used++; - } - if (mixer.wave.used>(MIXER_WAVESIZE-1024)){ - fwrite(mixer.wave.buf,1,mixer.wave.used*MIXER_SSIZE,mixer.wave.handle); - mixer.wave.length+=mixer.wave.used*MIXER_SSIZE; - mixer.wave.used=0; - } - } + mixer.done=needed; } static void MIXER_Mix(void) { + SDL_LockAudio(); + MIXER_MixData(mixer.needed); mixer.tick_remain+=mixer.tick_add; - Bitu count=mixer.tick_remain>>MIXER_SHIFT; - mixer.tick_remain&=((1<=MIXER_BUFSIZE) size-=MIXER_BUFSIZE; - if (size>mixer.blocksize*2) return; - MIXER_MixData(count); + mixer.needed+=(mixer.tick_remain>>MIXER_SHIFT); + mixer.tick_remain&=MIXER_REMAIN; + SDL_UnlockAudio(); } static void MIXER_Mix_NoSound(void) { + mixer.done=0; mixer.tick_remain+=mixer.tick_add; Bitu count=mixer.tick_remain>>MIXER_SHIFT; - mixer.tick_remain&=((1<=MIXER_BUFSIZE) size-=MIXER_BUFSIZE; - if (size*MIXER_SSIZE<(Bitu)len) { -// LOG(0,"Buffer underrun"); - /* When there's a buffer underrun, keep the data so there will be more next time */ + Bitu need=(Bitu)len/MIXER_SSIZE; + if (need>mixer.done) { +// LOG_MSG("Buffer underrun"); return; } - Bitu remain=MIXER_BUFSIZE-mixer.out.read; - if (remain>=(Bitu)len/MIXER_SSIZE) { - memcpy((void *)stream,(void *)&mixer.out.data[mixer.out.read][0],len); - } else { - memcpy((void *)stream,(void *)&mixer.out.data[mixer.out.read][0],remain*MIXER_SSIZE); - stream+=remain*MIXER_SSIZE; - memcpy((void *)stream,(void *)&mixer.out.data[0][0],(len)-remain*MIXER_SSIZE); + /* Reduce done count in all channels */ + for (MixerChannel * chan=mixer.channels;chan;chan=chan->next) { + if (chan->done>need) chan->done-=need; + else chan->done=0; + } + mixer.done-=need; + mixer.needed-=need; + if (mixer.done>mixer.min_needed) { + Bitu diff=mixer.done-mixer.min_needed; + mixer.tick_add=((mixer.freq-(diff>>2)) << MIXER_SHIFT)/1000; + } else { + Bitu diff=mixer.needed-mixer.done; + mixer.tick_add=((mixer.freq+diff*3) << MIXER_SHIFT)/1000; + } + Bit16s * output=(Bit16s *)stream; + while (need--) { + mixer.work[mixer.pos][0]>>=MIXER_VOLSHIFT; + *output++=MIXER_CLIP(mixer.work[mixer.pos][0]); + mixer.work[mixer.pos][0]=0; + mixer.work[mixer.pos][1]>>=MIXER_VOLSHIFT; + *output++=MIXER_CLIP(mixer.work[mixer.pos][1]); + mixer.work[mixer.pos][1]=0; + mixer.pos=(mixer.pos+1)&MIXER_BUFMASK; } - mixer.out.read+=(len/MIXER_SSIZE); - if (mixer.out.read>=MIXER_BUFSIZE) mixer.out.read-=MIXER_BUFSIZE; } static void MIXER_WaveEvent(void) { @@ -297,7 +351,7 @@ static void MIXER_Stop(Section* sec) { class MIXER : public Program { public: - void MakeVolume(char * scan,double & vol0,double & vol1) { + void MakeVolume(char * scan,float & vol0,float & vol1) { Bitu w=0; bool db=(toupper(*scan)=='D'); if (db) scan++; @@ -306,7 +360,7 @@ public: ++scan;w=1; } char * before=scan; - double val=strtod(scan,&scan); + float val=(float)strtod(scan,&scan); if (before==scan) { ++scan;continue; } @@ -321,7 +375,7 @@ public: } if (!w) vol1=vol0; } - void ShowVolume(char * name,double vol0,double vol1) { + void ShowVolume(char * name,float vol0,float vol1) { WriteOut("%-8s %3.0f:%-3.0f %+3.2f:%-+3.2f \n",name, vol0*100,vol1*100, 20*log(vol0)/log(10.0f),20*log(vol1)/log(10.0f) @@ -329,14 +383,14 @@ public: } void Run(void) { if (cmd->FindString("MASTER",temp_line,false)) { - MakeVolume((char *)temp_line.c_str(),mixer.mastervol[0],mixer.mastervol[1]); + MakeVolume((char *)temp_line.c_str(),mixer.mastervol[0],mixer.mastervol[1]); } - MIXER_Channel * chan=mixer.channels; + MixerChannel * chan=mixer.channels; while (chan) { if (cmd->FindString(chan->name,temp_line,false)) { - MakeVolume((char *)temp_line.c_str(),chan->vol_main[0],chan->vol_main[1]); + MakeVolume((char *)temp_line.c_str(),chan->volmain[0],chan->volmain[1]); } - MIXER_UpdateVolume(chan); + chan->UpdateVolume(); chan=chan->next; } if (cmd->FindExist("/NOSHOW")) return; @@ -344,7 +398,7 @@ public: WriteOut("Channel Main Main(dB)\n"); ShowVolume("MASTER",mixer.mastervol[0],mixer.mastervol[1]); for (chan=mixer.channels;chan;chan=chan->next) - ShowVolume(chan->name,chan->vol_main[0],chan->vol_main[1]); + ShowVolume(chan->name,chan->volmain[0],chan->volmain[1]); } }; @@ -362,9 +416,9 @@ void MIXER_Init(Section* sec) { /* Initialize the internal stuff */ mixer.channels=0; - mixer.out.write=0; - mixer.out.read=0; - memset(mixer.out.data,0,sizeof(mixer.out.data)); + mixer.pos=0; + mixer.done=0; + memset(mixer.work,0,sizeof(mixer.work)); mixer.wave.handle=0; mixer.wave.used=0; mixer.mastervol[0]=1.0f; @@ -393,10 +447,15 @@ void MIXER_Init(Section* sec) { } else { mixer.freq=obtained.freq; mixer.blocksize=obtained.samples; - mixer.tick_add=((mixer.freq+100) << MIXER_SHIFT)/1000; + mixer.tick_add=(mixer.freq << MIXER_SHIFT)/1000; TIMER_AddTickHandler(MIXER_Mix); SDL_PauseAudio(0); } + mixer.min_needed=section->Get_int("prebuffer"); + if (mixer.min_needed>100) mixer.min_needed=100; + mixer.min_needed=(mixer.freq*mixer.min_needed)/1000; + MAPPER_AddHandler(MIXER_WaveEvent,MK_f6,MMOD1,"recwave","Rec Wave"); PROGRAMS_MakeFile("MIXER.COM",MIXER_ProgramStart); } +