Category : Files from Magazines
Archive   : DEC91.ZIP
Filename : 2N12040A

 
Output of file : 2N12040A contained in archive : DEC91.ZIP
/*
* Listing 1: TOSPKR.C
* Play an audio file file out the PC speaker.
* Written for Turbo C 2.0, by Bob Bybee, 7/91
*
* usage: tospkr []
*
* is the audio file to be played.
*
* is the number of speaker bits per sample.
* if not entered, it will be calculated.
*/
#include
#include
#include
#include
#include
#include
#include

#ifndef __LARGE__
#error must use LARGE memory model!
#endif

/* audio sample rate, per second */
#define SAMPRATE 8000

/*
* 80x86 opcodes for the instruction sequences we
* will need, in order to drive the PC speaker.
*
* On entry, to set up the registers:
* BA 61 00 mov dx,61h
* B5 4A mov ch,4ah
* B1 48 mov cl,48h
*
* (Note: the 4a/48 values, at offset 4/6, should be
* changed to agree with the current value of port 61H.)
*
* To set the speaker bit high:
* 8A C5 mov al,ch
* EE out dx,al
*
* To set it low:
* 8A C1 mov al,cl
* EE out dx,al
*
* A "far return" to end the routine:
* CB retf
*/
unsigned char ops_begin[] =
{0xba, 0x61, 0x00, 0xb5, 0x4a, 0xb1, 0x48};
unsigned char ops_high[] = {0x8a, 0xc5, 0xee};
unsigned char ops_low[] = {0x8a, 0xc1, 0xee};
unsigned char ops_end[] = {0xcb};

/* input buffer size, and pointer to it */
#define INBUFSIZE 65000U
unsigned char *inbuf;

/* array of audio segment lengths in file */
#define MAX_SEGS 200
unsigned int seg_length[MAX_SEGS];
unsigned int num_segs = 0;
char got_lengths = 0;

/* PFV is a pointer to a function taking
* no args, and returning void */
typedef void (*PFV)( void );
PFV playfuncs[256];

unsigned int bits_per_sample;

/* prototypes */
void patch_opcodes( void );
int best_rate( void );
int test_time( int n_bits );
void generate_codes( void );


void main( int ac, char **av )
{
FILE *fp;
unsigned int nbytes, len, seg, n_to_read;
unsigned char *p;

--ac;
++av;
if (ac < 1)
{
printf("usage: tospkr []\n");
exit(1);
}

/* open the input file */
if ((fp = fopen(*av, "rb")) == NULL)
{
printf("Can't open voice file: %s\n", *av);
exit(1);
}

/* allocate memory for the input buffer */
if ((inbuf = calloc(1, INBUFSIZE)) == NULL)
{
printf("Can't get memory for input buffer!\n");
exit(1);
}

/* get the current value of port 61H */
patch_opcodes();

if (ac >= 2 && (bits_per_sample = atoi(av[1])) > 0)
; /* got our sample rate on the command line */
else
bits_per_sample = best_rate();

/* Create the 256 executable functions */
generate_codes();

/* See if the file is prefixed with a list of audio
* segment lengths. If so, read it into the
* seg_length[] array. It contains unsigned ints,
* zero-terminated.
*/
fread(inbuf, 4, 1, fp);
if (memcmp(inbuf, "LIST", 4) == 0)
{
got_lengths = 1;
do {
fread(&len, 2, 1, fp);
seg_length[num_segs++] = len;
} while (len != 0);
}
else
; /* We could rewind here, but
* 4 bytes isn't worth it.
*/

/* Read in the file and play it thru the speaker.
* On each read, get the next segment's worth (if a
* list of segments exists), or as much as we can.
*/
for (seg = 0; ; ++seg)
{
n_to_read =
got_lengths ? seg_length[seg] : INBUFSIZE;
if (n_to_read == 0 ||
(nbytes = fread(inbuf, 1, n_to_read, fp)) <= 0)
break;

p = inbuf;
disable();

/* For each byte, call one of 256 functions which
* were created in the playfuncs[] array.
*/
while (nbytes-- > 0)
(*playfuncs[*p++])();

enable();
if (kbhit()) /* if a key was hit, */
{
getch(); /* eat the key, */
break; /* and quit. */
}
}

fclose(fp);
exit(0);
}


