'===========================================================================
' Subject: PLAY ADLIB W/TEMPO COMMAND         Date: 11-10-97 (00:29)       
'  Author: Andrew S. Gibson                   Code: QB, QBasic, PDS        
'  Origin: zapf_dingbat@juno.com            Packet: SOUND.ABC
'===========================================================================
DEFINT A-Z
DECLARE SUB PlayAdLib (comand$)
DECLARE SUB WriteReg (reg AS INTEGER, value AS INTEGER)

'You say you wanted SoundBlaser code?  I've written a SUB that works with
'an AdLib-compatible speaker.  I think a SoundBlaster works too (at least,
'mine does).  It is very easy to use.  It uses the same command string that
'the PLAY command does.  The only difference is that at the top of your
'program you have to do a

PlayAdLib "{INIT}"

'to set the variables.  Here's the code:

'(The WriteReg SUB is needed for PlayAdLib to work.)

'I have a few other SUBs on my WWW site.  The address is in my signature.
'Glad I could help.

'- Borg953@aol.com
'  http://home.aol.com/Borg953

' Zapf_DingBat@Juno.com
' Play Adlib version 1.2 (?) - added tempo command !

PlayAdLib "T255L64O2ADFEGBBGEFDA"

SUB PlayAdLib (comand$)
  STATIC tempo, length, music, octave

  IF comand$ = "{INIT}" THEN
    tempo = 120
    length = 4
    music = 7 / 8
    octave = 4
    EXIT SUB
  END IF

  comand$ = UCASE$(comand$)
  FOR i% = 0 TO 224
    WriteReg i%, 0 'Clear all registers
  NEXT i%
  WriteReg &H20, &H1 'Plays carrier note at specified octave ch. 1
  WriteReg &H23, &H1 'Plays modulator note at specified octave ch. 1
  WriteReg &H40, &H1F 'Set carrier total level to softest ch. 1
  WriteReg &H43, &H0 'Set modulator level to loudest ch. 1
  WriteReg &H60, &HE4 'Set carrier attack and decay ch. 1
  WriteReg &H63, &HE4 'Set modulator attack and decay ch. 1
  WriteReg &H80, &H9D 'Set carrier sustain and release ch. 1
  WriteReg &H83, &H9D 'Set modulator sustain and release ch. 1

  C% = 1
  max% = LEN(comand$)
  WHILE C% <= max%
    x$ = MID$(comand$, C%, 1): C% = C% + 1    'extract subcommand
    IF INSTR("ABCDEFG", x$) <> 0 AND (C% <> max% AND INSTR("+#-", MID$(comand$, C%, 1))) THEN
      x$ = x$ + MID$(comand$, C%, 1): C% = C% + 1
      IF RIGHT$(x$, 1) = "+" THEN x$ = LEFT$(x$, 1) + "#"
      IF x$ = "D-" THEN x$ = "C#"
      IF x$ = "E-" THEN x$ = "D#"
      IF x$ = "G-" THEN x$ = "F#"   'convert all flats into equivalent sharps
      IF x$ = "A-" THEN x$ = "G#"
      IF x$ = "B-" THEN x$ = "A#"
    ELSEIF x$ = "O" THEN
      adj% = VAL(MID$(comand$, C%, 1)): C% = C% + 1
      IF adj% >= 0 AND adj% <= 6 THEN octave = adj%
    ELSEIF x$ = "<" AND octave > 0 THEN
      octave = octave - 1
    ELSEIF x$ = ">" AND octave < 6 THEN
      octave = octave + 1
    ELSEIF x$ = "T" THEN
      num$ = ""
      WHILE INSTR("0123456789", MID$(comand$, C%, 1))
        num$ = num$ + MID$(comand$, C%, 1): C% = C% + 1
      WEND
      IF VAL(LTRIM$(num$)) >= 1 AND VAL(LTRIM$(num$)) <= 255 THEN tempo = VAL(LTRIM$(num$))
    ELSEIF x$ = "L" THEN
      num$ = ""
      WHILE INSTR("0123456789", MID$(comand$, C%, 1))
        num$ = num$ + MID$(comand$, C%, 1): C% = C% + 1
      WEND
      IF VAL(LTRIM$(num$)) >= 1 AND VAL(LTRIM$(num$)) <= 64 THEN length = VAL(LTRIM$(num$))
    ELSEIF x$ = "P" THEN
      dotfac = 1
      num$ = ""
      WHILE INSTR("0123456789", MID$(comand$, C%, 1))
        num$ = num$ + MID$(comand$, C%, 1): C% = C% + 1
      WEND
      WHILE MID$(comand$, C%, 1) = "."
        dotfac = dotfac * 1.5: C% = C% + 1
      WEND
      IF VAL(num$) >= 1 AND VAL(num$) <= 64 THEN
        start! = TIMER
        ender! = start! + (((1 / VAL(num$)) * dotfac) * ((tempo / 120) * 2) * music)
        DO
        LOOP UNTIL TIMER >= ender!  'start! + (1 / VAL(num$))
      END IF
    ELSEIF x$ = "M" THEN
      next$ = MID$(comand$, C%, 1): C% = C% + 1
      x$ = x$ + next$
      IF x$ = "MN" THEN music = 7 / 8
      IF x$ = "MS" THEN music = 3 / 4
      IF x$ = "ML" THEN music = 1

    END IF
    IF INSTR("ABCDEFG", LEFT$(x$, 1)) THEN
      dotfac = 1
      WHILE MID$(comand$, C%, 1) = "."
        dotfac = dotfac * 1.5: C% = C% + 1
      WEND
      SELECT CASE x$
      CASE "C#"
        WriteReg &HA0, &H6B                 'Set note number
        WriteReg &HB0, &H21 + 4 * octave    'Set octave and turn on voice
      CASE "D"
        WriteReg &HA0, &H81
        WriteReg &HB0, &H21 + 4 * octave
      CASE "D#"
        WriteReg &HA0, &H98
        WriteReg &HB0, &H21 + 4 * octave
      CASE "E"
        WriteReg &HA0, &HB0
        WriteReg &HB0, &H21 + 4 * octave
      CASE "F"
        WriteReg &HA0, &HCA
        WriteReg &HB0, &H21 + 4 * octave
      CASE "F#"
        WriteReg &HA0, &HE5
        WriteReg &HB0, &H21 + 4 * octave
      CASE "G"
        WriteReg &HA0, &H2
        WriteReg &HB0, &H22 + 4 * octave
      CASE "G#"
        WriteReg &HA0, &H20
        WriteReg &HB0, &H22 + 4 * octave
      CASE "A"
        WriteReg &HA0, &H41
        WriteReg &HB0, &H22 + 4 * octave
      CASE "A#"
        WriteReg &HA0, &H63
        WriteReg &HB0, &H22 + 4 * octave
      CASE "B"
        WriteReg &HA0, &H87
        WriteReg &HB0, &H22 + 4 * octave
      CASE "C"
        WriteReg &HA0, &HAE
        WriteReg &HB0, &H22 + 4 * (octave - 1)
      END SELECT
      start! = TIMER
      ender! = start! + (((1 / length) * dotfac) * (((ABS(NOT tempo AND 255)) / 120) * 2) * music)
      DO
      LOOP UNTIL TIMER >= ender!  '(start! + (1 / length))
      WriteReg &HB0, 0
    END IF
  WEND
END SUB

SUB WriteReg (reg AS INTEGER, value AS INTEGER)
                      '&H is QBASIC hexadecimal prefix
  OUT &H388, reg      '&H388 is AdLib register/status port
                      'tells what register to write to
 
  FOR C% = 0 TO 5     'reading hardware port 6 times creates manditory 3.3 ms
    a% = INP(&H388)   'delay
  NEXT C%

  OUT &H389, value    '&H389 is AdLib data port
                      'sends data to the register specified above
                     
  FOR C% = 0 TO 34    'reading reg/stat port 35 times creates manditory
    a% = INP(&H388)   '23 ms delay
  NEXT C%
END SUB
