Edit: Fixed it by fixing the counter handling. 8088 MPH's volume still moves up and down constantly during normal mode 3(I think) square wave sound generation(until the credits), though.
What volume you mean? Do you mean high tones have less amplitude than low tones? After low pass filter?
What does a low pass filter do? It attenuates high frequencies, and does not attenuate low frequencies.
Therefore, after low pass filtering, high tones will have less amplitude than low tones. Right?
The low pass filter uses (as well as the low pass of the MIDI renderer and the high pass 1Hz used before sending the full mixed output of all emulated hardware to SDL) a simple RC filter:
1OPTINLINE static float calcSoundHighpassFilter(float cutoff_freq, float samplerate, float currentsample, float previoussample, float previousresult) 2{ 3 float RC = 1.0 / (cutoff_freq * 2 * 3.14); 4 float dt = 1.0 / samplerate; 5 float alpha = RC / (RC + dt); 6 return alpha * (previousresult + currentsample - previoussample); 7} 8 9OPTINLINE static float calcSoundLowpassFilter(float cutoff_freq, float samplerate, float currentsample, float previousresult) 10{ 11 float RC = (float)1.0f / (cutoff_freq * (float)2 * (float)3.14); 12 float dt = (float)1.0f / samplerate; 13 float alpha = dt / (RC + dt); 14 return previousresult + (alpha*(currentsample - previousresult)); 15} 16 17void applySoundHighpassFilter(float cutoff_freq, float samplerate, float *currentsample, float *sound_last_result, float *sound_last_sample, byte *isFirstSample) 18{ 19 //We're using a high pass filter! 20 if (*isFirstSample) //No last? Only executed once when starting playback! 21 { 22 *sound_last_result = *sound_last_sample = *currentsample; //Save the current sample! 23 *isFirstSample = 0; //Not the first sample anymore! 24 return; //Abort: don't filter the first sample! 25 } 26 *sound_last_result = calcSoundHighpassFilter(cutoff_freq, samplerate, *currentsample, *sound_last_sample, *sound_last_result); //High pass filter! 27 *sound_last_sample = *currentsample; //The last sample that was processed! 28 *currentsample = *sound_last_result; //Give the new result! 29} 30 31void applySoundLowpassFilter(float cutoff_freq, float samplerate, float *currentsample, float *sound_last_result, float *sound_last_sample, byte *isFirstSample) 32{ 33 //We're using a low pass filter! 34 if (*isFirstSample) //No last? Only executed once when starting playback! 35 { 36 *sound_last_result = *sound_last_sample = *currentsample; //Save the current sample! 37 *isFirstSample = 0; //Not the first sample anymore! 38 return; //Abort: don't filter the first sample! 39 } 40 *sound_last_result = calcSoundLowpassFilter(cutoff_freq, samplerate, *currentsample, *sound_last_result); //Low pass filter! 41 *sound_last_sample = *currentsample; //The last sample that was processed! 42 *currentsample = *sound_last_result; //Give the new result! 43}
The low pass of 20kHz is executed on the 1.19MHz samples before downsampling to 44.1kHz. The high pass of 1kHz is executed on the data that ends up in the rendering buffer (which is at that point mixed with all other hardware(the Adlib, Disney Sound Source and Covox devices(mono and stereo Covox on LPT, based on Dosbox's emulation)) before sending the data to SDL.
About your last post: The volume of all output (even the PC speaker mode 3-like ones(if it's actually square waves it's requested to generate, don't know what 8088 MPH does at that point though)) from the point the big image with the car (first 'real' 1K color image of 8088 MPH, if you don't count the fake CGA text mode comparing C64 vs CGA) is faded off the screen to continue the demo. So after the image is displayed and the sound becoming lower, making the sound of something falling on the ground and becomes quiet. Then when it continues with the next notes, it sounds like the PC speaker's volume (or Windows' volume slider) is being pulled up and down repeatedly. So like a sinus modulating the amplitude, only it's sinus-like and seems to be a bit random in speed(going slower and faster with time).
This problem won't show up in the recordings though. Maybe it's got something to do with the skipping to archieve 44.1kHz from the 1.19Mhz input? Or the high pass RC filter?
Edit: It seems I was right: disabling the 1Hz high pass filter removes the effect and makes the PC speaker music play properly (although unprotected against DC blowing up your speaker and outputting -MAXVOLTAGE with PC speaker off(PIT output is 0 since turned off=>-32878=>output=>-5V DC)). So at least the RC high pass filter isn't correct. The low pass filter might also be wrong, since it's based on the same source code by the same post(and poster/author).
Unfortunately I don't have anything else than that RC filter for high and low pass filtering(I don't know enough to make such a thing myself, not can I seem to find any c code anywhere on how to do it for different playback frequencies and cutoff frequencies, to actually fully cut off that audio). Anyone has a link to code or some code to use instead with correct high&low pass filtering?
It's essentially the only I could find, which also allows different cutoff frequencies and rendering frequencies as far as I could see. That it's a RC filter might be a problem though.
I am wondering though: Besides the filters not working properly(apparently), is the PIT output itself even correct?
1 time_ticktiming += timepassed; //Add the amount of time passed to the PIT timing! 2 3 //Render 1.19MHz samples for the time that has passed! 4 length = (uint_32)SAFEDIV(time_ticktiming, time_tick); //How many ticks to tick? 5 time_ticktiming -= (length*time_tick); //Rest the amount of ticks! 6 7 if (length) //Anything to tick at all? 8 { 9 for (channel=0;channel<3;channel++) 10 { 11 byte mode,outputmask; 12 mode = PITchannels[channel].mode; //Current mode! 13 outputmask = (channel==2)?((PCSpeakerPort&2)>>1):1; //Mask output on/off for this timer! 14 15 switch (mode) //What mode are we rendering? 16 { 17 case 0: //Interrupt on Terminal Count? Is One-Shot without Gate Input? 18 case 1: //One-shot mode? 19 for (tickcounter = length;tickcounter;--tickcounter) //Tick all needed! 20 { 21 //Length counts the amount of ticks to render! 22 switch (PITchannels[channel].status) //What status? 23 { 24 case 0: //Output goes low/high? 25 PITchannels[channel].channel_status = mode; //We're high when mode 1, else low with mode 0! 26 if (PITchannels[channel].reload) 27 { 28 PITchannels[channel].status = 1; //Skip to 1: we're ready to run already! 29 goto mode0_1; //Skip to step 1! 30 } 31 break; 32 case 1: //Wait for next rising edge of gate input? 33 mode0_1: 34 if (!mode) //No wait on mode 0? 35 { 36 PITchannels[channel].status = 2; 37 goto mode0_2; 38 } 39 else if (PITchannels[channel].gatewenthigh) //Mode 1 waits for gate to become high! 40 { 41 PITchannels[channel].gatewenthigh = 0; //Not went high anymore! 42 PITchannels[channel].status = 2; 43 goto mode0_2; 44 } 45 break; 46 case 2: //Output goes low and we start counting to rise! After timeout we become 4(inactive) with mode 1! 47 mode0_2: 48 if (PITchannels[channel].reload) 49 { 50 PITchannels[channel].reload = 0; //Not reloading anymore! 51 PITchannels[channel].channel_status = 0; //Lower output! 52 reloadticker(channel); //Reload the counter! 53 } 54 55 oldvalue = PITchannels[channel].ticker; //Save old ticker for checking for overflow! 56 57 if (mode) --PITchannels[channel].ticker; //Mode 1 always ticks? 58 else if ((PCSpeakerPort&1) || (channel<2)) --PITchannels[channel].ticker; //Mode 0 ticks when gate is high! 59 60 if ((!PITchannels[channel].ticker) && oldvalue) //Timeout when ticking? We're done!
…Show last 173 lines
61 { 62 PITchannels[channel].channel_status = 1; //We're high again! 63 } 64 break; 65 default: //Unsupported! Ignore any input! 66 break; 67 } 68 writefifobuffer(PITchannels[channel].rawsignal, PITchannels[channel].channel_status&outputmask); //Add the data to the raw signal! 69 } 70 break; 71 case 2: //Also Rate Generator mode? 72 case 6: //Rate Generator mode? 73 for (tickcounter = length;tickcounter;--tickcounter) //Tick all needed! 74 { 75 //Length counts the amount of ticks to render! 76 switch (PITchannels[channel].status) //What status? 77 { 78 case 0: //Output going high! See below! Wait for reload register to be written! 79 PITchannels[channel].channel_status = 1; //We're high! 80 PITchannels[channel].status = 1; //Skip to 1: we're ready to run already! 81 goto mode2_1; //Skip to step 1! 82 break; 83 case 1: //We're starting the count? 84 mode2_1: 85 if (PITchannels[channel].reload) 86 { 87 reload2: 88 PITchannels[channel].reload = 0; //Not reloading! 89 reloadticker(channel); //Reload the counter! 90 PITchannels[channel].channel_status = 1; //We're high! 91 PITchannels[channel].status = 2; //Start counting! 92 } 93 break; 94 case 2: //We start counting to rise!! 95 if (PITchannels[channel].gatewenthigh) //Gate went high? 96 { 97 PITchannels[channel].gatewenthigh = 0; //Not anymore! 98 goto reload2; //Reload and execute! 99 } 100 if (((PCSpeakerPort & 1) && (channel==2)) || (channel<2)) //We're high or undefined? 101 { 102 --PITchannels[channel].ticker; //Decrement? 103 switch (PITchannels[channel].ticker) //Two to one? Go low! 104 { 105 case 1: 106 PITchannels[channel].channel_status = 0; //We're going low during this phase! 107 break; 108 case 0: 109 PITchannels[channel].channel_status = 1; //We're going high again during this phase! 110 reloadticker(channel); //Reload the counter! 111 break; 112 default: //No action taken! 113 break; 114 } 115 } 116 else //We're low? Output=High and wait for reload! 117 { 118 PITchannels[channel].channel_status = 1; //We're going high again during this phase! 119 } 120 break; 121 default: //Unsupported! Ignore any input! 122 break; 123 } 124 writefifobuffer(PITchannels[channel].rawsignal, PITchannels[channel].channel_status&outputmask); //Add the data to the raw signal! 125 } 126 break; 127 //mode 2==6 and mode 3==7. 128 case 3: //Square Wave mode? 129 case 7: //Also Square Wave mode? 130 for (tickcounter = length;tickcounter;--tickcounter) //Tick all needed! 131 { 132 //Length counts the amount of ticks to render! 133 switch (PITchannels[channel].status) //What status? 134 { 135 case 0: //Output going high! See below! Wait for reload register to be written! 136 PITchannels[channel].channel_status = 1; //We're high! 137 if (PITchannels[channel].reload) 138 { 139 PITchannels[channel].reload = 0; //Not reloading! 140 reloadticker(channel); //Reload the counter! 141 PITchannels[channel].status = 1; //Next status: we're loaded and ready to run! 142 goto mode3_1; //Skip to step 1! 143 } 144 break; 145 case 1: //We start counting to rise!! 146 mode3_1: 147 if (PITchannels[channel].gatewenthigh) 148 { 149 PITchannels[channel].gatewenthigh = 0; //Not anymore! 150 PITchannels[channel].reload = 0; //Reloaded! 151 reloadticker(channel); //Gate going high reloads the ticker immediately! 152 } 153 if ((PCSpeakerPort&1) || (channel<2)) //To tick at all? 154 { 155 PITchannels[channel].ticker -= 2; //Decrement by 2 instead? 156 switch (PITchannels[channel].ticker) 157 { 158 case 0: //Even counts decreased to 0! 159 case 0xFFFF: //Odd counts decreased to -1/0xFFFF. 160 PITchannels[channel].channel_status ^= 1; //We're toggling during this phase! 161 PITchannels[channel].reload = 0; //Reloaded! 162 reloadticker(channel); //Reload the next value to tick! 163 break; 164 default: //No action taken! 165 break; 166 } 167 } 168 break; 169 default: //Unsupported! Ignore any input! 170 break; 171 } 172 writefifobuffer(PITchannels[channel].rawsignal, PITchannels[channel].channel_status&outputmask); //Add the data to the raw signal! 173 } 174 break; 175 case 4: //Software Triggered Strobe? 176 case 5: //Hardware Triggered Strobe? 177 for (tickcounter = length;tickcounter;--tickcounter) //Tick all needed! 178 { 179 switch (PITchannels[channel].status) //What status? 180 { 181 case 0: //Output going high! See below! Wait for reload register to be written! 182 PITchannels[channel].channel_status = 1; //We're high! 183 PITchannels[channel].status = 1; //Skip to 1: we're ready to run already! 184 goto mode4_1; //Skip to step 1! 185 break; 186 case 1: //We're starting the count or waiting for rising gate(mode 5)? 187 mode4_1: 188 if (PITchannels[channel].reload) 189 { 190 pit45_reload: //Reload PIT modes 4&5! 191 if ((mode == 4) || ((PITchannels[channel].gatewenthigh) && (mode == 5))) //Reload when allowed! 192 { 193 PITchannels[channel].gatewenthigh = 0; //Reset gate high flag! 194 PITchannels[channel].reload = 0; //Not reloading! 195 reloadticker(channel); //Reload the counter! 196 PITchannels[channel].status = 2; //Start counting! 197 } 198 } 199 break; 200 case 2: //We start counting to rise!! 201 case 3: //We're counting, but ignored overflow? 202 if (PITchannels[channel].reload || (((mode==5) && PITchannels[channel].gatewenthigh))) //We're reloaded? 203 { 204 goto pit45_reload; //Reload when allowed! 205 } 206 if (((PCSpeakerPort & 1) && (channel == 2)) || (channel<2)) //We're high or undefined? 207 { 208 --PITchannels[channel].ticker; //Decrement? 209 if (!PITchannels[channel].ticker && (PITchannels[channel].status!=3)) //One to zero? Go low when not overflown already! 210 { 211 PITchannels[channel].channel_status = 0; //We're going low during this phase! 212 PITchannels[channel].status = 3; //We're ignoring any further overflows from now on! 213 } 214 else 215 { 216 PITchannels[channel].channel_status = 1; //We're going high again any other phase! 217 } 218 } 219 else //We're low? Output=High and wait for reload! 220 { 221 PITchannels[channel].channel_status = 1; //We're going high again during this phase! 222 } 223 break; 224 default: //Unsupported mode! Ignore any input! 225 break; 226 } 227 writefifobuffer(PITchannels[channel].rawsignal, PITchannels[channel].channel_status&outputmask); //Add the data to the raw signal! 228 } 229 break; 230 } 231 } 232 }
Also, strange enough, when I disable the high pass 1Hz filter now, I get clear sound when the PC speaker makes music (on the slow PC running at ~10% CPU Speed now on the 2GHz Pentium Dual core P6100 processor) in all recordings, but the output gives some low frequency rumble (about 20-50Hz) without the 1Hz high pass filter. I've enabled the high pass filter for now for release (disabling the Floppy disk Gap length and Density erroring out to be able to boot things like 160K PC booter games on the emulated drive(although the Turbo XT BIOS drives it wrongly in this case?)).
You could try the filters separately if you wanted, like read a wav file and filter it and write output to another wav file.
But I think I figured out at least one thing that could cause the problems you see.
You convert the PIT output of 0s and 1s blindly to full scale -32768 and +32767 without thinking about signal amplitudes and midpoints (I did mention earlier this leaves no headroom if the filters act funnily and amplify a bit, but now I realized it is a problem here as well).
Imagine you boot the emulator, and the PIT output is just zeroes, so your raw output is just -32768. When there is a beep, it jumps between 32767 and -32768 as we know, or in other words, the beep has a peak-to-peak amplitude of 65535, so it steps up by 65535 and down by 65535.
Now, when you have the DC-blocking 1Hz high-pass filter there, the -32768 gets slowly filtered away and the output signal is 0. But the beep signal still wants to jump up by 65535 and again down by 65535. But it won't fit into 16-bit signed integer any more, so the signal gets distorted (clips or overflows or whatever you have specified it to do in case it does not fit).
So at least halve the emulated amplitude, or even better, make it somewhat less than half, before filtering. Anyway, full scale 16-bit signal would be loud as hell and my gut feeling says people don't want their PC speakers as loud as their music or effects from other sound devices.
I notice that increasing the high pass filter to 18.2Hz removes the echo effect that occurred with 1Hz. Audio sounds fine so far. Still untested on 8088MPH though as the pc I was working on (The 2GHz one) was too slow(CPU now running at 10% due to the PC speaker's heavy low pass filter(1.19M floating point filters/second).
Emmm. Jumping up and down by 64K still fits inside 16-bits?
signed 16-bits?
Yes if you don't remove DC and start from -32768.
No if you remove DC and start from 0.
superfury wrote:
Also, the filter only attenuates(lowers volume), not amplifies it?
Yes, there is attenuation of low frequencies, yes, it does not amplify it. But still no, because, the DC blocking high pass filter attenuates low frequencies (removes DC) and passes high frequencies as they were (amplitude does not change). A step signal of 65535 into high pass filter will still come out as step signal of 65535, but without previous DC component, so nothing is amplified, but like I explained above, you can feed in a step from -32768 to 32767, but because DC was removed, the output would want to go from 0 to 65535 and it won't fit into signed 16 bit integer.
Why from 0 to 65535? The previous signal would be (as given by the PC Speaker) -32768, the new sample +32767, so the output should move as a result from the high pass filter (previous was zero) to 32767, not 65536? Unless you would double the amplitude from the PC Speaker itself(which isn't done)?
DC is only removed as the result of the mixer mixing rendered samples from the PC Speaker and other hardware, not in the PC Speaker renderer itself(which still moves from -32768 to +32767)? Since the previous sample is ignored, the output will move from 0(The DC current) to 32767(the new speaker sample), so nothing is out of limits with the high pass filter?
I don't know what's wrong with the PC Speaker emulation itself yet though.
Also, the filter only attenuates(lowers volume), not amplifies it?
I have to think about it wrt high pass filters, but filters in general definitely can. For example, look at the summed low order harmonics of a square wave. So, leaving head room is a very good idea.
As you can see, if you LPF at full scale you get clipping distortion.
Why from 0 to 65535? The previous signal would be (as given by the PC Speaker) -32768, the new sample +32767, so the output should move as a result from the high pass filter (previous was zero) to 32767, not 65536? Unless you would double the amplitude from the PC Speaker itself(which isn't done)?
DC is only removed as the result of the mixer mixing rendered samples from the PC Speaker and other hardware, not in the PC Speaker renderer itself(which still moves from -32768 to +32767)? Since the previous sample is ignored, the output will move from 0(The DC current) to 32767(the new speaker sample), so nothing is out of limits with the high pass filter?
OK so the DC removal filtering is not done at the speaker render any more, but globally.
I am sorry I don't follow your reasoning why you think a 64k sample step at input would become 32k sample step at output.
But here's an electronic version : if you make a RC high pass filter, a step of 10 volts (AC) at the input will create a step of 10 volts (AC) at the output. The actual input DC voltage can be -5V before the jump so it goes from -5V to +5V, but on the output there is no DC so the jump is from 0V to 10V. So, a jump of 65k values on input will be 65k jump values on output, even if the actual DC value at input was -32k and DC on the output was 0.
superfury wrote:
I don't know what's wrong with the PC Speaker emulation itself yet though.
I have only played back the raw dumps and they show some variation in tempo, most likely because some parts are running with music playing in timer interrupt, some parts run music on vertical retrace timing. Pretty good actually.
So the actual reason that 8088 MPH is making slow music at some parts of the demo(like the 1K color image, where it shows on the vga only half a screen with the bottom part in text mode with blinking text (1K colors text enlarged) and the top half shaking with the data from the 1K image(1K colors on a IBM CGA I believe it says in the demo). So this isn't caused by the CPU or PIT that's too slow, but actually because the vertical retrace interrupt(IRQ2) isn't firing fast enough because the rendering is too slow(because it's limiting it's VGA resources, sharing 50/50 with the CPU processing timing(it only executes samples for as long as the computer can safely handle it, thus processing below normal VGA speed, thus triggering too few IRQ2 interrupts, causing music to slow down? At least it should do so according to the calculations, if they're correct?
1//CPU cycle locked version of VGA rendering! 2void updateVGA(double timepassed) 3{ 4 uint_64 limitcalc; 5 double timeprocessed; 6 VGA_timing += timepassed; //Time has passed! 7 if ((VGA_timing >= VGA_rendertiming) && VGA_rendertiming) //Might have passed? 8 { 9 uint_64 renderings,renderingsbackup; 10 renderings = (uint_64)(VGA_timing/VGA_rendertiming); //Ammount of times to render! 11 VGA_timing -= (renderings*VGA_rendertiming); //Rest the amount we can process! 12 13 if ((renderings>VGA_limit) && VGA_limit) //Limit broken? 14 { 15 renderings = VGA_limit; //Limit the processing to the amount of time specified! 16 } 17 if (!renderings) return; //Nothing to render! 18 timeprocessed = (renderings*VGA_rendertiming); //How much are we processing? 19 timeprocessed *= 0.50; //We're running too slow at full rendering, so split 50/50! 20 renderingsbackup = renderings; //Save the backup for comparision! 21 22 if (!doVGA_Sequencer()) return; //Don't execute the sequencer if requested to! 23 24 SEQ_DATA *Sequencer; 25 Sequencer = GETSEQUENCER(getActiveVGA()); //Our sequencer! 26 27 getnspassed(&VGA_test); 28 do 29 { 30 if (renderings>=20) //20+ optimization? 31 { 32 VGA_Sequencer(Sequencer); //Tick the VGA once! 33 VGA_Sequencer(Sequencer); //Tick the VGA once! 34 VGA_Sequencer(Sequencer); //Tick the VGA once! 35 VGA_Sequencer(Sequencer); //Tick the VGA once! 36 VGA_Sequencer(Sequencer); //Tick the VGA once! 37 VGA_Sequencer(Sequencer); //Tick the VGA once! 38 VGA_Sequencer(Sequencer); //Tick the VGA once! 39 VGA_Sequencer(Sequencer); //Tick the VGA once! 40 VGA_Sequencer(Sequencer); //Tick the VGA once! 41 VGA_Sequencer(Sequencer); //Tick the VGA once! 42 VGA_Sequencer(Sequencer); //Tick the VGA once! 43 VGA_Sequencer(Sequencer); //Tick the VGA once! 44 VGA_Sequencer(Sequencer); //Tick the VGA once! 45 VGA_Sequencer(Sequencer); //Tick the VGA once! 46 VGA_Sequencer(Sequencer); //Tick the VGA once! 47 VGA_Sequencer(Sequencer); //Tick the VGA once! 48 VGA_Sequencer(Sequencer); //Tick the VGA once! 49 VGA_Sequencer(Sequencer); //Tick the VGA once! 50 VGA_Sequencer(Sequencer); //Tick the VGA once! 51 renderings -= 19; //We've processed 19 more! 52 } 53 VGA_Sequencer(Sequencer); //Tick the VGA once! 54 } while (--renderings); //Ticks left to tick? 55 limitcalc = getnspassed(&VGA_test); //How long have we taken? 56 57 //timeprocessed=how much time to use, limitcalc=how much time we have taken, renderingsbackup=How many pixels have we processed. 58 VGA_limit = (uint_64)(((float)renderingsbackup/(float)limitcalc)*timeprocessed); //Don't process any more than we're allowed to (timepassed). 59 if (limitcalc<=timeprocessed) VGA_limit = 0; //Don't limit if we're running at full speed (we're below time we are allowed to process)! 60 }
…Show last 1 line
61}
Where timepassed is the time the CPU emulation has progressed, in nanoseconds. This is essentially based on either the 8086 4.77MHz clock tick time multiplied by the instruction cycles(currently 9 on 80(1)88, 8 on 80(1)86) with default setting used. When a Dosbox count is set, it uses 1ms time units(like Dosbox cycles setting) instead for any instruction, making sure count times 1000 instructions are executed each millisecond, like Dosbox.
Is the calculation made based on used VGA processing time and CPU instruction time(timepassed) correct? Does it actually use full VGA speed(limit of 0) when the host CPU is fast enough to run CPU and VGA 50/50, both at full speed?
Besides the video timing is wildly different, using default CGA mode on real CGA adapter draws ~15700 lines per second at ~59.92 frames per second, while emulated CGA mode on VGA adapter draws ~31469 lines per second at ~70.09 frames per second. And who knows what tweaks are used to get possibly non-standard video modes or timings.
So unless you emulate CGA adapter, the timing of drawing scan lines and frames is not what the software expects.
And who knows what tweaks are used to get possibly non-standard video modes or timings.
All modes in 8088 MPH should maintain the standard 'near-NTSC' timing of CGA, as in 262 scanlines per field, and 59.92 fields per second. This is because our output is for NTSC-composite devices. So we don't have the flexibility of multisync monitors like you'd have with VGA.
I've increased the rendering parts speed a bit(VGA and text surfaces). Now 8088 MPH is running up to ~38-40 FPS framerate with ~90-100% CPU speed. Thus audio is a bit faster on those slow parts. It does hurt a little bit on the audio rendering for this reason though(only 90-100% samples rendered each second).
Edir: Once again increased it to ~70-80FPS. But running a simple game like Zool gets unstable framerate atm(Varying from 5-70FPS and back). Even though it locks to a certain rate after testing dynamical speed(old method) for 1000 pixels before locking the limiter(of pixels per handling). So if the host CPU can only handle x% of the pixels in the CPU time, only x% will be locked to until the next mode change(which affects rendering speed) with the new method. The old method won't lock. Full synchronization does neither, instead running full blast(and might be slowing the realtime CPU speed in the case the host isn't fast enough to handle it). Although only half of the actual CPU speed is used atm.
The old method continuously calculates, the new method calculates 1000 pixels and locks, full synchronization runs at full speed without limiting(100% CPU synchronized speed).
Edit: The unstable framerate seems to be fixed by setting the new synchronization setting BEFORE starting the game, as it seems to change the active display settings at that point only?