Add disney changes from h-a-l-9000
Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@3180
This commit is contained in:
parent
92dade7e5b
commit
d044321864
1 changed files with 266 additions and 26 deletions
|
@ -16,6 +16,8 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* $Id: disney.cpp,v 1.16 2008-07-14 19:39:10 qbix79 Exp $ */
|
||||
|
||||
#include <string.h>
|
||||
#include "dosbox.h"
|
||||
#include "inout.h"
|
||||
|
@ -25,33 +27,209 @@
|
|||
|
||||
#define DISNEY_BASE 0x0378
|
||||
|
||||
#define DISNEY_SIZE 32
|
||||
#define DISNEY_SIZE 128
|
||||
|
||||
typedef struct _dac_channel {
|
||||
Bit8u buffer[DISNEY_SIZE]; // data buffer
|
||||
Bitu used; // current data buffer level
|
||||
double speedcheck_sum;
|
||||
double speedcheck_last;
|
||||
bool speedcheck_failed;
|
||||
bool speedcheck_init;
|
||||
} dac_channel;
|
||||
|
||||
static struct {
|
||||
// parallel port stuff
|
||||
Bit8u data;
|
||||
Bit8u status;
|
||||
Bit8u control;
|
||||
Bit8u buffer[DISNEY_SIZE];
|
||||
Bitu used;Bitu last_used;
|
||||
// the D/A channels
|
||||
dac_channel da[2];
|
||||
|
||||
Bitu last_used;
|
||||
MixerObject * mo;
|
||||
MixerChannel * chan;
|
||||
bool stereo;
|
||||
// which channel do we use for mono output?
|
||||
// and the channel used for stereo
|
||||
dac_channel* leader;
|
||||
|
||||
Bitu state;
|
||||
Bitu interface_det;
|
||||
} disney;
|
||||
|
||||
static void disney_write(Bitu port,Bitu val,Bitu iolen) {
|
||||
if (!disney.last_used) {
|
||||
disney.chan->Enable(true);
|
||||
#define DS_IDLE 0
|
||||
#define DS_RUNNING 1
|
||||
#define DS_FINISH 2
|
||||
#define DS_ANALYZING 3
|
||||
|
||||
static void DISNEY_CallBack(Bitu len);
|
||||
|
||||
static void DISNEY_disable(Bitu) {
|
||||
if(disney.mo) {
|
||||
disney.chan->AddSilence();
|
||||
disney.chan->Enable(false);
|
||||
delete disney.mo;
|
||||
}
|
||||
disney.interface_det = 0;
|
||||
disney.leader = 0;
|
||||
disney.last_used = 0;
|
||||
disney.mo = 0;
|
||||
disney.state = DS_IDLE;
|
||||
disney.interface_det = 0;
|
||||
}
|
||||
|
||||
static void DISNEY_enable(Bitu freq) {
|
||||
if(freq < 500 || freq > 100000) {
|
||||
// try again..
|
||||
disney.state = DS_IDLE;
|
||||
return;
|
||||
} else {
|
||||
#if 0
|
||||
if(disney.stereo) LOG(LOG_MISC,LOG_NORMAL)("disney enable %d Hz, stereo",freq);
|
||||
else LOG(LOG_MISC,LOG_NORMAL)("disney enable %d Hz, mono",freq);
|
||||
#endif
|
||||
disney.mo = new MixerObject();
|
||||
disney.chan=disney.mo->Install(&DISNEY_CallBack,freq,"DISNEY");
|
||||
disney.chan->Enable(true);
|
||||
disney.state = DS_RUNNING;
|
||||
}
|
||||
}
|
||||
|
||||
static void DISNEY_analyze(Bitu channel){
|
||||
switch(disney.state) {
|
||||
case DS_RUNNING: // should not get here
|
||||
break;
|
||||
case DS_IDLE:
|
||||
// initialize channel data
|
||||
for(int i = 0; i < 2; i++) {
|
||||
disney.da[i].used = 0;
|
||||
disney.da[i].speedcheck_sum = 0;
|
||||
disney.da[i].speedcheck_failed = false;
|
||||
disney.da[i].speedcheck_init = false;
|
||||
}
|
||||
disney.da[channel].speedcheck_last = PIC_FullIndex();
|
||||
disney.da[channel].speedcheck_init = true;
|
||||
|
||||
disney.state = DS_ANALYZING;
|
||||
break;
|
||||
|
||||
case DS_FINISH:
|
||||
{
|
||||
// detect stereo: if we have about the same data amount in both channels
|
||||
Bits st_diff = disney.da[0].used - disney.da[1].used;
|
||||
|
||||
// find leader channel (the one with higher rate) [this good for the stereo case?]
|
||||
if(disney.da[0].used > disney.da[1].used) {
|
||||
//disney.monochannel=0;
|
||||
disney.leader = &disney.da[0];
|
||||
} else {
|
||||
//disney.monochannel=1;
|
||||
disney.leader = &disney.da[1];
|
||||
}
|
||||
|
||||
if((st_diff < 5) && (st_diff > -5)) disney.stereo = true;
|
||||
else disney.stereo = false;
|
||||
|
||||
// calculate rate for both channels
|
||||
Bitu ch_speed[2];
|
||||
|
||||
for(Bitu i = 0; i < 2; i++) {
|
||||
ch_speed[i] = (Bitu)(1.0/((disney.da[i].speedcheck_sum/1000.0) /
|
||||
(float)(((float)disney.da[i].used)-1.0))); // -1.75
|
||||
}
|
||||
|
||||
// choose the larger value
|
||||
DISNEY_enable(ch_speed[0] > ch_speed[1]?
|
||||
ch_speed[0]:ch_speed[1]); // TODO
|
||||
break;
|
||||
}
|
||||
case DS_ANALYZING:
|
||||
{
|
||||
double current = PIC_FullIndex();
|
||||
dac_channel* cch = &disney.da[channel];
|
||||
|
||||
if(!cch->speedcheck_init) {
|
||||
cch->speedcheck_init = true;
|
||||
cch->speedcheck_last = current;
|
||||
break;
|
||||
}
|
||||
cch->speedcheck_sum += current - cch->speedcheck_last;
|
||||
//LOG_MSG("t=%f",current - cch->speedcheck_last);
|
||||
|
||||
// sanity checks (printer...)
|
||||
if((current - cch-> speedcheck_last) < 0.01 ||
|
||||
(current - cch-> speedcheck_last) > 2)
|
||||
cch->speedcheck_failed = true;
|
||||
|
||||
// if both are failed we are back at start
|
||||
if(disney.da[0].speedcheck_failed && disney.da[1].speedcheck_failed) {
|
||||
disney.state=DS_IDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
cch->speedcheck_last = current;
|
||||
|
||||
// analyze finish condition
|
||||
if(disney.da[0].used > 30 || disney.da[1].used > 30)
|
||||
disney.state = DS_FINISH;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void disney_write(Bitu port,Bitu val,Bitu iolen) {
|
||||
//LOG_MSG("write disney time %f addr%x val %x",PIC_FullIndex(),port,val);
|
||||
disney.last_used=PIC_Ticks;
|
||||
switch (port-DISNEY_BASE) {
|
||||
case 0: /* Data Port */
|
||||
{
|
||||
disney.data=val;
|
||||
if (disney.used<DISNEY_SIZE) {
|
||||
disney.buffer[disney.used++]=disney.data;
|
||||
}
|
||||
// if data is written here too often without using the stereo
|
||||
// mechanism we use the simple DAC machanism.
|
||||
if(disney.state != DS_RUNNING) {
|
||||
disney.interface_det++;
|
||||
if(disney.interface_det > 5)
|
||||
DISNEY_analyze(0);
|
||||
}
|
||||
if(disney.interface_det > 5) {
|
||||
if(disney.da[0].used < DISNEY_SIZE) {
|
||||
disney.da[0].buffer[disney.da[0].used] = disney.data;
|
||||
disney.da[0].used++;
|
||||
} //else LOG_MSG("disney overflow 0");
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: /* Status Port */
|
||||
LOG(LOG_MISC,LOG_NORMAL)("DISNEY:Status write %x",val);
|
||||
break;
|
||||
case 2: /* Control Port */
|
||||
if((disney.control & 0x2) && !(val & 0x2)) {
|
||||
if(disney.state != DS_RUNNING) {
|
||||
disney.interface_det = 0;
|
||||
DISNEY_analyze(1);
|
||||
}
|
||||
|
||||
// stereo channel latch
|
||||
if(disney.da[1].used < DISNEY_SIZE) {
|
||||
disney.da[1].buffer[disney.da[1].used] = disney.data;
|
||||
disney.da[1].used++;
|
||||
} //else LOG_MSG("disney overflow 1");
|
||||
}
|
||||
|
||||
if((disney.control & 0x1) && !(val & 0x1)) {
|
||||
if(disney.state != DS_RUNNING) {
|
||||
disney.interface_det = 0;
|
||||
DISNEY_analyze(0);
|
||||
}
|
||||
// stereo channel latch
|
||||
if(disney.da[0].used < DISNEY_SIZE) {
|
||||
disney.da[0].buffer[disney.da[0].used] = disney.data;
|
||||
disney.da[0].used++;
|
||||
} //else LOG_MSG("disney overflow 0");
|
||||
}
|
||||
|
||||
// LOG_WARN("DISNEY:Control write %x",val);
|
||||
if (val&0x10) LOG(LOG_MISC,LOG_ERROR)("DISNEY:Parallel IRQ Enabled");
|
||||
disney.control=val;
|
||||
|
@ -67,7 +245,9 @@ static Bitu disney_read(Bitu port,Bitu iolen) {
|
|||
break;
|
||||
case 1: /* Status Port */
|
||||
// LOG(LOG_MISC,"DISNEY:Read from status port %X",disney.status);
|
||||
if (disney.used>=16) return 0x40;
|
||||
if (disney.leader && disney.leader->used >= 16) return 0x40;
|
||||
/* Stereo-on-1 and (or) New-Stereo DACs present */
|
||||
if(!(disney.data&0x80)) return 0xc4;
|
||||
else return 0x0;
|
||||
break;
|
||||
case 2: /* Control Port */
|
||||
|
@ -78,21 +258,79 @@ static Bitu disney_read(Bitu port,Bitu iolen) {
|
|||
return 0xff;
|
||||
}
|
||||
|
||||
static void DISNEY_PlayStereo(Bitu len, Bit8u* l, Bit8u* r) {
|
||||
static Bit8u stereodata[DISNEY_SIZE*2];
|
||||
for(Bitu i = 0; i < len; i++) {
|
||||
stereodata[i*2] = l[i];
|
||||
stereodata[i*2+1] = r[i];
|
||||
}
|
||||
disney.chan->AddSamples_s8(len,stereodata);
|
||||
}
|
||||
|
||||
static void DISNEY_CallBack(Bitu len) {
|
||||
if (!len) return;
|
||||
if (disney.used>len) {
|
||||
disney.chan->AddSamples_m8(len,disney.buffer);
|
||||
memmove(disney.buffer,&disney.buffer[len],disney.used-len);
|
||||
disney.used-=len;
|
||||
} else {
|
||||
disney.chan->AddSamples_m8(disney.used,disney.buffer);
|
||||
disney.chan->AddSilence();
|
||||
disney.used=0;
|
||||
|
||||
// get the smaller used
|
||||
Bitu real_used;
|
||||
if(disney.stereo) {
|
||||
real_used = disney.da[0].used;
|
||||
if(disney.da[1].used < real_used) real_used = disney.da[1].used;
|
||||
} else
|
||||
real_used = disney.leader->used;
|
||||
|
||||
if (real_used >= len) { // enough data for now
|
||||
if(disney.stereo) DISNEY_PlayStereo(len, disney.da[0].buffer, disney.da[1].buffer);
|
||||
else disney.chan->AddSamples_m8(len,disney.leader->buffer);
|
||||
|
||||
// put the rest back to start
|
||||
for(int i = 0; i < 2; i++) {
|
||||
// TODO for mono only one
|
||||
memmove(disney.da[i].buffer,&disney.da[i].buffer[len],DISNEY_SIZE/*real_used*/-len);
|
||||
disney.da[i].used -= len;
|
||||
}
|
||||
// TODO: len > DISNEY
|
||||
} else { // not enough data
|
||||
if(disney.stereo) {
|
||||
Bit8u gapfiller0 = 128;
|
||||
Bit8u gapfiller1 = 128;
|
||||
if(real_used) {
|
||||
gapfiller0 = disney.da[0].buffer[real_used-1];
|
||||
gapfiller1 = disney.da[1].buffer[real_used-1];
|
||||
};
|
||||
|
||||
memset(disney.da[0].buffer+real_used,
|
||||
gapfiller0,len-real_used);
|
||||
memset(disney.da[1].buffer+real_used,
|
||||
gapfiller1,len-real_used);
|
||||
|
||||
DISNEY_PlayStereo(len, disney.da[0].buffer, disney.da[1].buffer);
|
||||
len -= real_used;
|
||||
|
||||
} else { // mono
|
||||
Bit8u gapfiller = 128; //Keep the middle
|
||||
if(real_used) {
|
||||
// fix for some stupid game; it outputs 0 at the end of the stream
|
||||
// causing a click. So if we have at least two bytes availible in the
|
||||
// buffer and the last one is a 0 then ignore that.
|
||||
if(disney.leader->buffer[real_used-1]==0)
|
||||
real_used--;
|
||||
}
|
||||
// do it this way because AddSilence sounds like a gnawing mouse
|
||||
if(real_used)
|
||||
gapfiller = disney.leader->buffer[real_used-1];
|
||||
//LOG_MSG("gapfiller %x, fill len %d, realused %d",gapfiller,len-real_used,real_used);
|
||||
memset(disney.leader->buffer+real_used, gapfiller, len-real_used);
|
||||
disney.chan->AddSamples_m8(len, disney.leader->buffer);
|
||||
}
|
||||
disney.da[0].used =0;
|
||||
disney.da[1].used =0;
|
||||
|
||||
//LOG_MSG("disney underflow %d",len - real_used);
|
||||
}
|
||||
if (disney.last_used+5000<PIC_Ticks) {
|
||||
disney.last_used=0;
|
||||
disney.chan->Enable(false);
|
||||
if (disney.last_used+100<PIC_Ticks) {
|
||||
// disable sound output
|
||||
PIC_AddEvent(DISNEY_disable,0.0001f); // I think we shouldn't delete the
|
||||
// mixer while we are inside it
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,7 +338,7 @@ class DISNEY: public Module_base {
|
|||
private:
|
||||
IO_ReadHandleObject ReadHandler;
|
||||
IO_WriteHandleObject WriteHandler;
|
||||
MixerObject MixerChan;
|
||||
//MixerObject MixerChan;
|
||||
public:
|
||||
DISNEY(Section* configuration):Module_base(configuration) {
|
||||
Section_prop * section=static_cast<Section_prop *>(configuration);
|
||||
|
@ -109,14 +347,16 @@ public:
|
|||
WriteHandler.Install(DISNEY_BASE,disney_write,IO_MB,3);
|
||||
ReadHandler.Install(DISNEY_BASE,disney_read,IO_MB,3);
|
||||
|
||||
disney.chan=MixerChan.Install(&DISNEY_CallBack,7000,"DISNEY");
|
||||
|
||||
disney.status=0x84;
|
||||
disney.control=0;
|
||||
disney.used=0;
|
||||
disney.last_used=0;
|
||||
|
||||
disney.mo=0;
|
||||
DISNEY_disable(0);
|
||||
}
|
||||
~DISNEY(){
|
||||
DISNEY_disable(0);
|
||||
}
|
||||
~DISNEY(){ }
|
||||
};
|
||||
|
||||
static DISNEY* test;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue