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