diff --git a/sys/msdos/sound.c b/sys/msdos/sound.c new file mode 100644 index 000000000..847bde222 --- /dev/null +++ b/sys/msdos/sound.c @@ -0,0 +1,332 @@ +/* SCCS Id: @(#)sound.c 3.3 96/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 */