OSDN Git Service

upgrade to 3.6.2
[jnethack/source.git] / sys / msdos / sound.c
1 /* NetHack 3.6  sound.c $NHDT-Date: 1432512792 2015/05/25 00:13:12 $  $NHDT-Branch: master $:$NHDT-Revision: 1.8 $ */
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
27 assign_soundcard(sopt)
28 char *sopt;
29 {
30     iflags.hassound = 0;
31 #ifdef PCMUSIC
32     iflags.usepcspeaker = 0;
33 #endif
34
35     if (strncmpi(sopt, "def", 3) == 0) { /* default */
36                                          /* do nothing - default */
37     }
38 #ifdef PCMUSIC
39     else if (strncmpi(sopt, "speaker", 7) == 0) { /* pc speaker */
40         iflags.usepcspeaker = 1;
41     }
42 #endif
43     else if (strncmpi(sopt, "auto", 4) == 0) { /* autodetect */
44                                                /*
45                                                 * Auto-detect Priorities (arbitrary for now):
46                                                 *       Just pcspeaker
47                                                 */
48         if (0)
49             ;
50 #ifdef PCMUSIC
51         else
52             iflags.usepcspeaker = 1;
53 #endif
54     } else {
55         return 0;
56     }
57     return 1;
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
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 */
81
82 #define BINARY (0 << 0) /* Binary counter (16 bits) */
83 #define BCD (1 << 0)    /* Binary Coded Decimal (BCD) Counter (4 Decades) */
84
85 /* Misc 8254/3 Defines */
86
87 #define TIMRFRQ (1193180UL) /* Input frequency to the clock (Hz) */
88
89 /* Speaker Defines */
90
91 #define TIMER (1 << 0)   /* Timer 2 Output connected to Speaker */
92 #define SPKR_ON (1 << 1) /* Turn on/off Speaker */
93
94 /* Port Definitions */
95
96 /* 8254/3 Ports */
97
98 #define CTR0 0x40
99 #define CTR1 0x41
100 #define CTR2 0x42
101 #define CTRL 0x43
102
103 /* Speaker Port */
104
105 #define SPEAKER 0x61
106
107 void
108 startsound(unsigned freq)
109 {
110     /* To start a sound on the PC:
111      *
112      * First, set the second counter to have the correct frequency:
113      */
114
115     unsigned count;
116
117     if (freq == 0)
118         freq = 523;
119
120     count = TIMRFRQ / freq; /* Divide frequencies to get count. */
121
122 #ifdef TESTING
123     printf("freq = %u, count = %u\n", freq, count);
124 #endif
125
126     outportb(CTRL, CTR2SEL | RW_LSB | RW_MSB | MODE3 | BINARY);
127     outportb(CTR2, count & 0x0FF);
128     outportb(CTR2, count / 0x100);
129
130     /* Next, turn on the speaker */
131
132     outportb(SPEAKER, inportb(SPEAKER) | TIMER | SPKR_ON);
133 }
134
135 void
136 stopsound(void)
137 {
138     outportb(SPEAKER, inportb(SPEAKER) & ~(TIMER | SPKR_ON));
139 }
140
141 static unsigned tempo, length, octave, mtype;
142
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.
150
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.
155  */
156
157 unsigned long notefactors[12] = { 483852, 456695, 431063, 406869,
158                                   384033, 362479, 342135, 322932,
159                                   304808, 287700, 271553, 256312 };
160
161 void
162 note(long notenum)
163 {
164     startsound((unsigned) (4050816000UL / notefactors[notenum % 12]
165                            >> (7 - notenum / 12)));
166 }
167
168 int notes[7] = { 9, 11, 0, 2, 4, 5, 7 };
169
170 char *
171 startnote(char *c)
172 {
173     long n;
174
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++;
183     }
184
185     note(n);
186
187     return --c;
188 }
189
190 void
191 delaytime(unsigned time)
192 {
193     /* time and twait are in units of milliseconds */
194
195     unsigned twait;
196
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;
207     }
208
209     msleep(time - twait);
210     stopsound();
211     msleep(twait);
212 }
213
214 char *
215 delaynote(char *c)
216 {
217     unsigned time = 0;
218
219     while (isdigit(*c))
220         time = time * 10 + (*c++ - '0');
221
222     if (!time)
223         time = length;
224
225     time = (unsigned) (240000 / time / tempo);
226
227     while (*c == '.') {
228         time = time * 3 / 2;
229         c++;
230     }
231
232     delaytime(time);
233
234     return c;
235 }
236
237 void
238 initspeaker(void)
239 {
240     tempo = 120, length = 4, octave = 3, mtype = 'N';
241 }
242
243 void
244 play(char *tune)
245 {
246     char *c, *n;
247     unsigned num;
248
249     for (c = tune; *c;) {
250         sscanf(c + 1, "%u", &num);
251         for (n = c + 1; isdigit(*n); n++) /* do nothing */
252             ;
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;
299             }
300     }
301 }
302
303 #ifndef TESTING
304 void
305 pc_speaker(struct obj *instr, char *tune)
306 {
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;
327     }
328     play(tune);
329 }
330
331 #else
332
333 main()
334 {
335     char s[80];
336     int tool;
337
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;
355     }
356     printf("Enter tune:");
357     fgets(s, 80, stdin);
358     play(s);
359 }
360 #endif
361
362 #endif /* PCMUSIC */
363
364 /* sound.c */