[comp.sys.ibm.pc.hardware] Soundblaster code revisited

tjw@unix.cis.pitt.edu (TJ Wood WA3VQJ) (08/25/90)

I've updated the code that I wrote for the Soundblaster.  This new code
will work with the Microsoft C "huge" memory model.  

The applications "PLAY.C" and "RECORD.C" turn your PC into a tape (CD?)
recorder by using the Soundblaster's A/D D/A converters.

To compile/assemble these routines, I used MSC 5.0 and MSA 4.0.  You'll
also need CT-VOICE.DRV (which is part of the VOXKIT) and you'll need to
copy one of the ".VOC" files to EXIT.VOC (I used TV4.VOC which comes
with the VOXKIT).  Be sure to compile this code with the /AH switch to
get the huge memory model.

The general syntax for using these routines is: "RECORD fn.ext", where
fn.ext is the name of the file you wish to record into.  PLAY works the
same way.  The RECORD program prompts for a scan rate (5000 to 13000 Hz). 

If you try this code, please drop me a line via e-mail.  Maybe we can
share some ideas about the board.  I've sent for the programmer's tool
kit, and I hope to figure out how to use the other "voices" of the
board.

Anyway here's the code:

SB.ASM:


PGROUP  GROUP   INTERFACE
    INTERFACE SEGMENT   BYTE PUBLIC 'DATA'

    ASSUME  CS:INTERFACE

    PUBLIC _sb_driver
_SB_DRIVER PROC    FAR

    db  2500 dup (0)

_SB_DRIVER ENDP


    PUBLIC  _SB_STATUS_WORD
_SB_STATUS_WORD PROC FAR 
        dw 0
_SB_STATUS_WORD ENDP


    PUBLIC _SB3          ;Init Sound Board
_SB3 PROC    FAR


    mov     bx,3        ;Init Function code

    mov     cx,0
    jmp     cx          ;Go to top of segment (where the driver starts)

_SB3 ENDP

    PUBLIC _SB4          ;Speaker off(0)/on(1) Sound Board Interface
_SB4 PROC    FAR


    push    bp          ;save the original base pointer
    mov     bp,sp       ;store the beginning of the stack in bp
                        ;push (in order) any additional registers here

    mov     bx,4
    mov     ax,[bp+6]   ;Function code
    pop     bp          ;restore the base pointer

    mov     cx,0
    jmp     cx          ;Go to top of segment (where the driver starts)

_SB4 ENDP

    PUBLIC _SB5          ;Set status word address Sound Board Interface
_SB5 PROC    FAR


    push    bp          ;save the base pointer
    mov     bp,sp       ;store the beginning of the stack in bp

    push    di		;save for later
    push    si

    mov     bx,5        ;function code 5

    mov     di,[bp+6]   ;offset address of status word into reg di
    mov     es,[bp+8]   ;Segment address

    mov     si,0        ;set si to point to the start of the driver

    push    cs          ;set up for a far return
    call    si          ;call the driver with a pseudo "far" call


    pop     si          ;restore the value of the si register 
    pop     di          ;restore the value of the di register
    pop     bp          ;restore the base pointer
    ret                 ;"Clem Clone! Back to the shadows again! (Appologies to The Firesign Theatre)


_SB5 ENDP

    PUBLIC _SB6          ;Output voice - Sound Board Interface
_SB6 PROC    FAR

    push    bp          ;save the base pointer
    mov     bp,sp       ;store the beginning of the stack in bp

    push    di
    push    si

    mov     bx,6        ;function code 6

    mov     di,[bp+6]   ;offset address of sound buffer into reg di
    mov     es,[bp+8]   ;Segment address

    mov     si,0        ;set si to point to the start of the driver

    push    cs          ;set up for a far return
    call    si          ;call the driver with a pseudo "far" call


    pop     si          ;restore the value of the si register 
    pop     di          ;restore the value of the di register
    pop     bp          ;restore the base pointer
    ret                 ;"Clem Clone! Back to the shadows again! (Appologies to The Firesign Theatre)

