home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SDN¹ Plus
/
SDN1_.cdr
/
sdn
/
other
/
mimic11.sdn
/
MIMIC.C
< prev
next >
Wrap
Text File
|
1990-03-13
|
16KB
|
254 lines
/* MIMIC.C Version 1.0 */
/* (c) 1990 Eletech Electronics, Inc. All rights UNreserved. */
/* */
/* Creation : 3-13-90 by Wei Lu. */
/* Function : Works with MINT.ASM to play Eletech Digicorder recorded voice */
/* file thru PC's internal speaker at specified sampling rate. */
/* Usage : MIMIC sampling_rate filename */
/* Compiler : Microsoft C 5.0 and up. */
/* Compiling Options: None. */
/* */
/* Operation: First MIMIC.C prepares for playback by: */
/* */
/* 1. allocating 64K bytes of play buffer memory from DOS, */
/* 2. loading voice data into play buffer bank 1, */
/* 3. re-directing (intercepting) timer 0 interrupt vector, */
/* 4. re-programming timer 0 to desired sampling rate. */
/* */
/* At this point MINT.ASM should start sending voice data to the */
/* speaker bit by bit upon each timer 0 interrupt. Meanwhile */
/* MIMIC.C will try to load more data into play buffer bank 2 */
/* and, if any key is pressed, stop playing. After bank 1 is */
/* played up, MIMIC.C switches playing to bank 2 and tries */
/* loading more data into bank 1. This goes on until all data are */
/* played and then MIMIC.C stops playing. */
/* */
/* When the playback is stopped (either by a key press or by */
/* running out of voice data) MIMIC restores timer 0 and its */
/* interrupt vector to normal operation. At this point, MINT.ASM */
/* is unhooked from timer 0 interrupt and its operation stopped. */
/* */
/* MIMIC.C communicates with MINT.ASM thru the following shared */
/* variables: */
/* */
/* play_addr - the far pointer points to the start address of */
/* the 64KB play buffer MIMIC.C allocates from DOS. */
/* Set by MIMIC.C and read by MINT.ASM to find out */
/* where the voice data are. Note that its offset */
/* address is always 0 since DOS allocates memory */
/* in paragraphs (16 bytes.) */
/* */
/* play_ptr - the pointer points to the currently-been-played */
/* byte. Starts from 0 and may go as high as 64K-1. */
/* Initially set by MIMIC.C and subsequently */
/* updated by MINT.ASM. */
/* */
/* play_length - number of bytes left to play. Initially set by */
/* MIMIC.C and subsequently updated by MINT.ASM. */
/* */
/* tack_count - number of "tacks" to wait before MINT.ASM does a */
/* real clock interrupt. Calculated and set by */
/* MIMIC.C and read by MINT.ASM. */
/* */
/* Here a "tack" is eight "ticks" and a "tick" is a timer 0 */
/* interrupt. When a system is powered up or reset, DOS sets up */
/* timer 0 for clock interrupt at about 18.2 times per second (so */
/* the tick rate is 18.2 Hz at this moment.) This is achieved by */
/* dividing 1.19 MHz (input clock rate) by 65536. MIMIC.C */
/* re-programs timer 0 so that the tick rate equals the sampling */
/* rate. This can be achieved by dividing 1.19 MHz by the integer */
/* part of 1190000/sampling_rate. This integer is kept in the */
/* variable "timer_div" and used to re-program timer 0. Although */
/* the tick rate has been changed, MINT.ASM still has to do 18.2 */
/* "real" clock interrupts every second to keep the clock */
/* running. A little calculation (65536*sampling_rate/1190000) */
/* yields the number of ticks to wait between "real" clock */
/* interrupts. Since MINT.ASM only checks this value at the end */
/* of every byte (or 8 ticks), this value is further divided by 8 */
/* to become number of "tacks" (variable tack_count.) */
/* */
/* Note that for most sampling rates the above calculations do */
/* not yield integers without remainders. If this is the case */
/* then the clock that DOS keeps will go slight faster (since we */
/* always round down the quotient) during the execution of the */
/* program. This deviation equals 1 - timer_div*tack_count/8192 */
/* and is usually less than 1%. This effect will accumulate with */
/* each run of the program until the system is reset (cold or */
/* warm), at that point DOS will reset the time by reading from */
/* either the hardware clock or the user, both not affected by */
/* this program, hopefully. */
/* */
/* */
/* There is actually a chance that the DOS clock goes slightly */
/* slower during the execution of the program. To explain this, */
/* we will have to first explain how MINT.ASM works. Upon each */
/* tick interrupt, MINT.ASM is activated and does one of the */
/* following: */
/* */
/* a. If there is no more data to play, do a "real" clock */
/* interrupt if it's time to, then terminate. */
/* */
/* Otherwise, */
/* */
/* b. If it does not need a new byte of data, output a bit and */
/* terminate. (This takes the least amount of time.) */
/* */
/* c. If it needs a new byte of data but it's not time yet to do */
/* a "real" clock interrupt, get a byte and output a bit and */
/* terminate. (This takes a little bit more time than a.) */
/* */
/* d. If it needs a new byte of data and it's also time to do a */
/* "real" clock interrupt, get a byte, output a bit, do a */
/* "real" clock interrupt and terminate. (This takes a lot of */
/* and the most amount of time.) */
/* */
/* So in normal operation the system has to complete the worst */
/* case (case d) within a sample period. If the system is slow so */
/* that it misses at least a tick interrupt while doing case d, */
/* an 18.2 Hz abnormality is introduced (which you can barely */
/* notice.) If the system is even slower that it misses at least */
/* a tick interrupt while doing case c, the playback tone will be */
/* slightly lower than normal due to slower effective playback */
/* rate. If the system is so slow as to miss at least a tick */
/* interrupt while doing case b, the tone will be significantly */
/* lower due to much slower effective playback rate. Case a is */
/* usually no problem except when it's time to do a "real" clock */
/* interrupt, at that time tick interrupts can be missed and the */
/* 18.2 Hz abnormality occurs. */
/* */
/* All these abnormalities work towards slowing down the DOS */
/* clock. Being slow to a certain extent (having only the 18.2 Hz */
/* abnormality) is sometimes a blessing since it works to offset */
/* the clock speedup effect as mentioned above. */
/* */
/* Also note that the playback is non-destructive, meaning that */
/* to play the same data again all you have to do is reset the */
/* play_ptr and play_length. Part or all of the recording can be */
/* instantly replayed in this manner. */
/* */
/* A final warning is that this program not only re-directs clock */
/* interrupt, but also borrowed software interrupt #255. Certain */
/* TSR programs hooking to these two interrupts may not work */
/* correctly after running this program. You can, of course, */
/* change the new clock interrupt to something other than #255 */
/* should conflict results. */
#include <stdio.h>
#include <dos.h>
#include <fcntl.h>
#define CLOCK_IRQ 8 /* old clock IRQ = vector #8 */
#define NEW_CLOCK_IRQ 255 /* new clock IRQ = vector #255 */
#define TIMER_COUNT 64 /* 8253 "load timer 0" I/O port */
#define TIMER_MODE 67 /* 8253 "write mode" I/O port */
main(argc, argv)
int argc;
char **argv;
{
extern void interrupt far int_handler(); /* new clock interrupt handler */
extern char far *play_addr; /* play buffer start address */
extern unsigned play_ptr; /* play byte pointer */
extern unsigned play_length; /* play length (in byte) */
extern unsigned char tack_count; /* how often we do old clock int */
void (interrupt far *old_vector1)(); /* where we put old timer vector */
void (interrupt far *old_vector2)(); /* where we put old vector #255 */
unsigned buffer_seg; /* play buffer segment address */
unsigned rate; /* rate = sampling_rate */
unsigned fhandle, bytes_read, timer_div, current_bank;
char filename[80];
char far *p_addr1; /* points to start of bank 1 */
char far *p_addr2; /* points to start of bank 2 */
if (_dos_allocmem(4096, &buffer_seg)) /* allocate 64KB of play buffer */
{
printf("MIMIC ERROR: can not allocate 64K bytes of buffer memory\n");
exit(0);
}
FP_SEG(play_addr) = FP_SEG(p_addr1) = FP_SEG(p_addr2) = buffer_seg;
FP_OFF(play_addr) = FP_OFF(p_addr1) = 0; /* bank 1 from 0 to 32K */
FP_OFF(p_addr2) = 32768; /* bank 2 from 32K to 64K */
if (argc != 3)
{
printf("MIMIC USAGE: MIMIC sampling_rate filename\n");
exit(0);
}
if ((sscanf(argv[1], "%d", &rate) == 0) || (rate < 12) || (rate > 64))
{
printf("MIMIC USAGE: sampling_rate must be an integer between 12 and 64\n");
exit(0);
}
if (_dos_open(argv[2], O_RDONLY, &fhandle))
{
printf("MIMIC ERROR: can not open file '%s'\n", argv[2]);
exit(0);
}
_dos_read(fhandle, (void far *)p_addr1, 32768, &bytes_read);
current_bank = 1;
play_ptr = 0; /* start from the first byte */
play_length = bytes_read;
timer_div = 1190/rate; /* sampling_rate is in KHz */
tack_count = 8192*rate/1190; /* see below */
/* How often should we do an real clock interrupt? Once every 65536 ticks, */
/* or 8192 tacks, when the counting frequency is 1190 KHz. Now the counting */
/* frequency is "rate" in KHz, we should do it every 8192*rate/1190 tacks. */
old_vector1 = _dos_getvect(CLOCK_IRQ); /* get old clock int vector */
old_vector2 = _dos_getvect(NEW_CLOCK_IRQ); /* get old vector #255 */
_dos_setvect(NEW_CLOCK_IRQ, old_vector1); /* relocate old clock int handler*/
_dos_setvect(CLOCK_IRQ, int_handler); /* intercept clock interrupts */
outp(TIMER_MODE, 0x36); /* prepare to load new count */
outp(TIMER_COUNT, (char)timer_div); /* load low byte of new count */
outp(TIMER_COUNT, 0x0); /* high byte should be 0 */
/* MINT.ASM now starts sending data to speaker on each timer 0 interrupt */
while (bytes_read != 0)
{
if (bytes_read == 32768) /* there might be more data */
{
if (current_bank == 1)
_dos_read(fhandle, (void far *)p_addr2, 32768, &bytes_read);
else
_dos_read(fhandle, (void far *)p_addr1, 32768, &bytes_read);
}
else
bytes_read = 0; /* no more data to play */
while (play_length != 0)
{
if (kbhit()) /* terminate if any key is hit */
break; /* get out of this small loop */
}
if (kbhit())
{
getch(); /* remove the unneeded "any key" */
break; /* get out of this big loop */
}
if (current_bank == 1)
{
play_ptr = 32768; /* starts from bank 2 now */
current_bank = 2;
}
else
{
play_ptr = 0; /* restarts from bank 1 now */
current_bank = 1;
}
play_length = bytes_read;
}
outp(TIMER_MODE, 0x36); /* prepare to restore count */
outp(TIMER_COUNT, 0); /* old count = 0 (actually 65536)*/
outp(TIMER_COUNT, 0);
_dos_setvect(CLOCK_IRQ, old_vector1); /* restore clock int vector */
_dos_setvect(NEW_CLOCK_IRQ, old_vector2); /* restore int vector #255 */
}