'' ------------------------------------------------------------------------ ''
''  @@ Source Documentation                     *** BASIC Version ***       ''
''                                                                          ''
''  TITLE : VOCWALK.BAS                                                     ''
''                                                                          ''
''  DESCRIPTION :                                                           ''
''      This program demonstrates how to process and interprete             ''
''      Creative Voice file. It processes an input VOC file and             ''
''      shows the VOC file's blocks information.                            ''
''                                                                          ''
''      The process will be terminated once an invalid block type is        ''
''      found.                                                              ''
''                                                                          ''
''  Note :                                                                  ''
''      Use switch /Fs for Microsoft Basic PDS 7.1 compiler.                ''
''                                                                          ''
''  Copyright (c) Creative Technology Ltd, 1993. All rights reserved.       ''
''                                                                          ''
'' ------------------------------------------------------------------------ ''

'$INCLUDE: 'SBKVOICE.BI'
'$INCLUDE: 'SBKX.BI'

' Local function prototypes
DECLARE FUNCTION CheckHeader%(iHandle%)
DECLARE FUNCTION BlockTypeOne%(iHandle%)
DECLARE FUNCTION BlockTypeTwo%(iHandle%)
DECLARE FUNCTION BlockTypeThree%(iHandle%)
DECLARE FUNCTION BlockTypeFour%(iHandle%)
DECLARE FUNCTION BlockTypeFive%(iHandle%)
DECLARE FUNCTION BlockTypeSix%(iHandle%)
DECLARE FUNCTION BlockTypeSeven%(iHandle%)
DECLARE FUNCTION BlockTypeEight%(iHandle%)
DECLARE FUNCTION BlockTypeNine%(iHandle%)

' VOC file block types
CONST TERMINATOR%           = 0
CONST VOCxDATAxVERx110%     = 1
CONST VOCxCONTINUE%         = 2
CONST VOCxSILENCE%          = 3
CONST MARKER%               = 4
CONST ASCIIxTEXT%           = 5
CONST REPEATxLOOP%          = 6
CONST ENDxREPEAT%           = 7
CONST VOCxEXTENDED%         = 8
CONST VOCxDATAxVERx120%     = 9

' currently supported Creative Voice Format version
CONST VER110%            = &H010A
CONST VER120%            = &H0114

' voice file packed format for block type 1 and 8
CONST BIT8p0UNPACK       = 0
CONST BIT4p0PACK         = 1
CONST BIT2p6PACK         = 2
CONST BIT2p0PACK         = 3


REM $DYNAMIC
CLEAR

' Global variable
DIM SHARED channel AS INTEGER            ' input channel : 1 - mono, 2 - stereo
DIM SHARED BlkType8Exist AS INTEGER      ' if block type 8 exists, it will
                                         '  supersede block type 1
DIM SHARED SampRate AS LONG              ' sampling rate
DIM SHARED MonoStereo(1 TO 2,0 TO 5) AS STRING

