/* ------------------------------------------------------------------------ */
/*  @@ Source Documentation                         *** C Version ***       */
/*                                                                          */
/*  TITLE : VOCWALK.C                                                       */
/*                                                                          */
/*  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 :                                                                  */
/*      Compile with switch /Zp1 if you are using Microsoft C compiler.     */
/*                                                                          */
/*  Copyright (c) Creative Technology Ltd, 1993. All rights reserved.       */
/*                                                                          */
/* ------------------------------------------------------------------------ */

#include <fcntl.h>
#include <stdio.h>
#include <string.h>

#include "ctstddef.h"
#include "sbkvoice.h"
#include "sbkmacro.h"
#include "sbkx.h"


#define Type1SampRate(TC)  ( (long) (1000000L /         \
                                (long) (256 - (unsigned)TC)) )

#define Type8SampRateMono(TC)  ( (long) (256000000L /  \
                                 (long) (65536 - (unsigned)TC)) )

#define Type8SampRateStereo(TC)  ( (long) (256000000L /        \
                                 (long) ((65536 - (unsigned)TC) * 2)) )


/* VOC file block types */
#define TERMINATOR          0
#define VOC_DATA_VER110     1
#define VOC_CONTINUE        2
#define VOC_SILENCE         3
#define MARKER              4
#define ASCII_TEXT          5
#define REPEAT_LOOP         6
#define END_REPEAT          7
#define VOC_EXTENDED        8
#define VOC_DATA_VER120     9

/* supported Creative Voice Format version */
#define VER110              0x010A
#define VER120              0x0114

/* voice file packed format for block type 1 and 8 */
#define BIT8p0_UNPACK       0   /* 8 bit unpacked */
#define BIT4p0_PACK         1   /* 8 bit to 4 bit */
#define BIT2p6_PACK         2   /* 8 bit to 3 bit */
#define BIT2p0_PACK         3   /* 8 bit to 2 bit */


/* Local function prototypes */
int CheckHeader(int handle) ;
int BlockTypeOne(int handle) ;
int BlockTypeTwo(int handle) ;
int BlockTypeThree(int handle) ;
int BlockTypeFour(int handle) ;
int BlockTypeFive(int handle) ;
int BlockTypeSix(int handle) ;
int BlockTypeSeven(int handle) ;
int BlockTypeEight(int handle) ;
int BlockTypeNine(int handle) ;

/* Global variable */
const char MonoStereo[3][7] = {" ","Mono","Stereo"} ;

int channel = 1 ;           /* input channel : 1 - mono, 2 - stereo */
int BlkType8Exist = 0 ;     /* if block type 8 exists, it will supersede */
                            /* block type 1 */
long SampRate = 0 ;         /* sampling rate */

#ifdef _MSC_VER
    #pragma pack(1)
#endif

main(int argv, char *argc[])
{
    int         handle ;    /* input file handle */
    int         BlockType ; /* possible block type is 0 to 9 */
    int         fExit ;
    unsigned    numRead ;


    printf("\nCreative Voice Walker") ;

    if (argv > 1)
    {
        /* open voice file */
        handle = sbkDosOpen(argc[1]) ;
        if (handle != -1)
        {
            printf("\nFile name     : %s",strupr(argc[1])) ;
            printf("\nFile size     : %ld",filelength(handle)) ;

            /* check the voice file format */
            if (!CheckHeader(handle))
            {
                printf("\n\nBlock Type\tBlock Length") ;
                printf("\tSampling Rate\tDescription\n") ;

                for (numRead=0; numRead<76; numRead++)
                    printf("%c",205) ;

                fExit = 0 ;

                while(!fExit)
                {
                    /* read block id into BlockType */
                    if (!sbkDosRead(handle,(char far *)&BlockType,1,
                                    (WORD far *)&numRead))
                    {
                        switch(BlockType & 0xff)
                        {
                            case VOC_DATA_VER110 :
                                fExit = BlockTypeOne(handle) ;
                                break ;
                            case VOC_CONTINUE :
                                fExit = BlockTypeTwo(handle) ;
                                break ;
                            case VOC_SILENCE :
                                fExit = BlockTypeThree(handle) ;
                                break ;
                            case MARKER :
                                fExit = BlockTypeFour(handle) ;
                                break ;
                            case ASCII_TEXT :
                                fExit = BlockTypeFive(handle) ;
                                break ;
                            case REPEAT_LOOP :
                                fExit = BlockTypeSix(handle) ;
                                break ;
                            case END_REPEAT :
                                fExit = BlockTypeSeven(handle) ;
                                break ;
                            case VOC_EXTENDED :
                                fExit = BlockTypeEight(handle) ;
                                break ;
                            case VOC_DATA_VER120 :
                                fExit = BlockTypeNine(handle) ;
                                break ;
                            case TERMINATOR :
                                printf("\n0 Terminator") ;
                                fExit = 1 ;
                                break ;
                            default :
                                printf("\nInvalid block type : %d",
                                                   BlockType & 0xff) ;
                                fExit = 1 ;
                                break ;
                        }
                    }
                    else
                    {
                        printf("\nError reading %s",argc[1]) ;
                        fExit = 1 ;
                    }
                }
            }
            sbkDosClose(handle) ;
        }
        else
            printf("\nError open %s",argc[1]) ;
    }
    else
        printf("\nUsage : VOCWALK voc_filename\n") ;

    printf("\n") ;
    return 0 ;
}


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

