fasciano@IRO.UMontreal.CA (Massimo Fasciano) (10/08/90)
Hi everybody, a while ago, somebody posted some sources in ASM/C that showed how to access the sound driver (CT-VOICE.DRV) used to record/playback digitized sounds on the Soundblaster. I erased them by mistake... Could somebody send them to me? If you don't have these sources, but have a copy of the old SB manual, (it contains a description of the 12(?) functions of the driver) could you summarize the functions for me? Thanks in advance -- Massimo Fasciano (fasciano@iro.umontreal.ca)
tjw@unix.cis.pitt.edu (TJ Wood WA3VQJ) (10/13/90)
In article <1990Oct7.215640.24241@IRO.UMontreal.CA> fasciano@IRO.UMontreal.CA (Massimo Fasciano) writes: >Hi everybody, > a while ago, somebody posted some sources in ASM/C that showed > how to access the sound driver (CT-VOICE.DRV) used to record/playback > digitized sounds on the Soundblaster. I erased them by mistake... > Could somebody send them to me? I'll plead guilty! :-) I did it! When I get a chance, this weekend, I'll send you my routines. If anybody else wants them, send me e-mail. I'm trying to get caught up with everything here at work, so I'm somewhat behind on my mail, though! I've not had much time to play with the sound blaster card lately. 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
tjw@unix.cis.pitt.edu (Terry J. Wood) (10/15/90)
Well, since my mailbox has been flooded for requests for this code, I conclude that sound blaster sales must be quite brisk! So, I'm going to post my sound blaster interface routines (SB1.ASM) that work with the CT-VOICE.DRV file. Also enclosed please find 2 demo programs: RECORD1.C and PLAY1.C which demonstrate how to call the SB1.ASM routines. RECORD1.C records sounds to a file. PLAY1.C plays those sounds. They do NOT work with ".VOC" files since ".VOC" files have headers in them that I've not bother to write. RECORD1.C will play a ".VOC" file when it's done recording and you should copy TV4.VOC (that comes with the VOXKIT) to EXIT.VOC. You can examine the routine "EXIT_VOICE" to see how to strip off headers of ".VOC" files before playing them, if you're interesting in doing that. PLAY1.C and RECORD1.C are not really all that well written. I was more interested in getting the SB1.ASM files to work. One of the problems that I faced is that the CT-VOICE.DRV file must be loaded at the beginning of a memory segment. I used the crude method of using the huge memory model and allocating a 64K byte vector array for use with the CT-VOICE.DRV file. While this guarantees that the code is loaded at the beginning of a segment, it's really wastefull. The driver is less than 3K bytes long! If I was really interested in the application program, I would use the rest of the 60 some K vector array for playing/recording sounds. Or I might learn more about the assembler and figure out how to allocate memory correctly! :-) I guess I should point out that I usually program VMS systems and I avoid using assembly like I avoid the plague. Try as I might, I couldn't find a way to load the proper registers from "C" and I broke down and purchased a macro assembler for this project. (How red my face is now :-) RECORD.C and PLAY.C are similar programs to what now comes with the sound blaster. When I bought my sound blaster, no such programs existed. Great minds think alike, I guess. These programs will gobble up your hard disk's free space if you record more than several minutes of sound, btw! They don't check for write errors, so don't be suprised if you use it all up while recording! If you want to play files generated with RECORD1.C with the sound blaster provided programs, use VOC-HDR.EXE to add headers to the files. One thing that my RECORD1.C will do that the sound blaster counter part will not do, is let you listen in to the recording. If you invoke RECORD1 program with "RECORD DEMO.SNG 12000 1" (which means record to DEMO.SNG at 12KHz and TURN THE SPEAKER ON), you'll hear what you're recording. Don't panic that you'll also hear squeaks and squacks, the recording will probably come out OK. The manual says to turn off the speaker during recording, but as burger king sez: "Sometimes you gotta break the rules". Becareful not to blow your speakers/ears out, though. I've come close! Note that I'm using V5.0 of Microsoft C and V.5.1 of Microsoft Macro Assembler to compile/assemble these routines. These routines are in the public domain. They are offered to you free of charge, with no warranty of course! No warranty whatsoever! They are yours to do with as you see fit and you assume all responsibility for their use. (I'm not a lawyer, nor do I play one on TV, but I wish I did). If you make a million dollars (or more) off of these routines, please send me 10% :-). Seriously, if you do make something neat from them, send me a copy of the source code so I can play with it too. Games are always welcome. (Anybody got a Star Trek program in "C"? I've recorded some great Star Trek sound effects and I'd love to add them to one of those old Star Trek Games. If anyone out there wants to exchange recordings, I'd be game for that too!) Oh, yes one last thing: I've bought the developer's kit. If you're going to do some serious programming with the board, you'll probably want to get a copy for yourself. It's about $100 from creative labs. It provides an interface for almost everything the board will do. If you're a "haquer" like myself, it will provide you with hours of entertainment. So, here they are! Enjoy: SB1.ASM ---------------------------------------------------------------------- PGROUP GROUP INTERFACE .MODEL HUGE ; INTERFACE SEGMENT BYTE PUBLIC 'PROG' INTERFACE SEGMENT BYTE PUBLIC 'DATA' ASSUME CS:INTERFACE PUBLIC _SB0 ;Get Version number Returns: AH - Major AL - Minor _SB0 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,0 ;Init Function code call dword ptr [bp+6] ;call the driver pop bp ret _SB0 ENDP PUBLIC _SB1 ;Set Base I/O Address _SB1 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,3 ;Init Function code mov ax,[bp+6] ;Set AX to be the base address (210x - 260x) call dword ptr [bp+8] ;call the driver pop bp ret _SB1 ENDP PUBLIC _SB2 ;Set Interrupt number _SB2 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,3 ;Init Function code mov ax,[bp+6] ;Set AX to be the interrupt number for the DMA (2,3,5 or 7) call dword ptr [bp+8] ;call the driver pop bp ret _SB2 ENDP PUBLIC _SB3 ;Init Sound Blaster _SB3 PROC FAR ;Returns: 0 - OK!; 1 - Voice Card Fails ; 2 - I/O read/write fails; 3 -Interrupt for DMA Fails 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,3 ;Init Function code call dword ptr [bp+6] ;call the driver pop bp ret _SB3 ENDP PUBLIC _SB4 ;Speaker off(0)/on(1) _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 call dword ptr [bp+8] ;call the driver pop bp ret _SB4 ENDP PUBLIC _SB5 ;Set status word address _SB5 PROC FAR push bp ;save the base pointer mov bp,sp ;store the beginning of the stack in bp push di mov bx,5 ;function code 5 mov di,[bp+6] ;offset address of status word into reg di mov es,[bp+8] ;Segment address call dword ptr [bp+10] ;call the driver pop di ;restore the value of the di register pop bp ;restore the base pointer ret _SB5 ENDP PUBLIC _SB6 ;Output voice _SB6 PROC FAR push bp ;save the base pointer mov bp,sp ;store the beginning of the stack in bp push di mov bx,6 ;function code 6 mov di,[bp+6] ;offset address of sound buffer into reg di mov es,[bp+8] ;Segment address call dword ptr [bp+10] ;call the driver pop di ;restore the value of the di register pop bp ;restore the base pointer ret _SB6 ENDP PUBLIC _SB7 ;Input voice _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 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 call dword ptr [bp+16] ;call the driver pop di ;restore the value of the di register pop bp ;restore the base pointer ret _SB7 ENDP PUBLIC _SB8 ;Stop Voice Process _SB8 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,8 ;Init Function code call dword ptr [bp+6] ;call the driver pop bp ret _SB8 ENDP PUBLIC _SB9 ;Uninstall Driver _SB9 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,9 ;Init Function code call dword ptr [bp+6] ;call the driver pop bp ret _SB9 ENDP PUBLIC _SB10 ;Pause Output Voice _SB10 PROC FAR ;Returns: 0 - OK!; 1- Voice output not in progress 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,10 ;Init Function code call dword ptr [bp+6] ;call the driver pop bp ret _SB10 ENDP PUBLIC _SB11 ;Continue Output Voice _SB11 PROC FAR ;Returns: 0 - OK!; 1- No Voice to continue 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,11 ;Init Function code call dword ptr [bp+6] ;call the driver pop bp ret _SB11 ENDP PUBLIC _SB12 ;Break-out Looping Voice _SB12 PROC FAR ;Returns: 0 - OK!; 1- No voice in loop 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 call dword ptr [bp+8] ;call the driver pop bp ret _SB12 ENDP INTERFACE ENDS END ---------------------------------------------------------------------- RECORD1.C ---------------------------------------------------------------------- #include <stdio.h> char huge SB_DRIVER[65536]; unsigned int far SB_STATUS_WORD; unsigned long int array_size = 65536; char huge array1[65536]; char huge array2[65536]; extern int far SB0(); /* Get version of driver */ extern int far SB1(); /* Set Base I/O Address */ extern int far SB2(); /* Set Interrupt number (2,3,5, or 7) */ extern int far SB3(); /* Init Driver */ extern int far SB4(); /* Turn speaker off (0) or (1) On */ extern int far SB5(); /* Tell Driver the address of the Status Word */ extern int far SB6(); /* Play voice */ extern int far SB7(); /* Record Voice */ extern int far SB8(); /* Stop Voice Process */ extern int far SB9(); /* Uninstall Driver (clean up, etc) */ extern int far SB10(); /* Pause playing voice */ extern int far SB11(); /* Continue playing voice */ extern int far SB12(); /* Break-out looping voice */ unsigned int c; unsigned long int count; unsigned int SB_DRIVER_SIZE; unsigned long int start_seg_addr; char far *ptr; main(argc,argv) int argc; char *argv[]; { FILE *fp; unsigned long int i; unsigned int j; unsigned short int k; unsigned int digi_rate; unsigned int speaker_setting; unsigned int version; 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); } if (argc >= 3){ sscanf(argv[2],"%d",&digi_rate); } else { printf("Rate to Scan? (4000 to 12000) >"); scanf("%d",&digi_rate); printf("\n\n"); } if (argc >= 4){ sscanf(argv[3],"%d",&speaker_setting); } else { speaker_setting = 0; } k = LOAD_DRIVER(); version = SB0(SB_DRIVER); printf("CT-DRIVER version is %d.%d\n\n",(version & 0xFF00) >> 8,version & 0x00FF); k = SB3(SB_DRIVER); if( k != 0){ printf("Init FAILS %d\n",k); } k = SB5(&SB_STATUS_WORD,SB_DRIVER); if( k != 0){ printf("Set status word FAILS %d\n",k); } k = SB4(speaker_setting,SB_DRIVER); if( k != 0){ printf("Speaker control FAILS %d\n",k); } printf("Press any key to BEGIN Recording..."); while (kbhit() == 0); k = getche(); printf("\rPress any key to STOP Recording... "); k = SB7(array1,digi_rate,array_size,SB_DRIVER); 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,SB_DRIVER); 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,SB_DRIVER); 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,SB_DRIVER); if( k != 0){ printf("Speaker on FAILS %d\n",k); } printf("\rYour recording is complete... "); k = EXIT_VOICE(); k = SB6(array1,SB_DRIVER); SB_WAIT(); if( k != 0){ printf("Voice out FAILS %d\n",k); } k = SB4(0,SB_DRIVER); if( k != 0){ printf("Speaker off FAILS %d\n",k); } k = SB9(SB_DRIVER); if( k != 0){ printf("Uninstall driver 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; if( (fp = fopen("CT-VOICE.DRV","rb")) == NULL){ return(1); } SB_DRIVER_SIZE = 0; while ( (c = fgetc(fp)) != EOF){ SB_DRIVER[SB_DRIVER_SIZE] = c; SB_DRIVER_SIZE++; } fclose(fp); return(0); } ---------------------------------------------------------------------- PLAY1.C ---------------------------------------------------------------------- #include <stdio.h> char huge SB_DRIVER[65536]; unsigned int far SB_STATUS_WORD; unsigned long int array_size = 65536; char huge array1[65536]; char huge array2[65536]; extern int far SB0(); /* Get version of driver */ extern int far SB1(); /* Set Base I/O Address */ extern int far SB2(); /* Set Interrupt number (2,3,5, or 7) */ extern int far SB3(); /* Init Driver */ extern int far SB4(); /* Turn speaker off (0) or (1) On */ extern int far SB5(); /* Tell Driver the address of the Status Word */ extern int far SB6(); /* Play voice */ extern int far SB7(); /* Record Voice */ extern int far SB8(); /* Stop Voice Process */ extern int far SB9(); /* Uninstall Driver (clean up, etc) */ extern int far SB10(); /* Pause playing voice */ extern int far SB11(); /* Continue playing voice */ extern int far SB12(); /* Break-out looping voice */ unsigned int c; unsigned long int count; unsigned int SB_DRIVER_SIZE; unsigned long int start_seg_addr; char far *ptr; main(argc,argv) int argc; char *argv[]; { FILE *fp; unsigned long int i; unsigned int j; unsigned short int k; unsigned int digi_rate; unsigned int version; 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 */ version = SB0(SB_DRIVER); /* Get version number of driver */ printf("CT-DRIVER version is %d.%d\n\n",(version & 0xFF00) >> 8,version & 0x00FF); k = SB3(SB_DRIVER); /* Init */ if( k != 0){ printf("Init FAILS %d\n",k); } k = SB5(&SB_STATUS_WORD,SB_DRIVER); /* Assign the status word */ if( k != 0){ printf("Set status word FAILS %d\n",k); } k = SB4(1,SB_DRIVER); /* Speaker On */ if( k != 0){ printf("Speaker on FAILS %d\n",k); } while(now_at_end == 0){ printf("loading buffer 1\r"); 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){ printf(" \r"); SB_WAIT(); /* Wait for sb to finish playing array2 */ if(kbhit() != 0){ /* If any key is pressed, exit */ k = getche(); break; } } k = SB6(array1,SB_DRIVER); if( k != 0){ printf("Voice out FAILS %d\n",k); } now_playing = 1; /* raise the flag */ if (now_at_end != 0){ break; } printf("loading buffer 2\r"); 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; } } printf(" \r"); 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,SB_DRIVER); if( k != 0){ printf("Voice out array2 FAILS %d\n",k); } } fclose(fp); k = SB4(0,SB_DRIVER); if( k != 0){ printf("Speaker off FAILS %d\n",k); } k = SB9(SB_DRIVER); if( k != 0){ printf("Uninstall driver FAILS %d\n",k); } } int SB_WAIT() /* Waits until sound board is done */ { 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; if( (fp = fopen("CT-VOICE.DRV","rb")) == NULL){ return(1); } SB_DRIVER_SIZE = 0; while ( (c = fgetc(fp)) != EOF){ SB_DRIVER[SB_DRIVER_SIZE] = c; SB_DRIVER_SIZE++; } fclose(fp); return(0); } -- 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