' local variable
DIM iHandle%, BlockType%, fExit%
DIM NumArg AS INTEGER
DIM szCmdArg(1 TO 1) AS STRING


    MonoStereo(1,0) = "Mono"
    MonoStereo(2,0) = "Stereo"

    PRINT "Creative Voice Walker"

    ' get command line argument
    CALL sbkGetCmdLine(NumArg,szCmdArg(),1)
    IF NumArg < 1 THEN
        PRINT "Usage : VOCWALK voc_filename"
        SYSTEM
    END IF

    ' open voice file
    iHandle% = sbkDosOpen%(sbkMakeAsciizString&(szCmdArg(1)))
    IF iHandle% <> -1 THEN

        PRINT "File name    :  ";szCmdArg(1)
        PRINT "File size    : ";sbkFileSize&(iHandle%)

        ' check the voice file format
        IF CheckHeader%(iHandle%) = 0 THEN

            PRINT "Block Type";
            LOCATE ,18
            PRINT "Block Length";
            LOCATE ,34
            PRINT "Sampling Rate";
            LOCATE ,50
            PRINT "Description"

            FOR retVal% = 1 TO 76
                PRINT CHR$(205);
            NEXT retVal%
            PRINT ""

            fExit% = 0

            WHILE fExit% = 0

                ' read block id into BlockType
                IF sbkDosRead%(iHandle%,(VARSEG(BlockType%)),_
                               (VARPTR(BlockType%)),1&) <> 0 THEN

                    SELECT CASE (BlockType% AND &HFF)

                        CASE VOCxDATAxVERx110
                            fExit% = BlockTypeOne%(iHandle%)

                        CASE VOCxCONTINUE
                            fExit% = BlockTypeTwo%(iHandle%)

                        CASE VOCxSILENCE
                            fExit% = BlockTypeThree%(iHandle%)

                        CASE MARKER
                            fExit% = BlockTypeFour%(iHandle%)

                        CASE ASCIIxTEXT
                            fExit% = BlockTypeFive%(iHandle%)

                        CASE REPEATxLOOP
                            fExit% = BlockTypeSix%(iHandle%)

                        CASE ENDxREPEAT
                            fExit% = BlockTypeSeven%(iHandle%)

                        CASE VOCxEXTENDED
                            fExit% = BlockTypeEight%(iHandle%)

                        CASE VOCxDATAxVERx120
                            fExit% = BlockTypeNine%(iHandle%)

                        CASE TERMINATOR
                            PRINT "0 Terminator"
                            fExit% = 1

                        CASE ELSE
                            PRINT "Invalid block type : ";
                            PRINT BlockType% AND &HFF
                            fExit% = 1
                    END SELECT
                ELSE
                    PRINT "Error reading ";szFilename$;
                    PRINT " or Invalid Voice File format."
                    fExit% = 1
                END IF
            WEND
        END IF

        CALL sbkDosClose(iHandle%)
    ELSE
        PRINT "Error open ";szCmdArg(1)
    END IF
END


' ------------------------------------------------------------------------- '
'   @@ Usage                                                                '
'                                                                           '
'   FUNCTION CheckHeader% (iHandle%)                                        '
'                                                                           '
'   Description :                                                           '
'       Retrieve Creative Voice header from the file and check              '
'       if it is a valid voice file.                                        '
'                                                                           '
'   Entry :                                                                 '
'       iHandle% - voice file handle.                                       '
'                                                                           '
'   Exit :                                                                  '
'       zero if successful else return non-zero.                            '
'                                                                           '
' ------------------------------------------------------------------------- '

FUNCTION CheckHeader% (iHandle%)

    DIM header AS VOCHDR
    DIM ID AS STRING


    ' read voice header
    IF sbkDosRead%(iHandle%,VARSEG(header.id),VARPTR(header.id),_
                                    LEN(header)) <> 0 THEN
        ' check header id
        ID = "Creative Voice File" + CHR$(&H1A)
        IF header.id = ID THEN

            ' check voice file format version
            IF (header.version = VER110%) OR (header.version = VER120%) THEN

                ' check voice file identification code
                IF header.checkcode = ((NOT header.version) + &H1234) THEN
                    PRINT "Version      :  ";
                    PRINT USING "#.";sbkHighByte%(header.version);
                    PRINT USING      "##";sbkLowByte%(header.version);
                    PRINT CHR$(13)
                    CheckHeader% = 0
                    EXIT FUNCTION
                END IF
            END IF
        END IF
    END If

    PRINT "Error reading file."

    CheckCode% = 1

END FUNCTION


' ------------------------------------------------------------------------- '
'   @@ Usage                                                                '
'                                                                           '
'                                                                           '
'   FUNCTION BlockType???% (iHandle%)                                       '
'                                                                           '
'   Description :                                                           '
'       The following functions inteprete and display nine kinds of         '
'       Creative Voice block type.                                          '
'                                                                           '
'   Entry :                                                                 '
'       iHandle% - voice file handle.                                       '
'                                                                           '
'   Exit :                                                                  '
'       zero if successful else return non-zero.                            '
'                                                                           '
' ------------------------------------------------------------------------  '

