Conversion of mci midi sequencer.
Originals on \\sysuk1\data\win31mm\src\sonic\mciseq





List utility
------------
    LIST     H        1238   - List management definitions
    LIST     C        5648   - Memory management

    SUPPORT  C                 ** NEW FOR NT ** (replace .asm etc).

Common header file
------------------
    MMSYS    H        7810   - Common definitions for MCI and SEQ

MCI processing
--------------
    MCISEQ   H        5410   - Definitions for MCI part of driver
    MCICMDS  C       23354   - Commands from mciDriverEntry - msXxx
    MCISEQ   C       36751   - DriverProc -> mciDriverEntry
    CALLBACK C        2147   - Sequencer callback
    FORMATS  C       10845   - Time formats
      -- Convert to passing around MMTIME structures ?

Sequencer
---------
    MMSEQI   H       13524   - Definitions local to mmseq.c (internal sequencer)
    MMSEQ    C       63220   - midiSeqMessage - the sequencer
    MMSEQ    D        3116   - Doc
    UTIL     C       21200   - callback, track and timer helpers for sequencer

Other bits (not reqd for NT)
----------------------------
    TASK     ASM      2856   - interface to mmTask (why ?)
    CRIT     ASM      1895   - critical sections
    MCISEQ   DEF       304   - def file


DATA FLOW
==================================================================


  MCI            (MMSYS.H)               SEQ

               midiSeqMessage
                (SEQ_...)
            --------------------->

                MIDISEQINFO

                MIDISEQOPEN

                MIDISEQHDR


                MIDISEQCALLBACK
                (MIDISEQ_...)
                         DONE     -> TaskSignal - TO stream task
                         DONEPLAY -> TaskSignal - TO stream task
                         RESET    -> StreamTrackReset -> TaskSignal

            <---------------------

                MIDISEQERR_...
            <---------------------


==================================================================



Notes :

   mciGetDriverData returns a pointer to the stream - SeqStreamType so
   the stream is equated to a logical device.

   GETMOTWORD = 'Get Motorola word' (!)

   Within the timer interrupt routine use the critical section to get
   the other side of the mutual exclusion.

   SERIALIZATION :  A critical section is used which is held over calls
   to mciDriverEntry and in the sequencer tasks but released when
   getting messages (blocking) or temporarily while yielding.


STRUCTURE
=========

DRV_... + MCI_... messages
  (mciseq.c!DriverEntry)

       |
       V

MCI messages :
  (mciseq.c!mciDriverEntry)


    MCI_OPEN_DRIVER
      mcicmds.c!msOpen

    MCI_CLOSE_DRIVER
      mcicmds.c!msClose

    MCI_PLAY
      mcicmds.c!msPlay

    MCI_PAUSE
    MCI_STOP
      mmseq.c!midiSeqMessage SEQ_SETPORTOFF

    MCI_SEEK
      mcicmds.c!msSeek

    MCI_STATUS
      mcicmds.c!msStatus

    MCI_GETDEVCAPS
      mcicmds.c!msGetDevCaps

    MCI_INFO
      mcicmds.c!msInfo

    MCI_SET
      mcicmds.c!msSet


    Rest unsupported


MMSEQ messages :
    (mmseq.c!midiSeqMessage)

    SEQ_OPEN

....................................................................


Tasking structure :
-------------------


 Application task :
    mcicmds.c!msOpen
         |
         V
    mciseq.c!msOpenStream -> mmTaskCreate with routine mciStreamCycle
                         |
                         V
                    TaskSignal
                    PostAppMessage
                         .
                         . WTM_QUITTASK, WTM_DONEPLAY, WTM_FILLBUFFER
                         .
 Sequencer task :

                    GetMessage
                    TaskBlock
                         |
                         V
               mciseq.c!mciStreamCycle
                         |
                         V
               mmseq.c!midiSeqMessage - SEQ_TRACKDATA, SEQ_SETPORTOFF
                                            |          SEQ_GETINFO
                                            V
                                        NewTrackData


mciStreamCycle just keeps the buffers for the current stream stoked up.

Thus : there is 1 (extra) task PER LOGICAL DEVICE.
There is 1 SEQ structure per task and 1 stream structure per task (which
contains an HMIDISEQ handle hSeq to the SEQ structure).  The SEQ
structure remembers the HTASK.


The SeqStreamType structure is the per-stream data on the MCI side.
This is unfortunately shared between application and stream tasks.


There is no need to inhibit threading between sequencer tasks, only
between the main task and associated sequencer tasks.

Use of Yield etc :

    mciseq.c!msOpenStream - appears to have a spurious yield near the start

    mcicmds.c!msOpen - calls Yield to allow the stream task to start
        properly.  We need to force this to happen now.  Note that this
        is subtle because multiple people might be running - so we
        probably need a special event per task.

    mcicmds.c!msPlay - sits in a loop at the moment !  Need the event
                       again.

    mmseq NEVER yields or blocks

    mciseq.c!mciStreamCycle :
           TaskBlock near start when finished init - other side of
           Yield in msOpen.  Also later Blocks again for more work.
           This is the other side of msPlay ?

           mmTaskYeild - probably unnecessary since nothing was kicked off ?

......................................................................


MIDISEQ_DONE


Timers :
========

util.c!OneShotTimer has pointer to NPSEQ structure passed to it.
         |
         --> mmseq.c!TimerIntRoutine
         |
         |
util.c!MIDICallback

Conversion Issues

    tasking
    File format
      Seems OK because :

      1. mmio... is used to get at the chunks
      2. The data is in big-endian format and macros are used which will
         work for either format (GETMODWORD, GETMOTDWORD etc).

    file access

      OK - via MMIO

    parameter sizes
    api
    build
    globals
      list.c - arrayOfLists

    window messages
    alignment / structure packing / structure overlays



Comments :
==========

1. This code is essentially uncommented.  On the positive side the
   structure is reasonably clean (though overcomplex and relying
   on 'messages' (untyped calls) rather than subroutine calls which
   means many bugs will get by the compiler into runtime code.

BUGS
====

Pointer to MMIOPROC in mcicmds.c should be pointer to pointer to.