int CheckHeader (int handle)
{
    VOCHDR      header ;
    WORD        wByteRead ;


    /* read voice header */
    if (!sbkDosRead(handle,(char far *)&header,
                 (WORD)sizeof(VOCHDR),(WORD far *)&wByteRead))
    {
        if (wByteRead == (WORD)sizeof(VOCHDR))
        {
            /* check header id */
            if (!strncmp(header.id,"Creative Voice File\x1A",20))
            {
                /* check voice file format version */
                if ( (header.version == VER110) ||
                     (header.version == VER120) )
                {
                    /* check voice file identification code */
                    if (header.check_code == (WORD)(~header.version + 0x1234))
                    {
                        printf("\nVersion       : %d.%d",
                                        (header.version & 0xff00) >> 8,
                                        header.version & 0x00ff) ;
                        return (0) ;
                    }
                }
            }
        }
        printf("\nInvalid format - Not Creative Voice file.") ;
    }
    else
        printf("\nError reading file.") ;

    return (1) ;

}


/* ------------------------------------------------------------------------ */
/*  @@ Usage                                                                */
/*                                                                          */
/*  int BlockType??? (int handle)                                           */
/*                                                                          */
/*  Description :                                                           */
/*      The following functions inteprete and display ten kinds of          */
/*      Creative Voice block type.                                          */
/*                                                                          */
/*  Entry :                                                                 */
/*      handle - voice file handle.                                         */
/*                                                                          */
/*  Exit :                                                                  */
/*      zero if successful else return non-zero.                            */
/*                                                                          */
/* ------------------------------------------------------------------------ */

int BlockTypeOne(int handle)
{
    struct
    {
        BYTE        BlkLen1 ;
        unsigned    BlkLen2 ;
        BYTE        TC ;
        BYTE        Pack ;
    } stVoc ;

    long        Len ;
    WORD        wByteRead ;

    /* read block type 1 header */
    if (!sbkDosRead(handle,(char far *)&stVoc,
                (unsigned)sizeof(stVoc),(WORD far *)&wByteRead))
    {
        if (wByteRead == (WORD)sizeof(stVoc))
        {
            /* if block type 8 exists, it will supersede block type 1 */
            if (!BlkType8Exist)
                /* calculate sampling rate */
                SampRate = Type1SampRate(stVoc.TC) ;

            /* calculate voice data length excluding block header */
            Len = stVoc.BlkLen2 ;
            Len = (Len << 8) + stVoc.BlkLen1 - (BYTE)2 ;

            printf("\n1 Voice\t\t%ld    \t%ld Hz",Len,SampRate) ;

            if (BlkType8Exist)
            {
                printf(" \t%s 8 bit unsigned",MonoStereo[channel]) ;
                BlkType8Exist = 0 ;
            }
            else
            {
                /* display voice pack mode */
                switch ((int)(stVoc.Pack & 0xff))
                {
                    case BIT8p0_UNPACK :
                        printf(" \t%s 8 bit unsigned",MonoStereo[channel]) ;
                        break ;
                    case BIT4p0_PACK :
                        printf(" \t%s 4 bit ADPCM",
                                        MonoStereo[channel]) ;
                        break ;
                    case BIT2p6_PACK :
                        printf(" \t%s 2.6 bit ADPCM",
                                        MonoStereo[channel]) ;
                        break ;
                    case BIT2p0_PACK :
                        printf(" \t%s 2 bit ADPCM",
                                        MonoStereo[channel]) ;
                        break ;
                    default :
                        printf(" \tUnknown data packed format") ;
                        break ;
                }
            }
            /* move file pointer to next block */
            lseek(handle,Len,SEEK_CUR) ;

            return (0) ;
        }
        else
            printf("\nBlock type 1 - Invalid format.") ;
    }
    else
        printf("\nError Reading file.") ;

    return (1) ;
}

/* ------------------------------------------------------------------------ */

