DOSBox-X branch

Here you can discuss the development of patches.

Re: DOSBox-X branch

Postby hail-to-the-ryzen » 2018-6-11 @ 00:09

There is some sound crackling when starting Windows 95 and where sbtype is not SB16. It is from the Windows Start Sound wave file and is reproducible by playing that file again in the guest OS. Tested various configuration switches without much effect.

Did note a difference in the frequency that it plays among the different devices.
sbpro1: SBLASTER:DMA Transfer:8-bits PCM Mono Auto-Init freq 22222 rate 22222 size 1376 gold 0
sb16: SBLASTER:DMA Transfer:8-bits PCM Mono Auto-Init freq 22050 rate 22050 size 1376 gold 0
hail-to-the-ryzen
Member
 
Posts: 204
Joined: 2017-3-09 @ 01:34

Re: DOSBox-X branch

Postby TheGreatCodeholio » 2018-6-11 @ 00:44

That is known, yes. It seems to me the driver expects to start the next buffer right away when the interrupt happens. The problem is that Sound Blaster emulation inherits DOSBox SVN's 1ms rendering code. It's not yet 100% precise enough.
DOSBox-X project: more emulation better accuracy.
DOSLIB and DOSLIB2: Learn how to tinker and hack hardware and software from DOS.
User avatar
TheGreatCodeholio
Oldbie
 
Posts: 511
Joined: 2011-8-18 @ 20:15
Location: Seattle, WA

Re: DOSBox-X branch

Postby hail-to-the-ryzen » 2018-6-11 @ 01:04

Thank you for the information. I assume that the sbpro/sb drivers are rendering the sound differently than the sb16, or the sb16 is polling more frequently. Is this an issue that just requires better timing and that a workaround is not possible?
hail-to-the-ryzen
Member
 
Posts: 204
Joined: 2017-3-09 @ 01:34

Re: DOSBox-X branch

Postby TheGreatCodeholio » 2018-6-11 @ 03:35

hail-to-the-ryzen wrote:Thank you for the information. I assume that the sbpro/sb drivers are rendering the sound differently than the sb16, or the sb16 is polling more frequently. Is this an issue that just requires better timing and that a workaround is not possible?

As far as I know, the workaround is to implement Sound Blaster timing in a more precise manner.
DOSBox-X project: more emulation better accuracy.
DOSLIB and DOSLIB2: Learn how to tinker and hack hardware and software from DOS.
User avatar
TheGreatCodeholio
Oldbie
 
Posts: 511
Joined: 2011-8-18 @ 20:15
Location: Seattle, WA

Re: DOSBox-X branch

Postby hail-to-the-ryzen » 2018-6-11 @ 06:38

Your version of the sdl waveout driver seems to sound better for the above case.
hail-to-the-ryzen
Member
 
Posts: 204
Joined: 2017-3-09 @ 01:34

Re: DOSBox-X branch

Postby hail-to-the-ryzen » 2018-6-14 @ 03:10

The sound crackling in Win95 and sbtype < SB16 is mainly heard with a sound file of long length, perhaps >3 ms. Also, these settings lessen the sound artifact: prebuffer=0; blocksize=256 (or possibly lower).
hail-to-the-ryzen
Member
 
Posts: 204
Joined: 2017-3-09 @ 01:34

Re: DOSBox-X branch

Postby hail-to-the-ryzen » 2018-6-17 @ 04:38

This change to correct a compiler warning should be reverted.
Code: Select all
--- dosbox-before//src/hardware/voodoo_data.h
+++ dosbox-after/src/hardware/voodoo_data.h
@@ -164,7 +164,7 @@ static const UINT8 dither_matrix_2x2[16]
    (c) = (int)((((unsigned int)(val) << 2u) & 0xf8u) | (((unsigned int)(val) >>  3u) & 0x07u));   \
 
 #define EXTRACT_1555_TO_8888(val, a, b, c, d)            \
-   (a) = (int)(((unsigned int)(val) >> 15u) & 0xffu);                  \
+   (a) = ((INT16)(val) >> 15) & 0xff;                  \
    EXTRACT_x555_TO_888(val, b, c, d)                  \
 
 #define EXTRACT_5551_TO_8888(val, a, b, c, d)            \
hail-to-the-ryzen
Member
 
Posts: 204
Joined: 2017-3-09 @ 01:34

Re: DOSBox-X branch

Postby hail-to-the-ryzen » 2018-6-24 @ 12:40

Study code for the midi input patch (win32):
Code: Select all
diff -rupN dosbox-before//include/midi.h dosbox-after/include/midi.h
--- dosbox-before//include/midi.h
+++ dosbox-after/include/midi.h
@@ -28,6 +28,7 @@ class MidiHandler {
 public:
    MidiHandler();
    virtual bool Open(const char * /*conf*/) { return true; };
+   virtual bool OpenInput(const char * inconf) {return false;};
    virtual void Close(void) {};
    virtual void PlayMsg(Bit8u * /*msg*/) {};
    virtual void PlaySysex(Bit8u * /*sysex*/,Bitu /*len*/) {};
@@ -45,6 +46,7 @@ struct DB_Midi {
    Bitu cmd_pos;
    Bit8u cmd_buf[8];
    Bit8u rt_buf[8];
+   Bitu cmd_r;
    struct {
       Bit8u buf[SYSEX_SIZE];
       Bitu used;
@@ -53,6 +55,7 @@ struct DB_Midi {
    } sysex;
    bool available;
    MidiHandler * handler;
+   MidiHandler * in_handler;
 };
 
 extern DB_Midi midi;
diff -rupN dosbox-before//src/gui/midi.cpp dosbox-after/src/gui/midi.cpp
--- dosbox-before//src/gui/midi.cpp
+++ dosbox-after/src/gui/midi.cpp
@@ -37,6 +37,11 @@
 
 #define RAWBUF   1024
 
+INLINE void MIDI_InputMsg(Bit8u msg[4]);
+INLINE Bits MIDI_InputSysex(Bit8u *sysex,Bitu len,bool abort);
+
+static Bit8u MIDI_InSysexBuf[SYSEX_SIZE];
+
 Bit8u MIDI_evt_len[256] = {
   0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,  // 0x00
   0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,  // 0x10
@@ -94,6 +99,28 @@ MidiHandler Midi_none;
 
 DB_Midi midi;
 
+void MIDI_RawOutRTByte(Bit8u data) {
+   if (data == 0xf8) return;
+   midi.cmd_r=data<<24;
+   midi.handler->PlayMsg((Bit8u*)&midi.cmd_r);
+}
+
+INLINE void MIDI_InputMsg(Bit8u msg[4]) {
+   MPU401_InputMsg(msg);
+   break;
+}
+
+INLINE Bits MIDI_InputSysex(Bit8u *sysex,Bitu len, bool abort) {
+   return MPU401_InputSysex(sysex,len,abort);
+}
+
+void MIDI_ClearBuffer(Bit8u slot) {
+   midi.sysex.used=0;
+   midi.status=0x00;
+   midi.cmd_pos=0;
+   midi.cmd_len=0;
+}
+
 void MIDI_RawOutByte(Bit8u data) {
    if (midi.sysex.start) {
       Bit32u passed_ticks = GetTicks() - midi.sysex.start;
@@ -168,6 +195,9 @@ public:
       Section_prop * section=static_cast<Section_prop *>(configuration);
       const char * dev=section->Get_string("mididevice");
       std::string fullconf=section->Get_string("midiconfig");
+
+      const char * inconf = 1; // change to integer value of midi input device
+
       /* If device = "default" go for first handler that works */
       MidiHandler * handler;
 //      MAPPER_AddHandler(MIDI_SaveRawEvent,MK_f8,MMOD1|MMOD2,"caprawmidi","Cap MIDI");
@@ -183,6 +213,7 @@ public:
       midi.status=0x00;
       midi.cmd_pos=0;
       midi.cmd_len=0;
+      midi.sysex.used=0;
       if (!strcasecmp(dev,"default")) goto getdefault;
       handler=handler_list;
       while (handler) {
@@ -194,7 +225,7 @@ public:
             midi.handler=handler;
             midi.available=true;
             LOG(LOG_MISC,LOG_DEBUG)("MIDI: Opened device:%s",handler->GetName());
-            return;
+            goto midiin;
          }
          handler=handler->next;
       }
@@ -206,16 +237,27 @@ getdefault:
             midi.available=true;
             midi.handler=handler;
             LOG(LOG_MISC,LOG_DEBUG)("MIDI: Opened device:%s",handler->GetName());
-            return;
+            goto midiin;
          }
          handler=handler->next;
       }
       /* This shouldn't be possible */
    }
+midiin:
+   MidiHandler * output_handler=0;
+   output_handler=handler;
+   if (handler->OpenInput(inconf)) {
+      LOG_MSG("MIDI:Opened input device:%s.",handler->GetName());
+      midi.in_handler=handler;
+      return;
+   }
+   handler=handler_list;
    ~MIDI(){
+      midi.in_handler->Close();
       if(midi.available) midi.handler->Close();
       midi.available = false;
       midi.handler = 0;
+      midi.in_handler = 0;
    }
 };
 
diff -rupN dosbox-before//src/gui/midi_win32.h dosbox-after/src/gui/midi_win32.h
--- dosbox-before//src/gui/midi_win32.h
+++ dosbox-after/src/gui/midi_win32.h
@@ -25,14 +25,60 @@
 #include <string>
 #include <sstream>
 
