333 lines
6.7 KiB
C
333 lines
6.7 KiB
C
/* SCCS Id: @(#)sound.c 3.5 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 <stdio.h>
|
|
#include "portio.h"
|
|
|
|
#include <dos.h>
|
|
#include <ctype.h>
|
|
|
|
#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 */
|