int BlockTypeTwo(int handle)
{
    long        BlkLen ;
    WORD        wByteRead ;


    /* read block length */
    if (!sbkDosRead(handle,(char far *)&BlkLen,3,(WORD far *)&wByteRead))
    {
        if (wByteRead == 3)
        {
            BlkLen &= 0xffffffL ;

            printf("\n2 Continuation") ;
            printf("\t%ld",BlkLen) ;

            /* move file pointer to next block */
            lseek(handle,BlkLen,SEEK_CUR) ;
            return (0) ;
        }
        else
            printf("\nBlock type 2 - Invalid format.") ;
    }
    else
        printf("\nError reading file.") ;

    return (1) ;

}

/* ------------------------------------------------------------------------ */

int BlockTypeThree(int handle)
{
    struct
    {
        BYTE        BlkLen1 ;
        unsigned    BlkLen2 ;
        unsigned    Period ;
        BYTE        TC ;
    } stVoc ;

    float       fPeriod ;
    WORD        wByteRead, SampRate ;


    /* read block header */
    if (!sbkDosRead(handle,(char far *)&stVoc,
                 (WORD)sizeof(stVoc),(WORD far *)&wByteRead))
    {
        if (wByteRead == (WORD)sizeof(stVoc))
        {
            /* calculate sampling rate */
            SampRate = (WORD)Type1SampRate(stVoc.TC) ;

            /* calculate period of silence */
            fPeriod = (float)stVoc.Period / SampRate ;

            printf("\n3 Silence\t\t\t%u Hz\t\t%f Sec",SampRate,fPeriod) ;

            return (0) ;
        }
        else
            printf("\nBlock type 3 - Invalid format.") ;
    }
    else
        printf("\nError reading file.") ;

    return (1) ;

}

/* ------------------------------------------------------------------------ */

int BlockTypeFour(int handle)
{
     struct
     {
         BYTE         BlkLen1 ;
         unsigned    BlkLen2 ;
         unsigned    Marker ;
     } stVoc ;

    WORD    wByteRead ;


    /* read block header */
    if (!sbkDosRead(handle,(char far *)&stVoc,
                 (WORD)sizeof(stVoc),(WORD far *)&wByteRead))
    {
        if (wByteRead == (WORD)sizeof(stVoc))
        {
            /* display marker value */
            printf("\n4 Marker\t\t\t\t\t%u",stVoc.Marker) ;

            return (0) ;
        }
        else
            printf("\nBlock type 4 - Invalid format.") ;
    }
    else
        printf("\nError reading file.") ;

    return (1) ;

}

/* ------------------------------------------------------------------------ */

int BlockTypeFive(int handle)
{
    WORD    wByteRead ;
    long    BlkLen ;


    /* read length of ASCII string */
    if (!sbkDosRead(handle,(char far *)&BlkLen,3,(WORD far *)&wByteRead))
    {
        if (wByteRead == 3)
        {
            BlkLen &= 0xffffffL ;

            printf("\n5 ASCII\t\t%ld",BlkLen) ;

            /* move file pointer to next block */
            lseek(handle,BlkLen,SEEK_CUR) ;

            return (0) ;
        }
        else
            printf("\nBlock type 5 - Invalid format.") ;
    }
    else
        printf("\nError reading file.") ;

    return (1) ;

}

/* ------------------------------------------------------------------------ */

int BlockTypeSix(int handle)
{
    struct
    {
        BYTE        BlkLen1 ;
        unsigned    BlkLen2 ;
        unsigned    Count ;
    } stVoc ;

    WORD    wByteRead ;


    /* read block header */
    if (!sbkDosRead(handle,(char far *)&stVoc,
                 (WORD)sizeof(stVoc),(WORD far *)&wByteRead))
    {
        if (wByteRead == (WORD)sizeof(stVoc))
        {
            if (stVoc.Count == 0xffff)
                printf("\n6 Repeat Loop\t\t\t\t\tEndless (-1)") ;
            else
                printf("\n6 Repeat Loop\t\t\t\t\tCount = %u",stVoc.Count) ;

            return (0) ;
        }
        else
            printf("\nBlock type 6 - Invalid format.") ;
    }
    else
        printf("\nError reading file.") ;

    return (1) ;

}

/* ------------------------------------------------------------------------ */

int BlockTypeSeven(int handle)
{
    WORD    wByteRead ;
    long    BlkLen ;


    /* read block length */
    if (!sbkDosRead(handle,(char far *)&BlkLen,3,(WORD far *)&wByteRead))
    {
        if (wByteRead == 3)
        {
            printf("\n7 Repeat End") ;

            return (0) ;
        }
        else
            printf("\nBlock type 7 - Invalid format.") ;
    }
    else
        printf("\nError reading file.") ;

    return (1) ;

}

