1 /*   SCCS Id: @(#)sound.c   3.4     1996/02/19                        */
2 /*   Copyright (c) NetHack PC Development Team 1993,1995            */
3 /*   NetHack may be freely redistributed.  See license for details. */
4 /*                                                                  */
5 /*
6  * sound.c - Hardware sound support
7  *
8  *Edit History:
9  *     Initial Creation                              93/10/01
10  *     Added PC Speaker Support for BC compilers     95/06/14
11  *     Completed various fixes			     96/02/19
12  *
13  */
14 
15 #include "hack.h"
16 #include <stdio.h>
17 #include "portio.h"
18 
19 #include <dos.h>
20 #include <ctype.h>
21 
22 #ifndef TESTING
23 
24 #define printf	pline
25 
26 int
assign_soundcard(sopt)27 assign_soundcard(sopt)
28 char *sopt;
29 {
30 
31 	iflags.hassound = 0;
32 #  ifdef PCMUSIC
33 	iflags.usepcspeaker = 0;
34 #  endif
35 
36 	if (strncmpi(sopt,"def",3) == 0) {              /* default */
37 		/* do nothing - default */
38 	}
39 #  ifdef PCMUSIC
40 	else if (strncmpi(sopt,"speaker",7) == 0) {	/* pc speaker */
41 		iflags.usepcspeaker = 1;
42 	}
43 #  endif
44 	else if (strncmpi(sopt,"auto",4) == 0) {	/* autodetect */
45 	/*
46 	 * Auto-detect Priorities (arbitrary for now):
47 	 *	Just pcspeaker
48 	 */
49 		if (0) ;
50 #  ifdef PCMUSIC
51 		else iflags.usepcspeaker = 1;
52 #  endif
53 	} else {
54 		return 0;
55 	}
56 	return 1;
57 
58 }
59 #endif
60 
61 #ifdef PCMUSIC
62 
63 /* 8254/3 Control Word Defines */
64 
65 #define CTR0SEL (0<<6)
66 #define	CTR1SEL	(1<<6)
67 #define	CTR2SEL	(2<<6)
68 #define RDBACK	(3<<6)
69 
70 #define LATCH	(0<<4)
71 #define	RW_LSB	(1<<4)
72 #define RW_MSB	(2<<4)	/* If both LSB and MSB are read, LSB is done first */
73 
74 #define MODE0	(0<<1)	/* Interrupt on terminal count */
75 #define MODE1	(1<<1)	/* Hardware One-Shot */
76 #define MODE2	(2<<1)	/* Pulse Generator */
77 #define MODE3	(3<<1)	/* Square Wave Generator */
78 #define MODE4	(4<<1)	/* Software Triggered Strobe */
79 #define MODE5	(5<<1)	/* Hardware Triggered Strobe */
80 
81 #define BINARY	(0<<0)	/* Binary counter (16 bits) */
82 #define BCD	(1<<0)	/* Binary Coded Decimal (BCD) Counter (4 Decades) */
83 
84 /* Misc 8254/3 Defines */
85 
86 #define TIMRFRQ (1193180UL)	/* Input frequency to the clock (Hz) */
87 
88 /* Speaker Defines */
89 
90 #define TIMER	(1<<0)	/* Timer 2 Output connected to Speaker */
91 #define SPKR_ON	(1<<1)	/* Turn on/off Speaker */
92 
93 /* Port Definitions */
94 
95 /* 8254/3 Ports */
96 
97 #define CTR0	0x40
98 #define CTR1	0x41
99 #define CTR2	0x42
100 #define CTRL	0x43
101 
102 /* Speaker Port */
103 
104 #define SPEAKER	0x61
105 
106 void
startsound(unsigned freq)107 startsound (unsigned freq)
108 {
109 	/* To start a sound on the PC:
110 	 *
111 	 * First, set the second counter to have the correct frequency:
112 	 */
113 
114 	unsigned count;
115 
116 	if (freq == 0) freq = 523;
117 
118 	count = TIMRFRQ / freq; /* Divide frequencies to get count. */
119 
120 #ifdef TESTING
121 	printf ("freq = %u, count = %u\n", freq, count);
122 #endif
123 
124 	outportb (CTRL, CTR2SEL|RW_LSB|RW_MSB|MODE3|BINARY);
125 	outportb (CTR2, count & 0x0FF);
126 	outportb (CTR2, count / 0x100);
127 
128 	/* Next, turn on the speaker */
129 
130         outportb (SPEAKER, inportb(SPEAKER)|TIMER|SPKR_ON);
131 }
132 
133 void
stopsound(void)134 stopsound (void)
135 {
136         outportb (SPEAKER, inportb(SPEAKER) & ~(TIMER|SPKR_ON));
137 }
138 
139 static unsigned tempo, length, octave, mtype;
140 
141 /* The important numbers here are 287700UL for the factors and 4050816000UL
142  * which gives the 440 Hz for the A below middle C. "middle C" is assumed to
143  * be the C at octave 3. The rest are computed by multiplication/division of
144  * 2^(1/12) which came out to 1.05946 on my calculator.  It is assumed that
145  * no one will request an octave beyond 6 or below 0.  (At octave 7, some
146  * notes still come out ok, but by the end of the octave, the "notes" that
147  * are produced are just ticks.
148 
149  * These numbers were chosen by a process based on the C64 tables (which
150  * weren't standardized) and then were 'standardized' by giving them the
151  * closest value.  That's why they don't seem to be based on any sensible
152  * number.
153  */
154 
155 unsigned long notefactors[12] = { 483852, 456695, 431063, 406869, 384033,
156 	362479, 342135, 322932, 304808, 287700, 271553, 256312 };
157 
158 void
note(long notenum)159 note (long notenum)
160 {
161 	startsound ((unsigned) (4050816000UL / notefactors[notenum % 12]
162 			   >> (7 - notenum / 12)));
163 }
164 
165 int notes[7] = { 9, 11, 0, 2, 4, 5, 7 };
166 
167 char *
startnote(char * c)168 startnote (char *c)
169 {
170 	long n;
171 
172 	n = notes[toupper(*c++) - 'A'] + octave * 12;
173 	if (*c == '#' || *c == '+') { n++; c++; }
174 	else if (*c == '-') { if (n) n--; c++; }
175 
176 	note (n);
177 
178 	return --c;
179 }
180 
181 void
delaytime(unsigned time)182 delaytime (unsigned time)
183 {
184 	/* time and twait are in units of milliseconds */
185 
186 	unsigned twait;
187 
188 	switch (toupper (mtype)) {
189 	   case 'S': twait = time / 4; break;
190 	   case 'L': twait = 0; break;
191 	   default: twait = time / 8; break;
192 	}
193 
194 	msleep (time - twait);
195 	stopsound ();
196 	msleep (twait);
197 }
198 
199 char *
delaynote(char * c)200 delaynote (char *c)
201 {
202 	unsigned time = 0;
203 
204 	while (isdigit(*c)) time = time * 10 + (*c++ - '0');
205 
206 	if (!time) time = length;
207 
208 	time = (unsigned)(240000 / time / tempo);
209 
210 	while (*c == '.') { time = time * 3 / 2; c++; }
211 
212 	delaytime (time);
213 
214 	return c;
215 }
216 
217 void
initspeaker(void)218 initspeaker (void)
219 {
220 	tempo = 120, length = 4, octave = 3, mtype = 'N';
221 }
222 
223 
224 void
play(char * tune)225 play (char *tune)
226 {
227 	char *c, *n;
228 	unsigned num;
229 
230 	for (c = tune; *c; ) {
231 	    sscanf (c + 1, "%u", &num);
232 	    for (n = c + 1; isdigit(*n); n++) /* do nothing */;
233 	    if (isspace(*c)) c++;
234 	    else switch (toupper(*c)) {
235 		case 'A':
236 		case 'B':
237 		case 'C':
238 		case 'D':
239 		case 'E':
240 		case 'F':
241 		case 'G':
242 		    c = startnote (c);
243 		case 'P':
244 		    c = delaynote (++c);
245 		    break;
246 #if 0
247 		case 'M': c++; mtype = *c; c++; break;
248 		case 'T':
249 		    if (num) tempo = num;
250 		    else printf ("Zero Tempo (%s)!\n", c);
251 		    c = n;
252 		    break;
253 		case 'L':
254 		    if (num) length = num;
255 		    else printf ("Zero Length (%s)!\n", c);
256 		    c = n;
257 		    break;
258 		case 'O':
259 		    if (num <= 7)
260 			octave = num;
261 		    c = n;
262 		    break;
263 		case 'N':
264 		    note (num);
265 		    delaytime ((240000/length/tempo));
266 		    c = n;
267 		    break;
268 		case '>': if (octave < 7) octave++; c++; break;
269 		case '<': if (octave) octave--; c++; break;
270 #endif
271 		case ' ': c++; break;
272 		default:
273 		    printf ("Unrecognized play value (%s)!\n", c);
274                     return;
275 	    }
276 	}
277 }
278 
279 #ifndef TESTING
280 void
pc_speaker(struct obj * instr,char * tune)281 pc_speaker (struct obj *instr, char *tune)
282 {
283     if (!iflags.usepcspeaker) return;
284     initspeaker ();
285     switch (instr->otyp)
286     {
287 	case WOODEN_FLUTE:
288 	case MAGIC_FLUTE:
289 	    octave = 5; /* up one octave */
290 	    break;
291 	case TOOLED_HORN:
292 	case FROST_HORN:
293 	case FIRE_HORN:
294 	    octave = 2; /* drop two octaves */
295 	    break;
296 	case BUGLE:
297 	    break;
298 	case WOODEN_HARP:
299 	case MAGIC_HARP:
300 	    length = 8;
301 	    mtype = 'L'; /* fast, legato */
302 	    break;
303     }
304     play (tune);
305 }
306 
307 #else
308 
main()309 main ()
310 {
311 	char s[80];
312 	int tool;
313 
314 	initspeaker();
315 	printf ("1) flute\n2) horn\n3) harp\n4) other\n");
316 	fgets (s, 80, stdin);
317 	sscanf (s, "%d", &tool);
318 	switch (tool) {
319 	case 1: octave = 5; break;
320 	case 2: octave = 2; break;
321 	case 3: length = 8; mtype = 'L'; break;
322 	default: break;
323 	}
324 	printf ("Enter tune:");
325 	fgets(s, 80, stdin);
326 	play (s);
327 }
328 #endif
329 
330 #endif /* PCMUSIC */
331 
332 /* sound.c */
333