/*
* Read from port 61H. Use this value with bit 1
* set and cleared, in ops_begin[] opcodes.
*/
void patch_opcodes( void )
{
int val;

val = inportb(0x61);
val &= ~0x03; /* lose bits 0, 1 */
ops_begin[6] = val;
ops_begin[4] = val | 0x02; /* add bit 1 */
}


/*
* Calculate the number of bits we can play to the
* speaker, for each audio sample, in order to make
* the playback come out at SAMPRATE samples
* per second.
*/
int best_rate( void )
{
int bits1, bits2, ticks1, ticks2;
float fbits;

/* Start with 10 bits/sample. Double it until
* it takes at least one second (18 ticks) */
printf("I'm timing your CPU, please wait...\n");
bits1 = 10;
while ((ticks1 = test_time(bits1)) < 18)
bits1 *= 2;

/* Now time it at 5x this number of bits. */
bits2 = bits1 * 5;
ticks2 = test_time(bits2);

/* Interpolate to get the optimum number of bits
* per sample for this PC. */
fbits = (18.2 - ticks1) * (bits2 - bits1);
fbits = fbits / (ticks2 - ticks1) + bits1;
return ((int)fbits);
}


/*
* Build a set of instructions into inbuf, which will
* play n_bits out to the speaker. Return the number
* of BIOS ticks it takes to play this many bits out,
* SAMPRATE times.
*/
int test_time( int n_bits )
{
unsigned char *p;
int i;
long t1, t2;
PFV testcode;

p = inbuf;
memcpy(p, ops_begin, sizeof(ops_begin));
p += sizeof(ops_begin);
for (i = 0; i < n_bits; ++i)
{
memcpy(p, ops_high, sizeof(ops_high));
p += sizeof(ops_high);
}
memcpy(p, ops_end, sizeof(ops_end));

testcode = (PFV)inbuf;
t1 = biostime(0, 0L);
for (i = 0; i < SAMPRATE; ++i)
(*testcode)();
t2 = biostime(0, 0L) - t1;
printf("bits: %d ticks: %d\n", n_bits, (int)t2);
return (int)t2;
}


/*
* Generate opcode streams, in allocated memory, to push
* the proper bit patterns out to the PC speaker.
*/
#define DEBUG_WAVE 0 /* 1 to enable printouts */
void generate_codes( void )
{
unsigned char *p;
unsigned int value, sum, i;

printf("generating code for %d bits per sample...\n",
bits_per_sample);
for (value = 0; value < 256; ++value)
{
/* Get memory for the next instruction stream
* we're going to create. */
p = calloc(1, sizeof(ops_begin) + sizeof(ops_end) +
bits_per_sample * sizeof(ops_high));
if (p == NULL)
{
printf("out of memory at value %d\n", value);
exit(1);
}

playfuncs[value] = (PFV)p;
#if DEBUG_WAVE
printf("\n%3d: ", value);
#endif
memcpy(p, ops_begin, sizeof(ops_begin));
p += sizeof(ops_begin);

/* evenly distribute the ones over the bits. */
sum = 0;
for (i = 0; i < bits_per_sample; ++i)
{
sum += value;
if (sum >= 255)
{
sum -= 255;
memcpy(p, ops_high, sizeof(ops_high));
#if DEBUG_WAVE
putchar('1');
#endif
}
else
{
memcpy(p, ops_low, sizeof(ops_low));
#if DEBUG_WAVE
putchar('.');
#endif
}
p += sizeof(ops_low);
}
memcpy(p, ops_end, sizeof(ops_end));
}
}



  3 Responses to “Category : Files from Magazines
Archive   : DEC91.ZIP
Filename : 2N12040A

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/