'*****************************************************************************
'*                                                                           *
'*   This program was written as a demonstration of using the MK Music       *
'*     Loader / Player in a program that does a moderate amount of           *
'*  calculations.  This is not designed to be the revolutionary graphics     *
'*                     demonstration or anything.                            *
'*                                                                           *
'*   This program was written by Molnar \ Kucalaba Productions in 1996       *
'*                                                                           *
'*****************************************************************************

DECLARE SUB Main ()
DECLARE SUB SBInit ()
DECLARE SUB PlayMusic ()
DECLARE SUB InitStars ()
DECLARE SUB CloseChannels ()
DECLARE SUB SetIns (Channel%)
DECLARE SUB WriteReg (Reg%, value%)
DECLARE FUNCTION LoadJamFile% (File$)


CONST BasePort% = &H220  ' Change this for other sound cards.  It is most
                         ' likely 220h.

CLS
SCREEN 13

'$DYNAMIC
RANDOMIZE TIMER

TYPE StarType
 X AS INTEGER
 Y AS INTEGER
 Speed AS INTEGER
END TYPE

DIM SHARED MaxChannel%, MaxNotes%

CONST MaxPlay% = -1

TYPE InsType
 MMult AS INTEGER
 MLevel AS INTEGER
 MAttack AS INTEGER
 MSustain AS INTEGER
 CMult AS INTEGER
 CLevel AS INTEGER
 CAttack AS INTEGER
 CSustain AS INTEGER
END TYPE

TYPE ChannelType
 Defined AS INTEGER
 Speed AS SINGLE
 SongPtr AS INTEGER
 PStat AS SINGLE
 Octave AS INTEGER
 WaveForm AS INTEGER
 Flag AS INTEGER
 Flag2 AS INTEGER
 IsPlaying AS INTEGER
 IsDone AS INTEGER
END TYPE



DIM SHARED ChannelStat(1 TO 2) AS ChannelType
DIM SHARED Songbuf(1 TO 2) AS INTEGER
DIM SHARED CurrentIns AS InsType

DIM SHARED Stars(100) AS StarType

SBInit
InitStars

ResCode% = LoadJamFile("mobydick.mkj")

IF ResCode% <> 1 THEN SYSTEM


Main

SBInit

SYSTEM

REM $STATIC
SUB CloseChannels
FOR Channel% = 0 TO 3
 WriteReg &HB0 + Channel%, 0
NEXT
FOR Channel% = 4 TO 9
 WriteReg &HB0 + Channel% + 6, 0
NEXT
END SUB

SUB InitStars
FOR Fill% = 1 TO 100
 Stars(Fill%).X = INT(RND * 320) + 1
 Stars(Fill%).Y = INT(RND * 200) + 1
 Stars(Fill%).Speed = INT(RND * 5) + 1
NEXT

'Make the palette coincide with the star speed
FOR X% = 1 TO 5
  OUT &H3C8, X%

    OUT &H3C9, X% * 12
    OUT &H3C9, X% * 12
    OUT &H3C9, X% * 12

NEXT
END SUB

FUNCTION LoadJamFile% (File$)

DIM MKByte AS STRING * 1

OPEN File$ FOR BINARY AS #1

 IF LOF(1) < 2 THEN
  CLOSE #1
  KILL File$
  LoadJamFile% = -1
  EXIT FUNCTION
 END IF
 FOR X% = 1 TO 6
  GET #1, , MKByte
  Id$ = Id$ + MKByte
 NEXT

 IF Id$ <> "MKJamz" THEN
   LoadJamFile% = -2
   EXIT FUNCTION
 END IF

 GET #1, , Version!
 IF Version! > 1.1 THEN
  LoadJamFile% = -3
  EXIT FUNCTION
 END IF

 GET #1, , MaxChannel%
  FOR X% = 1 TO MaxChannel%
    GET #1, , CurrentIns
    Save% = X%
    SetIns X%
    X% = Save%
   NEXT
 GET #1, , MaxNotes%

  REDIM Songbuf(1 TO (MaxChannel% + 1) * MaxNotes%) AS INTEGER
  REDIM ChannelStat(1 TO MaxChannel%) AS ChannelType

  FOR X% = 1 TO MaxChannel%
    GET #1, , ChannelStat(X%).Defined
    ChannelStat(X%).SongPtr = 1
    ChannelStat(X%).Octave = 4
  NEXT

  FOR X% = 1 TO UBOUND(Songbuf)
    GET #1, , Songbuf(X%)
  NEXT