_SB6 ENDP


    PUBLIC _SB7          ;Input voice Sound Board Interface
_SB7 PROC    FAR


    push    bp          ;save the original base pointer
    mov     bp,sp       ;store the beginning of the stack in bp

    push    di          ;push (in order) any additional registers here
    push    si

    mov     bx,7
    mov     di,[bp+6]  ;offset address of sound buffer into reg di
    mov     es,[bp+8]  ;Segment address
    mov     ax,[bp+10] ;Sampling rate
    mov     cx,[bp+12] ;ditto
    mov     dx,[bp+14] ;Size of buffer


    mov     si,0        ;set si to point to the start of the driver

    push    cs          ;set up for a far return
    call    si          ;call the driver with a pseudo "far" call


    pop     si          ;restore the value of the si register 
    pop     di          ;restore the value of the di register 
    pop     bp          ;restore the base pointer
    ret                 ;"Clem Clone! Back to the shadows again! (Appologies to The Firesign Theatre)

_SB7 ENDP




    PUBLIC _SB8          ;Stop Voice Process Sound Board Interface
_SB8 PROC    FAR


    mov     bx,8

    mov     ax,0
    jmp     ax          ;Go to top of segment (where the driver starts)

_SB8 ENDP

    PUBLIC _SB9          ;Uninstall Driver - Sound Board Interface
_SB9 PROC    FAR


    mov     bx,9

    mov     ax,0
    jmp     ax          ;Go to top of segment (where the driver starts)

_SB9 ENDP

    PUBLIC _SB10          ;Sound Board Interface
_SB10 PROC    FAR


    mov     bx,10

    mov     ax,0
    jmp     ax          ;Go to top of segment (where the driver starts)

_SB10 ENDP

    PUBLIC _SB11         ;Sound Board Interface
_SB11 PROC    FAR


    mov     bx,11

    mov     ax,0
    jmp     ax          ;Go to top of segment (where the driver starts)

_SB11 ENDP

    PUBLIC _SB12          ;Sound Board Interface
_SB12 PROC    FAR


    push    bp          ;save the original base pointer
    mov     bp,sp       ;store the beginning of the stack in bp
                        ;push (in order) any additional registers here

    mov     bx,12
    mov     ax,[bp+6]   ;load Parameter into ax
    pop     bp          ;restore the base pointer

    mov     cx,0
    jmp     cx          ;Go to top of segment (where the driver starts)

_SB12 ENDP

    INTERFACE ENDS

    END
-------------------------------CUT HERE----------------------------------

PLAY.C


#include <stdio.h>

extern  char far SB_DRIVER;
unsigned long int array_size = 50000;
char huge array1[50000];
char huge array2[50000];
extern  unsigned int far SB_STATUS_WORD;

extern  int far SB3();
extern  int far SB4();
extern  int far SB5();
extern  int far SB6();
extern  int far SB7();
extern  int far SB8();
extern  int far SB9();
extern  int far SB10();
extern  int far SB11();
extern  int far SB12();

unsigned int c;
unsigned long int count;
unsigned int SB_DRIVER_SIZE;
unsigned long int start_seg_addr;
char far *ptr;

unsigned char SB_DRIVER_CODE[3000];


