'===========================================================================
' Subject: SOUND BLASTER MIC OSCILLOSCOPE     Date: 11-13-95 (00:00)       
'  Author: Toshihiro Horie                    Code: QB, QBasic, PDS        
'  Origin: www.ocf.berkeley.edu/~horie/     Packet: SOUND.ABC
'===========================================================================
DEFINT A-Z
DECLARE FUNCTION DetectSB16Addr% ()
DECLARE SUB GetDSPVersion ()
DECLARE FUNCTION SB16RESET ()
DECLARE FUNCTION SB16DSPREAD% ()
DECLARE SUB SB16DSPWRITE (V%)
DECLARE SUB SBSCOPE ()
DECLARE SUB SBPLAY ()
'============================================================
'REAL SOUND BLASTER PROGRAMMING IN QBASIC v1.0
'Routines humbly converted from pascal by TOSHIHIRO HORIE
'Motto: Taking Basic to the MAX!
'ORIGINAL DSP ROUTINES (C)1995 ABACUS _PC_Underground_
'============================================================

DIM SHARED DSPADDR
DSPADDR = &H220 'can be anywhere from 210h to 280h
title$ = "TOSHI'S REAL QBASIC SOUND BLASTER PROGRAM v1.0"
CONST false = 0, TRUE = NOT false
CONST ready = &HAA
CONST SpeakerOn = &HD1
CONST SpeakerOff = &HD3
CONST record8bit = &H20 'direct
CONST output8bit = &H10 'direct
CONST VersionQuery = &HE1
'---------BEGIN MAIN PROGRAM--------------------------------
CLS
COLOR 15, 1
PRINT title$
PRINT
'...................NOTE............................
'This program worked on my SBPRO compatible, but is
'NOT guaranteed to work on all SB's and clones.
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OK = DetectSB16Addr
IF OK THEN
        PRINT "Soundblaster detected at " + HEX$(DSPADDR) + "H"
        PRINT "Connect microphone for cool oscilloscope."
        PRINT : COLOR 14, 3:
        PRINT "Press any key to when ready..."
ELSE
        PRINT "Too bad. SoundBlaster not found.": END
END IF
DO: LOOP UNTIL INKEY$ > ""
SBSCOPE
SBPLAY
'If I have time, I may add a DMA output procedure that
'gives you real sound-effects, like music, or voices.
CLS
PRINT "End of Real SoundBlaster Demonstration."
END

'--------------------------------------------------------------------
'P.S. To the hundreds of QBASIC programmers who look at this code:
'One of the purposes of the internet is to share information.  Be
'generous with your source code. For "No man is an island."   TH 96
'--------------------------------------------------------------------

FUNCTION DetectSB16Addr
'function returns TRUE if a sounblaster was initialized
'otherwise FALSE  The DSPADDR variable is set to the
'base address of the SB.

PORT = &H210
A = 0

DetectSB16Addr = false
FOR PORT = &H210 TO &H280 STEP &H10
        DSPADDR = PORT
        A = SB16RESET
        IF A = TRUE THEN
                DetectSB16Addr = TRUE
                EXIT FUNCTION
        END IF
NEXT PORT
END FUNCTION

SUB GetDSPVersion
I = 0: 'WORD = 2 BYTES = INTEGER
T = 0: 'WORD
S$ = "": 'STRING
      

        aa = SB16RESET
        SB16DSPWRITE (VersionQuery)
        SBVERSMAJ = SB16DSPREAD
        SBVERSMIN = SB16DSPREAD
        S$ = STR$(SBVERSMAJ)
        IF SBVERSMIN > 9 THEN
                SBVERSTR$ = S$ + "." + LTRIM$(STR$(SBVERSMIN))
        ELSE
                SBVERSTR$ = S$ + ".0" + LTRIM$(STR$(SBVERSMIN))
        END IF
        PRINT "SoundBlaster DSP VERSION :"; SBVERSTR$;
END SUB

FUNCTION SB16DSPREAD
'function waits until the DSP can be read
'and returns the read value

OffsetA = DSPADDR + &HA

'--- You should wait until DSP is ready------------------------
t1# = TIMER
'DO
'        D = INP(OffsetA)
         t2# = TIMER
'        IF t2# - t1# > .5 THEN PRINT "SBRead Locked up": EXIT DO
'LOOP WHILE D = &HAA
'----------------------------------------------------------------
SB16DSPREAD = INP(OffsetA)

END FUNCTION

SUB SB16DSPWRITE (V)
'waits until the DSP is ready to write and then writes the byte
'passed in "v" to the DSP.

c = DSPADDR + &HC
'I think the Do..Loop can be replaced
'with a WAIT C,128, but not sure.
DO: CREG = INP(c)
LOOP WHILE CREG >= 128
OUT c, V
END SUB

FUNCTION SB16RESET
'This function resets the Soundblaster DSP (Digital Signal Processor)
'if function is successful TRUE is returned, otherwise FALSE.

ct = 0: stat = 0: 'byte


OUT DSPADDR + &H6, 1
DO
        OUT DSPADDR + &H6, 0
        stat = INP(DSPADDR + &HE)
        stat = INP(DSPADDR + &HA)
        IF stat = ready THEN EXIT DO
        ct = ct + 1
LOOP WHILE ct < 100 'wait about 100 ms
IF stat = ready THEN SB16RESET = TRUE ELSE SB16RESET = false
END FUNCTION

SUB SBPLAY
aa = SB16RESET
SB16DSPWRITE (SpeakerOn)
CLS
PRINT "----------------------------------------------------"
PRINT "    Outputting 8 bit tone on directly to SB"
PRINT "    pitch depends on computer speed"
PRINT "    You should hear some clicking noise."
PRINT "----------------------------------------------------"
LOCATE 25, 20: PRINT "==== Press any key to exit. =====";

rate = 24
DO
        CCC = CCC + 1
        aa = SB16RESET
        SB16DSPWRITE (output8bit)
        aa = SB16RESET
        DDD = INT((SIN(CCC / rate) * 128) + 128)
        SB16DSPWRITE (DDD)
        IF CCC > 32000 THEN CCC = 0
LOOP UNTIL INKEY$ > ""
aa = SB16RESET
SB16DSPWRITE (SpeakerOff)
END SUB

SUB SBSCOPE
SCREEN 9: CLS
LOCATE 2, 50: PRINT "SB MIC OSCILLOSCOPE"
LOCATE 3, 50: PRINT "in QBASIC!! :) "
LOCATE 4, 50: PRINT "by Toshihiro Horie"
LOCATE 23, 1: PRINT "Sound Blaster settings: "; ENVIRON$("BLASTER")
GetDSPVersion
LINE (0, 0)-(321, 257), 2, B
LINE (321, 128)-(323, 128), 15

'real-time 8-bit sampling from microphone
'I DON'T KNOW HOW TO GET RID OF THE ANNOYING SOUND FROM THE
'OUPUT JACK WHILE SAMPLING.

aa = SB16RESET
SB16DSPWRITE (SpeakerOff)
DO
        aa = SB16RESET
        SB16DSPWRITE (record8bit)
        bb = SB16DSPREAD
        'PRINT bb - 128
        PSET (xx, 256 - bb)
        xx = xx + 1
        IF xx MOD 320 = 0 THEN
                LINE (0, 0)-(320, 256), 0, BF
                xx = 0
        END IF
LOOP UNTIL INKEY$ > ""

END SUB
