'===========================================================================
' Subject: SOUND BLASTER TO DISK              Date: 11-10-97 (00:29)       
'  Author: Andrew S. Gibson                   Code: QB, QBasic, PDS        
'  Origin: zapf_dingbat@juno.com            Packet: SOUND.ABC
'===========================================================================
'Hello fellow coders !  This little program is a cheap efficient way to
'*record* incoming audio from your Sound Blaster's Line-in or Microphone jack
'is you don't have an internal CD player or you want to use a cassette
'player. *You won't need to turn up the input volume very much at all !*
'This started as an experiment, but I turned it into a relatively viable
'application. You need to load QuickBASIC with this switch /L, compile
'Sound Blaster to Disk for extremely optimal speed ! ý
'I've tested this using a 386sx running at 25Mhz and (it didn't work so well
'but don't let that stop you from trying to record :)
'With My 486sx 33mhz system I been able to achieve a sampling rate of 10,250
'Hertz this is satisfatory and the file size was 2.167 Megabytes (that's
'standard for a nearly four minute song.)
'I highly recommend that You shouldn't alter any of the code, until you
'try it!
'Email me at this address if you desire: Zapf_DingBat@JUNO.COM
'My real name is Andrew Gibson and I wrote 25% of the code !
'Not the sound blaster dectection code though..
'I have tested this with a Sound Blaster 2.0 (the only sound card I have!)
'A 486sx 33Mhz based system is recommended and a very *fast* hardrive, try it
'wherever/whenever you can though !  Windows Protected mode really slows
'this down especially if you have applications is use !  EMM386 on a slow
'computers will make this program drag !
'
' Sound Blaster to Disk *Won't* stop recording unless to press any key, fill
' your remaining hardrive space or it stops because of an error.
'Once you compile this type SB2DSK /? for help.
'Release 1 / Update 2

DEFINT A-Z
DECLARE SUB HELP ()
DECLARE SUB ParseCmdLine (cmd$, Params$(), Found%)
DECLARE SUB STDOUT (MESSAGETEXT$)

TYPE RegType
  AX    AS INTEGER
  BX    AS INTEGER
  CX    AS INTEGER
  DX    AS INTEGER
  BP    AS INTEGER
  SI    AS INTEGER
  DI    AS INTEGER
  FLAGS AS INTEGER
  DS    AS INTEGER
  ES    AS INTEGER
END TYPE

CONST TRUE = -1, FALSE = 0
DIM SHARED Registers AS RegType
DIM Params$(1 TO 10)

' program defaults
  Hrequest = FALSE

ParseCmdLine COMMAND$, Params$(), NumParams%
'IF NumParams% = FALSE THEN Help: Noff = TRUE

FOR I% = 1 TO NumParams%
  q$ = Params$(I%)
  SELECT CASE q$
  CASE "?", "HELP", "-?": HELP: Hrequest = TRUE: EXIT FOR
END SELECT
NEXT
IF Hrequest = TRUE THEN GOTO Term 'bail out

STDOUT "SB2DSK Version .23á" + CHR$(10) + CHR$(13) + CHR$(10)
ON ERROR GOTO ErrHandle
IF LEFT$(q$, 3) = "-F:" THEN Name$ = MID$(q$, 4)
IF LEN(Name$) = 0 THEN ERROR 64

'STDOUT "Sound Blaster routines by Gary Maddox, obtained from SB-Digest Issue #5" + CHR$(10) + CHR$(13)

 BPORT = &H210: XPORT = &H216: WPORT = &H21C: RPORT = &H21A
 APORT = &H21E: READY = &HAA

DO
   OUT XPORT, 1
 
   FOR T = 0 TO 10
   ll! = TIMER
   DO
   LOOP WHILE ll! = TIMER
   NEXT
   OUT XPORT, 0
   loopctr = 0
   DO
     Byte = INP(RPORT)
     loopctr = loopctr + 1
   IF Byte = &HAA THEN EXIT DO
   IF loopctr > 100 THEN EXIT DO
   LOOP
   IF Byte <> &HAA THEN
     BPORT = BPORT + &H10
     XPORT = XPORT + &H10
     WPORT = WPORT + &H10
     RPORT = RPORT + &H10
 
     APORT = APORT + &H10
   END IF
 IF Byte = &HAA THEN EXIT DO
 IF RPORT = &H270 THEN EXIT DO
 LOOP
 
 IF BPORT = &H270 THEN
   STDOUT "Sound Blaster Not Found!" + CHR$(10) + CHR$(13)
   GOTO Term
 ELSE
   STDOUT "Sound Blaster Installed at Port: " + HEX$(BPORT) + "h." + CHR$(10) + CHR$(13)
 END IF
  STDOUT "Writing SB DAC output to " + Name$ + "." + CHR$(10) + CHR$(13)
  STDOUT "Synchronize your input and press a key to begin." + CHR$(13)
  DO: LOOP UNTIL INKEY$ <> ""
  STDOUT STRING$(48, 32) + CHR$(13)
  STDOUT "Press any key to stop." + CHR$(13)