/* ------------------------------------------------------------------------ */

int BlockTypeEight(int handle)
{
    struct
    {
        BYTE        BlkLen1 ;
        unsigned    BlkLen2 ;
        unsigned    TC ;
        BYTE        Pack ;
        BYTE        Mode ;
    } stVoc ;

    WORD    wByteRead ;


    /* read block header */
    if (!sbkDosRead(handle,(char far *)&stVoc,
                 (WORD)sizeof(stVoc),(WORD far *)&wByteRead))
    {
        if (wByteRead == (WORD)sizeof(stVoc))
        {
            if((int)stVoc.Mode & 0xff)
            {
                /* global variable stereo mode */
                channel = 2 ;
                /* calculate stereo sampling rate */
                SampRate = Type8SampRateStereo(stVoc.TC) ;
            }
            else
            {
                /* mono mode */
                channel = 1 ;
                /* calculate mono sampling rate */
                SampRate = Type8SampRateMono(stVoc.TC) ;
            }

            printf("\n8 Extended\t\t\t%ld Hz",SampRate) ;

            switch ((int)(stVoc.Pack & 0xff))
            {
                case BIT8p0_UNPACK :
                    printf(" \t%s 8 bit unsigned",MonoStereo[channel]) ;
                    break ;
                case BIT4p0_PACK :
                    printf(" \t%s 4 bit ADPCM",MonoStereo[channel]) ;
                    break ;
                case BIT2p6_PACK :
                    printf(" \t%s 2.6 bit ADPCM",MonoStereo[channel]) ;
                    break ;
                case BIT2p0_PACK :
                    printf(" \t%s 2 bit ADPCM",MonoStereo[channel]) ;
                    break ;
                default :
                    printf(" \tUnknown data packed format") ;
                    break ;
            }
            /* Global variable */
            BlkType8Exist = 1 ;
            return (0) ;
        }
        else
            printf("\nBlock type 8 - Invalid format.") ;
    }
    else
        printf("\nError Reading file.") ;

    return (1) ;

}

/* ------------------------------------------------------------------------ */

int BlockTypeNine(int handle)
{
    struct
    {
        BYTE    BlkLen1 ;
        WORD    BlkLen2 ;
        DWORD   dwSamplesPerRec ;
        BYTE    bBitsPerSample ;
        BYTE    bChannel ;
        WORD    wFormat ;
        BYTE    reserved[4] ;
    } stVoc ;

    WORD    wByteRead ;
    long    Len ;
    int     bit ;


    /* read block header */
    if (!sbkDosRead(handle,(char far *)&stVoc,
                 (WORD)sizeof(stVoc),(WORD far *)&wByteRead))
    {
        if (wByteRead == (WORD)sizeof(stVoc))
        {
            /* global variable stereo oR mono */
            channel = (int)stVoc.bChannel ;
            bit = (int)stVoc.bBitsPerSample ;

            /* calculate block length excluding block header */
            Len = stVoc.BlkLen2 ;
            Len = (Len << 8) + stVoc.BlkLen1 - (BYTE)12 ;

            printf("\n9 Voice\t\t%ld   \t%ld Hz",Len,stVoc.dwSamplesPerRec) ;


            /* show voice pack format */
            switch (stVoc.wFormat)
            {
                case VOC_FORMAT_08_PCM :
                    printf(" \t%s %d bit unsigned",MonoStereo[channel],bit) ;
                    break ;
                case VOC_FORMAT_CT4_ADPCM :
                case VOC_FORMAT_CT3_ADPCM :
                case VOC_FORMAT_CT2_ADPCM :
                    printf(" \t%s %d bit ADPCM",MonoStereo[channel],bit) ;
                    break ;
                case VOC_FORMAT_16_PCM :
                    printf(" \t%s %d bit signed",MonoStereo[channel],bit) ;
                    break ;
                case VOC_FORMAT_ALAW :
                    printf(" \t%s %d bit ALAW",MonoStereo[channel],bit) ;
                    break ;
                case VOC_FORMAT_MULAW :
                    printf(" \t%s %d bit MULAW",MonoStereo[channel],bit) ;
                    break ;
                case VOC_FORMAT_CREATIVE_ADPCM :
                    printf(" \t%s %d bit ADPCM",MonoStereo[channel],bit) ;
                    break ;
                default :
                    printf(" \tUnknown data packed format") ;
                    break ;
            }
            /* move file pointer to next block */
            lseek(handle,Len,SEEK_CUR) ;
            return (0) ;
        }
        else
            printf("\nBlock type 9 - Invalid format.") ;
    }
    else
        printf("\nError Reading file.") ;

    return (1) ;

}

#ifdef _MSC_VER
    #pragma pack()
#endif
/* End of file */