CLOSE #1
LoadJamFile% = 1
END FUNCTION

SUB Main

DO
 
  FOR X% = 1 TO 100
    PSET (Stars(X%).X, Stars(X%).Y), 0
    Stars(X%).X = Stars(X%).X + Stars(X%).Speed
      IF Stars(X%).X > 320 THEN
        Stars(X%).X = 0
        Stars(X%).Y = INT(RND * 200) + 1
      END IF
    PSET (Stars(X%).X, Stars(X%).Y), Stars(X%).Speed
  NEXT

 PlayMusic

LOOP UNTIL INKEY$ <> ""

END SUB

SUB PlayMusic


FOR Channel% = 1 TO MaxChannel%

  IF ChannelStat(Channel%).Defined = 0 THEN GOTO NoGotosPlease

  RChan% = Channel% - 1

  IF ChannelStat(Channel%).IsDone = 1 THEN
   ChannelStat(Channel%).PStat = TIMER + ChannelStat(Channel%).Speed
   ChannelStat(Channel%).SongPtr = ChannelStat(Channel%).SongPtr + MaxChannel%
   ChannelStat(Channel%).IsDone = 0
   ChannelStat(Channel%).IsPlaying = 0
  END IF

   IF ChannelStat(Channel%).IsPlaying = 0 AND Songbuf(ChannelStat(Channel%).SongPtr + Channel% - 1) <> 0 THEN
    ChannelStat(Channel%).PStat = TIMER + ChannelStat(Channel%).Speed
     SELECT CASE Songbuf(ChannelStat(Channel%).SongPtr + RChan%)
       CASE 68   ' "D"
         WriteReg &HA0 + RChan%, &H81
         WriteReg &HB0 + RChan%, &H21 + 4 * ChannelStat(Channel%).Octave
       CASE 69   ' "E"
         WriteReg &HA0 + RChan%, &HB0
         WriteReg &HB0 + RChan%, &H21 + 4 * ChannelStat(Channel%).Octave
       CASE 70   ' "F"
         WriteReg &HA0 + RChan%, &HCA
         WriteReg &HB0 + RChan%, &H21 + 4 * ChannelStat(Channel%).Octave
       CASE 71    ' "G"
         WriteReg &HA0 + RChan%, &H2
         WriteReg &HB0 + RChan%, &H22 + 4 * ChannelStat(Channel%).Octave
       CASE 65 ' "A"
         WriteReg &HA0 + RChan%, &H41
         WriteReg &HB0 + RChan%, &H22 + 4 * ChannelStat(Channel%).Octave
       CASE 66   ' "B"
         WriteReg &HA0 + RChan%, &H87
         WriteReg &HB0 + RChan%, &H22 + 4 * ChannelStat(Channel%).Octave
       CASE 67  '  "C"
         WriteReg &HA0 + RChan%, &HAE
         WriteReg &HB0 + RChan%, &H22 + 4 * ChannelStat(Channel%).Octave
       CASE 17 ' "C#"
         WriteReg &HA0 + RChan%, &H6B
         WriteReg &HB0 + RChan%, &H21 + 4 * ChannelStat(Channel%).Octave
       CASE 18 ' "D#"
         WriteReg &HA0 + RChan%, &H98
         WriteReg &HB0 + RChan%, &H21 + 4 * ChannelStat(Channel%).Octave
       CASE 20 ' F#"
         WriteReg &HA0 + RChan%, &HE5
         WriteReg &HB0 + RChan%, &H21 + 4 * ChannelStat(Channel%).Octave
       CASE 21 ' "G#"
         WriteReg &HA0 + RChan%, &H20
         WriteReg &HB0 + RChan%, &H22 + 4 * ChannelStat(Channel%).Octave
       CASE 15 ' "A#"
         WriteReg &HA0 + RChan%, &H63
         WriteReg &HB0 + RChan%, &H22 + 4 * ChannelStat(Channel%).Octave
       
       CASE 255
         ChannelStat(Channel%).IsPlaying = 1
         ChannelStat(Channel%).PStat = TIMER + Songbuf(ChannelStat(Channel%).SongPtr + Channel% - 1 + MaxChannel%) / 100
         ChannelStat(Channel%).Flag = -1
       CASE 254
         ChannelStat(Channel%).Octave = Songbuf(ChannelStat(Channel%).SongPtr + Channel% - 1 + MaxChannel%)
         ChannelStat(Channel%).SongPtr = ChannelStat(Channel%).SongPtr + MaxChannel%
       CASE 253
         ChannelStat(Channel%).Speed = Songbuf(ChannelStat(Channel%).SongPtr + Channel% - 1 + MaxChannel%) / 100
         ChannelStat(Channel%).SongPtr = ChannelStat(Channel%).SongPtr + MaxChannel%
       CASE 252
         ChannelStat(Channel%).WaveForm = Songbuf(ChannelStat(Channel%).SongPtr + Channel% - 1 + MaxChannel%) - 300
         ChannelStat(Channel%).SongPtr = ChannelStat(Channel%).SongPtr + MaxChannel%
         IF RChan% > 2 THEN RRChan% = RChan% + 6 ELSE RRChan% = 0
         WriteReg &HE0 + RChan% + RRChan%, ChannelStat(Channel%).WaveForm
       CASE 251
         ChannelStat(1).Flag2 = -1
          FOR MaxChan% = 1 TO MaxChannel%
           ChannelStat(MaxChan%).SongPtr = 1
           ChannelStat(MaxChan%).IsPlaying = 0
           ChannelStat(MaxChan%).IsDone = 0
          NEXT
         CloseChannels
         EXIT SUB
     END SELECT
   END IF

    IF ChannelStat(Channel%).IsPlaying = 1 THEN
     IF TIMER >= ChannelStat(Channel%).PStat THEN
      WriteReg &HB0 + RChan%, 0
      ChannelStat(Channel%).IsDone = 1
       IF ChannelStat(Channel%).Flag = -1 THEN
        ChannelStat(Channel%).SongPtr = ChannelStat(Channel%).SongPtr + MaxChannel%
        ChannelStat(Channel%).Flag = 0
       END IF
     END IF
    END IF
  
   IF ChannelStat(Channel%).SongPtr > MaxChannel% + 1 THEN
     IF Songbuf(ChannelStat(Channel%).SongPtr + RChan%) <> 0 AND Songbuf(ChannelStat(Channel%).SongPtr - MaxChannel% + RChan%) < 250 THEN
       IF ChannelStat(Channel%).IsPlaying = 0 THEN ChannelStat(Channel%).IsPlaying = 1
     END IF
   END IF

