tjw@unix.cis.pitt.edu (TJ Wood WA3VQJ) (09/01/90)
For this week, anyway! ;-) I purchased MSC 5.1, so that I could join the civilized world and I've made a couple of significant changes to the code I wrote to interface the Sound Blaster AD/DA program loadable driver to the C language. First, I got rid of some very klugie code that overwrote some segments. No more "swapping" everytime I want to use printf. Of course, I introduced something that is almost as klugie: I allocate 64K bytes for the driver (which is less than 3K bytes long). Given enough free time, I'll get "malloc" or "halloc" to work and I will not have to play these games. (Oh, if only I didn't have to work for a living...) In a "real" application, I could use the extra space I've set aside for the driver for buffering the data from/to the AD/DA. I just wasn't concerned about efficiency. Other changes that I made include a new `syntax' for the record command: "RECORD fn.ext digi-rate speaker-status". To record to the file ANY.SNG at 12KHz and to have the speaker turned on during recording, you'd type: "RECORD ANY.SNG 12000 1". My old Soundblaster user's guide (and the developer's kit) state that you should NOT have the speaker turned on during recording. So naturally, I did it, just to see why. We'll there is a lot of whistling and chirping that can be heard during the recording, but I'd rather be able to monitor what the soundblaster is recording, even if it isn't "perfect" quality. I'm also now passing the address of the driver down to the SBn() routines. It's always the last (or only) argument of the routines. Functions 1-12 (of the 13 possible) are now supported. I also now call SB0 (get driver version number) and SB9 (uninstall driver) as part of the startup/shutdown of the programs. In the past week, I've received a couple of questions worth mentioning here. One was "Why can't your play.c program play VOC files". The answer is that VOC files have a header on them. To play a VOC file, all one needs to do is strip off the header. My RECORD.C has a function called EXIT_VOICE which strips off the header off of the EXIT.VOC file. Since what I really wanted to do was record entire songs, not just a couple of seconds of voice, I really didn't think about putting headers on the files (or about stripping those headers off). If you want to play VOC files, copy the for loop out of EXIT_VOICE and use it to strip off that header. Another question was: "Why did you write programs that already exist?", referring to VPLAY and VREC that (now) come with the sound blaster. Well, my old package didn't have these. I guess great minds think alike. Lastly, a very good question was: "How'd you figure all this out?". Well, my old user manual contained information not in the new manual. The driver and the Creative Voice Format was documented. This is now in the developer's kit. I thought everyone got this. That's why I didn't really comment the code. On the topic of the developer's kit: So far it looks pretty good! There is a great deal of technical information as well as listings of the code that Creative Lab's has created. The code is in C and duplicated in BASIC for those that still use it! I've only played a bit with these examples. I modified one for the text-to-speech interface and got it working in short order. There are descriptions of how the other drivers work and some hardware information, too. There's quite a bit to be learned (on my part) about MIDI interfaces, etc, before I can take advantage of everything that's in there. Other thoughts: I purchased my soundblaster at the "old" price ($199). As of yesterday, Electronics Boutique (and everyone else at the mall) was selling it for $179. However, I got lucky. My "old" soundblaster has those 2 extra chips! I got a fully populated board. Finally: If you've read this far, you must be interested in all of this. Drop me a line and say "Hello" if you get the chance. I'd like hear what other folks are doing with the soundblaster. Now for the code: Here's the deal. You can do anything you want with this code. It's yours to play with (or even sell if you can find someone who'd buy it). The only thing I ask is that if you do develop something "neat" with it, you'd send me a copy. Sound fair to you? Obviously, there's no warranty to this code. It's in the public domain and you get it "as is". I disown it! ;-) Compile the follwing with MSC V5.0 (or later I guess) using the Huge memory model (/AH switch). I also build it for use with codeview. You'll also need to use your CT-VOICE.DRV file and copy TV4.VOC to EXIT.VOC once again. --------------------------------Cut Here-------------------------------- /* The new Play.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); } ----------------------------------Cut Here-------------------------------- /* The new Record.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 */ { 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); } ---------------------------------Cut Here--------------------------------- ;New SB.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,1 ;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,2 ;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 ;"Clem Clone! Back to the shadows again! (Appologies to The Firesign Theatre) _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 ;"Clem Clone! Back to the shadows again! (Appologies to The Firesign Theatre) _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 ;"Clem Clone! Back to the shadows again! (Appologies to The Firesign Theatre) _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 ---------------------------------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