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