NoGotosPlease:
NEXT Channel%


FOR Channel% = 1 TO MaxChannel%

 IF ChannelStat(Channel%).IsPlaying = 0 THEN
  ChannelStat(Channel%).SongPtr = ChannelStat(Channel%).SongPtr + MaxChannel%
  IF ChannelStat(Channel%).SongPtr >= UBOUND(Songbuf) / MaxChannel% THEN
   ChannelStat(Channel%).SongPtr = 1
  END IF
 END IF
NEXT Channel%


END SUB

SUB SBInit
   FOR z% = 1 TO &HF5
     WriteReg z%, 0
   NEXT z%
END SUB

SUB SetIns (Channel%)

Channel% = Channel% - 1
CarChan% = Channel%
ModChan% = Channel%

IF Channel% >= 3 THEN
 CarChan% = CarChan% + 5
 ModChan% = ModChan% + 5
END IF

IF Channel% >= 6 THEN
 CarChan% = CarChan% + 5
 ModChan% = ModChan% + 5
END IF

WriteReg &H20 + CarChan%, CurrentIns.CMult  'Plays carrier note at specified octave ch. 1
WriteReg &H23 + ModChan%, CurrentIns.MMult  'Plays modulator note at specified octave ch. 1
WriteReg &H40 + CarChan%, CurrentIns.CLevel   'Set carrier total level to softest ch. 1
WriteReg &H43 + ModChan%, CurrentIns.MLevel   'Set modulator level to loudest ch. 1
WriteReg &H60 + CarChan%, CurrentIns.CAttack  'Set carrier attack and decay ch. 1
WriteReg &H63 + ModChan%, CurrentIns.MAttack  'Set modulator attack and decay ch. 1
WriteReg &H80 + CarChan%, CurrentIns.CSustain 'Set carrier sustain and release ch. 1
WriteReg &H83 + ModChan%, CurrentIns.MSustain 'Set modulator sustain and release ch. 1
END SUB

DEFINT A-Z
SUB WriteReg (Reg%, value%)
OUT BasePort% + 8, Reg%
 FOR V% = 1 TO 6
  Buf% = INP(BasePort% + 8)
 NEXT
OUT BasePort% + 9, value%
 FOR V% = 1 TO 34
  Buf% = INP(BasePort% + 9)
 NEXT

END SUB