main(argc,argv)
int argc;
char *argv[];
{
    FILE *fp;

    unsigned long  int i;
    unsigned int j;
    unsigned short int k;
    unsigned int digi_rate;


    int LOAD_DRIVER();

    int now_at_end=0;
    int now_playing=0;

    if (argc != 2){
        printf("Usage:  Play fn.ext\n");
        exit(1);
    }

    if ( (fp = fopen(argv[1],"rb") ) == NULL) { /* Open input file */
        printf("PLAY: error opening %s , file not found\n",argv[1]);
        exit(2);
    }

    printf("Press any key to EXIT\n\n");    

    k = LOAD_DRIVER();      /* Read the VOXKIT driver into memory */

    k = SB3();              /* Init */
    if( k != 0){
        printf("Init FAILS %d\n",k);
    }

    k = SB5(&SB_STATUS_WORD);   /* Assign the status word */
    if( k != 0){
        printf("Set status word FAILS %d\n",k);
    }


    k = SB4(1);             /* Speaker On */
    if( k != 0){
        printf("Speaker on FAILS %d\n",k);
    }


    while(now_at_end == 0){

        SAVE_DRIVER();
        printf("loading buffer 1\r");
        RESTORE_DRIVER();


        for(i=0; i < array_size; i++){
            c = fgetc(fp);
            if(c != EOF){
                array1[i] = c;
            }
            else {
                if(i == 0){
                    now_at_end = 1;
                    break;
                }
                else {
                    now_at_end = 2;
                    for (j = i; j < array_size; j++){
                        array1[j] = 0;
                    }
                }
                break;
            }
        }

        if(now_playing == 1){

            SB_WAIT();  /* Wait for sb to finish playing array2 */

            if(kbhit() != 0){   /* If any key is pressed, exit */
                k = getche();
                break;
            }

        }

        k = SB6(array1);

        if( k != 0){
            printf("Voice out FAILS %d\n",k);
        }

        now_playing = 1;    /* raise the flag */

        if (now_at_end != 0){
            break;
        }


        SAVE_DRIVER();
        printf("loading buffer 2\r");
        RESTORE_DRIVER();


        for(i=0; i < array_size; i++){
            c = fgetc(fp);
            if(c != EOF){
                array2[i] = c;
            }
            else {
                if(i == 0){
                    now_at_end = 1;
                    break;
                }
                else {
                    now_at_end = 2;
                    for (j = i; j < array_size; j++){
                        array2[j] = 0;
                    }
                }
                break;
            }
        }


        SB_WAIT();  /* Wait for sb to finish playing array1 */

        if(kbhit() != 0){   /* If any key is pressed, exit */
            k = getche();
            break;
        }

        if(now_at_end != 0){
            break;
        }

        k = SB6(array2);

        if( k != 0){
            printf("Voice out array2 FAILS %d\n",k);
        }

    }

    fclose(fp);

    k = SB4(0);
    if( k != 0){
        printf("Speaker on FAILS %d\n",k);
    }


}

int SB_WAIT()  /* Waits until sound board is done */
{               /* Returns the number of loops we made */
    unsigned int j;

    do {    /* Loop until sound board is done */ 
        j = SB_STATUS_WORD;
    } while(j != 0);

}

int LOAD_DRIVER()   /* Load driver from disk file */
{

    FILE *fp;

    ptr = &SB_DRIVER;
    start_seg_addr = ptr;
    start_seg_addr = start_seg_addr & 0xFFFF0000;
    ptr = start_seg_addr;

    if( (fp = fopen("CT-VOICE.DRV","rb")) == NULL){
        return(1);
    }

        
    SB_DRIVER_SIZE = 0;
    while ( (c = fgetc(fp)) != EOF){
        *(ptr+SB_DRIVER_SIZE) = c;
        SB_DRIVER_SIZE++;
    }

    fclose(fp);

    return(0);
}


int SAVE_DRIVER()   /* Save current state of driver from memory */
{

    int i;

    ptr = start_seg_addr;

    for (i=0; i < SB_DRIVER_SIZE; i++){
        SB_DRIVER_CODE[i] = *(ptr+i);
    }
}

int RESTORE_DRIVER()    /* Restore driver back into memory */
{
    int i;

    ptr = start_seg_addr;

    for (i=0; i < SB_DRIVER_SIZE; i++){
        *(ptr+i) = SB_DRIVER_CODE[i];
    }
}

----------------------------CUT HERE---------------------------


RECORD.C