+void CALLBACK Win32_midiInCallback(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
+   //LOG_MSG("wMsg:%x %x %x",wMsg, dwParam1, dwParam2);
+   Bit8u msg[4] = {(Bit8u(dwParam1&0xff)),(Bit8u((dwParam1&0xff00)>>8)),
+               (Bit8u((dwParam1&0xff0000)>>16)),MIDI_evt_len[(Bit8u(dwParam1&0xff))]};
+   Bit8u *sysex;
+   Bitu len;
+   MIDIHDR *t_hdr;
+   switch (wMsg) {
+      case MM_MIM_DATA:  /* 0x3C3 - midi message */
+         MIDI_InputMsg(msg);
+         break;
+      case MM_MIM_OPEN:  /* 0x3C1 */
+         break;
+      case MM_MIM_CLOSE: /* 0x3C2 */
+         break;
+      case MM_MIM_LONGDATA: /* 0x3C4 - sysex */
+         t_hdr=(MIDIHDR*)dwParam1;
+         sysex=(Bit8u*)t_hdr->lpData;
+         len=(Bitu)t_hdr->dwBytesRecorded;
+         {
+            Bitu cnt=5;
+            while (cnt) { //abort if timed out
+               Bitu ret = Bitu(MIDI_InputSysex(sysex,len,false));
+               if (!ret) {len=0;break;}
+               if (len==ret) cnt--; else cnt=5;
+               sysex+=len-ret;
+               len=ret;
+               Sleep(5);//msec
+            }
+            if (len) MIDI_InputSysex(sysex,0,false);
+         }
+         midiInUnprepareHeader(hMidiIn,t_hdr,sizeof(*t_hdr));
+         t_hdr->dwBytesRecorded = 0 ;
+         midiInPrepareHeader(hMidiIn,t_hdr,sizeof(*t_hdr));
+         break;
+      case MM_MIM_ERROR:
+      case MM_MIM_LONGERROR:
+         break;
+      default:
+         LOG(LOG_MISC, LOG_NORMAL) ("MIDI: Unhandled input type %x",wMsg);
+   }
+};
+
 class MidiHandler_win32: public MidiHandler {
 private:
    HMIDIOUT m_out;
    MIDIHDR m_hdr;
    HANDLE m_event;
    bool isOpen;
+   HMIDIIN m_in;
+   MIDIHDR m_inhdr;
+   bool isOpenInput;
 public:
-   MidiHandler_win32() : MidiHandler(),isOpen(false) {};
+   MidiHandler_win32() : isOpen(false),isOpenInput(false),MidiHandler() {};
    const char * GetName(void) { return "win32";};
    bool Open(const char * conf) {
       if (isOpen) return false;
@@ -71,12 +117,46 @@ public:
       isOpen=true;
       return true;
    };
+   bool OpenInput(const char *inconf) {
+      if (isOpenInput) return false;
+      void * midiInCallback=Win32_midiInCallback;
+      MMRESULT res;
+      if(inconf && *inconf) {
+         std::string strinconf(inconf);
+         std::istringstream configmidiin(strinconf);
+         unsigned int nummer = midiInGetNumDevs();
+         configmidiin >> nummer;
+         if(nummer < midiInGetNumDevs()){
+            MIDIINCAPS mididev;
+            midiInGetDevCaps(nummer, &mididev, sizeof(MIDIINCAPS));
+            LOG_MSG("MIDI:win32 selected input %s",mididev.szPname);
+            res = midiInOpen (&m_in, nummer, (DWORD_PTR)midiInCallback, 0, CALLBACK_FUNCTION);
+         }
+      } else {
+         res = midiInOpen(&m_in, MIDI_MAPPER, (DWORD_PTR)midiInCallback, 0, CALLBACK_FUNCTION);
+      }
+      if (res != MMSYSERR_NOERROR) return false;
 
+      m_inhdr.lpData = (char*)&MIDI_InSysexBuf[0];
+      m_inhdr.dwBufferLength = SYSEX_SIZE;
+      m_inhdr.dwBytesRecorded = 0 ;
+      m_inhdr.dwUser = 0;
+      midiInPrepareHeader(m_in,&m_inhdr,sizeof(m_inhdr));
+      midiInStart(m_in);
+      isOpenInput=true;
+      return true;
+   };
    void Close(void) {
-      if (!isOpen) return;
-      isOpen=false;
-      midiOutClose(m_out);
-      CloseHandle (m_event);
+      if (isOpen) {
+         isOpen=false;
+         midiOutClose(m_out);
+         CloseHandle (m_event);
+      }
+      if (isOpenInput) {
+         isOpenInput=false;
+         midiInStop(m_in);
+         midiInClose(m_in);
+      }
    };
    void PlayMsg(Bit8u * msg) {
       midiOutShortMsg(m_out, *(Bit32u*)msg);
diff -rupN dosbox-before//src/hardware/mpu401.cpp dosbox-after/src/hardware/mpu401.cpp
--- dosbox-before//src/hardware/mpu401.cpp
+++ dosbox-after/src/hardware/mpu401.cpp
@@ -24,22 +24,38 @@
 #include "setup.h"
 #include "cpu.h"
 #include "support.h"
+#include "mixer.h"
+#include "midi.h"
 
 void MIDI_RawOutByte(Bit8u data);
 bool MIDI_Available(void);
+SDL_mutex * MPULock;
 
 static void MPU401_Event(Bitu);
 static void MPU401_Reset(void);
 static void MPU401_ResetDone(Bitu);
 static void MPU401_EOIHandler(Bitu val=0);
 static void MPU401_EOIHandlerDispatch(void);
+static void MPU401_IntelligentOut(Bitu data);
+static void MPU401_WriteCommand(Bitu port,Bitu val,Bitu iolen);
+static void MPU401_WriteData(Bitu port,Bitu val,Bitu iolen);
+static void MPU401_NotesOff(Bitu chan);
+static void MPU401_RecQueueBuffer(Bit8u * buf, Bitu len,bool block);
 
 #define MPU401_VERSION   0x15
 #define MPU401_REVISION   0x01
-#define MPU401_QUEUE 32
+#define MPU401_QUEUE 64
+#define MPU401_INPUT_QUEUE 1024
 #define MPU401_TIMECONSTANT (60000000/1000.0f)
 #define MPU401_RESETBUSY 27.0f
 
+#define M_GETKEY key[key/32]&(1<<(key%32))
+#define M_SETKEY key[key/32]|=(1<<(key%32))
+#define M_DELKEY key[key/32]&=~(1<<(key%32))
+enum RecState { M_RECOFF,M_RECSTB,M_RECON };
+static Bitu MPUClockBase[8]={48,72,96,120,144,168,192};
+static Bit8u cth_data[16]={0,0,0,0,1,0,0,0,1,0,1,0,1,1,1,0};
+
 enum MpuMode { M_UART,M_INTELLIGENT };
 enum MpuDataType {T_OVERFLOW,T_MARK,T_MIDI_SYS,T_MIDI_NORM,T_COMMAND};
 
@@ -58,20 +74,38 @@ static void MPU401_WriteData(Bitu port,B
 #define MSG_MPU_ACK                     0xfe
 
 static struct {
+    bool intelligent;
+    Bitu irq;
+   bool midi_thru;
+   IO_ReadHandleObject ReadHandler[2];
+   IO_WriteHandleObject WriteHandler[2];
+} mpuhw;
+
+static struct {
    bool intelligent;
    MpuMode mode;
    Bitu irq;
    Bit8u queue[MPU401_QUEUE];
    Bitu queue_pos,queue_used;
+   Bit8u rec_queue[MPU401_INPUT_QUEUE];
+   Bitu rec_queue_pos,rec_queue_used;
    struct track {
       Bits counter;
-      Bit8u value[8],sys_val;
-      Bit8u vlength,length;
+      Bit8u value[3],sys_val;
+      Bitu length;
       MpuDataType type;
    } playbuf[8],condbuf;
    struct {
-      bool conductor,cond_req,cond_set, block_ack;
-      bool playing,reset;
+      bool tx_ready;
+      bool conductor,cond_req,cond_set;
+      bool track_req;
+      bool block_ack;
+      bool playing;
+      bool clock_to_host;
+      bool sync_in;
+      bool sysex_in_finished;
+      bool rec_copy;
+      RecState rec;
       bool wsd,wsm,wsd_start;
       bool run_irq,irq_pending;
       bool send_now;
@@ -81,220 +115,470 @@ static struct {
       Bit8u tmask,cmask,amask;
       Bit16u midi_mask;
       Bit16u req_mask;
-      Bit8u channel,old_chan;
+      Bitu track,old_track;
+      Bit8u last_rtcmd;
    } state;
    struct {
       Bit8u timebase,old_timebase;
       Bit8u tempo,old_tempo;
       Bit8u tempo_rel,old_tempo_rel;
       Bit8u tempo_grad;
-      Bit8u cth_rate,cth_counter;
-      bool clock_to_host,cth_active;
+      Bit8u cth_rate[4],cth_mode;
+      Bit8u midimetro,metromeas;
+      Bitu cth_counter,cth_old;
+      Bitu rec_counter;
+      Bit32s measure_counter,meas_old;
+      Bit32s freq;
+      Bits ticks_in;
+      float freq_mod;
+      bool active;
    } clock;
+   struct {
+      bool all_thru,midi_thru,sysex_thru,commonmsgs_thru;
+      bool modemsgs_in, commonmsgs_in, bender_in, sysex_in;
+      bool allnotesoff_out;
+      bool rt_affection, rt_out,rt_in;
+      bool timing_in_stop;
+      bool data_in_stop;
+      bool rec_measure_end;
+      Bit8u prchg_buf[16];
+      Bit16u prchg_mask;
+   } filter;
+   Bitu ch_toref[16];
+   struct {
+      Bit8u chan;
+      Bit32u key[4];
+      Bit8u trmask;
+      bool on;
+   }chanref[5],inputref[16];
 } mpu;
 
+static void MPU401_ReCalcClock(void) {
+   Bit32s maxtempo=240, mintempo=16;
+   if (mpu.clock.timebase>=168) maxtempo=179;
+   if (mpu.clock.timebase==144) maxtempo=208;
+   if (mpu.clock.timebase>=120) mintempo=8;
+   mpu.clock.freq=(Bit32u(mpu.clock.tempo*2*mpu.clock.tempo_rel))>>6;
+
+   mpu.clock.freq=mpu.clock.timebase*
+      (mpu.clock.freq<(mintempo*2) ? mintempo :
+      ((mpu.clock.freq/2)<maxtempo?(mpu.clock.freq/2):maxtempo) );
+
+   if (mpu.state.sync_in) {
+      Bit32s freq= Bit32s(float(mpu.clock.freq)*mpu.clock.freq_mod);
+      if (freq>mpu.clock.timebase*mintempo && freq<mpu.clock.timebase*maxtempo)
+         mpu.clock.freq=freq;
+   }
+}
+
+static INLINE void MPU401_StartClock(void) {
+   if (mpu.clock.active) return;
+   if (!(mpu.state.clock_to_host || mpu.state.playing || mpu.state.rec==M_RECON)) return;
+   mpu.clock.active=true;
+   PIC_RemoveEvents(MPU401_Event);
+   PIC_AddEvent(MPU401_Event,MPU401_TIMECONSTANT/mpu.clock.freq);
+}
+
+static void MPU401_StopClock(void) {
+   if (mpu.state.playing || mpu.state.rec!=M_RECON || mpu.state.clock_to_host)  return;
+   mpu.clock.active=false;
+   PIC_RemoveEvents(MPU401_Event);
+}
+
 int MPU401_GetIRQ() {
    return (int)mpu.irq;
 }
 
-static void QueueByte(Bit8u data) {
+static INLINE void MPU401_RunClock(void) {
+   if (!mpu.clock.active) return;
+   PIC_RemoveEvents(MPU401_Event);
+   PIC_AddEvent(MPU401_Event,MPU401_TIMECONSTANT/(mpu.clock.freq));
+}
+
+static INLINE void MPU401_QueueByte(Bit8u data) {
    if (mpu.state.block_ack) {mpu.state.block_ack=false;return;}
-   if (mpu.queue_used==0 && mpu.intelligent) {
+   if (mpu.queue_used==0 && mpuhw.intelligent) {
       mpu.state.irq_pending=true;
-      PIC_ActivateIRQ(mpu.irq);
+      PIC_ActivateIRQ(mpuhw.irq);
    }
    if (mpu.queue_used<MPU401_QUEUE) {
       Bitu pos=mpu.queue_used+mpu.queue_pos;
-      if (mpu.queue_pos>=MPU401_QUEUE) mpu.queue_pos-=MPU401_QUEUE;
       if (pos>=MPU401_QUEUE) pos-=MPU401_QUEUE;
       mpu.queue_used++;
       mpu.queue[pos]=data;
-   } else LOG(LOG_MISC,LOG_NORMAL)("MPU401:Data queue full");
+   }
+}
+
+static void MPU401_RecQueueBuffer(Bit8u * buf, Bitu len,bool block) {
+   if (block) {
+      if (MPULock) SDL_mutexP(MPULock);
+      else return;
+   }
+   Bitu cnt=0;
+   while (cnt<len) {
+      if (mpu.rec_queue_used<MPU401_INPUT_QUEUE) {
+         Bitu pos=mpu.rec_queue_used+mpu.rec_queue_pos;
+         if (pos>=MPU401_INPUT_QUEUE) {pos-=MPU401_INPUT_QUEUE;}
+         mpu.rec_queue[pos]=buf[cnt];
+         mpu.rec_queue_used++;
+         if (!mpu.state.sysex_in_finished && buf[cnt]==MSG_EOX) {//finish sysex
+            mpu.state.sysex_in_finished=true;
+            break;
+         }
+         cnt++;
+      }
+   }
+   if (mpu.queue_used==0) {
+      if (mpu.state.rec_copy || mpu.state.irq_pending) {
+         if (block && MPULock) SDL_mutexV(MPULock);
+         return;
+      }
+      mpu.state.rec_copy=true;
+      if (mpu.rec_queue_pos>=MPU401_INPUT_QUEUE) mpu.rec_queue_pos-=MPU401_INPUT_QUEUE;
+      MPU401_QueueByte(mpu.rec_queue[mpu.rec_queue_pos]);
+      mpu.rec_queue_used--;
+      mpu.rec_queue_pos++;
+   }
+   if (block && MPULock) SDL_mutexV(MPULock);
 }
 
-static void ClrQueue(void) {
+static void MPU401_ClrQueue(void) {
    mpu.queue_used=0;
    mpu.queue_pos=0;
+   mpu.rec_queue_used=0;
+   mpu.rec_queue_pos=0;
+   mpu.state.sysex_in_finished=true;
+   mpu.state.irq_pending=false;
 }
 
 static Bitu MPU401_ReadStatus(Bitu port,Bitu iolen) {
        (void)iolen;//UNUSED
        (void)port;//UNUSED
-   Bit8u ret=0x3f;   /* Bits 6 and 7 clear */
-   if (mpu.state.cmd_pending) ret|=0x40;
-   if (!mpu.queue_used) ret|=0x80;
-   return ret;
+   return (0x3f | (mpu.queue_used ? 0: 0x80));
+}
+
+static Bitu MPU401_ReadStatusTx(Bitu port,Bitu iolen) {
+   return (0x3f | (mpu.queue_used ? 0: 0x80) | (mpu.state.tx_ready ? 0: 0x40));
+}
+
+
+void MPU401_SetTx(bool status) {
+   mpu.state.tx_ready=status;
 }
 
 static void MPU401_WriteCommand(Bitu port,Bitu val,Bitu iolen) {
-       (void)iolen;//UNUSED
-       (void)port;//UNUSED
-   if (mpu.mode==M_UART && val!=0xff) return;
-   if (mpu.state.reset) {
-      if (mpu.state.cmd_pending || (val!=0x3f && val!=0xff)) {
-         mpu.state.cmd_pending=val+1;
-         return;
-      }
-      PIC_RemoveEvents(MPU401_ResetDone);
-      mpu.state.reset=false;
-   }
-   if (val<=0x2f) {
-      switch (val&3) { /* MIDI stop, start, continue */
-         case 1: {MIDI_RawOutByte(0xfc);break;}
-         case 2: {MIDI_RawOutByte(0xfa);break;}
-         case 3: {MIDI_RawOutByte(0xfb);break;}
-      }
-      if (val&0x20) LOG(LOG_MISC,LOG_ERROR)("MPU-401:Unhandled Recording Command %x",(int)val);
-      switch (val&0xc) {
-         case  0x4:   /* Stop */
-            PIC_RemoveEvents(MPU401_Event);
-            mpu.state.playing=false;
-            for (Bitu i=0xb0;i<0xbf;i++) {   /* All notes off */
-               MIDI_RawOutByte(i);
-               MIDI_RawOutByte(0x7b);
-               MIDI_RawOutByte(0);
-            }
-            break;
-         case 0x8:   /* Play */
-            LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Intelligent mode playback started");
-            mpu.state.playing=true;
-            PIC_RemoveEvents(MPU401_Event);
-            PIC_AddEvent(MPU401_Event,MPU401_TIMECONSTANT/(mpu.clock.tempo*mpu.clock.timebase));
-            ClrQueue();
-            break;
-      }
-   }
-   else if (val>=0xa0 && val<=0xa7) {   /* Request play counter */
-      if (mpu.state.cmask&(1<<(val&7))) QueueByte(mpu.playbuf[val&7].counter);
-   }
-   else if (val>=0xd0 && val<=0xd7) {   /* Send data */
-      mpu.state.old_chan=mpu.state.channel;
-      mpu.state.channel=val&7;
-      mpu.state.wsd=true;
-      mpu.state.wsm=false;
-      mpu.state.wsd_start=true;
-   }
-   else
-   switch (val) {
-      case 0xdf:   /* Send system message */
-         mpu.state.wsd=false;
-         mpu.state.wsm=true;
-         mpu.state.wsd_start=true;
+   //LOG(LOG_MISC,LOG_NORMAL)("MPU401:command %x",val);
+   SDL_mutexP(MPULock);
+
+   //hack:enable midi through after the first mpu401 command is written
+   mpuhw.midi_thru=true;
+
+   if (val<=0x2f) { /* Sequencer state */
+      bool send_prchg=false;
+      if ((val&0xf)<0xc) {
+         switch (val&3) { /* MIDI realtime messages */
+            case 1:
+               mpu.state.last_rtcmd=0xfc;
+               if (mpu.filter.rt_out) MIDI_RawOutRTByte(0xfc);
+               mpu.clock.meas_old=mpu.clock.measure_counter;
+               mpu.clock.cth_old=mpu.clock.cth_counter;
+               break;
+            case 2:
+               mpu.state.last_rtcmd=0xfa;
+               if (mpu.filter.rt_out) MIDI_RawOutRTByte(0xfb);
+               mpu.clock.measure_counter=mpu.clock.meas_old=0;
+               mpu.clock.cth_counter=mpu.clock.cth_old=0;
+               break;
+            case 3:
+               mpu.state.last_rtcmd=0xfc;
+               if (mpu.filter.rt_out) MIDI_RawOutRTByte(0xfa);
+               mpu.clock.measure_counter=mpu.clock.meas_old;
+               mpu.clock.cth_counter=mpu.clock.cth_old;
+               break;
+         }
+         switch (val&0xc) { /* Playing */
+            case  0x4:   /* Stop */
+               mpu.state.playing=false;
+               MPU401_StopClock();
+               for (Bitu i=0;i<16;i++) MPU401_NotesOff(i);
+               mpu.filter.prchg_mask=0;
+               break;
+            case 0x8:   /* Start */
+               LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Intelligent mode playback");
+               mpu.state.playing=true;
+               MPU401_StartClock();
+         }
+         switch (val&0x30) { /* Recording */
+            case 0: //check if it waited for MIDI RT command
+               //if (val&8 && mpu.state.rec!=M_RECON) mpu.clock.rec_counter=0;
+               if ((val&3)<2 || !mpu.filter.rt_affection || mpu.state.rec!=M_RECSTB) break;
+               mpu.state.rec=M_RECON;
+               MPU401_StartClock();
+               if (mpu.filter.prchg_mask) send_prchg=true;
+               break;
+            case 0x10:  /* Stop */
+               //if (val&8 && mpu.state.rec!=M_RECON) mpu.clock.rec_counter=0;
+               mpu.state.rec=M_RECOFF;
+               MPU401_StopClock();   
+               MPU401_QueueByte(MSG_MPU_ACK);
+               MPU401_QueueByte(mpu.clock.rec_counter);
+               MPU401_QueueByte(MSG_MPU_END);
+               mpu.filter.prchg_mask=0;
+               mpu.clock.rec_counter=0;
+               SDL_mutexV(MPULock);
+               return;               
+            case 0x20:  /* Start */
+               LOG(LOG_MISC,LOG_NORMAL)("MPU-401: intelligent mode recording");
+               if (!(mpu.state.rec==M_RECON)) {
+                  mpu.clock.rec_counter=0;
+                  mpu.state.rec=M_RECSTB;
+               }
+               if (mpu.state.last_rtcmd==0xfa || mpu.state.last_rtcmd==0xfb) {
+                  mpu.clock.rec_counter=0;
+                  mpu.state.rec=M_RECON;
+                  if (mpu.filter.prchg_mask) send_prchg=true;
+                  MPU401_StartClock();
+               }
+         }
+       }
+      MPU401_QueueByte(MSG_MPU_ACK);
+      //record counter hack: needed by Prism, but sent only on cmd 0x20/0x26 (or breaks Ballade)
+      Bit8u rec_cnt=mpu.clock.rec_counter;
+      if ((val==0x20 || val==0x26) && mpu.state.rec==M_RECON)
+         MPU401_RecQueueBuffer(&rec_cnt,1,false);
+
+      if (send_prchg) for (Bitu i=0;i<16;i++)
+         if (mpu.filter.prchg_mask&(1<<i)) {
+            Bit8u recmsg[3]={mpu.clock.rec_counter,0xc0|i,mpu.filter.prchg_buf[i]};
+            MPU401_RecQueueBuffer(recmsg,3,false);
+            mpu.filter.prchg_mask&=~(1<<i);
+         }
+      SDL_mutexV(MPULock);
+      return;
+    }
+    else if (val>=0xa0 && val<=0xa7) {   /* Request play counter */
+      //if (mpu.state.cmask&(1<<(val&7)))
+      MPU401_QueueByte(mpu.playbuf[val&7].counter);
+    }
+    else if (val>=0xd0 && val<=0xd7) {   /* Send data */
+      mpu.state.old_track=mpu.state.track;
+      mpu.state.track=val&7;
+       mpu.state.wsd=true;
+       mpu.state.wsm=false;
+       mpu.state.wsd_start=true;
+    }
+   else if (val<0x80 && val>=0x40) { /* Set reference table channel */
+      mpu.chanref[(val>>4)-4].on=true;
+      mpu.chanref[(val>>4)-4].chan=val&0x0f;
+      mpu.chanref[(val>>4)-4].trmask=0;
+      for (Bitu i=0;i<4;i++) mpu.chanref[(val>>4)-4].key[i]=0;
+      for (Bitu i=0;i<16;i++) if (mpu.ch_toref[i]==((val>>4)-4)) mpu.ch_toref[i]=4;
+      mpu.ch_toref[val&0x0f]=(val>>4)-4;
+   }
+    else
+    switch (val) {
+      case 0x30: /* Configuration 0x30 - 0x39 */
+         mpu.filter.allnotesoff_out=false;
+         break;
+      case 0x32:
+         mpu.filter.rt_out=false;
+          break;
+      case 0x33:
+         mpu.filter.all_thru=false;
+         mpu.filter.commonmsgs_thru=false;
+         mpu.filter.midi_thru=false;
+         for (Bitu i=0;i<16;i++) {
+            mpu.inputref[i].on=false;
+            for (Bitu j=0;j<4;j++) mpu.inputref[i].key[j]=0;
+         }
+          break;
+      case 0x34:
+         mpu.filter.timing_in_stop=true;
+          break;
+      case 0x35:
+         mpu.filter.modemsgs_in=true;
+          break;
+      case 0x37:
+         mpu.filter.sysex_thru=true;
          break;
-      case 0x8e:   /* Conductor */
-         mpu.state.cond_set=false;
+      case 0x38:
+         mpu.filter.commonmsgs_in=true;
          break;
-      case 0x8f:
-         mpu.state.cond_set=true;
+      case 0x39:
+         mpu.filter.rt_in=true;
          break;
+      case 0x3f:   /* UART mode */
+         LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Set UART mode %X",val);
+         mpu.mode=M_UART;
+         mpuhw.midi_thru=false;
+          break;
+      case 0x80:  /* Internal clock */
+         if (mpu.clock.active && mpu.state.sync_in) {
+            PIC_AddEvent(MPU401_Event,MPU401_TIMECONSTANT/(mpu.clock.freq));
+            mpu.clock.freq_mod=1.0;
+         }
+         mpu.state.sync_in=false;
+          break;
+      case 0x82:  /* Sync to MIDI */
+         mpu.clock.ticks_in=0;
+         mpu.state.sync_in=true;
+         break;
+      case 0x86: case 0x87: /* Bender */
+         mpu.filter.bender_in=bool(val&1);
+         break;
+      case 0x88: case 0x89:/* MIDI through */
+         mpu.filter.midi_thru=bool(val&1);
+         for (Bitu i=0;i<16;i++) {
+            mpu.inputref[i].on=mpu.filter.midi_thru;
+            if (!(val&1)) for (Bitu j=0;j<4;j++) mpu.inputref[i].key[j]=0;
+         }
+          break;
+      case 0x8a: case 0x8b: /* Data in stop */
+         mpu.filter.data_in_stop=bool(val&1);
+          break;
+      case 0x8c: case 0x8d: /* Send measure end */
+         mpu.filter.rec_measure_end=bool(val&1);
+          break;
+      case 0x8e: case 0x8f: /* Conductor */
+         mpu.state.cond_set=bool(val&1);
+          break;
+      case 0x90: case 0x91: /* Realtime affection */
+         mpu.filter.rt_affection=bool(val&1);
+          break;
       case 0x94: /* Clock to host */
-         mpu.clock.clock_to_host=false;
-         break;
+         mpu.state.clock_to_host=false;
+         MPU401_StopClock();
+          break;
       case 0x95:
-         mpu.clock.clock_to_host=true;
-         break;
-      case 0xc2: /* Internal timebase */
-         mpu.clock.timebase=48;
-         break;
-      case 0xc3:
-         mpu.clock.timebase=72;
-         break;
-      case 0xc4:
-         mpu.clock.timebase=96;
-         break;
-      case 0xc5:
-         mpu.clock.timebase=120;
-         break;
-      case 0xc6:
-         mpu.clock.timebase=144;
+         mpu.state.clock_to_host=true;
+         MPU401_StartClock();
          break;
-      case 0xc7:
-         mpu.clock.timebase=168;
-         break;
-      case 0xc8:
-         mpu.clock.timebase=192;
-         break;
-      /* Commands with data byte */
-      case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6:
-      case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef:
-         mpu.state.command_byte=val;
-         break;
-      /* Commands 0xa# returning data */
-      case 0xab:   /* Request and clear recording counter */
-         QueueByte(MSG_MPU_ACK);
-         QueueByte(0);
-         return;
-      case 0xac:   /* Request version */
-         QueueByte(MSG_MPU_ACK);
-         QueueByte(MPU401_VERSION);
-         return;
-      case 0xad:   /* Request revision */
-         QueueByte(MSG_MPU_ACK);
-         QueueByte(MPU401_REVISION);
-         return;
-      case 0xaf:   /* Request tempo */
-         QueueByte(MSG_MPU_ACK);
-         QueueByte(mpu.clock.tempo);
-         return;
-      case 0xb1:   /* Reset relative tempo */
-            mpu.clock.old_tempo_rel=mpu.clock.tempo_rel;
-            mpu.clock.tempo_rel=0x40;
+      case 0x96: case 0x97: /* Sysex input allow */
+         mpu.filter.sysex_in=bool(val&1);
+         if (val&1) mpu.filter.sysex_thru=false;
+         break;
+      case 0x98:case 0x99:case 0x9a: case 0x9b: /* Reference tables on/off */
+      case 0x9c:case 0x9d:case 0x9e: case 0x9f:
+         mpu.chanref[(val-0x98)/2].on=bool(val&1);
+          break;
+       /* Commands 0xa# returning data */
+       case 0xab:   /* Request and clear recording counter */
+         MPU401_QueueByte(MSG_MPU_ACK);
+         MPU401_QueueByte(0);
+         SDL_mutexV(MPULock);
+          return;
+       case 0xac:   /* Request version */
+         MPU401_QueueByte(MSG_MPU_ACK);
+         MPU401_QueueByte(MPU401_VERSION);
+         SDL_mutexV(MPULock);
+          return;
+       case 0xad:   /* Request revision */
+         MPU401_QueueByte(MSG_MPU_ACK);
+         MPU401_QueueByte(MPU401_REVISION);
+         SDL_mutexV(MPULock);
+          return;
+       case 0xaf:   /* Request tempo */
+         MPU401_QueueByte(MSG_MPU_ACK);
+         MPU401_QueueByte(mpu.clock.tempo);
+         SDL_mutexV(MPULock);
+          return;
+       case 0xb1:   /* Reset relative tempo */
+         mpu.clock.tempo_rel=0x40;
+          break;
+       case 0xb8:   /* Clear play counters */
+         mpu.state.last_rtcmd=0;
+          for (Bitu i=0;i<8;i++) {
+             mpu.playbuf[i].counter=0;
+             mpu.playbuf[i].type=T_OVERFLOW;
+          }
+          mpu.condbuf.counter=0;
+          mpu.condbuf.type=T_OVERFLOW;
+
+          mpu.state.amask=mpu.state.tmask;
+         mpu.state.conductor=mpu.state.cond_set;
+         mpu.clock.cth_counter=mpu.clock.cth_old=0;
+         mpu.clock.measure_counter=mpu.clock.meas_old=0;
          break;
       case 0xb9:   /* Clear play map */
-      case 0xb8:   /* Clear play counters */
-         for (Bitu i=0xb0;i<0xbf;i++) {   /* All notes off */
-            MIDI_RawOutByte(i);
-            MIDI_RawOutByte(0x7b);
-            MIDI_RawOutByte(0);
-         }
+         for (Bitu i=0;i<16;i++) MPU401_NotesOff(i);
          for (Bitu i=0;i<8;i++) {
             mpu.playbuf[i].counter=0;
             mpu.playbuf[i].type=T_OVERFLOW;
          }
-         mpu.condbuf.counter=0;
-         mpu.condbuf.type=T_OVERFLOW;
-         if (!(mpu.state.conductor=mpu.state.cond_set)) mpu.state.cond_req=0;
-         mpu.state.amask=mpu.state.tmask;
-         mpu.state.req_mask=0;
-         mpu.state.irq_pending=true;
-         break;
-      case 0xff:   /* Reset MPU-401 */
-         LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Reset %X",(int)val);
-         PIC_AddEvent(MPU401_ResetDone,MPU401_RESETBUSY);
-         mpu.state.reset=true;
-         if (mpu.mode==M_UART) {
-            MPU401_Reset();
-            return;   //do not send ack in UART mode
-         }
-         MPU401_Reset();
+         mpu.state.last_rtcmd=0;
+         mpu.clock.cth_counter=mpu.clock.cth_old=0;
+         mpu.clock.measure_counter=mpu.clock.meas_old=0;
+         break;
+      case 0xba: /* Clear record counter */
+         mpu.clock.rec_counter=0;
+         break;
+      case 0xc2: case 0xc3: case 0xc4: /* Internal timebase */
+      case 0xc5: case 0xc6: case 0xc7: case 0xc8:
+         mpu.clock.timebase=MPUClockBase[val-0xc2];
+         MPU401_ReCalcClock();
          break;
-      case 0x3f:   /* UART mode */
-         LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Set UART mode %X",(int)val);
-         mpu.mode=M_UART;
+      case 0xdf:   /* Send system message */
+         mpu.state.wsd=false;
+         mpu.state.wsm=true;
+         mpu.state.wsd_start=true;
          break;
-      default:;
-         //LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Unhandled command %X",val);
-   }
-   QueueByte(MSG_MPU_ACK);
+      case 0xe0: case 0xe1: case 0xe2: case 0xe4: case 0xe6: /* Commands with data byte */
+      case 0xe7: case 0xec: case 0xed: case 0xee: case 0xef:
+         mpu.state.command_byte=val;
+          break;
+       case 0xff:   /* Reset MPU-401 */
+         LOG(LOG_MISC,LOG_NORMAL)("MPU-401:Reset");
+          if (CPU_Cycles > 5) { //It came from the desert wants a fast irq
+             CPU_CycleLeft += CPU_Cycles;
+             CPU_Cycles = 5;
+          }
+          MPU401_Reset();
+          break;
+       default:;
+         //LOG(LOG_MISC,LOG_WARN)("MPU-401:Unhandled command %X",val);
+    }
+   MPU401_QueueByte(MSG_MPU_ACK);
+   SDL_mutexV(MPULock);
 }
 
 static Bitu MPU401_ReadData(Bitu port,Bitu iolen) {
        (void)iolen;//UNUSED
        (void)port;//UNUSED
+   SDL_mutexP(MPULock);
+
    Bit8u ret=MSG_MPU_ACK;
    if (mpu.queue_used) {
       if (mpu.queue_pos>=MPU401_QUEUE) mpu.queue_pos-=MPU401_QUEUE;
       ret=mpu.queue[mpu.queue_pos];
       mpu.queue_pos++;mpu.queue_used--;
    }
-   if (!mpu.intelligent) return ret;
-
-   if (mpu.queue_used == 0) PIC_DeActivateIRQ(mpu.irq);
+   if (mpu.mode==M_UART) {
+      if (mpuhw.intelligent && !mpu.queue_used) PIC_DeActivateIRQ(mpuhw.irq);
+      SDL_mutexV(MPULock);
+      return ret;
+   }
+   if (mpu.state.rec_copy && !mpu.rec_queue_used) {
+      mpu.state.rec_copy=false;
+      MPU401_EOIHandler();
+      SDL_mutexV(MPULock);
+      //LOG(LOG_MISC,LOG_NORMAL)("MPU401:read data eoi %x", ret);
+      return ret;
+   }
+
+   //copy from recording buffer
+   if (!mpu.queue_used && mpu.rec_queue_used) {
+      mpu.state.rec_copy=true;
+      if (mpu.rec_queue_pos>=MPU401_INPUT_QUEUE) mpu.rec_queue_pos-=MPU401_INPUT_QUEUE;
+      MPU401_QueueByte(mpu.rec_queue[mpu.rec_queue_pos]);
+      mpu.rec_queue_pos++;mpu.rec_queue_used--;
+   }
+   if (!mpu.queue_used) PIC_DeActivateIRQ(mpuhw.irq);
 
    if (ret>=0xf0 && ret<=0xf7) { /* MIDI data request */
-      mpu.state.channel=ret&7;
+      mpu.state.track=ret&7;
       mpu.state.data_onoff=0;
       mpu.state.cond_req=false;
+      mpu.state.track_req=true;
    }
    if (ret==MSG_MPU_COMMAND_REQ) {
       mpu.state.data_onoff=0;
@@ -303,36 +587,50 @@ static Bitu MPU401_ReadData(Bitu port,Bi
          mpu.state.block_ack=true;
          MPU401_WriteCommand(0x331,mpu.condbuf.value[0],1);
          if (mpu.state.command_byte) MPU401_WriteData(0x330,mpu.condbuf.value[1],1);
+         mpu.condbuf.type=T_OVERFLOW;
       }
-   mpu.condbuf.type=T_OVERFLOW;
    }
-   if (ret==MSG_MPU_END || ret==MSG_MPU_CLOCK || ret==MSG_MPU_ACK) {
-      mpu.state.data_onoff=-1;
+   if (ret==MSG_MPU_END || ret==MSG_MPU_CLOCK || ret==MSG_MPU_ACK || ret==MSG_MPU_OVERFLOW)
       MPU401_EOIHandlerDispatch();
    }
+   SDL_mutexV(MPULock);
+   //LOG(LOG_MISC,LOG_NORMAL)("MPU401:read data %x", ret);
    return ret;
 }
 
 static void MPU401_WriteData(Bitu port,Bitu val,Bitu iolen) {
        (void)iolen;//UNUSED
        (void)port;//UNUSED
-   if (mpu.mode==M_UART) {MIDI_RawOutByte(val);return;}
+   //LOG(LOG_MISC,LOG_NORMAL)("MPU401:write data %x", val);
+   if (mpu.mode==M_UART) {MIDI_RawOutByte(val,MOUT_MPU);return;}
+   static Bitu length,cnt;
+
    switch (mpu.state.command_byte) {   /* 0xe# command data */
       case 0x00:
          break;
       case 0xe0:   /* Set tempo */
          mpu.state.command_byte=0;
-         mpu.clock.tempo=val;
+         if (mpu.clock.tempo<8) mpu.clock.tempo=8;
+         else if (mpu.clock.tempo>250) mpu.clock.tempo=250;
+            else mpu.clock.tempo=val;
+         MPU401_ReCalcClock();
          return;
       case 0xe1:   /* Set relative tempo */
          mpu.state.command_byte=0;
             mpu.clock.old_tempo_rel=mpu.clock.tempo_rel;
             mpu.clock.tempo_rel=val;
-            if (val != 0x40) LOG(LOG_MISC,LOG_ERROR)("MPU-401:Relative tempo change value 0x%x (%.3f)",(unsigned int)val,(double)val / 0x40);
+         mpu.clock.tempo_rel=val;
+         MPU401_ReCalcClock();
+         return;
+      case 0xe2:   /* Set gradation for relative tempo */
+         mpu.clock.tempo_grad=val;
+         mpu.state.command_byte=0;
          return;
       case 0xe7:   /* Set internal clock to host interval */
          mpu.state.command_byte=0;
-         mpu.clock.cth_rate=val>>2;
+         if (!val) val=64;
+         for (Bitu i=0;i<4;i++) mpu.clock.cth_rate[i]=(val>>2)+cth_data[(val&3)*4+i];
+         mpu.clock.cth_mode=0;
          return;
       case 0xec:   /* Set active track mask */
          mpu.state.command_byte=0;
@@ -352,46 +650,46 @@ static void MPU401_WriteData(Bitu port,B
          mpu.state.midi_mask&=0x00ff;
          mpu.state.midi_mask|=((Bit16u)val)<<8;
          return;
-      //case 0xe2:   /* Set graduation for relative tempo */
-      //case 0xe4:   /* Set metronome */
-      //case 0xe6:   /* Set metronome measure length */
       default:
          mpu.state.command_byte=0;
          return;
    }
-   static Bitu length,cnt,posd;
-   if (mpu.state.wsd) {   /* Directly send MIDI message */
+   if (mpu.state.wsd && !mpu.state.track_req && !mpu.state.cond_req) {   /* Directly send MIDI message */
       if (mpu.state.wsd_start) {
          mpu.state.wsd_start=0;
          cnt=0;
-            switch (val&0xf0) {
-               case 0xc0:case 0xd0:
-                  mpu.playbuf[mpu.state.channel].value[0]=val;
-                  length=2;
-                  break;
-               case 0x80:case 0x90:case 0xa0:case 0xb0:case 0xe0:
-                  mpu.playbuf[mpu.state.channel].value[0]=val;
-                  length=3;
-                  break;
-               case 0xf0:
-                  LOG(LOG_MISC,LOG_ERROR)("MPU-401:Illegal WSD byte");
-                  mpu.state.wsd=0;
-                  mpu.state.channel=mpu.state.old_chan;
-                  return;
-               default: /* MIDI with running status */
-                  cnt++;
-                  MIDI_RawOutByte(mpu.playbuf[mpu.state.channel].value[0]);
-            }
+         switch (val&0xf0) {
+            case 0xc0:case 0xd0:
+               length=mpu.playbuf[mpu.state.track].length=2;
+               mpu.playbuf[mpu.state.track].type=T_MIDI_NORM;
+               break;
+            case 0x80:case 0x90:case 0xa0:case 0xb0:case 0xe0:
+               length=mpu.playbuf[mpu.state.track].length=3;
+               mpu.playbuf[mpu.state.track].type=T_MIDI_NORM;
+               break;
+            case 0xf0:
+               LOG(LOG_MISC,LOG_WARN)("MPU-401:Illegal MIDI voice message: %x",val);
+               mpu.state.wsd=0;
+               mpu.state.track=mpu.state.old_track;
+               return;
+            default: /* MIDI with running status */
+               cnt++;
+               length=mpu.playbuf[mpu.state.track].length;
+               mpu.playbuf[mpu.state.track].type=T_MIDI_NORM;
+         }
+      }
+      if (cnt<length) {
+         mpu.playbuf[mpu.state.track].value[cnt]=val;
+         cnt++;
       }
-      if (cnt<length) {MIDI_RawOutByte(val);cnt++;}
       if (cnt==length) {
+         MPU401_IntelligentOut(mpu.state.track);
          mpu.state.wsd=0;
-         mpu.state.channel=mpu.state.old_chan;
+         mpu.state.track=mpu.state.old_track;
       }
       return;
    }
-   if (mpu.state.wsm) {   /* Directly send system message */
-      if (val==MSG_EOX) {MIDI_RawOutByte(MSG_EOX);mpu.state.wsm=0;return;}
+   if (mpu.state.wsm && !mpu.state.track_req && !mpu.state.cond_req) {   /* Send system message */
       if (mpu.state.wsd_start) {
          mpu.state.wsd_start=0;
          cnt=0;
@@ -401,24 +699,37 @@ static void MPU401_WriteData(Bitu port,B
             case 0xf6:{ length=1; break;}
             case 0xf0:{ length=0; break;}
             default:
-               length=0;
+               LOG(LOG_MISC,LOG_WARN)
+               ("MPU401:Illegal MIDI system common/exclusive message: %x",val);
+               mpu.state.wsm=0;
+               return;
          }
+      } else if (val&0x80) {
+         MIDI_RawOutByte(MSG_EOX,MOUT_MPU);
+         mpu.state.wsm=0;
+         return;
+      }
+      if (!length || cnt<length) {
+         MIDI_RawOutByte(val,MOUT_MPU);
+         cnt++;
       }
-      if (!length || cnt<length) {MIDI_RawOutByte(val);cnt++;}
       if (cnt==length) mpu.state.wsm=0;
       return;
    }
+   SDL_mutexP(MPULock);
    if (mpu.state.cond_req) { /* Command */
       switch (mpu.state.data_onoff) {
          case -1:
+            SDL_mutexV(MPULock);
             return;
          case  0: /* Timing byte */
-            mpu.condbuf.vlength=0;
+            mpu.condbuf.length=0;
             if (val<0xf0) mpu.state.data_onoff++;
             else {
+               mpu.state.cond_req=false;
                mpu.state.data_onoff=-1;
                MPU401_EOIHandlerDispatch();
-               return;
+               break;
             }
             if (val==0) mpu.state.send_now=true;
             else mpu.state.send_now=false;
@@ -426,148 +737,203 @@ static void MPU401_WriteData(Bitu port,B
             break;
          case  1: /* Command byte #1 */
             mpu.condbuf.type=T_COMMAND;
-            if (val==0xf8 || val==0xf9) mpu.condbuf.type=T_OVERFLOW;
-            mpu.condbuf.value[mpu.condbuf.vlength]=val;
-            mpu.condbuf.vlength++;
-            if ((val&0xf0)!=0xe0) MPU401_EOIHandlerDispatch();
+            if (val==0xf8 || val==0xf9 || val==0xfc) mpu.condbuf.type=T_OVERFLOW;
+            mpu.condbuf.value[mpu.condbuf.length]=val;
+            mpu.condbuf.length++;
+            if ((val&0xf0)!=0xe0) { //no cmd data byte
+               MPU401_EOIHandler();
+               mpu.state.data_onoff=-1;
+               mpu.state.cond_req=false;
+            }
             else mpu.state.data_onoff++;
             break;
          case  2:/* Command byte #2 */
-            mpu.condbuf.value[mpu.condbuf.vlength]=val;
-            mpu.condbuf.vlength++;
+            mpu.condbuf.value[mpu.condbuf.length]=val;
+            mpu.condbuf.length++;
             MPU401_EOIHandlerDispatch();
+            mpu.state.data_onoff=-1;
+            mpu.state.cond_req=false;
             break;
       }
+      SDL_mutexV(MPULock);
       return;
    }
    switch (mpu.state.data_onoff) { /* Data */
       case   -1:
-         return;
+         break;
       case    0: /* Timing byte */
-         if (val<0xf0) mpu.state.data_onoff=1;
+         if (val<0xf0) mpu.state.data_onoff++;
          else {
             mpu.state.data_onoff=-1;
             MPU401_EOIHandlerDispatch();
+            mpu.state.track_req=false;
+            SDL_mutexV(MPULock);
             return;
          }
          if (val==0) mpu.state.send_now=true;
          else mpu.state.send_now=false;
-         mpu.playbuf[mpu.state.channel].counter=(Bits)val;
+         mpu.playbuf[mpu.state.track].counter=val;
          break;
-      case    1: /* MIDI */
-         mpu.playbuf[mpu.state.channel].vlength++;
-         posd=mpu.playbuf[mpu.state.channel].vlength;
-         if (posd==1) {
-            switch (val&0xf0) {
-               case 0xf0: /* System message or mark */
-                  if (val>0xf7) {
-                     mpu.playbuf[mpu.state.channel].type=T_MARK;
-                     mpu.playbuf[mpu.state.channel].sys_val=val;
-                     length=1;
-                  } else {
-                     LOG(LOG_MISC,LOG_ERROR)("MPU-401:Illegal message");
-                     mpu.playbuf[mpu.state.channel].type=T_MIDI_SYS;
-                     mpu.playbuf[mpu.state.channel].sys_val=val;
-                     length=1;
-                  }
-                  break;
-               case 0xc0: case 0xd0: /* MIDI Message */
-                  mpu.playbuf[mpu.state.channel].type=T_MIDI_NORM;
-                  length=mpu.playbuf[mpu.state.channel].length=2;
-                  break;
-               case 0x80: case 0x90: case 0xa0:  case 0xb0: case 0xe0:
-                  mpu.playbuf[mpu.state.channel].type=T_MIDI_NORM;
-                  length=mpu.playbuf[mpu.state.channel].length=3;
-                  break;
-               default: /* MIDI data with running status */
-                  posd++;
-                  mpu.playbuf[mpu.state.channel].vlength++;
-                  mpu.playbuf[mpu.state.channel].type=T_MIDI_NORM;
-                  length=mpu.playbuf[mpu.state.channel].length;
-                  break;
+      case 1: /* MIDI */
+         cnt=0;
+         mpu.state.data_onoff++;
+         switch (val&0xf0) {
+            case 0xc0:case 0xd0:
+               length=mpu.playbuf[mpu.state.track].length=2;
+               mpu.playbuf[mpu.state.track].type=T_MIDI_NORM;
+               break;
+            case 0x80:case 0x90:case 0xa0:case 0xb0:case 0xe0:
+               length=mpu.playbuf[mpu.state.track].length=3;
+               mpu.playbuf[mpu.state.track].type=T_MIDI_NORM;
+               break;
+            case 0xf0:
+               mpu.playbuf[mpu.state.track].sys_val=val;
+               if (val>0xf7) {
+                  mpu.playbuf[mpu.state.track].type=T_MARK;
+                  if (val==0xf9) mpu.clock.measure_counter=0;
+               } else {
+                  LOG(LOG_MISC,LOG_WARN)("MPU-401:Illegal message");
+                  mpu.playbuf[mpu.state.track].type=T_OVERFLOW;
+               }
+               mpu.state.data_onoff=-1;
+               MPU401_EOIHandler();
+               mpu.state.track_req=false;
+               SDL_mutexV(MPULock);
+               return;
+            default: /* MIDI with running status */
+               cnt++;
+               length=mpu.playbuf[mpu.state.track].length;
+               mpu.playbuf[mpu.state.track].type=T_MIDI_NORM;
             }
+      case 2:
+         if (cnt<length) {
+            mpu.playbuf[mpu.state.track].value[cnt]=val;
+            cnt++;
+         }
+         if (cnt==length) {
+            mpu.state.data_onoff=-1;
+            mpu.state.track_req=false;
+            MPU401_EOIHandlerDispatch();
          }
-         if (!(posd==1 && val>=0xf0)) mpu.playbuf[mpu.state.channel].value[posd-1]=val;
-         if (posd==length) MPU401_EOIHandlerDispatch();
+         break;
    }
+   SDL_mutexV(MPULock);
+   return;
 }
 
-static void MPU401_IntelligentOut(Bit8u chan) {
-   Bitu val;
-   switch (mpu.playbuf[chan].type) {
+static void MPU401_IntelligentOut(Bitu track) {
+   Bitu chan,chrefnum;
+   Bit8u key,msg;
+   bool send,retrigger;
+   switch (mpu.playbuf[track].type) {
       case T_OVERFLOW:
          break;
       case T_MARK:
-         val=mpu.playbuf[chan].sys_val;
-         if (val==0xfc) {
-            MIDI_RawOutByte(val);
-            mpu.state.amask&=~(1<<chan);
-            mpu.state.req_mask&=~(1<<chan);
+         if (mpu.playbuf[track].sys_val==0xfc) {
+            MIDI_RawOutRTByte(mpu.playbuf[track].sys_val);
+            mpu.state.amask&=~(1<<track);
          }
          break;
       case T_MIDI_NORM:
-         for (Bitu i=0;i<mpu.playbuf[chan].vlength;i++)
-            MIDI_RawOutByte(mpu.playbuf[chan].value[i]);
-         break;
-      default:
-         break;
+            chan=mpu.playbuf[track].value[0]&0xf;
+            key=mpu.playbuf[track].value[1]&0x7f;
+            chrefnum=mpu.ch_toref[chan];
+            send=true;
+            retrigger=false;
+         switch (msg=mpu.playbuf[track].value[0]&0xf0) {
+            case 0x80: //note off
+               if (mpu.inputref[chan].on && (mpu.inputref[chan].M_GETKEY)) send=false;
+               if (mpu.chanref[chrefnum].on && (!mpu.chanref[chrefnum].M_GETKEY)) send=false;
+               mpu.chanref[chrefnum].M_DELKEY;
+               break;
+            case 0x90: //note on
+               if (mpu.inputref[chan].on && (mpu.inputref[chan].M_GETKEY)) retrigger=true;
+               if (mpu.chanref[chrefnum].on && (mpu.chanref[chrefnum].M_GETKEY)) retrigger=true;
+               mpu.chanref[chrefnum].M_SETKEY;
+               break;
+            case 0xb0:         
+               if (mpu.playbuf[track].value[1]==123) {/* All notes off */
+                  MPU401_NotesOff(mpu.playbuf[track].value[0]&0xf);
+                  return;
+               }
+               break;
+         }
+         if (retrigger) {
+               MIDI_RawOutByte(0x80|chan,MOUT_MPU);
+               MIDI_RawOutByte(key,MOUT_MPU);
+               MIDI_RawOutByte(0,MOUT_MPU);
+         }
+         if (send) for (Bitu i=0;i<mpu.playbuf[track].length;i++)
+               MIDI_RawOutByte(mpu.playbuf[track].value[i],MOUT_MPU);
    }
 }
 
-static void UpdateTrack(Bit8u chan) {
-   MPU401_IntelligentOut(chan);
-   if (mpu.state.amask&(1<<chan)) {
-      mpu.playbuf[chan].vlength=0;
-      mpu.playbuf[chan].type=T_OVERFLOW;
-      mpu.playbuf[chan].counter=0xf0;
-      mpu.state.req_mask|=(1<<chan);
+static void UpdateTrack(Bit8u track) {
+   MPU401_IntelligentOut(track);
+   if (mpu.state.amask&(1<<track)) {
+      mpu.playbuf[track].type=T_OVERFLOW;
+      mpu.playbuf[track].counter=0xf0;
+      mpu.state.req_mask|=(1<<track);
    } else {
       if (mpu.state.amask==0 && !mpu.state.conductor) mpu.state.req_mask|=(1<<12);
    }
 }
 
-static void UpdateConductor(void) {
-       for (unsigned int i=0;i < mpu.condbuf.vlength;i++) {
-           if (mpu.condbuf.value[i] == 0xfc) {
-               mpu.condbuf.value[i] = 0;
-                   mpu.state.conductor=false;
-                   mpu.state.req_mask&=~(1<<9);
-                   if (mpu.state.amask==0) mpu.state.req_mask|=(1<<12);
-                  return;
-           }
-       }
-
-   mpu.condbuf.vlength=0;
-   mpu.condbuf.counter=0xf0;
-   mpu.state.req_mask|=(1<<9);
-}
-
 static void MPU401_Event(Bitu val) {
    (void)val;//UNUSED
    if (mpu.mode==M_UART) return;
    if (mpu.state.irq_pending) goto next_event;
-   for (Bitu i=0;i<8;i++) { /* Decrease counters */
-      if (mpu.state.amask&(1<<i)) {
-         mpu.playbuf[i].counter--;
-         if (mpu.playbuf[i].counter<=0) UpdateTrack(i);
-      }
-   }      
-   if (mpu.state.conductor) {
-      mpu.condbuf.counter--;
-      if (mpu.condbuf.counter<=0) UpdateConductor();
+   if (mpu.state.playing) {
+      for (Bitu i=0;i<8;i++) { /* Decrease counters */
+         if (mpu.state.amask&(1<<i)) {
+            mpu.playbuf[i].counter--;
+            if (mpu.playbuf[i].counter<=0) UpdateTrack(i);
+         }
+      }
+      if (mpu.state.conductor) {
+         mpu.condbuf.counter--;
+         if (mpu.condbuf.counter<=0) {
+               mpu.condbuf.counter=0xf0;
+               mpu.state.req_mask|=(1<<9);
+         }
+      }
    }
-   if (mpu.clock.clock_to_host) {
+   if (mpu.state.clock_to_host) {
       mpu.clock.cth_counter++;
-      if (mpu.clock.cth_counter >= mpu.clock.cth_rate) {
+      if (mpu.clock.cth_counter >= mpu.clock.cth_rate[mpu.clock.cth_mode]) {
          mpu.clock.cth_counter=0;
+         mpu.clock.cth_mode=(++mpu.clock.cth_mode)%4;
          mpu.state.req_mask|=(1<<13);
       }
    }
-   if (!mpu.state.irq_pending && mpu.state.req_mask) MPU401_EOIHandler();
+   if (mpu.state.rec==M_RECON) { //recording
+      mpu.clock.rec_counter++;
+      if (mpu.clock.rec_counter>=240) {
+         mpu.clock.rec_counter=0;
+         mpu.state.req_mask|=(1<<8);
+      }
+   }
+   bool accented;
+   if (mpu.state.playing || mpu.state.rec==M_RECON) {
+      accented=false;
+      Bits max_meascnt = (mpu.clock.timebase*mpu.clock.midimetro*mpu.clock.metromeas)/24;
+      if (max_meascnt!=0) { //measure end
+         if (++mpu.clock.measure_counter>=max_meascnt) {
+            if (mpu.filter.rt_out) MIDI_RawOutRTByte(0xf8);
+            mpu.clock.measure_counter=0;
+            if (mpu.filter.rec_measure_end && mpu.state.rec==M_RECON)
+               mpu.state.req_mask|=(1<<12);
+         }
+      }
+   }
+   if (!mpu.state.irq_pending && mpu.state.req_mask) {
+      SDL_mutexP(MPULock);
+      MPU401_EOIHandler();
+      SDL_mutexV(MPULock);
+   }
 next_event:
-   Bitu new_time;
-   if ((new_time=(Bitu)((mpu.clock.tempo*mpu.clock.timebase*mpu.clock.tempo_rel)/0x40))==0) return;
-   PIC_AddEvent(MPU401_Event,MPU401_TIMECONSTANT/new_time);
+   MPU401_RunClock();
+   if (mpu.state.sync_in) mpu.clock.ticks_in++;
 }
 
 
@@ -585,15 +951,19 @@ static void MPU401_EOIHandler(Bitu val)
    mpu.state.eoi_scheduled=false;
    if (mpu.state.send_now) {
       mpu.state.send_now=false;
-      if (mpu.state.cond_req) UpdateConductor();
-      else UpdateTrack(mpu.state.channel);
+      if (mpu.state.cond_req) {
+            mpu.condbuf.counter=0xf0;
+            mpu.state.req_mask|=(1<<9);
+      }
+      else UpdateTrack(mpu.state.track);
    }
+   if (mpu.state.rec_copy || !mpu.state.sysex_in_finished) return;
    mpu.state.irq_pending=false;
-   if (!mpu.state.playing || !mpu.state.req_mask) return;
+   if (!(mpu.state.req_mask && mpu.clock.active)) return;
    Bitu i=0;
    do {
       if (mpu.state.req_mask&(1<<i)) {
-         QueueByte(0xf0+i);
+         MPU401_QueueByte(0xf0+i);
          mpu.state.req_mask&=~(1<<i);
          break;
       }
@@ -607,43 +977,287 @@ static void MPU401_ResetDone(Bitu) {
       mpu.state.cmd_pending=0;
    }
 }
+
+static INLINE void MPU401_NotesOff(Bitu i) {
+   if (mpu.filter.allnotesoff_out && !(mpu.inputref[i].on &&
+      (mpu.inputref[i].key[0]|mpu.inputref[i].key[1]|
+      mpu.inputref[i].key[2]|mpu.inputref[i].key[3]))) {
+      for (Bitu j=0;j<4;j++) mpu.chanref[mpu.ch_toref[i]].key[j]=0;
+      //if (mpu.chanref[mpu.ch_toref[i]].on) {
+      MIDI_RawOutByte(0xb0|i,MOUT_MPU);
+      MIDI_RawOutByte(123,MOUT_MPU);
+      MIDI_RawOutByte(0,MOUT_MPU);
+   }
+   else if (mpu.chanref[mpu.ch_toref[i]].on)
+         for (Bitu key=0;key<128;key++) {
+            if ((mpu.chanref[mpu.ch_toref[i]].M_GETKEY) &&
+               !(mpu.inputref[i].on && (mpu.inputref[i].M_GETKEY))) {
+               MIDI_RawOutByte(0x80|i,MOUT_MPU);
+               MIDI_RawOutByte(key,MOUT_MPU);
+               MIDI_RawOutByte(0,MOUT_MPU);
+            }
+         mpu.chanref[mpu.ch_toref[i]].M_DELKEY;
+      }
+}
+
+//Input handler for SysEx
+Bits MPU401_InputSysex(Bit8u* buffer,Bitu len,bool abort) {
+   if (mpu.filter.sysex_in) {
+      if (abort) {
+         mpu.state.sysex_in_finished=true;
+         mpu.rec_queue_used=0;//reset also the input queue
+         return 0;
+      }
+      if (mpu.state.sysex_in_finished) {
+         if (mpu.rec_queue_used>=MPU401_INPUT_QUEUE) return len;
+         Bit8u val_ff=0xff;
+         MPU401_RecQueueBuffer(&val_ff,1,true);
+         mpu.state.sysex_in_finished=false;
+         mpu.clock.rec_counter=0;
+      }
+      if (mpu.rec_queue_used>=MPU401_INPUT_QUEUE) return len;
+      Bitu available=MPU401_INPUT_QUEUE-mpu.rec_queue_used;
+
+      if (available>=len) {
+         MPU401_RecQueueBuffer(buffer,len,true);
+         return 0;
+      }
+      else {
+         MPU401_RecQueueBuffer(buffer,available,true);
+         if (mpu.state.sysex_in_finished) return 0;
+         return (len-available);
+      }
+   }
+   else if (mpu.filter.sysex_thru && mpuhw.midi_thru) {
+      MIDI_RawOutByte(0xf0,MOUT_THRU);
+      for (Bitu i=0;i<len;i++) MIDI_RawOutByte(*(buffer+i),MOUT_THRU);
+   }
+   return 0;
+}
+
+//Input handler for MIDI
+void MPU401_InputMsg(Bit8u msg[4]) {
+   //abort if sysex transfer is in progress
+   if (!mpu.state.sysex_in_finished) return;
+   static Bit8u old_msg=0;
+   Bit8u len=msg[3];
+   bool send=true;
+   bool send_thru=false;
+   bool retrigger_thru=false;
+   bool midistatus=false;
+   if (mpu.mode==M_INTELLIGENT) {
+      if (msg[0]<0x80) {         // Expand running status
+         midistatus=true;
+         msg[2]=msg[1];msg[1]=msg[0];msg[0]=old_msg;
+      }
+      old_msg=msg[0];
+      Bitu chan=msg[0]&0xf;
+      Bitu chrefnum=mpu.ch_toref[chan];
+      Bit8u key=msg[1]&0x7f;
+      if (msg[0]<0xf0) { //if non-system msg
+         if (!(mpu.state.midi_mask&(1<<chan)) && mpu.filter.all_thru) send_thru=true;
+         else if (mpu.filter.midi_thru) send_thru=true;
+         switch (msg[0]&0xf0) {
+            case 0x80: //note off
+               if (send_thru) {
+                  if (mpu.chanref[chrefnum].on && (mpu.chanref[chrefnum].M_GETKEY))
+                     send_thru=false;
+                  if (!mpu.filter.midi_thru) break;
+                  if  (!(mpu.inputref[chan].M_GETKEY)) send_thru=false;
+                   mpu.inputref[chan].M_DELKEY;
+               }
+               break;
+            case 0x90: //note on
+               if (send_thru) {
+                  if (mpu.chanref[chrefnum].on && (mpu.chanref[chrefnum].M_GETKEY))
+                     retrigger_thru=true;
+                  if (!mpu.filter.midi_thru) break;
+                  if (mpu.inputref[chan].M_GETKEY) retrigger_thru=true;
+                  mpu.inputref[chan].M_SETKEY;
+               }
+               break;
+            case 0xb0:
+               if (msg[1]>=120) {
+                  send_thru=false;
+                  if (msg[1]==123)  /* All notes off */
+                     for (key=0;key<128;key++) {
+                        if (!(mpu.chanref[chrefnum].on && (mpu.chanref[chrefnum].M_GETKEY)))
+                           if (mpu.inputref[chan].on && mpu.inputref[chan].M_GETKEY) {
+                              MIDI_RawOutByte(0x80|chan,MOUT_THRU);
+                              MIDI_RawOutByte(key,MOUT_THRU);
+                              MIDI_RawOutByte(0,MOUT_THRU);
+                           }
+                        mpu.inputref[chan].M_DELKEY;
+                     }
+               }
+               break;
+         }
+      }
+      if (msg[0]>=0xf0 || (mpu.state.midi_mask&(1<<chan)))
+         switch (msg[0]&0xf0) {
+            case 0xa0: //aftertouch
+               if (!mpu.filter.bender_in) send=false;
+               break;
+            case 0xb0: //control change
+               if (!mpu.filter.bender_in && msg[1]<64) send=false;
+               if (msg[1]>=120) if (mpu.filter.modemsgs_in) send=true;
+               break;
+            case 0xc0: //program change
+               if (mpu.state.rec!=M_RECON && !mpu.filter.data_in_stop) {
+                     mpu.filter.prchg_buf[chan]=msg[1];
+                     mpu.filter.prchg_mask|=1<<chan;
+                  }
+               break;
+            case 0xd0: //ch pressure
+            case 0xe0: //pitch wheel
+               if (!mpu.filter.bender_in) send=false;
+               break;
+            case 0xf0: //system message
+               if (msg[0]==0xf8) {
+                  send=false;
+                  if (mpu.clock.active && mpu.state.sync_in) {
+                     send = false;//don't pass to host in this mode?
+                     Bits tick=mpu.clock.timebase/24;
+                     if (mpu.clock.ticks_in!=tick) {
+                        if (!mpu.clock.ticks_in || mpu.clock.ticks_in>tick*2) mpu.clock.freq_mod*=2.0;
+                        else {
+                           if (abs(mpu.clock.ticks_in-tick)==1)
+                              mpu.clock.freq_mod/=mpu.clock.ticks_in/float(tick*2);
+                           else
+                              mpu.clock.freq_mod/=mpu.clock.ticks_in/float(tick);
+                        }
+                        MPU401_ReCalcClock();
+                     }
+                     mpu.clock.ticks_in=0;
+                  }
+               }
+               else if (msg[0]>0xf8) { //realtime
+                  if (!(mpu.filter.rt_in && msg[0]<=0xfc && msg[0]>=0xfa)) {
+                     Bit8u recdata[2]={0xff,msg[0]};
+                     MPU401_RecQueueBuffer(recdata,2,true);
+                     send=false;
+                  }
+               }
+               else { //common or system
+                  send=false;
+                  if (msg[0]==0xf2 || msg[0]==0xf3 || msg[0]==0xf6) {
+                     if (mpu.filter.commonmsgs_in) send=true;
+                     if (mpu.filter.commonmsgs_thru)
+                        for (Bitu i=0;i<len;i++) MIDI_RawOutByte(msg[i],MOUT_THRU);
+                  }
+               }
+               if (send) {
+                  Bit8u recmsg[4]={0xff,msg[0],msg[1],msg[2]};
+                  MPU401_RecQueueBuffer(recmsg,len+1,true);
+               }
+               if (mpu.filter.rt_affection) switch(msg[0]) {
+                  case 0xf2:case 0xf3:
+                     mpu.state.block_ack=true;
+                     MPU401_WriteCommand(0x331,0xb8,1);//clear play counters
+                     break;
+                  case 0xfa:
+                     mpu.state.block_ack=true;
+                     MPU401_WriteCommand(0x331,0xa,1);//start,play
+                     if (mpu.filter.rt_out) MIDI_RawOutThruRTByte(msg[0]);
+                     break;
+                  case 0xfb:
+                     mpu.state.block_ack=true;
+                     MPU401_WriteCommand(0x331,0xb,1);//continue,play
+                     if (mpu.filter.rt_out) MIDI_RawOutThruRTByte(msg[0]);
+                     break;
+                  case 0xfc:
+                     mpu.state.block_ack=true;
+                     MPU401_WriteCommand(0x331,0xd,1);//stop: play,rec,midi
+                     if (mpu.filter.rt_out) MIDI_RawOutThruRTByte(msg[0]);
+                     break;
+               }
+               return;
+         }
+      if (send_thru && mpuhw.midi_thru) {
+         if (retrigger_thru) {
+            MIDI_RawOutByte(0x80|(msg[0]&0xf),MOUT_THRU);
+            MIDI_RawOutByte(msg[1],MOUT_THRU);
+            MIDI_RawOutByte(msg[2],MOUT_THRU);
+         }
+         for (Bitu i=0/*((midistatus && !retrigger_thru)? 1:0)*/;i<len;i++)
+            MIDI_RawOutByte(msg[i],MOUT_THRU);
+      }
+      if (send) {
+         if (mpu.state.rec==M_RECON) {
+            Bit8u recmsg[4]={mpu.clock.rec_counter,msg[0],msg[1],msg[2]};
+            MPU401_RecQueueBuffer(recmsg,len+1,true);
+            mpu.clock.rec_counter=0;
+         }
+         else if (mpu.filter.data_in_stop) {
+            if (mpu.filter.timing_in_stop) {
+               Bit8u recmsg[4]={0,msg[0],msg[1],msg[2]};
+               MPU401_RecQueueBuffer(recmsg,len+1,true);
+            }
+            else {
+               Bit8u recmsg[4]={msg[0],msg[1],msg[2],0};
+               MPU401_RecQueueBuffer(recmsg,len,true);
+            }
+         }
+      }
+      return;
+   }
+   //UART mode input
+   for (Bitu i=0;i<len;i++) MPU401_QueueByte(msg[i]);
+}
+
 static void MPU401_Reset(void) {
-   PIC_DeActivateIRQ(mpu.irq);
-   mpu.mode=(mpu.intelligent ? M_INTELLIGENT : M_UART);
    PIC_RemoveEvents(MPU401_EOIHandler);
    mpu.state.eoi_scheduled=false;
-   mpu.state.wsd=false;
-   mpu.state.wsm=false;
-   mpu.state.conductor=false;
-   mpu.state.cond_req=false;
-   mpu.state.cond_set=false;
-   mpu.state.playing=false;
-   mpu.state.run_irq=false;
+   PIC_DeActivateIRQ(mpuhw.irq);
    mpu.state.irq_pending=false;
-   mpu.state.cmask=0xff;
-   mpu.state.amask=mpu.state.tmask=0;
+   memset(&mpu,0,sizeof(mpu));
+   mpu.mode=(mpuhw.intelligent ? M_INTELLIGENT : M_UART);
+   mpuhw.midi_thru=false;
+   mpu.state.rec=M_RECOFF;
    mpu.state.midi_mask=0xffff;
-   mpu.state.data_onoff=0;
-   mpu.state.command_byte=0;
-   mpu.state.block_ack=false;
    mpu.clock.tempo=mpu.clock.old_tempo=100;
    mpu.clock.timebase=mpu.clock.old_timebase=120;
    mpu.clock.tempo_rel=mpu.clock.old_tempo_rel=0x40;
-   mpu.clock.tempo_grad=0;
-   mpu.clock.clock_to_host=false;
-   mpu.clock.cth_rate=60;
-   mpu.clock.cth_counter=0;
-   ClrQueue();
-   mpu.state.req_mask=0;
-   mpu.condbuf.counter=0;
+   mpu.clock.freq_mod=1.0;
+   MPU401_StopClock();
+   MPU401_ReCalcClock();
+   for (Bitu i=0;i<4;i++) mpu.clock.cth_rate[i]=60;
+   mpu.clock.midimetro=12;
+   mpu.clock.metromeas=8;
+   mpu.filter.rec_measure_end=true;
+   mpu.filter.rt_out=true;
+   mpu.filter.rt_affection=true;
+   mpu.filter.allnotesoff_out=true;
+   mpu.filter.all_thru=true;
+   mpu.filter.midi_thru=true;
+   mpu.filter.commonmsgs_thru=true;
+   //reset channel reference and input tables
+   for (Bitu i=0;i<4;i++) {
+      mpu.chanref[i].on=true;
+      mpu.chanref[i].chan=i;
+      mpu.ch_toref[i]=i;
+   }
+   for (Bitu i=0;i<16;i++) {
+      mpu.inputref[i].on=true;
+      mpu.inputref[i].chan=i;
+      if (i>3) mpu.ch_toref[i]=4;//dummy reftable
+   }
+   MPU401_ClrQueue();
+   mpu.state.data_onoff=-1;
    mpu.condbuf.type=T_OVERFLOW;
-   for (Bitu i=0;i<8;i++) {mpu.playbuf[i].type=T_OVERFLOW;mpu.playbuf[i].counter=0;}
+   for (Bitu i=0;i<8;i++) mpu.playbuf[i].type=T_OVERFLOW;
+   //clear MIDI buffers, terminate notes
+   MIDI_ClearBuffer(MOUT_MPU);
+   MIDI_ClearBuffer(MOUT_THRU);
+   for (Bitu i=0xb0;i<=0xbf;i++){
+      MIDI_RawOutByte(i,MOUT_MPU);
+      MIDI_RawOutByte(0x7b,MOUT_MPU);
+      MIDI_RawOutByte(0,MOUT_MPU);
+   }
 }
 
 class MPU401:public Module_base{
 private:
-   IO_ReadHandleObject ReadHandler[2];
-   IO_WriteHandleObject WriteHandler[2];
    bool installed; /*as it can fail to install by 2 ways (config and no midi)*/
 public:
    MPU401(Section* configuration):Module_base(configuration){
@@ -657,28 +1271,34 @@ public:
       /*Enabled and there is a Midi */
       installed = true;
       
-      WriteHandler[0].Install(0x330,&MPU401_WriteData,IO_MB);
-      WriteHandler[1].Install(0x331,&MPU401_WriteCommand,IO_MB);
-      ReadHandler[0].Install(0x330,&MPU401_ReadData,IO_MB);
-      ReadHandler[1].Install(0x331,&MPU401_ReadStatus,IO_MB);
+      MPULock=SDL_CreateMutex();
+
+      mpuhw.WriteHandler[0].Install(0x330,&MPU401_WriteData,IO_MB);
+      mpuhw.WriteHandler[1].Install(0x331,&MPU401_WriteCommand,IO_MB);
+      mpuhw.ReadHandler[0].Install(0x330,&MPU401_ReadData,IO_MB);
+      mpuhw.ReadHandler[1].Install(0x331,&MPU401_ReadStatus,IO_MB);
    
       mpu.queue_used=0;
       mpu.queue_pos=0;
+      mpu.state.irq_pending=false;
       mpu.mode=M_UART;
-      mpu.irq=9;   /* Princess Maker 2 wants it on irq 9 */
+      mpuhw.irq=9;   /* Princess Maker 2 wants it on irq 9 */
 
-      mpu.intelligent = true;   //Default is on
-      if(strcasecmp(s_mpu,"uart") == 0) mpu.intelligent = false;
-      if (!mpu.intelligent) return;
+      mpuhw.intelligent = true;   //Default is on
+      if(strcasecmp(s_mpu,"uart") == 0) mpuhw.intelligent = false;
+      if (!mpuhw.intelligent) return;
       /*Set IRQ and unmask it(for timequest/princess maker 2) */
-      PIC_SetIRQMask(mpu.irq,false);
+      PIC_SetIRQMask(mpuhw.irq,false);
       MPU401_Reset();
    }
    ~MPU401(){
       if(!installed) return;
+      PIC_RemoveEvents(MPU401_Event);
+      SDL_DestroyMutex(MPULock);
+      MPULock=0;
       Section_prop * section=static_cast<Section_prop *>(m_configuration);
       if(strcasecmp(section->Get_string("mpu401"),"intelligent")) return;
-      PIC_SetIRQMask(mpu.irq,true);
+      PIC_SetIRQMask(mpuhw.irq,true);
       }
 };
 
hail-to-the-ryzen
Member
 
Posts: 204
Joined: 2017-3-09 @ 01:34

Re: DOSBox-X branch

Postby hail-to-the-ryzen » 2018-7-02 @ 08:58

I haven't verified where these mode lines are accurate to S3 specification, but they support the modes below in DOS and in Windows 95 OSR2 with the 9x Vesa driver.
Code: Select all
{ 0x15C ,M_LIN8,512 ,384 ,64 ,48 ,8, 8  ,1 ,0xA0000 ,0x10000,168 ,806 ,128,768 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
{ 0x159 ,M_LIN8,400 ,300 ,50 ,37 ,8 ,8  ,1 ,0xA0000 ,0x10000,132 ,628 ,100,600 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
{ 0x15D ,M_LIN16,512 ,384 ,64 ,48 ,8, 16 ,1 ,0xA0000 ,0x10000,168 ,806 ,128,768 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
{ 0x15A ,M_LIN16,400 ,300 ,50 ,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,132 ,628 ,100,600 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
hail-to-the-ryzen
Member
 
Posts: 204
Joined: 2017-3-09 @ 01:34

Re: DOSBox-X branch

Postby TheGreatCodeholio » 2018-7-02 @ 16:57

hail-to-the-ryzen wrote:I haven't verified where these mode lines are accurate to S3 specification, but they support the modes below in DOS and in Windows 95 OSR2 with the 9x Vesa driver.
Code: Select all
{ 0x15C ,M_LIN8,512 ,384 ,64 ,48 ,8, 8  ,1 ,0xA0000 ,0x10000,168 ,806 ,128,768 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
{ 0x159 ,M_LIN8,400 ,300 ,50 ,37 ,8 ,8  ,1 ,0xA0000 ,0x10000,132 ,628 ,100,600 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
{ 0x15D ,M_LIN16,512 ,384 ,64 ,48 ,8, 16 ,1 ,0xA0000 ,0x10000,168 ,806 ,128,768 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },
{ 0x15A ,M_LIN16,400 ,300 ,50 ,37 ,8 ,16 ,1 ,0xA0000 ,0x10000,132 ,628 ,100,600 , _VGA_PIXEL_DOUBLE | _EGA_LINE_DOUBLE },


Just added those, thanks!
DOSBox-X project: more emulation better accuracy.
DOSLIB and DOSLIB2: Learn how to tinker and hack hardware and software from DOS.
User avatar
TheGreatCodeholio
Oldbie
 
Posts: 511
Joined: 2011-8-18 @ 20:15
Location: Seattle, WA

Re: DOSBox-X branch

Postby TheGreatCodeholio » 2018-7-15 @ 21:44

I've just started adding a VERY BASIC emulation load/save state system to DOSBox-X. In it's current form, it saves/loads system memory, VGA memory, basic CPU state, and the VGA palette. It's enough to save-state games without crashing like Jill of the Jungle and Cosmo's Cosmic Adventures. Wolfenstein 3D works but the wall textures come out messed up after loading saved state. Obviously it's far from complete, but the foundation is set for it to further develop.

I'm also curious if anyone here is interested if I were to take some small DOSBox-X features (like "goldplay mode") and add them to SVN as a patch?

Regarding DOSBox SVN, I just tested the latest SVN commit against some post-1996 demoscene stuff and it seems like DOSBox SVN crashes too easily with "FPU stack underflow" when running these demos. Did something happen to the FPU emulation and normal core?
DOSBox-X project: more emulation better accuracy.
DOSLIB and DOSLIB2: Learn how to tinker and hack hardware and software from DOS.
User avatar
TheGreatCodeholio
Oldbie
 
Posts: 511
Joined: 2011-8-18 @ 20:15
Location: Seattle, WA

Re: DOSBox-X branch

Postby hail-to-the-ryzen » 2018-7-15 @ 23:05

hail-to-the-ryzen
Member
 
Posts: 204
Joined: 2017-3-09 @ 01:34

Previous

Return to DOSBox Patches

Who is online

Users browsing this forum: No registered users and 1 guest