FUNCTION BlockTypeOne% (iHandle%)

    DIM BlkLen AS LONG
    DIM TC AS INTEGER
    DIM Pack AS INTEGER


    ' read block length
    IF sbkDosRead%(iHandle%,VARSEG(BlkLen),VARPTR(BlkLen),3&) <> 0 THEN
        ' read sampling rate
        IF sbkDosRead%(iHandle%,VARSEG(TC),VARPTR(TC),1&) <> 0 THEN
            ' read Pack mode
            IF sbkDosRead%(iHandle%,VARSEG(Pack),VARPTR(Pack),1&) <> 0 THEN

                ' if block type 8 exists, it will supersede block type 1
                IF BlkType8Exist = 0 THEN
                   ' calculate sampling rate - global variable
                    SampRate = 1000000& / (256 - (TC AND &HFF))
                END IF
                ' get voice data length excluding block type header
                BlkLen = (BlkLen AND &HFFFFFF) - 2&

                PRINT "1 Voice";
                LOCATE ,17
                PRINT BlkLen;
                LOCATE ,33
                PRINT SampRate;"Hz";
                LOCATE ,49

                IF BlkType8Exist <> 0 THEN
                    PRINT " 8 bit unsigned ";MonoStereo(channel,0)
                    BlkType8Exist = 0
                ELSE
                    ' mono mode
                    channel = 1

                    ' display voice pack mode
                    SELECT CASE (Pack AND &HFF)

                        CASE BIT8p0UNPACK
                            PRINT " 8 bit unsigned ";MonoStereo(channel,0)

                        CASE BIT4p0PACK
                            PRINT " 4 bit ADPCM ";MonoStereo(channel,0)

                        CASE BIT2p6PACK
                            PRINT " 2.6 bit ADPCM ";MonoStereo(channel,0)

                        CASE BIT2p0PACK
                            PRINT " 2 bit ADPCM ";MonoStereo(channel,0)

                        CASE ELSE
                            PRINT " Unknown data packed format"

                    END SELECT
                END IF
                ' move file pointer to next block
                retVal& = sbkDosLSeek&(iHandle%,BlkLen,SEEKCUR)
                BlockTypeOne% = 0
                EXIT FUNCTION
            END IF
        END IF
    END IF

    PRINT "Block type 1 -  Invalid format."

    BlockTypeOne% = 1

END FUNCTION

' ------------------------------------------------------------------------ '

FUNCTION BlockTypeTwo% (iHandle%)

    DIM BlkLen AS LONG


    ' read block length
    IF sbkDosRead%(iHandle%,VARSEG(BlkLen),VARPTR(BlkLen),3&) <> 0 THEN

        BlkLen = BlkLen AND &HFFFFFF&

        PRINT "2 Continuation";
        LOCATE ,17
        PRINT BlkLen

        ' move file pointer to next block
        retVal& = sbkDosLSeek&(iHandle%,BlkLen,SEEKCUR)
        BlockTypeTwo% = 0
        EXIT FUNCTION
    END IF

    PRINT "Block type 2 - Invalid format."

    BlockTypeTwo% = 1

END FUNCTION

' ------------------------------------------------------------------------ '

FUNCTION BlockTypeThree% (iHandle%)

    DIM BlkLen AS LONG
    DIM Period AS INTEGER
    DIM TC AS INTEGER
    DIM fPeriod AS SINGLE
    DIM SilenceRate AS INTEGER


    ' read block header
    IF sbkDosRead%(iHandle%,VARSEG(BlkLen),VARPTR(BlkLen),3&) <> 0 THEN
        ' read silence period
        IF sbkDosRead%(iHandle%,VARSEG(Period),VARPTR(Period),2&) <> 0 THEN
            ' read sampling rate
            IF sbkDosRead%(iHandle%,VARSEG(TC),VARPTR(TC),1&) <> 0 THEN
                ' calculate sampling rate
                SilenceRate = 1000000& / (256 - (TC AND &HFF))

                ' calculate period of silence
                fPeriod = CSNG(Period) / SilenceRate

                PRINT "3 Silence";
                LOCATE ,33
                PRINT ABS(SilenceRate);"Hz";
                LOCATE ,49
                PRINT USING " #.######";ABS(fPeriod);
                PRINT " Sec"

                BlockTypeThree% = 0
                EXIT FUNCTION
            END IF
        END IF
    END IF

    PRINT "Block type 3 - Invalid format."

    BlockTypeThree% = 1