#include <stdio.h>

extern  char far SB_DRIVER;
unsigned long int array_size = 50000;
char huge array1[50000];
char huge array2[50000];
extern  unsigned int far SB_STATUS_WORD;

extern  int far SB3();
extern  int far SB4();
extern  int far SB5();
extern  int far SB6();
extern  int far SB7();
extern  int far SB8();
extern  int far SB9();
extern  int far SB10();
extern  int far SB11();
extern  int far SB12();

unsigned int c;
unsigned long int count;
unsigned int SB_DRIVER_SIZE;
unsigned long int start_seg_addr;
char far *ptr;

unsigned char SB_DRIVER_CODE[3000];

main(argc,argv)
int argc;
char *argv[];
{

    FILE *fp;

    unsigned long  int i;
    unsigned int j;
    unsigned short int k;
    unsigned int digi_rate;

    int LOAD_DRIVER();


    if (argc != 2){
        printf("Usage:  RECORD fn.ext\n");
        exit(1);
    }

    if ( (fp = fopen(argv[1],"wb") ) == NULL) { /* Open output file */
        printf("RECORD: error opening %s\n",argv[1]);
        exit(2);
    }

    printf("Rate to Scan? (5000 to 13000) >");
    scanf("%d",&digi_rate);
    printf("\n\n");



    k = LOAD_DRIVER();

    k = SB3();
    if( k != 0){
        printf("Init FAILS %d\n",k);
    }

    k = SB5(&SB_STATUS_WORD);
    if( k != 0){
        printf("Set status word FAILS %d\n",k);
    }

    k = SB4(0);
    if( k != 0){
        printf("Speaker off FAILS %d\n",k);
    }

    SAVE_DRIVER();
    printf("Press any key to BEGIN Recording...");

    while (kbhit() == 0);
    k = getche();

    printf("\rPress any key to STOP Recording... ");
    RESTORE_DRIVER();

    k = SB7(array1,digi_rate,array_size);

    if( k != 0){
        printf("Voice in FAILS %d\n",k);
    }


    while (1 == 1){

        SB_WAIT();

        if(kbhit() != 0){
            k = getche();
            break;
        }

        k = SB7(array2,digi_rate,array_size);

        for(i=0; i < array_size; i++){  /* Write first buffer to file */
            fputc(array1[i],fp);
        }

        SB_WAIT();


        k = SB7(array1,digi_rate,array_size);

        if( k != 0){
            printf("Voice in FAILS %d\n",k);
        }

        for(i=0; i < array_size; i++){  /* Write second buffer to file */
            fputc(array2[i],fp);
        }
    }

    for(i=0; i < array_size; i++){  /* Write first buffer to file */
        fputc(array1[i],fp);
    }

    fclose(fp);

    k = SB4(1);
    if( k != 0){
        printf("Speaker on FAILS %d\n",k);
    }


    SAVE_DRIVER();
    printf("\rYour recording is complete...      ");
    RESTORE_DRIVER();

    k = EXIT_VOICE();    

    k = SB6(array1);

    SB_WAIT();

    if( k != 0){
        printf("Voice out FAILS %d\n",k);
    }

    k = SB4(0);
    if( k != 0){
        printf("Speaker off FAILS %d\n",k);
    }

}

int EXIT_VOICE()
{
    FILE *fp;

    unsigned long int i;

    if( (fp = fopen("EXIT.VOC","rb")) == NULL){
        return(1);
    }

    for(i=0; i < 0x1a; i++){ /*Strip off the header */
        c = fgetc(fp);
    }

    i=0;
    while ( (c = fgetc(fp)) != EOF){
        array1[i] = c;
        i++;
    }

    fclose(fp);

    return(0);
}


int SB_WAIT()  /* Waits until sound board is done */
{               /* Returns the number of loops we made */
    unsigned int j;

    do {    /* Loop until sound board is done */ 
        j = SB_STATUS_WORD;
    } while(j != 0);

}

