Just to put this thread to bed (for a few more years) I thought I
would update Rod's deconstructed code to remove the obfuscation of the
"long *" and "char *" (and modern compilers' need to explicitly cast
them). And also provide some support code for anyone else wanting to
write "music" for the PC speaker.
1. Instead of the "long music[]" array, try this instead:
struct asbyte {
unsigned char lo;
unsigned char hi;
};
union DIVISOR {
unsigned short w;
struct asbyte b;
};
struct TUNE {
union DIVISOR divisor;
unsigned char ontime;
unsigned char offtime;
};
initialised as:
struct TUNE music[] = {
{ { 0x08E9 }, 0x08, 0x01 },
{ { 0x08E9 }, 0x08, 0x01 },
{ { 0x07F0 }, 0x08, 0x01 },
{ { 0x096F }, 0x10, 0x01 },
{ { 0x08E9 }, 0x03, 0x01 },
{ { 0x07F0 }, 0x08, 0x02 },
{ { 0x0712 }, 0x08, 0x01 },
{ { 0x0712 }, 0x08, 0x01 },
{ { 0x06AD }, 0x08, 0x01 },
{ { 0x0712 }, 0x10, 0x01 },
{ { 0x07F0 }, 0x03, 0x01 },
{ { 0x08E9 }, 0x08, 0x02 },
{ { 0x07F0 }, 0x08, 0x01 },
{ { 0x08E9 }, 0x08, 0x01 },
{ { 0x096F }, 0x08, 0x01 },
{ { 0x08E9 }, 0x08, 0x02 },
{ { 0x08E9 }, 0x02, 0x01 },
{ { 0x07F0 }, 0x02, 0x01 },
{ { 0x0712 }, 0x02, 0x01 },
{ { 0x06AD }, 0x02, 0x02 },
{ { 0x05F1 }, 0x08, 0x01 },
{ { 0x05F1 }, 0x08, 0x01 },
{ { 0x05F1 }, 0x08, 0x01 },
{ { 0x05F1 }, 0x10, 0x01 },
{ { 0x06AD }, 0x03, 0x01 },
{ { 0x0712 }, 0x08, 0x02 },
{ { 0x06AD }, 0x08, 0x01 },
{ { 0x06AD }, 0x08, 0x01 },
{ { 0x06AD }, 0x08, 0x01 },
{ { 0x06AD }, 0x10, 0x01 },
{ { 0x0712 }, 0x03, 0x01 },
{ { 0x07F0 }, 0x08, 0x02 },
{ { 0x0712 }, 0x08, 0x01 },
{ { 0x06AD }, 0x02, 0x01 },
{ { 0x0712 }, 0x02, 0x01 },
{ { 0x07F0 }, 0x02, 0x01 },
{ { 0x08E9 }, 0x02, 0x01 },
{ { 0x0712 }, 0x10, 0x01 },
{ { 0x06AD }, 0x03, 0x01 },
{ { 0x05F1 }, 0x08, 0x02 },
{ { 0x054B }, 0x03, 0x01 },
{ { 0x06AD }, 0x03, 0x01 },
{ { 0x0712 }, 0x0C, 0x01 },
{ { 0x07F0 }, 0x0C, 0x01 },
{ { 0x08E9 }, 0x10, 0x01 },
};
#define NR_NOTES (sizeof(music)/sizeof(struct TUNE))
The note() and main() functions then become
void note(struct TUNE *song)
{
outport(0x43,0xb6);
outport(0x42,song->divisor.b.lo);
outport(0x42,song->divisor.b.hi);
outport(0x61,inport(0x61)|3);
length(song->ontime);
outport(0x61,inport(0x61)&~3);
length(song->offtime);
}
int main(void)
{
struct TUNE *play;
#ifdef __DJGPP__
__djgpp_nearptr_enable();
#endif
for (play = music; play < music + NR_NOTES; play++)
note(play);
return 0;
}
2. This program is an aid to establishing the correct timer divisor to
produce the frequency of the desired notes:
#include <stdio.h>
#include <math.h>
double timer_freq = 1193180.0; /* countdown timer freq */
double middleC = 261.625; /* frequency (Hz) of middle C */
char *octave[] = {
"C ", "C#", "D ", "D#", "E ", "F ",
"F#", "G ", "G#", "A ", "A#", "B "
};
#define OSIZE (sizeof(octave)/sizeof(char *))
#define NR_OCTAVES 8 /* or however many you want */
int main()
{
int oct;
double startC;
double _12root2 = pow (2.0, 1.0/12.0); /* twelfth root of 2 */
for (oct = 0, startC = middleC / 8.0; /* start 3 octaves lower */
oct < NR_OCTAVES;
oct++, startC *= 2.0)
{
int i;
double freq = startC;
printf ("---------\nOctave %d:\n---------\n", oct);
for (i = 0; i < OSIZE; i++, freq *= _12root2)
{
double div = timer_freq / freq;
printf ("%s freq = %7.2lf, divisor = %8.2lf [0x%04lx]\n",
octave[i], freq, div, (long) (div + 0.5));
}
}
return 0;
}
Pete