END FUNCTION

' ------------------------------------------------------------------------ '

FUNCTION BlockTypeFour% (iHandle%)

    DIM BlkLen AS LONG, Mark AS INTEGER


    ' read block header
    IF sbkDosRead%(iHandle%,VARSEG(BlkLen),VARPTR(BlkLen),3&) <> 0 THEN
        ' read marker
        IF sbkDosRead%(iHandle%,VARSEG(Mark),VARPTR(Mark),2&) <> 0 THEN
            ' display marker value
            PRINT "4 Marker";
            LOCATE ,49
            PRINT ABS(Mark)
            BlockTypeFour% = 0
            EXIT FUNCTION
        END IF
    END IF

    PRINT "Block type 4 - Invalid format."

    BlockTypeFour% = 1

END FUNCTION

' ------------------------------------------------------------------------

FUNCTION BlockTypeFive% (iHandle%)

    DIM BlkLen AS LONG


    ' read length of ASCII string
    IF sbkDosRead%(iHandle%,VARSEG(BlkLen),VARPTR(BlkLen),3&) <> 0 THEN

        BlkLen = BlkLen AND &HFFFFFF&

        PRINT "5 ASCII";
        LOCATE ,17
        PRINT BlkLen

        ' move file pointer to next block
        retVal& = sbkDosLSeek&(iHandle%,BlkLen,SEEKCUR)
        BlockTypeFive% = 0
        EXIT FUNCTION
    END IF

    PRINT "Block type 5 - Invalid format."

    BlockTypeFive% = 1

END FUNCTION

' ------------------------------------------------------------------------  '

FUNCTION BlockTypeSix% (iHandle%)

    DIM BlkLen AS LONG
    DIM Count AS INTEGER


    ' read block header
    IF sbkDosRead%(iHandle%,VARSEG(BlkLen),VARPTR(BlkLen),3&) <> 0 THEN
        ' read repeat count
        IF  sbkDosRead%(iHandle%,VARSEG(Count),VARPTR(Count),2&) <> 0 THEN
            PRINT "6 Repeat Loop";
            LOCATE ,49
            IF      Count = &HFFFF THEN
                PRINT "Endless (-1)"
            ELSE
                PRINT " Count =";ABS(Count)
            END IF

            BlockTypeSix% = 0
            EXIT FUNCTION
        END If
    END IF

    PRINT "Block type 6 - Invalid format."

    BlockTypeSix% = 1

END FUNCTION

' ------------------------------------------------------------------------ '

FUNCTION BlockTypeSeven% (iHandle%)

    DIM BlkLen AS LONG


    ' read block length
    IF sbkDosRead%(iHandle%,VARSEG(BlkLen),VARPTR(BlkLen),3& ) <> 0 THEN
        PRINT "7 Repeat End"
        BlockTypeSeven% = 0
        EXIT FUNCTION
    END IF

    PRINT "Block type 7 - Invalid format."

    BlockTypeSeven% = 0

END FUNCTION

' ------------------------------------------------------------------------ '

