NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / sys / msdos / sound.c
blobfa0ad0cbbb5f154183183178ae1dd50d877003b1
1 /* aNetHack 0.0.1 sound.c $ANH-Date: 1432512792 2015/05/25 00:13:12 $ $ANH-Branch: master $:$ANH-Revision: 1.8 $ */
2 /* Copyright (c) aNetHack PC Development Team 1993,1995 */
3 /* aNetHack may be freely redistributed. See license for details. */
4 /* */
5 /*
6 * sound.c - Hardware sound support
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
15 #include "hack.h"
16 #include <stdio.h>
17 #include "portio.h"
19 #include <dos.h>
20 #include <ctype.h>
22 #ifndef TESTING
24 #define printf pline
26 int
27 assign_soundcard(sopt)
28 char *sopt;
30 iflags.hassound = 0;
31 #ifdef PCMUSIC
32 iflags.usepcspeaker = 0;
33 #endif
35 if (strncmpi(sopt, "def", 3) == 0) { /* default */
36 /* do nothing - default */
38 #ifdef PCMUSIC
39 else if (strncmpi(sopt, "speaker", 7) == 0) { /* pc speaker */
40 iflags.usepcspeaker = 1;
42 #endif
43 else if (strncmpi(sopt, "auto", 4) == 0) { /* autodetect */
45 * Auto-detect Priorities (arbitrary for now):
46 * Just pcspeaker
48 if (0)
50 #ifdef PCMUSIC
51 else
52 iflags.usepcspeaker = 1;
53 #endif
54 } else {
55 return 0;
57 return 1;
59 #endif
61 #ifdef PCMUSIC
63 /* 8254/3 Control Word Defines */
65 #define CTR0SEL (0 << 6)
66 #define CTR1SEL (1 << 6)
67 #define CTR2SEL (2 << 6)
68 #define RDBACK (3 << 6)
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 \
75 #define MODE0 (0 << 1) /* Interrupt on terminal count */
76 #define MODE1 (1 << 1) /* Hardware One-Shot */
77 #define MODE2 (2 << 1) /* Pulse Generator */
78 #define MODE3 (3 << 1) /* Square Wave Generator */
79 #define MODE4 (4 << 1) /* Software Triggered Strobe */
80 #define MODE5 (5 << 1) /* Hardware Triggered Strobe */
82 #define BINARY (0 << 0) /* Binary counter (16 bits) */
83 #define BCD (1 << 0) /* Binary Coded Decimal (BCD) Counter (4 Decades) */
85 /* Misc 8254/3 Defines */
87 #define TIMRFRQ (1193180UL) /* Input frequency to the clock (Hz) */
89 /* Speaker Defines */
91 #define TIMER (1 << 0) /* Timer 2 Output connected to Speaker */
92 #define SPKR_ON (1 << 1) /* Turn on/off Speaker */
94 /* Port Definitions */
96 /* 8254/3 Ports */
98 #define CTR0 0x40
99 #define CTR1 0x41
100 #define CTR2 0x42
101 #define CTRL 0x43
103 /* Speaker Port */
105 #define SPEAKER 0x61
107 void
108 startsound(unsigned freq)
110 /* To start a sound on the PC:
112 * First, set the second counter to have the correct frequency:
115 unsigned count;
117 if (freq == 0)
118 freq = 523;
120 count = TIMRFRQ / freq; /* Divide frequencies to get count. */
122 #ifdef TESTING
123 printf("freq = %u, count = %u\n", freq, count);
124 #endif
126 outportb(CTRL, CTR2SEL | RW_LSB | RW_MSB | MODE3 | BINARY);
127 outportb(CTR2, count & 0x0FF);
128 outportb(CTR2, count / 0x100);
130 /* Next, turn on the speaker */
132 outportb(SPEAKER, inportb(SPEAKER) | TIMER | SPKR_ON);
135 void
136 stopsound(void)
138 outportb(SPEAKER, inportb(SPEAKER) & ~(TIMER | SPKR_ON));
141 static unsigned tempo, length, octave, mtype;
143 /* The important numbers here are 287700UL for the factors and 4050816000UL
144 * which gives the 440 Hz for the A below middle C. "middle C" is assumed to
145 * be the C at octave 3. The rest are computed by multiplication/division of
146 * 2^(1/12) which came out to 1.05946 on my calculator. It is assumed that
147 * no one will request an octave beyond 6 or below 0. (At octave 7, some
148 * notes still come out ok, but by the end of the octave, the "notes" that
149 * are produced are just ticks.
151 * These numbers were chosen by a process based on the C64 tables (which
152 * weren't standardized) and then were 'standardized' by giving them the
153 * closest value. That's why they don't seem to be based on any sensible
154 * number.
157 unsigned long notefactors[12] = { 483852, 456695, 431063, 406869,
158 384033, 362479, 342135, 322932,
159 304808, 287700, 271553, 256312 };
161 void
162 note(long notenum)
164 startsound((unsigned) (4050816000UL / notefactors[notenum % 12]
165 >> (7 - notenum / 12)));
168 int notes[7] = { 9, 11, 0, 2, 4, 5, 7 };
170 char *
171 startnote(char *c)
173 long n;
175 n = notes[toupper(*c++) - 'A'] + octave * 12;
176 if (*c == '#' || *c == '+') {
177 n++;
178 c++;
179 } else if (*c == '-') {
180 if (n)
181 n--;
182 c++;
185 note(n);
187 return --c;
190 void
191 delaytime(unsigned time)
193 /* time and twait are in units of milliseconds */
195 unsigned twait;
197 switch (toupper(mtype)) {
198 case 'S':
199 twait = time / 4;
200 break;
201 case 'L':
202 twait = 0;
203 break;
204 default:
205 twait = time / 8;
206 break;
209 msleep(time - twait);
210 stopsound();
211 msleep(twait);
214 char *
215 delaynote(char *c)
217 unsigned time = 0;
219 while (isdigit(*c))
220 time = time * 10 + (*c++ - '0');
222 if (!time)
223 time = length;
225 time = (unsigned) (240000 / time / tempo);
227 while (*c == '.') {
228 time = time * 3 / 2;
229 c++;
232 delaytime(time);
234 return c;
237 void
238 initspeaker(void)
240 tempo = 120, length = 4, octave = 3, mtype = 'N';
243 void
244 play(char *tune)
246 char *c, *n;
247 unsigned num;
249 for (c = tune; *c;) {
250 sscanf(c + 1, "%u", &num);
251 for (n = c + 1; isdigit(*n); n++) /* do nothing */
253 if (isspace(*c))
254 c++;
255 else
256 switch (toupper(*c)) {
257 case 'A':
258 case 'B':
259 case 'C':
260 case 'D':
261 case 'E':
262 case 'F':
263 case 'G':
264 c = startnote(c);
265 case 'P':
266 c = delaynote(++c);
267 break;
268 #if 0
269 case 'M': c++; mtype = *c; c++; break;
270 case 'T':
271 if (num) tempo = num;
272 else printf ("Zero Tempo (%s)!\n", c);
273 c = n;
274 break;
275 case 'L':
276 if (num) length = num;
277 else printf ("Zero Length (%s)!\n", c);
278 c = n;
279 break;
280 case 'O':
281 if (num <= 7)
282 octave = num;
283 c = n;
284 break;
285 case 'N':
286 note (num);
287 delaytime ((240000/length/tempo));
288 c = n;
289 break;
290 case '>': if (octave < 7) octave++; c++; break;
291 case '<': if (octave) octave--; c++; break;
292 #endif
293 case ' ':
294 c++;
295 break;
296 default:
297 printf("Unrecognized play value (%s)!\n", c);
298 return;
303 #ifndef TESTING
304 void
305 pc_speaker(struct obj *instr, char *tune)
307 if (!iflags.usepcspeaker)
308 return;
309 initspeaker();
310 switch (instr->otyp) {
311 case WOODEN_FLUTE:
312 case MAGIC_FLUTE:
313 octave = 5; /* up one octave */
314 break;
315 case TOOLED_HORN:
316 case FROST_HORN:
317 case FIRE_HORN:
318 octave = 2; /* drop two octaves */
319 break;
320 case BUGLE:
321 break;
322 case WOODEN_HARP:
323 case MAGIC_HARP:
324 length = 8;
325 mtype = 'L'; /* fast, legato */
326 break;
328 play(tune);
331 #else
333 main()
335 char s[80];
336 int tool;
338 initspeaker();
339 printf("1) flute\n2) horn\n3) harp\n4) other\n");
340 fgets(s, 80, stdin);
341 sscanf(s, "%d", &tool);
342 switch (tool) {
343 case 1:
344 octave = 5;
345 break;
346 case 2:
347 octave = 2;
348 break;
349 case 3:
350 length = 8;
351 mtype = 'L';
352 break;
353 default:
354 break;
356 printf("Enter tune:");
357 fgets(s, 80, stdin);
358 play(s);
360 #endif
362 #endif /* PCMUSIC */
364 /* sound.c */