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