OSDN Git Service

Initial Import
[nethackexpress/trunk.git] / sys / msdos / sound.c
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
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
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
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
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 *
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
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 *
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
218 initspeaker (void)
219 {
220         tempo = 120, length = 4, octave = 3, mtype = 'N';
221 }
222
223
224 void
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
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
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 */