ON ERROR GOTO ErrHandle
OPEN Name$ FOR OUTPUT AS #1 LEN = 32767 '<- don't change this buffer length !
' shovel data from SB DAC to disk
ON ERROR GOTO ErrHandle
DO
OUT WPORT, &H20
Byte$ = CHR$(INP(RPORT))
 PRINT #1, Byte$;
' ý
' If you are testing this in the QuickBASIC environment you will probably
' want to comment the next line, however you should remember to uncomment
' the line for compilation of SB2DSK. You might even have to tweak this
' delay for the sound to be audible without clicking or missing parts.
T = 0: DO: T = T + 1: LOOP UNTIL T = 124 '<- that number works for with my 486sx 33Mhz system.
LOOP UNTIL INKEY$ <> ""
CLOSE
STDOUT CHR$(10) + "Bye." + CHR$(10) + CHR$(13)
Term:
END

ErrHandle:
CLOSE
SELECT CASE ERR
  CASE IS = 7
   STDOUT "Program Error: Out of memory" + CHR$(10) + CHR$(13)
   STDOUT "I used up all of the DGROUP space, Try again." + CHR$(10) + CHR$(13)
  CASE IS = 9
   STDOUT "Program Error: Array Subscript out of range" + CHR$(10) + CHR$(13)
   STDOUT "Upper limit of allocated space exceeded." + CHR$(10) + CHR$(13)
  CASE IS = 14
   STDOUT "Program Error: Out of String Space" + CHR$(10) + CHR$(13)
  CASE IS = 24
   STDOUT "Informative Error: Device timeout" + CHR$(10) + CHR$(13)
   STDOUT "Program didn't recive information from an I/O device in time." + CHR$(10) + CHR$(13)
  CASE IS = 25
   STDOUT "Printer Error: Device fault" + CHR$(10) + CHR$(13)
   STDOUT "I can't talk to your printer or print que." + CHR$(10) + CHR$(13)
  CASE IS = 27
   STDOUT "Printer Error: Out of paper" + CHR$(10) + CHR$(13)
   STDOUT "Put Paper in your printer." + CHR$(10) + CHR$(13)
  CASE IS = 52
   STDOUT "Program or User Error: Bad File Name or number" + CHR$(10) + CHR$(13)
   STDOUT "You provided a bad file name or I tried to use a file number that doesn't exist." + CHR$(10) + CHR$(13)
  CASE IS = 53
   STDOUT "FileManager/User input Error: File not found" + CHR$(10) + CHR$(13)
   STDOUT "I cannot find the file you requested." + CHR$(10) + CHR$(13)
  CASE IS = 54
   STDOUT "FileManager/Programmer Error: Bad file mode" + CHR$(10) + CHR$(13)
   STDOUT "I am unable to read a file properly. Bug fix by programmer required." + CHR$(10) + CHR$(13)
  CASE IS = 55
   STDOUT "FileManager Error: File already open" + CHR$(10) + CHR$(13)
   STDOUT "A file was already opened, all files are now closed." + CHR$(10) + CHR$(13)
  CASE IS = 57
   STDOUT "System Fault: Device I/O error" + CHR$(10) + CHR$(13)
   STDOUT "I can not 'talk' to a specific device." + CHR$(10) + CHR$(13)
  CASE IS = 58
   STDOUT "Informative Error: File already exists" + CHR$(10) + CHR$(13)
  CASE IS = 59
   STDOUT "FileManager Error: Bad record length" + CHR$(10) + CHR$(13)
   STDOUT "A database record of some sort is not long enough or is too long." + CHR$(10) + CHR$(13)
  CASE IS = 61
   STDOUT "Informative Error: Disk FULL" + CHR$(10) + CHR$(13)
   STDOUT "Free up some disk space." + CHR$(10) + CHR$(13)
  CASE IS = 62
   STDOUT "FileManager Error: Input past end of file." + CHR$(10) + CHR$(13)
   STDOUT "Attempt to read beyond the end of current file." + CHR$(10) + CHR$(13)
  CASE IS = 63
   STDOUT "Program Error: Bad record number" + CHR$(10) + CHR$(13)
   STDOUT "A database record of some sort doesn't exist." + CHR$(10) + CHR$(13)
  CASE IS = 64
   STDOUT "User Error: Bad file name" + CHR$(10) + CHR$(13)
   'STDOUT "You specifically told me to use a file name that is incorrect." + CHR$(10) + CHR$(13)
  CASE IS = 67
   STDOUT "FileManager Fault: Too many files open" + CHR$(10) + CHR$(13)
   STDOUT "I can't handle more files than listed in your CONFIG.SYS file." + CHR$(10) + CHR$(13)
  CASE IS = 68
   STDOUT "System Error: Device UNAVAILABLE" + CHR$(10) + CHR$(13)
   STDOUT "A user serviceable device is not accessable to the computer." + CHR$(10) + CHR$(13)
  CASE IS = 71
   STDOUT "User or System Error: Disk not Ready" + CHR$(10) + CHR$(13)
   STDOUT "Insert a diskette in the current drive. (Excepting Hardrives)" + CHR$(10) + CHR$(13)
  CASE IS = 72
   STDOUT "Serious Informative Error: Disk-Media error" + CHR$(10) + CHR$(13)
   STDOUT "The recordable media surface of the current disk has developed a defect." + CHR$(10) + CHR$(13)
  CASE IS = 73
   STDOUT "Informative Error: Feature unavailable" + CHR$(10) + CHR$(13)
   STDOUT "An advanced capability is not available within this computer." + CHR$(10) + CHR$(13)
  CASE IS = 75
   STDOUT "FileManager/User input Error: Path/File access error" + CHR$(10) + CHR$(13)
  CASE IS = 76
   STDOUT "FileManager/User input Error: Path not found" + CHR$(10) + CHR$(13)
   STDOUT "The path to a file is non-existant." + CHR$(10) + CHR$(13)
  CASE ELSE
   STDOUT "Bug squashing Required. Contact orginal programmer." + CHR$(10) + CHR$(13)
