diff -rupN dosbox-Original//src/dosbox.cpp dosbox/src/dosbox.cpp
--- dosbox-Original//src/dosbox.cpp
+++ dosbox/src/dosbox.cpp
@@ -615,7 +615,7 @@ void DOSBOX_Init(void) {
Pstring = Pmulti->GetSection()->Add_string("type",Property::Changeable::Always,"normal2x");
const char *scalers[] = {
- "none", "normal2x", "normal3x",
+ "none", "normal2x", "normal3x", "normal4x",
#if RENDER_USE_ADVANCED_SCALERS>2
"advmame2x", "advmame3x", "advinterp2x", "advinterp3x", "hq2x", "hq3x", "2xsai", "super2xsai", "supereagle",
#endif
diff -rupN dosbox-Original//src/gui/render.cpp dosbox/src/gui/render.cpp
--- dosbox-Original//src/gui/render.cpp
+++ dosbox/src/gui/render.cpp
@@ -386,6 +386,8 @@ static void RENDER_Reset( void ) {
simpleBlock = &ScaleNormal2x;
else if (render.scale.size == 3)
simpleBlock = &ScaleNormal3x;
+ else if (render.scale.size == 4)
+ simpleBlock = &ScaleNormal4x;
else
simpleBlock = &ScaleNormal1x;
/* Maybe override them */
@@ -700,6 +702,7 @@ void RENDER_Init(Section * sec) {
if (scaler == "none") { render.scale.op = scalerOpNormal;render.scale.size = 1; }
else if (scaler == "normal2x") { render.scale.op = scalerOpNormal;render.scale.size = 2; }
else if (scaler == "normal3x") { render.scale.op = scalerOpNormal;render.scale.size = 3; }
+ else if (scaler == "normal4x") { render.scale.op = scalerOpNormal;render.scale.size = 4; }
#if RENDER_USE_ADVANCED_SCALERS>2
else if (scaler == "advmame2x") { render.scale.op = scalerOpAdvMame;render.scale.size = 2; }
else if (scaler == "advmame3x") { render.scale.op = scalerOpAdvMame;render.scale.size = 3; }
diff -rupN dosbox-Original//src/gui/render_scalers.cpp dosbox/src/gui/render_scalers.cpp
--- dosbox-Original//src/gui/render_scalers.cpp
+++ dosbox/src/gui/render_scalers.cpp
@@ -256,6 +256,23 @@ ScalerSimpleBlock_t ScaleNormal3x = {
{ Normal3x_8_8_R, Normal3x_9_15_R , Normal3x_9_16_R , Normal3x_9_32_R }
}};
+ScalerSimpleBlock_t ScaleNormal4x = {
+ "Normal4x",
+ GFX_CAN_8|GFX_CAN_15|GFX_CAN_16|GFX_CAN_32,
+ 4,4,{
+{ Normal4x_8_8_L, Normal4x_8_15_L , Normal4x_8_16_L , Normal4x_8_32_L },
+{ 0, Normal4x_15_15_L, Normal4x_15_16_L, Normal4x_15_32_L},
+{ 0, Normal4x_16_15_L, Normal4x_16_16_L, Normal4x_16_32_L},
+{ 0, Normal4x_32_15_L, Normal4x_32_16_L, Normal4x_32_32_L},
+{ Normal4x_8_8_L, Normal4x_9_15_L , Normal4x_9_16_L , Normal4x_9_32_L }
+},{
+{ Normal4x_8_8_R, Normal4x_8_15_R , Normal4x_8_16_R , Normal4x_8_32_R },
+{ 0, Normal4x_15_15_R, Normal4x_15_16_R, Normal4x_15_32_R},
+{ 0, Normal4x_16_15_R, Normal4x_16_16_R, Normal4x_16_32_R},
+{ 0, Normal4x_32_15_R, Normal4x_32_16_R, Normal4x_32_32_R},
+{ Normal4x_8_8_R, Normal4x_9_15_R , Normal4x_9_16_R , Normal4x_9_32_R }
+}};
+
#if RENDER_USE_ADVANCED_SCALERS>0
ScalerSimpleBlock_t ScaleTV2x = {
"TV2x",
diff -rupN dosbox-Original//src/gui/render_scalers.h dosbox/src/gui/render_scalers.h
--- dosbox-Original//src/gui/render_scalers.h
+++ dosbox/src/gui/render_scalers.h
@@ -113,6 +113,7 @@ extern ScalerSimpleBlock_t ScaleNormalDw
extern ScalerSimpleBlock_t ScaleNormalDh;
extern ScalerSimpleBlock_t ScaleNormal2x;
extern ScalerSimpleBlock_t ScaleNormal3x;
+extern ScalerSimpleBlock_t ScaleNormal4x;
#if RENDER_USE_ADVANCED_SCALERS>0
extern ScalerSimpleBlock_t ScaleTV2x;
extern ScalerSimpleBlock_t ScaleTV3x;
diff -rupN dosbox-Original//src/gui/render_templates.h dosbox/src/gui/render_templates.h
--- dosbox-Original//src/gui/render_templates.h
+++ dosbox/src/gui/render_templates.h
@@ -271,6 +271,32 @@ static inline void conc3d(Cache,SBPP,DBP
#undef SCALERHEIGHT
#undef SCALERFUNC
+#define SCALERNAME Normal4x
+#define SCALERWIDTH 4
+#define SCALERHEIGHT 4
+#define SCALERFUNC \
+ line0[0] = P; \
+ line0[1] = P; \
+ line0[2] = P; \
+ line0[3] = P; \
+ line1[0] = P; \
+ line1[1] = P; \
+ line1[2] = P; \
+ line1[3] = P; \
+ line2[0] = P; \
+ line2[1] = P; \
+ line2[2] = P; \
+ line2[3] = P; \
+ line3[0] = P; \
+ line3[1] = P; \
+ line3[2] = P; \
+ line3[3] = P;
+#include "render_simple.h"
+#undef SCALERNAME
+#undef SCALERWIDTH
+#undef SCALERHEIGHT
+#undef SCALERFUNC
+
#define SCALERNAME NormalDw
#define SCALERWIDTH 2
#define SCALERHEIGHT 1
diff -rupN dosbox-Original//src/gui/render_simple.h dosbox/src/gui/render_simple.h
--- dosbox-Original//src/gui/render_simple.h
+++ dosbox/src/gui/render_simple.h
@@ -56,6 +56,9 @@ static inline void conc4d_sub_func(const
#if (SCALERHEIGHT > 2)
PTYPE *line2 = WC[1];
#endif
+#if (SCALERHEIGHT > 3)
+ PTYPE *line3 = WC[2];
+#endif
#else
#if (SCALERHEIGHT > 1)
PTYPE *line1 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch);
@@ -63,6 +66,9 @@ static inline void conc4d_sub_func(const
#if (SCALERHEIGHT > 2)
PTYPE *line2 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch * 2);
#endif
+#if (SCALERHEIGHT > 3)
+ PTYPE *line3 = (PTYPE *)(((Bit8u*)line0)+ render.scale.outPitch * 3);
+#endif
#endif //defined(SCALERLINEAR)
hadChange = 1;
unsigned int i = block_proc; /* WARNING: assume block_proc != 0 */
@@ -78,6 +84,9 @@ static inline void conc4d_sub_func(const
#if (SCALERHEIGHT > 2)
line2 += SCALERWIDTH;
#endif
+#if (SCALERHEIGHT > 3)
+ line3 += SCALERWIDTH;
+#endif
} while (--i != 0u);
#if defined(SCALERLINEAR)
#if (SCALERHEIGHT > 1)
When you say integration of SDL1, do you mean having an in-tree version of SDL1 the same way DOSBox-X does?
TheGreatCodeholio wrote:I have a "mirror" of SVN here: https://github.com/joncampbell123/dosbox-svn
I trans-planted the hacked SDL1 library from DOSBox-X to give DOSBox SVN something to compile against that displays on Mojave correctly. So far, it works.
[code]diff -rupN dosbox-svn/include/mixer.h dosbox-mixer//include/mixer.h
--- dosbox-svn/include/mixer.h
+++ dosbox-mixer//include/mixer.h
@@ -49,13 +49,22 @@ public:
void SetVolume(float _left,float _right);
void SetScale( float f );
void UpdateVolume(void);
- void SetFreq(Bitu _freq);
- void Mix(Bitu _needed);
+ void SetFreq(Bitu _freq,Bitu _den=1U);
+ void Mix(Bitu whole,Bitu frac);
void AddSilence(void); //Fill up until needed
+ void EndFrame(Bitu samples);
+
+ template<class Type,bool stereo,bool signeddata,bool nativeorder>
+ void loadCurrentSample(Bitu &len, const Type* &data);
template<class Type,bool stereo,bool signeddata,bool nativeorder>
void AddSamples(Bitu len, const Type* data);
+ double timeSinceLastSample(void);
+
+ bool runSampleInterpolation(const Bitu upto);
+ void padFillSampleInterpolation(const Bitu upto);
+ void finishSampleInterpolation(const Bitu upto);
void AddSamples_m8(Bitu len, const Bit8u * data);
void AddSamples_s8(Bitu len, const Bit8u * data);
void AddSamples_m8s(Bitu len, const Bit8s * data);
@@ -72,8 +81,6 @@ public:
void AddSamples_s16u_nonnative(Bitu len, const Bit16u * data);
void AddSamples_m32_nonnative(Bitu len, const Bit32s * data);
void AddSamples_s32_nonnative(Bitu len, const Bit32s * data);
-
- void AddStretched(Bitu len,Bit16s * data); //Strech block up into needed data
void FillUp(void);
void Enable(bool _yesno);
@@ -81,18 +88,17 @@ public:
float volmain[2];
float scale;
Bit32s volmul[2];
-
- //This gets added the frequency counter each mixer step
- Bitu freq_add;
- //When this flows over a new sample needs to be read from the device
- Bitu freq_counter;
- //Timing on how many samples have been done and were needed by th emixer
- Bitu done, needed;
- //Previous and next samples
- Bits prevSample[2];
- Bits nextSample[2];
+ unsigned int freq_f;
+ unsigned int rendering_to_n,rendering_to_d;
+ unsigned int rend_n,rend_d;
+ unsigned int freq_n,freq_d,freq_d_orig;
+ bool current_loaded;
+ Bit32s current[2],last[2],delta[2],max_change;
+ Bit32s msbuffer[2048][2]; // more than enough for 1ms of audio, at mixer sample rate
+ Bits last_sample_write;
+ Bitu msbuffer_o;
+ Bitu msbuffer_i;
const char * name;
- bool interpolate;
bool enabled;
MixerChannel * next;
};
diff -rupN dosbox-svn/src/hardware/adlib.cpp dosbox-mixer//src/hardware/adlib.cpp
--- dosbox-svn/src/hardware/adlib.cpp
+++ dosbox-mixer//src/hardware/adlib.cpp
@@ -703,6 +703,11 @@ static Bitu OPL_Read(Bitu port,Bitu iole
}
void OPL_Write(Bitu port,Bitu val,Bitu iolen) {
+ // if writing the data port, assume a change in OPL state that should be reflected immediately.
+ // this is a way to render "sample accurate" without needing "sample accurate" mode in the mixer.
+ // CHGOLF's Adlib digital audio hack works fine with this hack.
+ if (port&1) module->mixerChan->FillUp();
+
module->PortWrite( port, val, iolen );
}
diff -rupN dosbox-svn/src/hardware/mixer.cpp dosbox-mixer//src/hardware/mixer.cpp
--- dosbox-svn/src/hardware/mixer.cpp
+++ dosbox-mixer//src/hardware/mixer.cpp
@@ -50,42 +50,39 @@
#include "midi.h"
#define MIXER_SSIZE 4
-
-//#define MIXER_SHIFT 14
-//#define MIXER_REMAIN ((1<<MIXER_SHIFT)-1)
-
#define MIXER_VOLSHIFT 13
-#define FREQ_SHIFT 14
-#define FREQ_NEXT ( 1 << FREQ_SHIFT)
-#define FREQ_MASK ( FREQ_NEXT -1 )
-
-#define TICK_SHIFT 14
-#define TICK_NEXT ( 1 << TICK_SHIFT)
-#define TICK_MASK (TICK_NEXT -1)
-
-
static INLINE Bit16s MIXER_CLIP(Bits SAMP) {
if (SAMP < MAX_AUDIO) {
if (SAMP > MIN_AUDIO)
return SAMP;
- else return MIN_AUDIO;
- } else return MAX_AUDIO;
+ else
+ return MIN_AUDIO;
+ } else {
+ return MAX_AUDIO;
+ }
}
+struct mixedFraction {
+ unsigned int w;
+ unsigned int fn,fd;
+};
+
static struct {
- Bit32s work[MIXER_BUFSIZE][2];
- //Write/Read pointers for the buffer
- Bitu pos,done;
- Bitu needed, min_needed, max_needed;
- //For every millisecond tick how many samples need to be generated
- Bit32u tick_add;
- Bit32u tick_counter;
- float mastervol[2];
- MixerChannel * channels;
- bool nosound;
- Bit32u freq;
- Bit32u blocksize;
+ Bit32s work[MIXER_BUFSIZE][2];
+ Bitu work_in,work_out,work_wrap;
+ Bitu pos,done;
+ float mastervol[2];
+ float recordvol[2];
+ MixerChannel* channels;
+ Bit32u freq;
+ Bit32u blocksize;
+ struct mixedFraction samples_per_ms;
+ struct mixedFraction samples_this_ms;
+ struct mixedFraction samples_rendered_ms;
+ bool nosound;
+ bool prebuffer_wait;
+ Bitu prebuffer_samples;
} mixer;
Bit8u MixTemp[MIXER_BUFSIZE];
@@ -93,13 +90,24 @@ Bit8u MixTemp[MIXER_BUFSIZE];
MixerChannel * MIXER_AddChannel(MIXER_Handler handler,Bitu freq,const char * name) {
MixerChannel * chan=new MixerChannel();
chan->scale = 1.0;
+ chan->last_sample_write = 0;
+ chan->current_loaded = false;
chan->handler=handler;
chan->name=name;
+ chan->msbuffer_i = 0;
+ chan->msbuffer_o = 0;
+ chan->freq_n = chan->freq_d = 1;
+
+ chan->freq_d_orig = 1;
+ chan->freq_f = 0;
chan->SetFreq(freq);
chan->next=mixer.channels;
chan->SetVolume(1,1);
chan->enabled=false;
- chan->interpolate = false;
+ chan->last[0] = chan->last[1] = 0;
+ chan->delta[0] = chan->delta[1] = 0;
+ chan->current[0] = chan->current[1] = 0;
+
mixer.channels=chan;
return chan;
}
@@ -128,8 +136,8 @@ void MIXER_DelChannel(MixerChannel* delc
}
void MixerChannel::UpdateVolume(void) {
- volmul[0]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[0]*mixer.mastervol[0]);
- volmul[1]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[1]*mixer.mastervol[1]);
+ volmul[0]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[0]);
+ volmul[1]=(Bits)((1 << MIXER_VOLSHIFT)*scale*volmain[1]);
}
void MixerChannel::SetVolume(float _left,float _right) {
@@ -143,192 +151,231 @@ void MixerChannel::SetScale( float f ) {
UpdateVolume();
}
+static void MIXER_FillUp(void);
+
void MixerChannel::Enable(bool _yesno) {
if (_yesno==enabled) return;
enabled=_yesno;
- if (enabled) {
- freq_counter = 0;
- SDL_LockAudio();
- if (done<mixer.done) done=mixer.done;
- SDL_UnlockAudio();
- }
+ if (!enabled) freq_f=0;
}
-void MixerChannel::SetFreq(Bitu freq) {
- freq_add=(freq<<FREQ_SHIFT)/mixer.freq;
+void MixerChannel::SetFreq(Bitu _freq,Bitu _den) {
+ if (freq_n == _freq && freq_d == freq_d_orig)
+ return;
- if (freq != mixer.freq) {
- interpolate = true;
+ if (freq_d_orig != _den) {
+ Bit64u tmp = (Bit64u)freq_f * (Bit64u)_den * (Bit64u)mixer.freq;
+ freq_f = (unsigned int)(tmp / (Bit64u)freq_d_orig);
+ }
+
+ freq_n = _freq;
+ freq_d = _den * mixer.freq;
+ freq_d_orig = _den;
+}
+
+void MixerChannel::EndFrame(Bitu samples) {
+ rend_n = rend_d = 0;
+ if (msbuffer_o <= samples) {
+ msbuffer_o = 0;
+ msbuffer_i = 0;
}
else {
- interpolate = false;
+ msbuffer_o -= samples;
+ if (msbuffer_i >= samples) msbuffer_i -= samples;
+ else msbuffer_i = 0;
+ memmove(&msbuffer[0][0],&msbuffer[samples][0],msbuffer_o*sizeof(Bit32s)*2/*stereo*/);
}
+
+ last_sample_write -= (unsigned int)samples;
}
-void MixerChannel::Mix(Bitu _needed) {
- needed=_needed;
- while (enabled && needed>done) {
- Bitu left = (needed - done);
- left *= freq_add;
- left = (left >> FREQ_SHIFT) + ((left & FREQ_MASK)!=0);
- handler(left);
+void MixerChannel::Mix(Bitu whole,Bitu frac) {
+ unsigned int patience = 2;
+ Bitu upto;
+
+ if (whole <= rend_n) return;
+ // assert(whole <= mixer.samples_this_ms.w);
+ // assert(rend_n < mixer.samples_this_ms.w);
+ Bit32s *outptr = &mixer.work[mixer.work_in+rend_n][0];
+
+ if (!enabled) {
+ rend_n = whole;
+ rend_d = frac;
+ return;
+ }
+
+ // HACK: We iterate twice only because of the Sound Blaster emulation. No other emulation seems to need this.
+ rendering_to_n = whole;
+ rendering_to_d = frac;
+ while (msbuffer_o < whole) {
+ Bit64u todo = (Bit64u)(whole - msbuffer_o) * (Bit64u)freq_n;
+ todo += (Bit64u)freq_f;
+ todo += (Bit64u)freq_d - (Bit64u)1;
+ todo /= (Bit64u)freq_d;
+ if (!current_loaded) todo++;
+ handler(todo);
+
+ if (--patience == 0) break;
+ }
+
+ if (msbuffer_o < whole)
+ padFillSampleInterpolation(whole);
+
+ upto = whole;
+ if (upto > msbuffer_o) upto = msbuffer_o;
+
+ while (rend_n < whole && msbuffer_i < upto) {
+ *outptr++ += msbuffer[msbuffer_i][0];
+ *outptr++ += msbuffer[msbuffer_i][1];
+ msbuffer_i++;
+ rend_n++;
}
+
+ rend_n = whole;
+ rend_d = frac;
}
void MixerChannel::AddSilence(void) {
- if (done<needed) {
- done=needed;
- //Make sure the next samples are zero when they get switched to prev
- nextSample[0] = 0;
- nextSample[1] = 0;
- //This should trigger an instant request for new samples
- freq_counter = FREQ_NEXT;
- }
}
template<class Type,bool stereo,bool signeddata,bool nativeorder>
-inline void MixerChannel::AddSamples(Bitu len, const Type* data) {
- //Position where to write the data
- Bitu mixpos = mixer.pos + done;
- //Position in the incoming data
- Bitu pos = 0;
- //Mix and data for the full length
- while (1) {
- //Does new data need to get read?
- while (freq_counter >= FREQ_NEXT) {
- //Would this overflow the source data, then it's time to leave
- if (pos >= len)
- return;
- freq_counter -= FREQ_NEXT;
- prevSample[0] = nextSample[0];
- if (stereo) {
- prevSample[1] = nextSample[1];
- }
- if ( sizeof( Type) == 1) {
- if (!signeddata) {
- if (stereo) {
- nextSample[0]=(((Bit8s)(data[pos*2+0] ^ 0x80)) << 8);
- nextSample[1]=(((Bit8s)(data[pos*2+1] ^ 0x80)) << 8);
- } else {
- nextSample[0]=(((Bit8s)(data[pos] ^ 0x80)) << 8);
- }
- } else {
- if (stereo) {
- nextSample[0]=(data[pos*2+0] << 8);
- nextSample[1]=(data[pos*2+1] << 8);
- } else {
- nextSample[0]=(data[pos] << 8);
- }
- }
- //16bit and 32bit both contain 16bit data internally
- } else {
- if (signeddata) {
- if (stereo) {
- if (nativeorder) {
- nextSample[0]=data[pos*2+0];
- nextSample[1]=data[pos*2+1];
- } else {
- if ( sizeof( Type) == 2) {
- nextSample[0]=(Bit16s)host_readw((HostPt)&data[pos*2+0]);
- nextSample[1]=(Bit16s)host_readw((HostPt)&data[pos*2+1]);
- } else {
- nextSample[0]=(Bit32s)host_readd((HostPt)&data[pos*2+0]);
- nextSample[1]=(Bit32s)host_readd((HostPt)&data[pos*2+1]);
- }
- }
- } else {
- if (nativeorder) {
- nextSample[0] = data[pos];
- } else {
- if ( sizeof( Type) == 2) {
- nextSample[0]=(Bit16s)host_readw((HostPt)&data[pos]);
- } else {
- nextSample[0]=(Bit32s)host_readd((HostPt)&data[pos]);
- }
- }
- }
- } else {
- if (stereo) {
- if (nativeorder) {
- nextSample[0]=(Bits)data[pos*2+0]-32768;
- nextSample[1]=(Bits)data[pos*2+1]-32768;
- } else {
- if ( sizeof( Type) == 2) {
- nextSample[0]=(Bits)host_readw((HostPt)&data[pos*2+0])-32768;
- nextSample[1]=(Bits)host_readw((HostPt)&data[pos*2+1])-32768;
- } else {
- nextSample[0]=(Bits)host_readd((HostPt)&data[pos*2+0])-32768;
- nextSample[1]=(Bits)host_readd((HostPt)&data[pos*2+1])-32768;
- }
- }
- } else {
- if (nativeorder) {
- nextSample[0]=(Bits)data[pos]-32768;
- } else {
- if ( sizeof( Type) == 2) {
- nextSample[0]=(Bits)host_readw((HostPt)&data[pos])-32768;
- } else {
- nextSample[0]=(Bits)host_readd((HostPt)&data[pos])-32768;
- }
- }
- }
- }
- }
- //This sample has been handled now, increase position
- pos++;
+inline void MixerChannel::loadCurrentSample(Bitu &len, const Type* &data) {
+ last[0] = current[0];
+ last[1] = current[1];
+
+ if (sizeof(Type) == 1) {
+ const uint8_t xr = signeddata ? 0x00 : 0x80;
+
+ len--;
+ current[0] = ((Bit8s)((*data++) ^ xr)) << 8;
+ if (stereo)
+ current[1] = ((Bit8s)((*data++) ^ xr)) << 8;
+ else
+ current[1] = current[0];
+ }
+ else if (sizeof(Type) == 2) {
+ const uint16_t xr = signeddata ? 0x0000 : 0x8000;
+ uint16_t d;
+
+ len--;
+ if (nativeorder) d = ((Bit16u)((*data++) ^ xr));
+ else d = host_readw((HostPt)(data++)) ^ xr;
+ current[0] = (Bit16s)d;
+ if (stereo) {
+ if (nativeorder) d = ((Bit16u)((*data++) ^ xr));
+ else d = host_readw((HostPt)(data++)) ^ xr;
+ current[1] = (Bit16s)d;
}
- //Where to write
- mixpos &= MIXER_BUFMASK;
- Bit32s* write = mixer.work[mixpos];
- if (!interpolate) {
- write[0] += prevSample[0] * volmul[0];
- write[1] += (stereo ? prevSample[1] : prevSample[0]) * volmul[1];
+ else {
+ current[1] = current[0];
+ }
+ }
+ else if (sizeof(Type) == 4) {
+ const uint32_t xr = signeddata ? 0x00000000UL : 0x80000000UL;
+ uint32_t d;
+
+ len--;
+ if (nativeorder) d = ((Bit32u)((*data++) ^ xr));
+ else d = host_readd((HostPt)(data++)) ^ xr;
+ current[0] = (Bit32s)d;
+ if (stereo) {
+ if (nativeorder) d = ((Bit32u)((*data++) ^ xr));
+ else d = host_readd((HostPt)(data++)) ^ xr;
+ current[1] = (Bit32s)d;
}
else {
- Bits diff_mul = freq_counter & FREQ_MASK;
- Bits sample = prevSample[0] + (((nextSample[0] - prevSample[0]) * diff_mul) >> FREQ_SHIFT);
- write[0] += sample*volmul[0];
- if (stereo) {
- sample = prevSample[1] + (((nextSample[1] - prevSample[1]) * diff_mul) >> FREQ_SHIFT);
- }
- write[1] += sample*volmul[1];
+ current[1] = current[0];
+ }
+ }
+ else {
+ current[0] = current[1] = 0;
+ len = 0;
+ }
+
+ if (stereo) {
+ delta[0] = current[0] - last[0];
+ delta[1] = current[1] - last[1];
+ }
+ else {
+ delta[1] = delta[0] = current[0] - last[0];
+ }
+
+ current_loaded = true;
+}
+
+inline void MixerChannel::padFillSampleInterpolation(const Bitu upto) {
+ finishSampleInterpolation(upto);
+ if (msbuffer_o < upto) {
+ if (freq_f > freq_d) freq_f = freq_d; // this is an abrupt stop, so interpolation must not carry over, to help avoid popping artifacts
+
+ while (msbuffer_o < upto) {
+ msbuffer[msbuffer_o][0] = current[0];
+ msbuffer[msbuffer_o][1] = current[1];
+ msbuffer_o++;
}
- //Prepare for next sample
- freq_counter += freq_add;
- mixpos++;
- done++;
}
}
-void MixerChannel::AddStretched(Bitu len,Bit16s * data) {
- if (done >= needed) {
- LOG_MSG("Can't add, buffer full");
+void MixerChannel::finishSampleInterpolation(const Bitu upto) {
+ if (!current_loaded) return;
+ runSampleInterpolation(upto);
+}
+
+double MixerChannel::timeSinceLastSample(void) {
+ Bits delta = (Bits)mixer.samples_rendered_ms.w - (Bits)last_sample_write;
+ return ((double)delta) / mixer.freq;
+}
+
+inline bool MixerChannel::runSampleInterpolation(const Bitu upto) {
+ int sample;
+
+ if (msbuffer_o >= upto)
+ return false;
+
+ current[0] = last[0] + delta[0];
+ current[1] = last[1] + delta[1];
+ while (freq_f < freq_d) {
+ msbuffer[msbuffer_o][0] = current[0] * volmul[0];
+ msbuffer[msbuffer_o][1] = current[1] * volmul[1];
+
+ freq_f += freq_n;
+ if ((++msbuffer_o) >= upto)
+ return false;
+ }
+
+ return true;
+}
+
+template<class Type,bool stereo,bool signeddata,bool nativeorder>
+inline void MixerChannel::AddSamples(Bitu len, const Type* data) {
+ last_sample_write = mixer.samples_rendered_ms.w;
+
+ if (msbuffer_o >= 2048) {
+ fprintf(stderr,"WARNING: addSample overrun (immediate)\n");
return;
}
- //Target samples this inputs gets stretched into
- Bitu outlen = needed - done;
- Bitu index = 0;
- Bitu index_add = (len << FREQ_SHIFT)/outlen;
- Bitu mixpos = mixer.pos + done;
- done = needed;
- Bitu pos = 0;
-
- while (outlen--) {
- Bitu new_pos = index >> FREQ_SHIFT;
- if (pos != new_pos) {
- pos = new_pos;
- //Forward the previous sample
- prevSample[0] = data[0];
- data++;
- }
- Bits diff = data[0] - prevSample[0];
- Bits diff_mul = index & FREQ_MASK;
- index += index_add;
- mixpos &= MIXER_BUFMASK;
- Bits sample = prevSample[0] + ((diff * diff_mul) >> FREQ_SHIFT);
- mixer.work[mixpos][0] += sample * volmul[0];
- mixer.work[mixpos][1] += sample * volmul[1];
- mixpos++;
+
+ if (!current_loaded) {
+ if (len == 0) return;
+
+ loadCurrentSample<Type,stereo,signeddata,nativeorder>(len,data);
+ if (len == 0) {
+ freq_f = freq_d; /* encourage loading next round */
+ return;
+ }
+
+ loadCurrentSample<Type,stereo,signeddata,nativeorder>(len,data);
+ freq_f = 0; /* interpolate now from what we just loaded */
+ }
+
+ for (;;) {
+ if (freq_f >= freq_d) {
+ if (len == 0) break;
+ loadCurrentSample<Type,stereo,signeddata,nativeorder>(len,data);
+ freq_f -= freq_d;
+ }
+ if (!runSampleInterpolation(2048))
+ break;
}
}
@@ -381,195 +428,167 @@ void MixerChannel::AddSamples_s32_nonnat
AddSamples<Bit32s,true,true,false>(len,data);
}
-void MixerChannel::FillUp(void) {
- SDL_LockAudio();
- if (!enabled || done<mixer.done) {
- SDL_UnlockAudio();
- return;
- }
- float index=PIC_TickIndex();
- Mix((Bitu)(index*mixer.needed));
- SDL_UnlockAudio();
-}
-
extern bool ticksLocked;
+
+#if 0//unused
static inline bool Mixer_irq_important(void) {
/* In some states correct timing of the irqs is more important then
* non stuttering audo */
return (ticksLocked || (CaptureState & (CAPTURE_WAVE|CAPTURE_VIDEO)));
}
-
-static Bit32u calc_tickadd(Bit32u freq) {
-#if TICK_SHIFT >16
- Bit64u freq64 = static_cast<Bit64u>(freq);
- freq64 = (freq64<<TICK_SHIFT)/1000;
- Bit32u r = static_cast<Bit32u>(freq64);
- return r;
-#else
- return (freq<<TICK_SHIFT)/1000;
#endif
-}
-/* Mix a certain amount of new samples */
-static void MIXER_MixData(Bitu needed) {
- MixerChannel * chan=mixer.channels;
+unsigned long long mixer_sample_counter = 0;
+double mixer_start_pic_time = 0;
+
+/* once a millisecond, render 1ms of audio, up to whole samples */
+static void MIXER_MixData(Bitu fracs/*render up to*/) {
+ unsigned int prev_rendered = mixer.samples_rendered_ms.w;
+ MixerChannel *chan = mixer.channels;
+ unsigned int whole,frac;
+ bool endframe = false;
+
+ if (fracs >= (Bitu)(mixer.samples_this_ms.w * mixer.samples_this_ms.fd)) {
+ fracs = (Bitu)(mixer.samples_this_ms.w * mixer.samples_this_ms.fd);
+ endframe = true;
+ }
+
+ whole = (unsigned int)(fracs / mixer.samples_this_ms.fd);
+ frac = (unsigned int)(fracs % mixer.samples_this_ms.fd);
+ if (whole <= mixer.samples_rendered_ms.w) return;
+
while (chan) {
- chan->Mix(needed);
+ chan->Mix(whole,fracs);
+ if (endframe) chan->EndFrame(mixer.samples_this_ms.w);
chan=chan->next;
}
+
if (CaptureState & (CAPTURE_WAVE|CAPTURE_VIDEO)) {
+ Bit32s volscale1 = (Bit32s)(mixer.recordvol[0] * (1 << MIXER_VOLSHIFT));
+ Bit32s volscale2 = (Bit32s)(mixer.recordvol[1] * (1 << MIXER_VOLSHIFT));
Bit16s convert[1024][2];
- Bitu added=needed-mixer.done;
- if (added>1024)
- added=1024;
- Bitu readpos=(mixer.pos+mixer.done)&MIXER_BUFMASK;
+ Bitu added = whole - prev_rendered;
+ if (added>1024) added=1024;
+ Bitu readpos = mixer.work_in + prev_rendered;
for (Bitu i=0;i<added;i++) {
- Bits sample=mixer.work[readpos][0] >> MIXER_VOLSHIFT;
- convert[i][0]=MIXER_CLIP(sample);
- sample=mixer.work[readpos][1] >> MIXER_VOLSHIFT;
- convert[i][1]=MIXER_CLIP(sample);
- readpos=(readpos+1)&MIXER_BUFMASK;
+ convert[i][0]=MIXER_CLIP(((Bit64s)mixer.work[readpos][0] * (Bit64s)volscale1) >> (MIXER_VOLSHIFT + MIXER_VOLSHIFT));
+ convert[i][1]=MIXER_CLIP(((Bit64s)mixer.work[readpos][1] * (Bit64s)volscale2) >> (MIXER_VOLSHIFT + MIXER_VOLSHIFT));
+ readpos++;
}
+ // assert(readpos <= MIXER_BUFSIZE);
CAPTURE_AddWave( mixer.freq, added, (Bit16s*)convert );
}
- //Reset the the tick_add for constant speed
- if( Mixer_irq_important() )
- mixer.tick_add = calc_tickadd(mixer.freq);
- mixer.done = needed;
+
+ mixer.samples_rendered_ms.w = whole;
+ mixer.samples_rendered_ms.fd = frac;
+ mixer_sample_counter += mixer.samples_rendered_ms.w - prev_rendered;
}
-static void MIXER_Mix(void) {
+static void MIXER_FillUp(void) {
SDL_LockAudio();
- MIXER_MixData(mixer.needed);
- mixer.tick_counter += mixer.tick_add;
- mixer.needed+=(mixer.tick_counter >> TICK_SHIFT);
- mixer.tick_counter &= TICK_MASK;
+ float index = PIC_TickIndex();
+ if (index < 0) index = 0;
+ MIXER_MixData((Bitu)(index * ((Bitu)mixer.samples_this_ms.w * (Bitu)mixer.samples_this_ms.fd)));
SDL_UnlockAudio();
}
-static void MIXER_Mix_NoSound(void) {
- MIXER_MixData(mixer.needed);
- /* Clear piece we've just generated */
- for (Bitu 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;chan=chan->next) {
- if (chan->done>mixer.needed) chan->done-=mixer.needed;
- else chan->done=0;
- }
- /* Set values for next tick */
- mixer.tick_counter += mixer.tick_add;
- mixer.needed = (mixer.tick_counter >> TICK_SHIFT);
- mixer.tick_counter &= TICK_MASK;
- mixer.done=0;
+void MixerChannel::FillUp(void) {
+ MIXER_FillUp();
}
-static void SDLCALL MIXER_CallBack(void * userdata, Uint8 *stream, int len) {
- Bitu need=(Bitu)len/MIXER_SSIZE;
- Bit16s * output=(Bit16s *)stream;
- Bitu reduce;
- Bitu pos;
- //Local resampling counter to manipulate the data when sending it off to the callback
- Bitu index, index_add;
- Bits sample;
- /* Enough room in the buffer ? */
- if (mixer.done < need) {
-// LOG_MSG("Full underrun need %d, have %d, min %d", need, mixer.done, mixer.min_needed);
- if((need - mixer.done) > (need >>7) ) //Max 1 procent stretch.
- return;
- reduce = mixer.done;
- index_add = (reduce << TICK_SHIFT) / need;
- mixer.tick_add = calc_tickadd(mixer.freq+mixer.min_needed);
- } else if (mixer.done < mixer.max_needed) {
- Bitu left = mixer.done - need;
- if (left < mixer.min_needed) {
- if( !Mixer_irq_important() ) {
- Bitu needed = mixer.needed - need;
- Bitu diff = (mixer.min_needed>needed?mixer.min_needed:needed) - left;
- mixer.tick_add = calc_tickadd(mixer.freq+(diff*3));
- 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
- }
-// LOG_MSG("needed underrun need %d, have %d, min %d, left %d", need, mixer.done, mixer.min_needed, left);
- reduce = need - left;
- index_add = (reduce << TICK_SHIFT) / need;
- } else {
- reduce = need;
- index_add = (1 << TICK_SHIFT);
-// LOG_MSG("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 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 = calc_tickadd(mixer.freq-(diff/5));
- else if (diff > (mixer.min_needed>>2))
- mixer.tick_add = calc_tickadd(mixer.freq-(diff>>3));
- else
- mixer.tick_add = calc_tickadd(mixer.freq);
- }
- } else {
- /* There is way too much data in the buffer */
-// LOG_MSG("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 << TICK_SHIFT) / need;
- reduce = mixer.done - 2* mixer.min_needed;
- mixer.tick_add = calc_tickadd(mixer.freq-(mixer.min_needed/5));
- }
- /* Reduce done count in all channels */
- for (MixerChannel * chan=mixer.channels;chan;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 = calc_tickadd(mixer.freq);
-
- mixer.done -= reduce;
- mixer.needed -= reduce;
- pos = mixer.pos;
- mixer.pos = (mixer.pos + reduce) & MIXER_BUFMASK;
- index = 0;
- if(need != reduce) {
- while (need--) {
- Bitu i = (pos + (index >> TICK_SHIFT )) & MIXER_BUFMASK;
- index += index_add;
- sample=mixer.work[i][0]>>MIXER_VOLSHIFT;
- *output++=MIXER_CLIP(sample);
- sample=mixer.work[i][1]>>MIXER_VOLSHIFT;
- *output++=MIXER_CLIP(sample);
- }
- /* Clean the used buffer */
- while (reduce--) {
- pos &= MIXER_BUFMASK;
- mixer.work[pos][0]=0;
- mixer.work[pos][1]=0;
- pos++;
- }
- } else {
- while (reduce--) {
- pos &= MIXER_BUFMASK;
- sample=mixer.work[pos][0]>>MIXER_VOLSHIFT;
- *output++=MIXER_CLIP(sample);
- sample=mixer.work[pos][1]>>MIXER_VOLSHIFT;
- *output++=MIXER_CLIP(sample);
- mixer.work[pos][0]=0;
- mixer.work[pos][1]=0;
- pos++;
+static void MIXER_Mix(void) {
+ Bitu thr;
+
+ SDL_LockAudio();
+
+ /* render */
+ // assert((mixer.work_in+mixer.samples_per_ms.w) <= MIXER_BUFSIZE);
+ MIXER_MixData((Bitu)mixer.samples_this_ms.w * (Bitu)mixer.samples_this_ms.fd);
+ mixer.work_in += mixer.samples_this_ms.w;
+
+ /* how many samples for the next ms? */
+ mixer.samples_this_ms.w = mixer.samples_per_ms.w;
+ mixer.samples_this_ms.fn += mixer.samples_per_ms.fn;
+ if (mixer.samples_this_ms.fn >= mixer.samples_this_ms.fd) {
+ mixer.samples_this_ms.fn -= mixer.samples_this_ms.fd;
+ mixer.samples_this_ms.w++;
+ }
+
+ /* advance. we use in/out & wrap pointers to make sure the rendering code
+ * doesn't have to worry about circular buffer wraparound. */
+ thr = mixer.blocksize;
+ if (thr < mixer.samples_this_ms.w) thr = mixer.samples_this_ms.w;
+ if ((mixer.work_in+thr) > MIXER_BUFSIZE) {
+ mixer.work_wrap = mixer.work_in;
+ mixer.work_in = 0;
+ }
+ // assert((mixer.work_in+thr) <= MIXER_BUFSIZE);
+ // assert((mixer.work_in+mixer.samples_this_ms.w) <= MIXER_BUFSIZE);
+ memset(&mixer.work[mixer.work_in][0],0,sizeof(Bit32s)*2*mixer.samples_this_ms.w);
+ mixer.samples_rendered_ms.fn = 0;
+ mixer.samples_rendered_ms.w = 0;
+ SDL_UnlockAudio();
+ MIXER_FillUp();
+}
+
+static void MIXER_CallBack(void * userdata, Uint8 *stream, int len) {
+ (void)userdata;//UNUSED
+ Bit32s volscale1 = (Bit32s)(mixer.mastervol[0] * (1 << MIXER_VOLSHIFT));
+ Bit32s volscale2 = (Bit32s)(mixer.mastervol[1] * (1 << MIXER_VOLSHIFT));
+ Bitu need = (Bitu)len/MIXER_SSIZE;
+ Bit16s *output = (Bit16s*)stream;
+ int remains;
+ Bit32s *in;
+
+ if (mixer.prebuffer_wait) {
+ remains = (int)mixer.work_in - (int)mixer.work_out;
+ if (remains < 0) remains += (int)mixer.work_wrap;
+ if (remains < 0) remains = 0;
+
+ if ((unsigned int)remains >= mixer.prebuffer_samples)
+ mixer.prebuffer_wait = false;
+ }
+
+ if (!mixer.prebuffer_wait) {
+ in = &mixer.work[mixer.work_out][0];
+ while (need > 0) {
+ if (mixer.work_out == mixer.work_in) break;
+ *output++ = MIXER_CLIP((((Bit64s)(*in++)) * (Bit64s)volscale1) >> (MIXER_VOLSHIFT + MIXER_VOLSHIFT));
+ *output++ = MIXER_CLIP((((Bit64s)(*in++)) * (Bit64s)volscale2) >> (MIXER_VOLSHIFT + MIXER_VOLSHIFT));
+ mixer.work_out++;
+ if (mixer.work_out >= mixer.work_wrap) {
+ mixer.work_out = 0;
+ in = &mixer.work[mixer.work_out][0];
+ }
+ need--;
+ }
+ }
+
+ if (need > 0)
+ mixer.prebuffer_wait = true;
+
+ while (need > 0) {
+ *output++ = 0;
+ *output++ = 0;
+ need--;
+ }
+
+ remains = (int)mixer.work_in - (int)mixer.work_out;
+ if (remains < 0) remains += (int)mixer.work_wrap;
+
+ if ((unsigned long)remains >= (mixer.blocksize*2UL)) {
+ /* drop some samples to keep time */
+ unsigned int drop;
+
+ if ((unsigned long)remains >= (mixer.blocksize*3UL)) // hard drop
+ drop = ((unsigned int)remains - (unsigned int)(mixer.blocksize));
+ else // subtle drop
+ drop = (((unsigned int)remains - (unsigned int)(mixer.blocksize*2)) / 50U) + 1;
+
+ while (drop > 0) {
+ mixer.work_out++;
+ if (mixer.work_out >= mixer.work_wrap) mixer.work_out = 0;
+ drop--;
}
}
}
@@ -612,6 +631,9 @@ public:
if (cmd->FindString("MASTER",temp_line,false)) {
MakeVolume((char *)temp_line.c_str(),mixer.mastervol[0],mixer.mastervol[1]);
}
+ if (cmd->FindString("RECORD",temp_line,false)) {
+ MakeVolume((char *)temp_line.c_str(),mixer.recordvol[0],mixer.recordvol[1]);
+ }
MixerChannel * chan=mixer.channels;
while (chan) {
if (cmd->FindString(chan->name,temp_line,false)) {
@@ -624,6 +646,7 @@ public:
chan=mixer.channels;
WriteOut("Channel Main Main(dB)\n");
ShowVolume("MASTER",mixer.mastervol[0],mixer.mastervol[1]);
+ ShowVolume("RECORD",mixer.recordvol[0],mixer.recordvol[1]);
for (chan=mixer.channels;chan;chan=chan->next)
ShowVolume(chan->name,chan->volmain[0],chan->volmain[1]);
}
@@ -673,12 +696,16 @@ void MIXER_Init(Section* sec) {
mixer.blocksize=section->Get_int("blocksize");
/* Initialize the internal stuff */
+ mixer.prebuffer_samples=0;
+ mixer.prebuffer_wait=true;
mixer.channels=0;
mixer.pos=0;
mixer.done=0;
memset(mixer.work,0,sizeof(mixer.work));
mixer.mastervol[0]=1.0f;
mixer.mastervol[1]=1.0f;
+ mixer.recordvol[0]=1.0f;
+ mixer.recordvol[1]=1.0f;
/* Start the Mixer using SDL Sound at 22 khz */
SDL_AudioSpec spec;
@@ -691,29 +718,48 @@ void MIXER_Init(Section* sec) {
spec.userdata=NULL;
spec.samples=(Uint16)mixer.blocksize;
- mixer.tick_counter=0;
if (mixer.nosound) {
LOG_MSG("MIXER: No Sound Mode Selected.");
- mixer.tick_add=calc_tickadd(mixer.freq);
- TIMER_AddTickHandler(MIXER_Mix_NoSound);
+ TIMER_AddTickHandler(MIXER_Mix);
} else if (SDL_OpenAudio(&spec, &obtained) <0 ) {
mixer.nosound = true;
LOG_MSG("MIXER: Can't open audio: %s , running in nosound mode.",SDL_GetError());
- mixer.tick_add=calc_tickadd(mixer.freq);
- TIMER_AddTickHandler(MIXER_Mix_NoSound);
+ TIMER_AddTickHandler(MIXER_Mix);
} else {
if((mixer.freq != obtained.freq) || (mixer.blocksize != obtained.samples))
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=calc_tickadd(mixer.freq);
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;
- mixer.max_needed=mixer.blocksize * 2 + 2*mixer.min_needed;
- mixer.needed=mixer.min_needed+1;
+ mixer_start_pic_time = PIC_FullIndex();
+ mixer_sample_counter = 0;
+ mixer.work_in = mixer.work_out = 0;
+ mixer.work_wrap = MIXER_BUFSIZE;
+ if (mixer.work_wrap <= mixer.blocksize) E_Exit("blocksize too large");
+
+ {
+ int ms = section->Get_int("prebuffer");
+
+ if (ms < 0) ms = 20;
+
+ mixer.prebuffer_samples = ((unsigned int)ms * (unsigned int)mixer.freq) / 1000u;
+ if (mixer.prebuffer_samples > (mixer.work_wrap / 2))
+ mixer.prebuffer_samples = (mixer.work_wrap / 2);
+ }
+
+ // how many samples per millisecond? compute as improper fraction (sample rate / 1000)
+ mixer.samples_per_ms.w = mixer.freq / 1000U;
+ mixer.samples_per_ms.fn = mixer.freq % 1000U;
+ mixer.samples_per_ms.fd = 1000U;
+ mixer.samples_this_ms.w = mixer.samples_per_ms.w;
+ mixer.samples_this_ms.fn = 0;
+ mixer.samples_this_ms.fd = mixer.samples_per_ms.fd;
+ mixer.samples_rendered_ms.w = 0;
+ mixer.samples_rendered_ms.fn = 0;
+ mixer.samples_rendered_ms.fd = mixer.samples_per_ms.fd;
+
PROGRAMS_MakeFile("MIXER.COM",MIXER_ProgramStart);
}
diff -rupN dosbox-svn/src/hardware/sblaster.cpp dosbox-mixer//src/hardware/sblaster.cpp
--- dosbox-svn/src/hardware/sblaster.cpp
+++ dosbox-mixer//src/hardware/sblaster.cpp
@@ -1174,6 +1174,7 @@ static float calc_vol(Bit8u amount) {
}
static void CTMIXER_UpdateVolumes(void) {
if (!sb.mixer.enabled) return;
+ sb.chan->FillUp();
MixerChannel * chan;
float m0 = calc_vol(sb.mixer.master[0]);
float m1 = calc_vol(sb.mixer.master[1]);
[/code]
Qbix wrote:For the mixer, I would have the check it first. As you are aware, it is rather important for fluency and interacts indirectly with the auto cycles behavior.
mda, which games would benefit from that ?
TheGreatCodeholio wrote:To explain the A/V sync issue, I noticed with DOSBox SVN, as well as DOSBox-X up until the rewrite, that if you recorded a demo or game with the video capture program, that the audio and video would slowly drift out of sync. The reason is that "44.1KHz" on a sound card isn't exactly 44.1KHz, it can vary from 44.097KHz to 44.103Khz. At the same time, SDL_GetTicks() reads from the system clock, or performance counters, or any other time source that is not the sound card. So A/V sync would drift because DOSBox is running emulator time against SDL_GetTicks() while letting the sound card dictate how much audio to render.
TheGreatCodeholio wrote:This also allows the FillUp calls in the mixer to accurately pinpoint up to what point (in the 1ms) to render audio. This allows some emulation including Adlib, Tandy 3-voice, and Sound Blaster, to very accurately render state changes without causing too much overhead. There are DOS games that have hacks to play digitized speech/audio over Adlib (Pinball Dreams, Star Control II), and Tandy 3-voice (Skate or Die) that will render a muddy low-fi rendition without that.
TheGreatCodeholio wrote:As for the MDA option, the DOSBox-X philosophy is to offer Hercules mode trimmed down to MDA only I/O so that you can use it to see how well (or even IF) the game detects MDA vs Hercules. If you're doing retro-development, the option is a good way to test whether your code can detect MDA vs Hercules before trying it on actual hardware to help reduce the compile + copy to floppy + run development cycle. If MDA-only emulation isn't needed or wanted by DOSBox SVN, that's perfectly fine with me.
Users browsing this forum: No registered users and 2 guests