/* SCCS Id: @(#)sound.c 3.4 1996/02/19 */ /* Copyright (c) NetHack PC Development Team 1993,1995 */ /* NetHack may be freely redistributed. See license for details. */ /* */ /* * sound.c - Hardware sound support * *Edit History: * Initial Creation 93/10/01 * Added PC Speaker Support for BC compilers 95/06/14 * Completed various fixes 96/02/19 * */ #include "hack.h" #include #include "portio.h" #include #include #ifndef TESTING #define printf pline int assign_soundcard(sopt) char *sopt; { iflags.hassound = 0; # ifdef PCMUSIC iflags.usepcspeaker = 0; # endif if (strncmpi(sopt,"def",3) == 0) { /* default */ /* do nothing - default */ } # ifdef PCMUSIC else if (strncmpi(sopt,"speaker",7) == 0) { /* pc speaker */ iflags.usepcspeaker = 1; } # endif else if (strncmpi(sopt,"auto",4) == 0) { /* autodetect */ /* * Auto-detect Priorities (arbitrary for now): * Just pcspeaker */ if (0) ; # ifdef PCMUSIC else iflags.usepcspeaker = 1; # endif } else { return 0; } return 1; } #endif #ifdef PCMUSIC /* 8254/3 Control Word Defines */ #define CTR0SEL (0<<6) #define CTR1SEL (1<<6) #define CTR2SEL (2<<6) #define RDBACK (3<<6) #define LATCH (0<<4) #define RW_LSB (1<<4) #define RW_MSB (2<<4) /* If both LSB and MSB are read, LSB is done first */ #define MODE0 (0<<1) /* Interrupt on terminal count */ #define MODE1 (1<<1) /* Hardware One-Shot */ #define MODE2 (2<<1) /* Pulse Generator */ #define MODE3 (3<<1) /* Square Wave Generator */ #define MODE4 (4<<1) /* Software Triggered Strobe */ #define MODE5 (5<<1) /* Hardware Triggered Strobe */ #define BINARY (0<<0) /* Binary counter (16 bits) */ #define BCD (1<<0) /* Binary Coded Decimal (BCD) Counter (4 Decades) */ /* Misc 8254/3 Defines */ #define TIMRFRQ (1193180UL) /* Input frequency to the clock (Hz) */ /* Speaker Defines */ #define TIMER (1<<0) /* Timer 2 Output connected to Speaker */ #define SPKR_ON (1<<1) /* Turn on/off Speaker */ /* Port Definitions */ /* 8254/3 Ports */ #define CTR0 0x40 #define CTR1 0x41 #define CTR2 0x42 #define CTRL 0x43 /* Speaker Port */ #define SPEAKER 0x61 void startsound (unsigned freq) { /* To start a sound on the PC: * * First, set the second counter to have the correct frequency: */ unsigned count; if (freq == 0) freq = 523; count = TIMRFRQ / freq; /* Divide frequencies to get count. */ #ifdef TESTING printf ("freq = %u, count = %u\n", freq, count); #endif outportb (CTRL, CTR2SEL|RW_LSB|RW_MSB|MODE3|BINARY); outportb (CTR2, count & 0x0FF); outportb (CTR2, count / 0x100); /* Next, turn on the speaker */ outportb (SPEAKER, inportb(SPEAKER)|TIMER|SPKR_ON); } void stopsound (void) { outportb (SPEAKER, inportb(SPEAKER) & ~(TIMER|SPKR_ON)); } static unsigned tempo, length, octave, mtype; /* The important numbers here are 287700UL for the factors and 4050816000UL * which gives the 440 Hz for the A below middle C. "middle C" is assumed to * be the C at octave 3. The rest are computed by multiplication/division of * 2^(1/12) which came out to 1.05946 on my calculator. It is assumed that * no one will request an octave beyond 6 or below 0. (At octave 7, some * notes still come out ok, but by the end of the octave, the "notes" that * are produced are just ticks. * These numbers were chosen by a process based on the C64 tables (which * weren't standardized) and then were 'standardized' by giving them the * closest value. That's why they don't seem to be based on any sensible * number. */ unsigned long notefactors[12] = { 483852, 456695, 431063, 406869, 384033, 362479, 342135, 322932, 304808, 287700, 271553, 256312 }; void note (long notenum) { startsound ((unsigned) (4050816000UL / notefactors[notenum % 12] >> (7 - notenum / 12))); } int notes[7] = { 9, 11, 0, 2, 4, 5, 7 }; char * startnote (char *c) { long n; n = notes[toupper(*c++) - 'A'] + octave * 12; if (*c == '#' || *c == '+') { n++; c++; } else if (*c == '-') { if (n) n--; c++; } note (n); return --c; } void delaytime (unsigned time) { /* time and twait are in units of milliseconds */ unsigned twait; switch (toupper (mtype)) { case 'S': twait = time / 4; break; case 'L': twait = 0; break; default: twait = time / 8; break; } msleep (time - twait); stopsound (); msleep (twait); } char * delaynote (char *c) { unsigned time = 0; while (isdigit(*c)) time = time * 10 + (*c++ - '0'); if (!time) time = length; time = (unsigned)(240000 / time / tempo); while (*c == '.') { time = time * 3 / 2; c++; } delaytime (time); return c; } void initspeaker (void) { tempo = 120, length = 4, octave = 3, mtype = 'N'; } void play (char *tune) { char *c, *n; unsigned num; for (c = tune; *c; ) { sscanf (c + 1, "%u", &num); for (n = c + 1; isdigit(*n); n++) /* do nothing */; if (isspace(*c)) c++; else switch (toupper(*c)) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': c = startnote (c); case 'P': c = delaynote (++c); break; #if 0 case 'M': c++; mtype = *c; c++; break; case 'T': if (num) tempo = num; else printf ("Zero Tempo (%s)!\n", c); c = n; break; case 'L': if (num) length = num; else printf ("Zero Length (%s)!\n", c); c = n; break; case 'O': if (num <= 7) octave = num; c = n; break; case 'N': note (num); delaytime ((240000/length/tempo)); c = n; break; case '>': if (octave < 7) octave++; c++; break; case '<': if (octave) octave--; c++; break; #endif case ' ': c++; break; default: printf ("Unrecognized play value (%s)!\n", c); return; } } } #ifndef TESTING void pc_speaker (struct obj *instr, char *tune) { if (!iflags.usepcspeaker) return; initspeaker (); switch (instr->otyp) { case WOODEN_FLUTE: case MAGIC_FLUTE: octave = 5; /* up one octave */ break; case TOOLED_HORN: case FROST_HORN: case FIRE_HORN: octave = 2; /* drop two octaves */ break; case BUGLE: break; case WOODEN_HARP: case MAGIC_HARP: length = 8; mtype = 'L'; /* fast, legato */ break; } play (tune); } #else main () { char s[80]; int tool; initspeaker(); printf ("1) flute\n2) horn\n3) harp\n4) other\n"); fgets (s, 80, stdin); sscanf (s, "%d", &tool); switch (tool) { case 1: octave = 5; break; case 2: octave = 2; break; case 3: length = 8; mtype = 'L'; break; default: break; } printf ("Enter tune:"); fgets(s, 80, stdin); play (s); } #endif #endif /* PCMUSIC */ /* sound.c */