int LOAD_DRIVER()   /* Load driver from disk file */
{

    FILE *fp;

    ptr = &SB_DRIVER;
    start_seg_addr = ptr;
    start_seg_addr = start_seg_addr & 0xFFFF0000;
    ptr = start_seg_addr;

    if( (fp = fopen("CT-VOICE.DRV","rb")) == NULL){
        return(1);
    }

        
    SB_DRIVER_SIZE = 0;
    while ( (c = fgetc(fp)) != EOF){
        *(ptr+SB_DRIVER_SIZE) = c;
        SB_DRIVER_SIZE++;
    }

    fclose(fp);

    return(0);
}


int SAVE_DRIVER()   /* Save current state of driver from memory */
{

    int i;

    ptr = start_seg_addr;

    for (i=0; i < SB_DRIVER_SIZE; i++){
        SB_DRIVER_CODE[i] = *(ptr+i);
    }
}

int RESTORE_DRIVER()    /* Restore driver back into memory */
{
    int i;

    ptr = start_seg_addr;

    for (i=0; i < SB_DRIVER_SIZE; i++){
        *(ptr+i) = SB_DRIVER_CODE[i];
    }
}

---------------------CUT HERE------------------------
-- 
INTERNET: tjw@unix.cis.pitt.edu  BITNET: TJW@PITTVMS  CC-NET: 33802::tjw
UUCP: {decwrl!decvax!idis, allegra, bellcore}!pitt!unix.cis.pitt.edu!tjw
 And if dreams could come true, I'd still be there with you,
 On the banks of cold waters at the close of the day. - Craig Johnson 

tjw@unix.cis.pitt.edu (TJ Wood WA3VQJ) (08/29/90)

In article <31643@unix.cis.pitt.edu> tjw@unix.cis.pitt.edu I wrote:

>I've updated the code that I wrote for the Soundblaster.  This new code
>will work with the Microsoft C "huge" memory model.  

>The applications "PLAY.C" and "RECORD.C" turn your PC into a tape (CD?)
>recorder by using the Soundblaster's A/D D/A converters.

I just received my "developer's kit" today.  In it was another "User Reference
Manual" and new floppy disks for version 1.5 of the Soundboard software.

Also included are two new (to me) programs: SBTALKER and DR SBAITSO.

These were not included in my original package.  This leads me to
believe that I bought an "old" package (just how old, I'm not really
sure).

I notice all of the information that I used to construct my macro
assembly interface to CT-VOICE.DRV is not contained in the new user
reference manual.  If you've been unable to make heads-nor-tails of my
code, please accept my appology!  I assumed that everyone had the same
manual with the same appendix.  I'll post a description of the
routines and their use in the near future.  The appendix that I used
is included in the developer's kit.  Also there's a "tip" that explains
why my original polling loop would go into an infinite loop: the compiler
is playing optimization tricks on me and assigning a register instead of
the memory location I wanted!  (I'm not crazy after all) ;-)


I'm also concerned that perhaps I have old hardware.  The new flyer I have on
the SOUNDBLASTER says that the DAC can sample at 23KHz.  The old VOXKIT program
that I have limits sampling to 13KHz.  If I run my RECORD program at > 13KHz
all it does is "speed up" the playback.  Perhaps this is a function of
the CT-VOICE.DRV that came with my original package.  Has anyone tried their
VOXKIT at 23KHz?  I'm going to install the "new" software and see what happens.

The developer's kit looks interesting.  I'll post a "review" of sorts in the
future.

Terry

-- 
INTERNET: tjw@unix.cis.pitt.edu  BITNET: TJW@PITTVMS  CC-NET: 33802::tjw
UUCP: {decwrl!decvax!idis, allegra, bellcore}!pitt!unix.cis.pitt.edu!tjw
 And if dreams could come true, I'd still be there with you,
 On the banks of cold waters at the close of the day. - Craig Johnson