END SELECT
END

SUB HELP
STDOUT "SB2DSK Version .23á" + CHR$(10) + CHR$(13) + CHR$(10) + CHR$(13)
STDOUT "Sound Blaster to Disk samples the data input from the Microphone" + CHR$(10) + CHR$(13)
STDOUT "or LineIn Jack AND writes it to disk in REALTIME !!!!!" + CHR$(10) + CHR$(13)
STDOUT "A fast CPU is highly recommended for the best possible sound..." + CHR$(10) + CHR$(13) + CHR$(10) + CHR$(13)
STDOUT "Sound Blaster to Disk take these parameters:" + CHR$(10) + CHR$(13)
STDOUT " /F:filename - where 'filename' a fully qualified DOS filename" + CHR$(10) + CHR$(13)
STDOUT " /?          - Shows this help." + CHR$(10) + CHR$(13) + CHR$(10) + CHR$(13)
STDOUT "Coded by Andrew Gibson, have fun." + CHR$(10) + CHR$(13)
END SUB

SUB ParseCmdLine (cmd$, Params$(), Found%)
'This parsing sub does NOT mistake filenames like "F-14G.ZIP" as
'containing a switch. That's why it looks so big.
  Found% = FALSE: Sep$ = "-/": Temp$ = LTRIM$(RTRIM$(cmd$)): InParam% = FALSE
  FOR p% = 1 TO LEN(Temp$)
    C$ = MID$(Temp$, p%, 1)
    IF InParam% = TRUE THEN 'Inside of a switch?
      IF INSTR(Sep$, C$) THEN 'Found another switch?
        'Terminate current switch, then start parsing the next one.
        GOSUB MakeParam: MID$(Temp$, p%, 1) = LEFT$(Sep$, 1)
        ParamStart% = p%
      ELSEIF ASC(C$) = 32 OR ASC(C$) = 9 THEN
        GOSUB MakeParam: InParam% = FALSE 'Terminate current switch.
      END IF
    ELSEIF InParam% = -2 THEN 'Inside of a parameter?
      IF ASC(C$) = 32 OR ASC(C$) = 9 THEN 'Terminate parameter with
        GOSUB MakeParam: InParam% = FALSE     'space or TAB.
      END IF
    ELSE
      IF INSTR(Sep$, C$) THEN 'Found start of a switch?
        'Make sure all switches start with "-".
        MID$(Temp$, p%, 1) = LEFT$(Sep$, 1): InParam% = TRUE
        ParamStart% = p%
      ELSEIF ASC(C$) <> 32 AND ASC(C$) <> 9 THEN 'If char isn't a
        InParam% = -2: ParamStart% = p% 'space or TAB it's a parameter.
      END IF
    END IF
  NEXT
 IF InParam% THEN GOSUB MakeParam
  EXIT SUB
MakeParam:
  Found% = Found% + 1
  Params$(Found%) = MID$(Temp$, ParamStart%, p% - ParamStart%)
  IF Found% = UBOUND(Params$) THEN EXIT SUB
RETURN
END SUB

SUB STDOUT (MESSAGETEXT$)
Registers.AX = &H4000
Registers.BX = &H1
Registers.CX = LEN(MESSAGETEXT$)
Registers.DS = VARSEG(MESSAGETEXT$)
Registers.DX = SADD(MESSAGETEXT$)
CALL InterruptX(&H21, Registers, Registers)
END SUB
