package org.jpc.emulator.peripheral; import org.jpc.emulator.AbstractHardwareComponent; import org.jpc.emulator.HardwareComponent; import org.jpc.emulator.Timer; import org.jpc.emulator.TimerResponsive; import org.jpc.emulator.motherboard.InterruptController; import org.jpc.emulator.motherboard.IntervalTimer; import org.jpc.j2se.Option; import org.jpc.support.Clock; import java.util.logging.*; public class Mixer extends AbstractHardwareComponent { private static final Logger Log = Logger.getLogger(Mixer.class.getName()); private static final boolean LOG_BUFFERS = false; private static Clock timeSource; private static InterruptController irqDevice; private static long nextExpiry; private static Timer mix; private static Timer mix_nosound; static public interface MIXER_MixHandler { public void call(/*Bit8u*/short[] sampdate, /*Bit32u*/int len); } static public interface MIXER_Handler { public void call(/*Bitu*/int len); } static private final class BlahModes { static public final int MIXER_8MONO=0; static public final int MIXER_8STEREO=1; static public final int MIXER_16MONO=2; static public final int MIXER_16STEREO=3; } static private final class MixerModes { static public final int M_8M = 0; static public final int M_8S = 1; static public final int M_16M = 2; static public final int M_16S = 3; } static private final int MIXER_BUFSIZE = (16*1024); static private final int MIXER_BUFMASK = (MIXER_BUFSIZE-1); static public final int MAX_AUDIO = ((1<<(16-1))-1); static public final int MIN_AUDIO = -(1<<(16-1)); public static class MixerChannel { public void SetVolume(float _left,float _right) { volmain[0].value=_left; volmain[1].value=_right; UpdateVolume(); } public void SetScale( float f ){ scale = f; UpdateVolume(); } public void UpdateVolume() { volmul[0]=(/*Bits*/int)((1 << MIXER_VOLSHIFT)*scale*volmain[0].value*mixer.mastervol[0].value); volmul[1]=(/*Bits*/int)((1 << MIXER_VOLSHIFT)*scale*volmain[1].value*mixer.mastervol[1].value); } public void SetFreq(/*Bitu*/int _freq) { freq_add=(int)((_freq<<MIXER_SHIFT)/mixer.freq); } public void Mix(/*Bitu*/int _needed) { needed=_needed; while (enabled && needed>done) { /*Bitu*/int todo=needed-done; todo *= freq_add; todo = (todo >> MIXER_SHIFT) + ((todo & MIXER_REMAIN)!=0?1:0); handler.call(todo); } } //Fill up until needed public void AddSilence() { if (done<needed) { done=needed; last[0]=last[1]=0; freq_index=MIXER_REMAIN; } } static public class Type {} public void AddSamples(/*Bitu*/int len, Ptr data, boolean stereo,boolean signeddata,boolean nativeorder) { /*Bits*/int[] diff=new int[2]; /*Bitu*/int mixpos=mixer.pos+done; freq_index&=MIXER_REMAIN; /*Bitu*/int pos=0;/*Bitu*/int new_pos=0; boolean starting = true; for (;;) { if (!starting) new_pos=freq_index >> MIXER_SHIFT; if (starting || pos<new_pos) { if (!starting) { last[0]+=diff[0]; if (stereo) last[1]+=diff[1]; pos=new_pos; } else { starting = false; } if (pos>=len) return; if ( data.dataWidth() == 1) { if (!signeddata) { if (stereo) { diff[0]=(((/*Bit8s*/byte)(data.get(pos*2+0) ^ 0x80)) << 8)-last[0]; diff[1]=(((/*Bit8s*/byte)(data.get(pos*2+1) ^ 0x80)) << 8)-last[1]; } else { diff[0]=(((/*Bit8s*/byte)(data.get(pos) ^ 0x80)) << 8)-last[0]; } } else { if (stereo) { diff[0]=(data.get(pos*2+0) << 8)-last[0]; diff[1]=(data.get(pos*2+1) << 8)-last[1]; } else { diff[0]=(data.get(pos) << 8)-last[0]; } } //16bit and 32bit both contain 16bit data internally } else { if (signeddata) { if (stereo) { if (nativeorder) { diff[0]=(short)data.get(pos*2+0)-last[0]; diff[1]=(short)data.get(pos*2+1)-last[1]; } else { // :TODO: ? diff[0] = data.get(pos*2)-last[0]; diff[1] = data.get(pos*2+1)-last[1]; } } else { if (nativeorder) { diff[0]=(short)data.get(pos)-last[0]; } else { // :TODO: ? diff[0]=data.get(pos)-last[0]; } } } else { if (stereo) { if (nativeorder) { diff[0]=data.get(pos*2+0)-32768-last[0]; diff[1]=data.get(pos*2+1)-32768-last[1]; } else { // :TODO: ? diff[0] = data.get(pos*2)-32768-last[0]; diff[1] = data.get(pos*2+1)-32768-last[1]; } } else { if (nativeorder) { diff[0]=data.get(pos)-32768-last[0]; } else { // :TODO: ? diff[0]=data.get(pos)-32768-last[0]; } } } } } /*Bits*/int diff_mul=freq_index & MIXER_REMAIN; freq_index+=freq_add; mixpos&=MIXER_BUFMASK; /*Bits*/int 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++; } } static private interface getSample { public int call(int pos); } public void AddSamples(/*Bitu*/int len, short[] data, boolean stereo,boolean signeddata) { /*Bits*/int[] diff=new int[2]; /*Bitu*/int mixpos=mixer.pos+done; freq_index&=MIXER_REMAIN; /*Bitu*/int pos=0;/*Bitu*/int new_pos=0; boolean starting = true; for (;;) { if (!starting) new_pos=freq_index >> MIXER_SHIFT; if (starting || pos<new_pos) { if (!starting) { last[0]+=diff[0]; if (stereo) last[1]+=diff[1]; pos=new_pos; } else { starting = false; } if (pos>=len) return; if (signeddata) { if (stereo) { diff[0]=data[pos*2+0]-last[0]; diff[1]=data[pos*2+1]-last[1]; } else { diff[0]=data[pos]-last[0]; } } else { if (stereo) { diff[0]=data[pos*2+0]-32768-last[0]; diff[1]=data[pos*2+1]-32768-last[1]; } else { diff[0]=data[pos]-32768-last[0]; } } } /*Bits*/int diff_mul=freq_index & MIXER_REMAIN; freq_index+=freq_add; mixpos&=MIXER_BUFMASK; /*Bits*/int 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++; } } public void AddSamples(/*Bitu*/int len, int[] data, boolean stereo,boolean signeddata) { /*Bits*/int[] diff=new int[2]; /*Bitu*/int mixpos=mixer.pos+done; freq_index&=MIXER_REMAIN; /*Bitu*/int pos=0;/*Bitu*/int new_pos=0; boolean starting = true; for (;;) { if (!starting) new_pos=freq_index >> MIXER_SHIFT; if (starting || pos<new_pos) { if (!starting) { last[0]+=diff[0]; if (stereo) last[1]+=diff[1]; pos=new_pos; } else { starting = false; } if (pos>=len) return; if (signeddata) { if (stereo) { diff[0]=data[pos*2+0]-last[0]; diff[1]=data[pos*2+1]-last[1]; } else { diff[0]=data[pos]-last[0]; } } else { if (stereo) { diff[0]=data[pos*2+0]-32768-last[0]; diff[1]=data[pos*2+1]-32768-last[1]; } else { diff[0]=data[pos]-32768-last[0]; } } } /*Bits*/int diff_mul=freq_index & MIXER_REMAIN; freq_index+=freq_add; mixpos&=MIXER_BUFMASK; /*Bits*/int 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++; } } public void AddSamples(/*Bitu*/int len, byte[] data, boolean stereo,boolean signeddata) { /*Bits*/int[] diff=new int[2]; /*Bitu*/int mixpos=mixer.pos+done; freq_index&=MIXER_REMAIN; /*Bitu*/int pos=0;/*Bitu*/int new_pos=0; boolean starting = true; for (;;) { if (!starting) new_pos=freq_index >> MIXER_SHIFT; if (starting || pos<new_pos) { if (!starting) { last[0]+=diff[0]; if (stereo) last[1]+=diff[1]; pos=new_pos; } else { starting = false; } if (pos>=len) return; if (!signeddata) { if (stereo) { diff[0]=(((/*Bit8s*/byte)(data[pos*2+0] ^ 0x80)) << 8)-last[0]; diff[1]=(((/*Bit8s*/byte)(data[pos*2+1] ^ 0x80)) << 8)-last[1]; } else { diff[0]=(((/*Bit8s*/byte)(data[pos] ^ 0x80)) << 8)-last[0]; } } else { if (stereo) { diff[0]=(data[pos*2+0] << 8)-last[0]; diff[1]=(data[pos*2+1] << 8)-last[1]; } else { diff[0]=(data[pos] << 8)-last[0]; } } } /*Bits*/int diff_mul=freq_index & MIXER_REMAIN; freq_index+=freq_add; mixpos&=MIXER_BUFMASK; /*Bits*/int sample=last[0]+((diff[0]*diff_mul) >> MIXER_SHIFT); if (mixpos < 10) { System.out.printf("last[0]=%d diff[0]=%d diff_mull=%d \n",last[0], diff[0], diff_mul); System.out.printf("mixpos=%d 0=%d sample=%d volmul=%d\n", mixpos, mixer.work[mixpos][0], sample, volmul[0]); } 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++; } } public void AddSamples_m8(/*Bitu*/int len, /*Bit8u*/byte[] data) { AddSamples(len,data,false,false); } public void AddSamples_s8(/*Bitu*/int len, /*Bit8u*/byte[] data) { AddSamples(len,data,true,false); } public void AddSamples_m8s(/*Bitu*/int len, /*/*Bit8s*/byte[] data) { AddSamples(len,data,false,true); } public void AddSamples_s8s(/*Bitu*/int len, /*/*Bit8s*/byte[] data) { AddSamples(len,data,true,true); } public void AddSamples_m16(/*Bitu*/int len, /*Bit16s*/short[] data) { AddSamples(len,data,false,true); } public void AddSamples_s16(/*Bitu*/int len, /*Bit16s*/short[] data) { AddSamples(len,data,true,true); } public void AddSamples_m16u(/*Bitu*/int len, /*Bit16u*/short[] data) { AddSamples(len,data,false,false); } public void AddSamples_s16u(/*Bitu*/int len, /*Bit16u*/short[] data) { AddSamples(len,data,true,false); } public void AddSamples_m32(/*Bitu*/int len, /*Bit32s*/int[] data) { AddSamples(len,data,false,true); } public void AddSamples_s32(/*Bitu*/int len, /*Bit32s*/int[] data) { AddSamples(len,data,true,true); } // public void AddSamples_m16_nonnative(/*Bitu*/int len, /*Bit16s*/ShortPtr data) { // AddSamples(len,data,false,true,false); // } // public void AddSamples_s16_nonnative(/*Bitu*/int len, /*Bit16s*/ShortPtr data) { // AddSamples(len,data,true,true,false); // } // public void AddSamples_m16u_nonnative(/*Bitu*/int len, /*Bit16u*/ShortPtr data) { // AddSamples(len,data,false,false,false); // } // public void AddSamples_s16u_nonnative(/*Bitu*/int len, /*Bit16u*/ShortPtr data) { // AddSamples(len,data,true,false,false); // } // public void AddSamples_m32_nonnative(/*Bitu*/int len, /*Bit32s*/IntPtr data) { // AddSamples(len,data,false,true,false); // } // public void AddSamples_s32_nonnative(/*Bitu*/int len, /*Bit32s*/IntPtr data) { // AddSamples(len,data,true,true,false); // } //Strech block up into needed data public void AddStretched(/*Bitu*/int len, /*Bit16s*/short[] data) { if (done>=needed) { Log.log(Level.SEVERE, "Can't add, buffer full"); return; } /*Bitu*/int outlen=needed-done;/*Bits*/int diff; freq_index=0; /*Bitu*/int temp_add=(len << MIXER_SHIFT)/outlen; /*Bitu*/int mixpos=mixer.pos+done;done=needed; /*Bitu*/int pos=0; diff=data[0]-last[0]; while ((outlen--)!=0) { /*Bitu*/int new_pos=freq_index >> MIXER_SHIFT; if (pos<new_pos) { pos=new_pos; last[0]+=diff; diff=data[pos]-last[0]; } /*Bits*/int diff_mul=freq_index & MIXER_REMAIN; freq_index+=temp_add; mixpos&=MIXER_BUFMASK; /*Bits*/int sample=last[0]+((diff*diff_mul) >> MIXER_SHIFT); mixer.work[mixpos][0]+=sample*volmul[0]; mixer.work[mixpos][1]+=sample*volmul[1]; mixpos++; } } public void FillUp() { synchronized (audioMutex) { if (!enabled || done<mixer.done) { return; } // scale the sound so it is the right speed float index = 1.0f;//set this to not scale for now. Pic.PIC_TickIndex(); Mix((/*Bitu*/int)(index*mixer.needed)); } } public void Enable(boolean _yesno) { if (_yesno==enabled) return; enabled=_yesno; if (enabled) { freq_index=MIXER_REMAIN; synchronized (audioMutex) { if (done<mixer.done) done=mixer.done; } } } public MIXER_Handler handler; public FloatRef[] volmain = new FloatRef[2]; public float scale; public /*Bit32s*/int[] volmul = new int[2]; public /*Bitu*/int freq_add,freq_index; public /*Bitu*/int done,needed; public /*Bits*/int[] last = new int[2]; public String name; public boolean enabled; public MixerChannel next; public MixerChannel() { for (int i=0;i<volmain.length;i++) { volmain[i] = new FloatRef(); } } } /* Object to maintain a mixerchannel; As all objects it registers itself with create * and removes itself when destroyed. */ public static class MixerObject{ private boolean installed = false; private String m_name; public MixerChannel Install(MIXER_Handler handler,/*Bitu*/int freq,String name) { if(!installed) { if(name.length() > 31) throw new IllegalStateException("Too long mixer channel name"); m_name=name; installed = true; return MIXER_AddChannel(handler,freq,name); } else { throw new IllegalStateException("already added mixer channel."); //return null; //Compiler happy } } public void destroy() { if(!installed) return; MIXER_DelChannel(MIXER_FindChannel(m_name)); } } /* PC Speakers functions, tightly related to the timer functions */ public static void PCSPEAKER_SetCounter(/*Bitu*/int cntr,/*Bitu*/int mode){} public static void PCSPEAKER_SetType(/*Bitu*/int mode){} private static final int MIXER_SSIZE = 4; private static final int MIXER_SHIFT = 14; private static final int MIXER_REMAIN = ((1<<MIXER_SHIFT)-1); private static final int MIXER_VOLSHIFT = 13; static /*Bit16s*/short MIXER_CLIP(/*Bits*/int SAMP) { if (SAMP < MAX_AUDIO) { if (SAMP > MIN_AUDIO) return (short)SAMP; else return MIN_AUDIO; } else return MAX_AUDIO; } static class _Mixer { public _Mixer() { for (int i=0;i<mastervol.length;i++) { mastervol[i] = new FloatRef(); } } /*Bit32s*/int[][] work=new int[MIXER_BUFSIZE][2]; /*Bitu*/int pos,done; /*Bitu*/int needed, min_needed, max_needed; /*Bit32u*/long tick_add,tick_remain; FloatRef[] mastervol=new FloatRef[2]; MixerChannel channels; boolean nosound; /*Bit32u*/int freq; /*Bit32u*/int blocksize; } static private _Mixer mixer; static public /*Bit8u*/byte[] MixTemp8=new byte[MIXER_BUFSIZE]; static public short[] MixTemp16=new short[MIXER_BUFSIZE>>1]; static public int[] MixTemp32=new int[MIXER_BUFSIZE>>2]; public static MixerChannel MIXER_AddChannel(MIXER_Handler handler,/*Bitu*/int freq,String name) { MixerChannel chan=new MixerChannel(); chan.scale = 1.0f; chan.handler=handler; chan.name=name; chan.SetFreq(freq); chan.next=mixer.channels; chan.SetVolume(1,1); chan.enabled=false; mixer.channels=chan; return chan; } public static MixerChannel MIXER_FindChannel(String name) { MixerChannel chan=mixer.channels; while (chan!=null) { if (chan.name.equalsIgnoreCase(name)) break; chan=chan.next; } return chan; } /* Find the device you want to delete with findchannel "delchan gets deleted" */ public static void MIXER_DelChannel(MixerChannel delchan) { MixerChannel chan=mixer.channels; MixerChannel where=mixer.channels; while (chan!=null) { if (chan==delchan) { where.next=chan.next; return; } where=chan; chan=chan.next; } } static private boolean Mixer_irq_important() { /* In some states correct timing of the irqs is more important then * non stuttering audo */ return false; } /* Mix a certain amount of new samples */ static private void MIXER_MixData(/*Bitu*/int needed) { MixerChannel chan=mixer.channels; while (chan!=null) { chan.Mix(needed); chan=chan.next; } //Reset the the tick_add for constant speed if( Mixer_irq_important() ) mixer.tick_add = ((mixer.freq) << MIXER_SHIFT)/1000; mixer.done = needed; } static private TimerResponsive MIXER_Mix = new TimerResponsive() { public void callback() { synchronized (audioMutex) { MIXER_MixData(mixer.needed); mixer.tick_remain+=mixer.tick_add; mixer.needed+=(mixer.tick_remain>>MIXER_SHIFT); mixer.tick_remain&=MIXER_REMAIN; nextExpiry += 1000000; mix.setExpiry(nextExpiry); } } public int getType() { return -1; } }; static private TimerResponsive MIXER_Mix_NoSound = new TimerResponsive() { public void callback() { MIXER_MixData(mixer.needed); /* Clear piece we've just generated */ for (/*Bitu*/int i=0;i<mixer.needed;i++) { mixer.work[mixer.pos][0]=0; mixer.work[mixer.pos][1]=0; mixer.pos=(mixer.pos+1)&MIXER_BUFMASK; } /* Reduce count in channels */ for (MixerChannel chan=mixer.channels;chan!=null;chan=chan.next) { if (chan.done>mixer.needed) chan.done-=mixer.needed; else chan.done=0; } /* Set values for next tick */ mixer.tick_remain+=mixer.tick_add; mixer.needed=(int)(mixer.tick_remain>>MIXER_SHIFT); mixer.tick_remain&=MIXER_REMAIN; mixer.done=0; nextExpiry += 1000000; mix_nosound.setExpiry(nextExpiry); } public int getType() { return -1; } }; static boolean MIXER_CallBack(byte[] stream, int len) { /*Bitu*/int need=(/*Bitu*/int)len/MIXER_SSIZE; /*Bit16s*/ShortPtr output=new ShortPtr(stream,0); /*Bitu*/int reduce; /*Bitu*/int pos, index, index_add; /*Bits*/int sample; /* Enough room in the buffer ? */ if (mixer.done < need) { if (LOG_BUFFERS) Log.log(Level.INFO, String.format("Full underrun need %d, have %d, min %d", need, mixer.done, mixer.min_needed)); if((need - mixer.done) > (need >>7) ) //Max 1 percent stretch. return false; reduce = mixer.done; index_add = (reduce << MIXER_SHIFT) / need; mixer.tick_add = ((mixer.freq+mixer.min_needed) << MIXER_SHIFT)/1000; } else if (mixer.done < mixer.max_needed) { /*Bitu*/int left = mixer.done - need; if (left < mixer.min_needed) { if( !Mixer_irq_important() ) { /*Bitu*/int needed = mixer.needed - need; /*Bitu*/int diff = (mixer.min_needed>needed?mixer.min_needed:needed) - left; mixer.tick_add = ((mixer.freq+(diff*3)) << MIXER_SHIFT)/1000; left = 0; //No stretching as we compensate with the tick_add value } else { left = (mixer.min_needed - left); left = 1 + (2*left) / mixer.min_needed; //left=1,2,3 } if (LOG_BUFFERS) Log.log(Level.INFO, String.format("needed underrun need %d, have %d, min %d, left %d", need, mixer.done, mixer.min_needed, left)); reduce = need - left; index_add = (reduce << MIXER_SHIFT) / need; } else { reduce = need; index_add = (1 << MIXER_SHIFT); if (LOG_BUFFERS) Log.log(Level.INFO, String.format("regular run need %d, have %d, min %d, left %d", need, mixer.done, mixer.min_needed, left)); /* Mixer tick value being updated: * 3 cases: * 1) A lot too high. >division by 5. but maxed by 2* min to prevent too fast drops. * 2) A little too high > division by 8 * 3) A little to nothing above the min_needed buffer > go to default value */ /*Bitu*/int diff = left - mixer.min_needed; if(diff > (mixer.min_needed<<1)) diff = mixer.min_needed<<1; if(diff > (mixer.min_needed>>1)) mixer.tick_add = ((mixer.freq-(diff/5)) << MIXER_SHIFT)/1000; else if (diff > (mixer.min_needed>>4)) mixer.tick_add = ((mixer.freq-(diff>>3)) << MIXER_SHIFT)/1000; else mixer.tick_add = (mixer.freq<< MIXER_SHIFT)/1000; } } else { /* There is way too much data in the buffer */ if (LOG_BUFFERS) Log.log(Level.INFO, String.format("overflow run need %d, have %d, min %d", need, mixer.done, mixer.min_needed)); if (mixer.done > MIXER_BUFSIZE) index_add = MIXER_BUFSIZE - 2*mixer.min_needed; else index_add = mixer.done - 2*mixer.min_needed; index_add = (index_add << MIXER_SHIFT) / need; reduce = mixer.done - 2* mixer.min_needed; mixer.tick_add = ((mixer.freq-(mixer.min_needed/5)) << MIXER_SHIFT)/1000; } /* Reduce done count in all channels */ for (MixerChannel chan=mixer.channels;chan!=null;chan=chan.next) { if (chan.done>reduce) chan.done-=reduce; else chan.done=0; } // Reset mixer.tick_add when irqs are important if( Mixer_irq_important() ) mixer.tick_add=(mixer.freq<< MIXER_SHIFT)/1000; mixer.done -= reduce; mixer.needed -= reduce; pos = mixer.pos; mixer.pos = (mixer.pos + reduce) & MIXER_BUFMASK; index = 0; if(need != reduce) { while ((need--)!=0) { /*Bitu*/int i = (pos + (index >> MIXER_SHIFT )) & MIXER_BUFMASK; index += index_add; sample=mixer.work[i][0]>>MIXER_VOLSHIFT; output.setInc(MIXER_CLIP(sample)); sample=mixer.work[i][1]>>MIXER_VOLSHIFT; output.setInc(MIXER_CLIP(sample)); } /* Clean the used buffer */ while ((reduce--)!=0) { pos &= MIXER_BUFMASK; mixer.work[pos][0]=0; mixer.work[pos][1]=0; pos++; } } else { while ((reduce--)!=0) { pos &= MIXER_BUFMASK; sample=mixer.work[pos][0]>>MIXER_VOLSHIFT; output.setInc(MIXER_CLIP(sample)); sample=mixer.work[pos][1]>>MIXER_VOLSHIFT; output.setInc(MIXER_CLIP(sample)); mixer.work[pos][0]=0; mixer.work[pos][1]=0; pos++; } } return true; } public void MakeVolume(String scan, FloatRef vol0,FloatRef vol1) { /*Bitu*/int w=0; boolean db=(scan.toUpperCase().charAt(0)=='D'); if (db) scan=scan.substring(1); while (scan.length()>0) { if (scan.charAt(0)==':') { scan=scan.substring(0);w=1; } String before=scan; float val=0.0f; try { int pos = scan.indexOf(' '); if (pos>0) val = Float.parseFloat(scan.substring(0, pos)); else val = Float.parseFloat(scan); } catch (Exception e) { scan=scan.substring(1); } if (!db) val/=100; else val=(float)Math.pow(10.0f,(float)val/20.0f); if (val<0) val=1.0f; if (w==0) { vol0.value=val; } else { vol1.value=val; } } if (w==0) vol1.value=vol0.value; } private void ShowVolume(String name,FloatRef vol0,FloatRef vol1) { System.out.printf("%-8s %3.0f:%-3.0f %+3.2f:%-+3.2f \n",new Object[] {name, new Float(vol0.value*100),new Float(vol1.value*100), new Float(20*Math.log(vol0.value)/Math.log(10.0f)),new Float(20*Math.log(vol1.value)/Math.log(10.0f))} ); } public static void MIXER_Stop() { AudioLayer.stop(); mixer = null; } final static public Object audioMutex = new Object(); public static void MIXER_Init() { mixer = new _Mixer(); /* Read out config section */ mixer.freq = Option.mixer_rate.intValue(SBlaster.OPL_RATE); mixer.nosound = Option.mixer_nosound.value(); mixer.blocksize = Option.mixer_blocksize.intValue(512); /* Initialize the internal stuff */ mixer.channels=null; mixer.pos=0; mixer.done=0; mixer.mastervol[0].value=1.0f; mixer.mastervol[1].value=1.0f; mixer.tick_remain=0; mixer.min_needed = Option.mixer_prebuffer.intValue(20); if (mixer.min_needed>100) mixer.min_needed=100; mixer.min_needed=(int)(mixer.freq*mixer.min_needed)/1000; mixer.max_needed=(int)mixer.blocksize * 2 + 2*mixer.min_needed; mixer.needed=mixer.min_needed+1; if (mixer.nosound) { Log.log(Level.INFO, "MIXER:No Sound Mode Selected."); mixer.tick_add=((mixer.freq) << MIXER_SHIFT)/1000; //Timer.TIMER_AddTickHandler(MIXER_Mix_NoSound); mix_nosound = timeSource.newTimer(MIXER_Mix_NoSound); nextExpiry = timeSource.getEmulatedNanos(); mix_nosound.setExpiry(nextExpiry); // } // else if (!AudioLayer.open(Option.mixer_javabuffer.intValue(8820), mixer.freq)) { // else if (SDL_OpenAudio(&spec, &obtained) <0 ) { // mixer.nosound = true; // Log.log_msg("MIXER:Can't open audio: %s , running in nosound mode.",SDL_GetError()); // mixer.tick_add=((mixer.freq) << MIXER_SHIFT)/1000; // Timer.TIMER_AddTickHandler(MIXER_Mix_NoSound); } else { // if((mixer.freq != obtained.freq) || (mixer.blocksize != obtained.samples)) // Log.log_msg("MIXER:Got different values from SDL: freq %d, blocksize %d",obtained.freq,obtained.samples); // mixer.freq=obtained.freq; // mixer.blocksize=obtained.samples; mixer.tick_add=(mixer.freq << MIXER_SHIFT)/1000; //Timer.TIMER_AddTickHandler(MIXER_Mix); // SDL_PauseAudio(0); } } public boolean initialised() { return (irqDevice != null) && irqDevice.initialised() && (timeSource != null); } public boolean updated() { return (irqDevice.updated() && timeSource.updated()); } public void acceptComponent(HardwareComponent component) { if ((component instanceof InterruptController) && component.initialised()) irqDevice = (InterruptController) component; if ((component instanceof Clock) && component.initialised()) { timeSource = (Clock) component; mix = timeSource.newTimer(MIXER_Mix); nextExpiry = timeSource.getEmulatedNanos(); mix.setExpiry(nextExpiry); } if (this.initialised()) { } } }