FUNCTION BlockTypeEight% (iHandle%)

    DIM BlkLen AS LONG
    DIM TC AS LONG
    DIM Pack AS INTEGER
    DIM Mode AS INTEGER


    ' read block length
    IF sbkDosRead%(iHandle%,VARSEG(BlkLen),VARPTR(BlkLen),3&) <> 0 THEN
        ' read sampling rate
        IF sbkDosRead%(iHandle%,VARSEG(TC),VARPTR(TC),2&) <> 0 THEN
            ' read Pack mode
            IF sbkDosRead%(iHandle%,VARSEG(Pack),VARPTR(Pack),1&) <> 0 THEN
                ' read channel mode
                IF sbkDosRead%(iHandle%,VARSEG(Mode),VARPTR(Mode),1&) <> 0 THEN

                    IF (Mode AND &HFF) <> 0 THEN
                        ' global variable stereo mode
                        channel = 2
                        ' calculate stereo sampling rate
                        SampRate = 256000000& / _
                                   (2& * (65536& - (TC AND &HFFFF)))
                    ELSE
                        ' mono mode
                        channel = 1
                        ' calculate mono sampling rate
                        SampRate = 256000000& / (65536& - (TC AND &HFFFF))
                    END IF

                    PRINT "8 Extended";
                    LOCATE ,33
                    PRINT SampRate;"Hz";
                    LOCATE ,49

                    SELECT CASE (Pack AND &HFF)

                        CASE BIT8p0UNPACK
                            PRINT " 8 bit unsigned ";MonoStereo(channel,0)

                        CASE BIT4p0PACK
                            PRINT " 4 bit ADPCM ";MonoStereo(channel,0)

                        CASE BIT2p6PACK
                            PRINT " 2.6 bit ADPCM ";MonoStereo(channel,0)

                        CASE BIT2p0PACK
                            PRINT " 2 bit ADPCM ";MonoStereo(channel,0)

                        CASE ELSE
                            PRINT " Unknown data packed format"

                    END SELECT

                    'Global variable
                    BlkType8Exist = 1
                    BlockTypeEight% = 0
                    EXIT FUNCTION
                END IF
            END IF
        END IF
    END IF

    PRINT "Block type 8 - Invalid format."

    BlockTypeEight% = 1

END FUNCTION

' ------------------------------------------------------------------------ '

TYPE VOC
    BlkLen AS LONG
    dwSampRate AS LONG
    ' sample length in bit(low byte) and number of channel(high byte)
    bSampleLenORedbChannel AS INTEGER
    wFormat AS INTEGER
    reserved AS STRING * 4
END TYPE

FUNCTION BlockTypeNine% (iHandle%)

    DIM stVoc AS VOC
    DIM bit AS INTEGER


    ' move file pointer back one byte
    retVal& = sbkDosLSeek&(iHandle%,-1,SEEKCUR)

    ' read block header
    IF sbkDosRead%(iHandle%,VARSEG(stVoc.BlkLen),_
                   VARPTR(stVoc.BlkLen),LEN(stVoc)) <> 0 THEN

        ' global variable stereo oR mono
        channel = sbkHighByte%((stVoc.bSampleLenORedbChannel))
        ' bit per sample
        bit = sbkLowByte%((stVoc.bSampleLenORedbChannel))

        ' Get actual voice data length by shifting right one byte because
        ' the first byte contains the block type ID i.e. 9
        stVoc.BlkLen = (stVoc.BlkLen / &H100) - 12&

        PRINT "9 Voice";
        LOCATE ,17
        PRINT stVoc.BlkLen;
        LOCATE ,33
        PRINT stVoc.dwSampRate;"Hz";
        LOCATE ,49

        ' show voice pack format
        SELECT CASE stVoc.wFormat

            CASE VOCxFORMATx08xPCM
                PRINT bit;"bit unsigned ";MonoStereo(channel,0)

            CASE VOCxFORMATxCT4xADPCM, VOCFORMATCT3ADPCM, VOCFORMATCT2ADPCM
                PRINT bit;"bit ADPCM ";MonoStereo(channel,0)

            CASE VOCxFORMATx16xPCM
                PRINT bit;"bit signed ";MonoStereo(channel,0)

            CASE VOCxFORMATxALAW
                PRINT bit;"bit ALAW ";MonoStereo(channel,0)

            CASE VOCxFORMATxMULAW
                PRINT bit;"bit MULAW ";MonoStereo(channel,0)

            CASE VOCxFORMATxCREATIVExADPCM
                PRINT bit;"bit ADPCM ";MonoStereo(channel,0)

            CASE ELSE
                PRINT "Unknown data packed format"

        END SELECT
        ' move file pointer to next block
        retVal& = sbkDosLSeek&(iHandle%,stVoc.BlkLen,SEEKCUR)
        BlockTypeNine% = 0
        EXIT FUNCTION
    END IF

    PRINT "Block type 9 - Invalid format."

    BlockTypeNine% = 1

END FUNCTION
' End of file
