Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5131 | clevermous | 1 | /* |
2 | Copyright (C) 1996-1997 Id Software, Inc. |
||
3 | |||
4 | This program is free software; you can redistribute it and/or |
||
5 | modify it under the terms of the GNU General Public License |
||
6 | as published by the Free Software Foundation; either version 2 |
||
7 | of the License, or (at your option) any later version. |
||
8 | |||
9 | This program is distributed in the hope that it will be useful, |
||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||
12 | |||
13 | See the GNU General Public License for more details. |
||
14 | |||
15 | You should have received a copy of the GNU General Public License |
||
16 | along with this program; if not, write to the Free Software |
||
17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
||
18 | |||
19 | */ |
||
20 | //============================================================================= |
||
21 | // Routines for GUS support in QUAKE |
||
22 | // |
||
23 | // Author(s): Jayeson Lee-Steere |
||
24 | //============================================================================= |
||
25 | |||
26 | #include "quakedef.h" |
||
27 | #include "dosisms.h" |
||
28 | |||
29 | //============================================================================= |
||
30 | // Author(s): Jayeson Lee-Steere |
||
31 | |||
32 | #define INI_STRING_SIZE 0x100 |
||
33 | |||
34 | FILE *ini_fopen(const char *filename, const char *modes); |
||
35 | int ini_fclose(FILE *f); |
||
36 | void ini_fgets(FILE *f, const char *section, const char *field, char *s); |
||
37 | |||
38 | // Routines for reading from .INI files |
||
39 | // The read routines are fairly efficient. |
||
40 | // |
||
41 | // Author(s): Jayeson Lee-Steere |
||
42 | |||
43 | #define MAX_SECTION_WIDTH 20 |
||
44 | #define MAX_FIELD_WIDTH 20 |
||
45 | |||
46 | #define NUM_SECTION_BUFFERS 10 |
||
47 | #define NUM_FIELD_BUFFERS 20 |
||
48 | |||
49 | struct section_buffer |
||
50 | { |
||
51 | long offset; |
||
52 | char name[MAX_SECTION_WIDTH+1]; |
||
53 | }; |
||
54 | |||
55 | struct field_buffer |
||
56 | { |
||
57 | long offset; |
||
58 | int section; |
||
59 | char name[MAX_FIELD_WIDTH+1]; |
||
60 | }; |
||
61 | |||
62 | static FILE *current_file=NULL; |
||
63 | static int current_section; |
||
64 | |||
65 | static int current_section_buffer=0; |
||
66 | static int current_field_buffer=0; |
||
67 | |||
68 | static struct section_buffer section_buffers[NUM_SECTION_BUFFERS]; |
||
69 | static struct field_buffer field_buffers[NUM_FIELD_BUFFERS]; |
||
70 | //*************************************************************************** |
||
71 | // Internal routines |
||
72 | //*************************************************************************** |
||
73 | static char toupper(char c) |
||
74 | { |
||
75 | if (c>='a' && c<='z') |
||
76 | c-=('a'-'A'); |
||
77 | return(c); |
||
78 | } |
||
79 | |||
80 | static void reset_buffer(FILE *f) |
||
81 | { |
||
82 | int i; |
||
83 | |||
84 | for (i=0;i |
||
85 | section_buffers[i].name[0]=0; |
||
86 | for (i=0;i |
||
87 | field_buffers[i].name[0]=0; |
||
88 | |||
89 | current_file=f; |
||
90 | } |
||
91 | |||
92 | // Sees if the current string is section "name" (i.e. ["name"]). |
||
93 | // If "name"=="*", sees if the current string is any section |
||
94 | // (i.e. [....]). Returns 1 if true else 0 if false. |
||
95 | static int is_section(char *s,const char *name) |
||
96 | { |
||
97 | int wild=0; |
||
98 | |||
99 | // See if wild search |
||
100 | if (strcmp("*",name)==0) |
||
101 | wild=1; |
||
102 | |||
103 | // Skip leading spaces |
||
104 | while (s[0]==' ') |
||
105 | s++; |
||
106 | // Look for leading "[" |
||
107 | if (s[0]!='[') |
||
108 | return(0); |
||
109 | s++; |
||
110 | // Make sure name matches |
||
111 | while (s[0]!=']' && s[0]!=13 && s[0]!=10 && s[0]!=0 && name[0]!=0) |
||
112 | { |
||
113 | if (!wild) |
||
114 | if (toupper(s[0])!=toupper(name[0])) |
||
115 | return(0); |
||
116 | s++; |
||
117 | if (!wild) |
||
118 | name++; |
||
119 | } |
||
120 | if (!wild) |
||
121 | if (name[0]!=0) |
||
122 | return(0); |
||
123 | // Skip trailing spaces |
||
124 | while (s[0]==' ') |
||
125 | s++; |
||
126 | // Make sure we have trailing "]" |
||
127 | if (s[0]!=']') |
||
128 | return(0); |
||
129 | return(1); |
||
130 | } |
||
131 | |||
132 | // Sees if the current string is field "name" (i.e. "name"=...). |
||
133 | // If "name"=="*", sees if the current string is any field |
||
134 | // (i.e. ...=...). Returns 1 if true else 0 if false. |
||
135 | static int is_field(char *s,const char *name) |
||
136 | { |
||
137 | int wild=0; |
||
138 | |||
139 | // See if wild search |
||
140 | if (strcmp("*",name)==0) |
||
141 | wild=1; |
||
142 | |||
143 | // Skip leading spaces |
||
144 | while (s[0]==' ') |
||
145 | s++; |
||
146 | |||
147 | // Make sure name matches |
||
148 | while (s[0]!='=' && s[0]!=13 && s[0]!=10 && s[0]!=0 && name[0]!=0) |
||
149 | { |
||
150 | if (!wild) |
||
151 | if (toupper(s[0])!=toupper(name[0])) |
||
152 | return(0); |
||
153 | s++; |
||
154 | if (!wild) |
||
155 | name++; |
||
156 | } |
||
157 | if (!wild) |
||
158 | if (name[0]!=0) |
||
159 | return(0); |
||
160 | // Skip trailing spaces |
||
161 | while (s[0]==' ') |
||
162 | s++; |
||
163 | // Make sure we have an "=" |
||
164 | if (s[0]!='=') |
||
165 | return(0); |
||
166 | |||
167 | return(1); |
||
168 | } |
||
169 | |||
170 | // Extracts the section name from a section heading |
||
171 | // e.g. in="[hey man]" gives out="hey man" |
||
172 | static void get_section_name(char *out, char *in) |
||
173 | { |
||
174 | int i=0; |
||
175 | |||
176 | // Skip spaces before '[' |
||
177 | while (in[0]==' ') |
||
178 | in++; |
||
179 | // Make sure there is a '[' |
||
180 | if (in[0]!='[') |
||
181 | { |
||
182 | out[0]=0; |
||
183 | return; |
||
184 | } |
||
185 | // Skip past '[' |
||
186 | in++; |
||
187 | // Copy string if any to output string. |
||
188 | while (in[0]!=']' && in[0]!=13 && in[0]!=10 && in[0]!=0) |
||
189 | { |
||
190 | if (i |
||
191 | { |
||
192 | out[i]=in[0]; |
||
193 | i++; |
||
194 | } |
||
195 | in++; |
||
196 | } |
||
197 | // Make sure string was terminated with ']' |
||
198 | if (in[0]!=']') |
||
199 | { |
||
200 | out[0]=0; |
||
201 | return; |
||
202 | } |
||
203 | // Remove trailing spaces |
||
204 | while (i>0 && out[i-1]==' ') |
||
205 | i--; |
||
206 | // Null terminate the output string. |
||
207 | out[i]=0; |
||
208 | } |
||
209 | |||
210 | // Extracts the field name from a field line |
||
211 | // e.g. in="sooty=life be in it" gives out="sooty" |
||
212 | static void get_field_name(char *out, char *in) |
||
213 | { |
||
214 | int i=0; |
||
215 | |||
216 | // Skip leading spaces |
||
217 | while (in[0]==' ') |
||
218 | in++; |
||
219 | // Copy name to output string |
||
220 | while (in[0]!='=' && in[0]!=13 && in[0]!=10 && in[0]!=0) |
||
221 | { |
||
222 | if (i |
||
223 | { |
||
224 | out[i]=in[0]; |
||
225 | i++; |
||
226 | } |
||
227 | in++; |
||
228 | } |
||
229 | // Make sure we stopped on "=" |
||
230 | if (in[0]!='=') |
||
231 | { |
||
232 | out[0]=0; |
||
233 | return; |
||
234 | } |
||
235 | // Remove trailing spaces |
||
236 | while (i>0 && out[i-1]==' ') |
||
237 | i--; |
||
238 | // Null terminate the output string. |
||
239 | out[i]=0; |
||
240 | } |
||
241 | |||
242 | // Returns the field data from string s. |
||
243 | // e.g. in="wally = golly man" gives out="golly man" |
||
244 | static void get_field_string(char *out, char *in) |
||
245 | { |
||
246 | int i=0; |
||
247 | |||
248 | // Find '=' if it exists |
||
249 | while (in[0]!='=' && in[0]!=13 && in[0]!=10 && in[0]!=0) |
||
250 | in++; |
||
251 | // If there is an '=', skip past it. |
||
252 | if (in[0]=='=') |
||
253 | in++; |
||
254 | // Skip any spaces between the '=' and string. |
||
255 | while (in[0]==' ' || in[0]=='[') |
||
256 | in++; |
||
257 | // Copy string, if there is one, to the output string. |
||
258 | while (in[0]!=13 && in[0]!=10 && in[0]!=0 && i<(INI_STRING_SIZE-1)) |
||
259 | { |
||
260 | out[i]=in[0]; |
||
261 | in++; |
||
262 | i++; |
||
263 | } |
||
264 | // Null terminate the output string. |
||
265 | out[i]=0; |
||
266 | } |
||
267 | |||
268 | // Adds a section to the buffer |
||
269 | static int add_section(char *instring, long offset) |
||
270 | { |
||
271 | int i; |
||
272 | char section[MAX_SECTION_WIDTH+1]; |
||
273 | |||
274 | // Extract section name |
||
275 | get_section_name(section,instring); |
||
276 | // See if section already exists. |
||
277 | for (i=0;i |
||
278 | if (stricmp(section,section_buffers[i].name)==0) |
||
279 | return(i); |
||
280 | // Increment current_section_buffer |
||
281 | current_section_buffer++; |
||
282 | if (current_section_buffer>NUM_SECTION_BUFFERS) |
||
283 | current_section_buffer=0; |
||
284 | // Delete any field buffers that correspond to this section |
||
285 | for (i=0;i |
||
286 | if (field_buffers[i].section==current_section_buffer) |
||
287 | field_buffers[i].name[0]=0; |
||
288 | // Set buffer information |
||
289 | strcpy(section_buffers[current_section_buffer].name,section); |
||
290 | section_buffers[current_section_buffer].offset=offset; |
||
291 | return(current_section_buffer); |
||
292 | } |
||
293 | |||
294 | // Adds a field to the buffer |
||
295 | static void add_field(char *instring, int section, long offset) |
||
296 | { |
||
297 | int i; |
||
298 | char field[MAX_FIELD_WIDTH+1]; |
||
299 | |||
300 | // Extract field name |
||
301 | get_field_name(field,instring); |
||
302 | // See if field already exists |
||
303 | for (i=0;i |
||
304 | if (field_buffers[i].section==section) |
||
305 | if (stricmp(field_buffers[i].name,field)==0) |
||
306 | return; |
||
307 | // Increment current_field_buffer |
||
308 | current_field_buffer++; |
||
309 | if (current_field_buffer>NUM_FIELD_BUFFERS) |
||
310 | current_field_buffer=0; |
||
311 | // Set buffer information |
||
312 | strcpy(field_buffers[current_field_buffer].name,field); |
||
313 | field_buffers[current_field_buffer].section=section; |
||
314 | field_buffers[current_field_buffer].offset=offset; |
||
315 | } |
||
316 | |||
317 | // Identical to fgets except the string is trucated at the first ';', |
||
318 | // carriage return or line feed. |
||
319 | static char *stripped_fgets(char *s, int n, FILE *f) |
||
320 | { |
||
321 | int i=0; |
||
322 | |||
323 | if (fgets(s,n,f)==NULL) |
||
324 | return(NULL); |
||
325 | |||
326 | while (s[i]!=';' && s[i]!=13 && s[i]!=10 && s[i]!=0) |
||
327 | i++; |
||
328 | s[i]=0; |
||
329 | |||
330 | return(s); |
||
331 | } |
||
332 | |||
333 | //*************************************************************************** |
||
334 | // Externally accessable routines |
||
335 | //*************************************************************************** |
||
336 | // Opens an .INI file. Works like fopen |
||
337 | FILE *ini_fopen(const char *filename, const char *modes) |
||
338 | { |
||
339 | return(fopen(filename,modes)); |
||
340 | } |
||
341 | |||
342 | // Closes a .INI file. Works like fclose |
||
343 | int ini_fclose(FILE *f) |
||
344 | { |
||
345 | if (f==current_file) |
||
346 | reset_buffer(NULL); |
||
347 | return(fclose(f)); |
||
348 | } |
||
349 | |||
350 | // Puts "field" from "section" from .ini file "f" into "s". |
||
351 | // If "section" does not exist or "field" does not exist in |
||
352 | // section then s=""; |
||
353 | void ini_fgets(FILE *f, const char *section, const char *field, char *s) |
||
354 | { |
||
355 | int i; |
||
356 | long start_pos,string_start_pos; |
||
357 | char ts[INI_STRING_SIZE*2]; |
||
358 | |||
359 | if (f!=current_file) |
||
360 | reset_buffer(f); |
||
361 | |||
362 | // Default to "Not found" |
||
363 | s[0]=0; |
||
364 | |||
365 | // See if section is in buffer |
||
366 | for (i=0;i |
||
367 | if (strnicmp(section_buffers[i].name,section,MAX_SECTION_WIDTH)==0) |
||
368 | break; |
||
369 | |||
370 | // If section is in buffer, seek to it if necessary |
||
371 | if (i |
||
372 | { |
||
373 | if (i!=current_section) |
||
374 | { |
||
375 | current_section=i; |
||
376 | fseek(f,section_buffers[i].offset,SEEK_SET); |
||
377 | } |
||
378 | } |
||
379 | // else look through .ini file for it. |
||
380 | else |
||
381 | { |
||
382 | // Make sure we are not at eof or this will cause trouble. |
||
383 | if (feof(f)) |
||
384 | rewind(f); |
||
385 | start_pos=ftell(f); |
||
386 | while (1) |
||
387 | { |
||
388 | stripped_fgets(ts,INI_STRING_SIZE*2,f); |
||
389 | // If it is a section, add it to the section buffer |
||
390 | if (is_section(ts,"*")) |
||
391 | current_section=add_section(ts,ftell(f)); |
||
392 | // If it is the section we are looking for, break. |
||
393 | if (is_section(ts,section)) |
||
394 | break; |
||
395 | // If we reach the end of the file, rewind to the start. |
||
396 | if (feof(f)) |
||
397 | rewind(f); |
||
398 | if (ftell(f)==start_pos) |
||
399 | return; |
||
400 | } |
||
401 | } |
||
402 | |||
403 | // See if field is in buffer |
||
404 | for (i=0;i |
||
405 | if (field_buffers[i].section==current_section) |
||
406 | if (strnicmp(field_buffers[i].name,field,MAX_FIELD_WIDTH)==0) |
||
407 | break; |
||
408 | |||
409 | // If field is in buffer, seek to it and read it |
||
410 | if (i |
||
411 | { |
||
412 | fseek(f,field_buffers[i].offset,SEEK_SET); |
||
413 | stripped_fgets(ts,INI_STRING_SIZE*2,f); |
||
414 | get_field_string(s,ts); |
||
415 | } |
||
416 | else |
||
417 | // else search through section for field. |
||
418 | { |
||
419 | // Make sure we do not start at eof or this will cause problems. |
||
420 | if (feof(f)) |
||
421 | fseek(f,section_buffers[current_section].offset,SEEK_SET); |
||
422 | start_pos=ftell(f); |
||
423 | while (1) |
||
424 | { |
||
425 | string_start_pos=ftell(f); |
||
426 | stripped_fgets(ts,INI_STRING_SIZE*2,f); |
||
427 | // If it is a field, add it to the buffer |
||
428 | if (is_field(ts,"*")) |
||
429 | add_field(ts,current_section,string_start_pos); |
||
430 | // If it is the field we are looking for, save it |
||
431 | if (is_field(ts,field)) |
||
432 | { |
||
433 | get_field_string(s,ts); |
||
434 | break; |
||
435 | } |
||
436 | // If we reach the end of the section, start over |
||
437 | if (feof(f) || is_section(ts,"*")) |
||
438 | fseek(f,section_buffers[current_section].offset,SEEK_SET); |
||
439 | if (ftell(f)==start_pos) |
||
440 | return; |
||
441 | } |
||
442 | } |
||
443 | } |
||
444 | |||
445 | //============================================================================= |
||
446 | |||
447 | #define BYTE unsigned char |
||
448 | #define WORD unsigned short |
||
449 | #define DWORD unsigned long |
||
450 | |||
451 | #define BUFFER_SIZE 4096 |
||
452 | |||
453 | #define CODEC_ADC_INPUT_CONTROL_LEFT 0x00 |
||
454 | #define CODEC_ADC_INPUT_CONTROL_RIGHT 0x01 |
||
455 | #define CODEC_AUX1_INPUT_CONTROL_LEFT 0x02 |
||
456 | #define CODEC_AUX1_INPUT_CONTROL_RIGHT 0x03 |
||
457 | #define CODEC_AUX2_INPUT_CONTROL_LEFT 0x04 |
||
458 | #define CODEC_AUX2_INPUT_CONTROL_RIGHT 0x05 |
||
459 | #define CODEC_DAC_OUTPUT_CONTROL_LEFT 0x06 |
||
460 | #define CODEC_DAC_OUTPUT_CONTROL_RIGHT 0x07 |
||
461 | #define CODEC_FS_FORMAT 0x08 |
||
462 | #define CODEC_INTERFACE_CONFIG 0x09 |
||
463 | #define CODEC_PIN_CONTROL 0x0A |
||
464 | #define CODEC_ERROR_STATUS_AND_INIT 0x0B |
||
465 | #define CODEC_MODE_AND_ID 0x0C |
||
466 | #define CODEC_LOOPBACK_CONTROL 0x0D |
||
467 | #define CODEC_PLAYBACK_UPPER_BASE_COUNT 0x0E |
||
468 | #define CODEC_PLAYBACK_LOWER_BASE_COUNT 0x0F |
||
469 | |||
470 | #define SET_CONTROL 0x00 |
||
471 | #define SET_FREQUENCY 0x01 |
||
472 | #define SET_START_HIGH 0x02 |
||
473 | #define SET_START_LOW 0x03 |
||
474 | #define SET_END_HIGH 0x04 |
||
475 | #define SET_END_LOW 0x05 |
||
476 | #define SET_VOLUME_RATE 0x06 |
||
477 | #define SET_VOLUME_START 0x07 |
||
478 | #define SET_VOLUME_END 0x08 |
||
479 | #define SET_CURR_VOLUME 0x09 |
||
480 | #define SET_VOLUME 0x09 |
||
481 | #define SET_ACC_HIGH 0x0A |
||
482 | #define SET_ACC_LOW 0x0B |
||
483 | #define SET_BALANCE 0x0C |
||
484 | #define SET_VOLUME_CONTROL 0x0D |
||
485 | #define SET_VOICES 0x0E |
||
486 | |||
487 | #define DMA_CONTROL 0x41 |
||
488 | #define SET_DMA_ADDRESS 0x42 |
||
489 | #define SET_DRAM_LOW 0x43 |
||
490 | #define SET_DRAM_HIGH 0x44 |
||
491 | #define ADLIB_CONTROL 0x45 |
||
492 | #define ADLIB_TIMER1 0x46 |
||
493 | #define ADLIB_TIMER2 0x47 |
||
494 | #define SET_RECORD_RATE 0x48 |
||
495 | #define RECORD_CONTROL 0x49 |
||
496 | #define SET_JOYSTICK 0x4B |
||
497 | #define MASTER_RESET 0x4C |
||
498 | |||
499 | #define GET_CONTROL 0x80 |
||
500 | #define GET_FREQUENCY 0x81 |
||
501 | #define GET_START_HIGH 0x82 |
||
502 | #define GET_START_LOW 0x83 |
||
503 | #define GET_END_HIGH 0x84 |
||
504 | #define GET_END_LOW 0x85 |
||
505 | #define GET_VOLUME_RATE 0x86 |
||
506 | #define GET_VOLUME_START 0x87 |
||
507 | #define GET_VOLUME_END 0x88 |
||
508 | #define GET_VOLUME 0x89 |
||
509 | #define GET_ACC_HIGH 0x8A |
||
510 | #define GET_ACC_LOW 0x8B |
||
511 | #define GET_BALANCE 0x8C |
||
512 | #define GET_VOLUME_CONTROL 0x8D |
||
513 | #define GET_VOICES 0x8E |
||
514 | #define GET_IRQV 0x8F |
||
515 | |||
516 | struct CodecRateStruct |
||
517 | { |
||
518 | WORD Rate; |
||
519 | BYTE FSVal; |
||
520 | }; |
||
521 | |||
522 | struct Gf1RateStruct |
||
523 | { |
||
524 | WORD Rate; |
||
525 | BYTE Voices; |
||
526 | }; |
||
527 | |||
528 | //============================================================================= |
||
529 | // Reference variables in SND_DOS.C |
||
530 | //============================================================================= |
||
531 | extern short *dma_buffer; |
||
532 | |||
533 | //============================================================================= |
||
534 | // GUS-only variables |
||
535 | //============================================================================= |
||
536 | static BYTE HaveCodec=0; |
||
537 | |||
538 | static WORD CodecRegisterSelect; |
||
539 | static WORD CodecData; |
||
540 | static WORD CodecStatus; |
||
541 | static WORD Gf1TimerControl; |
||
542 | static WORD Gf1PageRegister; |
||
543 | static WORD Gf1RegisterSelect; |
||
544 | static WORD Gf1DataLow; |
||
545 | static WORD Gf1DataHigh; |
||
546 | |||
547 | static BYTE DmaChannel; |
||
548 | |||
549 | static BYTE PageRegs[] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; |
||
550 | static BYTE AddrRegs[] = { 0, 2, 4, 6, 0xc0, 0xc4, 0xc8, 0xcc }; |
||
551 | static BYTE CountRegs[] = { 1, 3, 5, 7, 0xc2, 0xc6, 0xca, 0xce }; |
||
552 | |||
553 | static WORD AddrReg; |
||
554 | static WORD CountReg; |
||
555 | static WORD ModeReg; |
||
556 | static WORD DisableReg; |
||
557 | static WORD ClearReg; |
||
558 | |||
559 | static struct CodecRateStruct CodecRates[]= |
||
560 | { |
||
561 | { 5512,0x01}, |
||
562 | { 6620,0x0F}, |
||
563 | { 8000,0x00}, |
||
564 | { 9600,0x0E}, |
||
565 | {11025,0x03}, |
||
566 | {16000,0x02}, |
||
567 | {18900,0x05}, |
||
568 | {22050,0x07}, |
||
569 | {27420,0x04}, |
||
570 | {32000,0x06}, |
||
571 | {33075,0x0D}, |
||
572 | {37800,0x09}, |
||
573 | {44100,0x0B}, |
||
574 | {48000,0x0C}, |
||
575 | { 0,0x00} // End marker |
||
576 | }; |
||
577 | |||
578 | static struct Gf1RateStruct Gf1Rates[]= |
||
579 | { |
||
580 | {19293,32}, |
||
581 | {19916,31}, |
||
582 | {20580,30}, |
||
583 | {21289,29}, |
||
584 | {22050,28}, |
||
585 | {22866,27}, |
||
586 | {23746,26}, |
||
587 | {24696,25}, |
||
588 | {25725,24}, |
||
589 | {26843,23}, |
||
590 | {28063,22}, |
||
591 | {29400,21}, |
||
592 | {30870,20}, |
||
593 | {32494,19}, |
||
594 | {34300,18}, |
||
595 | {36317,17}, |
||
596 | {38587,16}, |
||
597 | {41160,15}, |
||
598 | {44100,14}, |
||
599 | {0,0} |
||
600 | }; |
||
601 | |||
602 | //============================================================================= |
||
603 | // Basic GF1 functions |
||
604 | //============================================================================= |
||
605 | void SetGf18(BYTE reg,BYTE data) |
||
606 | { |
||
607 | dos_outportb(Gf1RegisterSelect,reg); |
||
608 | dos_outportb(Gf1DataHigh,data); |
||
609 | } |
||
610 | |||
611 | void SetGf116(BYTE reg,WORD data) |
||
612 | { |
||
613 | dos_outportb(Gf1RegisterSelect,reg); |
||
614 | dos_outportw(Gf1DataLow,data); |
||
615 | } |
||
616 | |||
617 | BYTE GetGf18(BYTE reg) |
||
618 | { |
||
619 | dos_outportb(Gf1RegisterSelect,reg); |
||
620 | return(dos_inportb(Gf1DataHigh)); |
||
621 | } |
||
622 | |||
623 | WORD GetGf116(BYTE reg) |
||
624 | { |
||
625 | dos_outportb(Gf1RegisterSelect,reg); |
||
626 | return(dos_inportw(Gf1DataLow)); |
||
627 | } |
||
628 | |||
629 | void Gf1Delay(void) |
||
630 | { |
||
631 | int i; |
||
632 | |||
633 | for (i=0;i<27;i++) |
||
634 | dos_inportb(Gf1TimerControl); |
||
635 | } |
||
636 | |||
637 | DWORD ConvertTo16(DWORD Address) |
||
638 | { |
||
639 | return( ((Address>>1) & 0x0001FFFF) | (Address & 0x000C0000L) ); |
||
640 | } |
||
641 | |||
642 | void ClearGf1Ints(void) |
||
643 | { |
||
644 | int i; |
||
645 | |||
646 | SetGf18(DMA_CONTROL,0x00); |
||
647 | SetGf18(ADLIB_CONTROL,0x00); |
||
648 | SetGf18(RECORD_CONTROL,0x00); |
||
649 | |||
650 | GetGf18(DMA_CONTROL); |
||
651 | GetGf18(RECORD_CONTROL); |
||
652 | for (i=0;i<32;i++); |
||
653 | GetGf18(GET_IRQV); |
||
654 | } |
||
655 | |||
656 | |||
657 | //============================================================================= |
||
658 | // Get Interwave (UltraSound PnP) configuration if any |
||
659 | //============================================================================= |
||
660 | static qboolean GUS_GetIWData(void) |
||
661 | { |
||
662 | char *Interwave,s[INI_STRING_SIZE]; |
||
663 | FILE *IwFile; |
||
664 | int CodecBase,CodecDma,i; |
||
665 | |||
666 | Interwave=getenv("INTERWAVE"); |
||
667 | if (Interwave==NULL) |
||
668 | return(false); |
||
669 | |||
670 | // Open IW.INI |
||
671 | IwFile=ini_fopen(Interwave,"rt"); |
||
672 | if (IwFile==NULL) |
||
673 | return(false); |
||
674 | |||
675 | // Read codec base and codec DMA |
||
676 | ini_fgets(IwFile,"setup 0","CodecBase",s); |
||
677 | sscanf(s,"%X",&CodecBase); |
||
678 | ini_fgets(IwFile,"setup 0","DMA2",s); |
||
679 | sscanf(s,"%i",&CodecDma); |
||
680 | |||
681 | ini_fclose(IwFile); |
||
682 | |||
683 | // Make sure numbers OK |
||
684 | if (CodecBase==0 || CodecDma==0) |
||
685 | return(false); |
||
686 | |||
687 | CodecRegisterSelect=CodecBase; |
||
688 | CodecData=CodecBase+1; |
||
689 | CodecStatus=CodecBase+2; |
||
690 | DmaChannel=CodecDma; |
||
691 | |||
692 | // Make sure there is a CODEC at the CODEC base |
||
693 | |||
694 | // Clear any pending IRQs |
||
695 | dos_inportb(CodecStatus); |
||
696 | dos_outportb(CodecStatus,0); |
||
697 | |||
698 | // Wait for 'INIT' bit to clear |
||
699 | for (i=0;i<0xFFFF;i++) |
||
700 | if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0) |
||
701 | break; |
||
702 | if (i==0xFFFF) |
||
703 | return(false); |
||
704 | |||
705 | // Get chip revision - can not be zero |
||
706 | dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); |
||
707 | if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) |
||
708 | return(false); |
||
709 | if ((dos_inportb(CodecData) & 0x0F) == 0) |
||
710 | return(false); |
||
711 | |||
712 | HaveCodec=1; |
||
713 | Con_Printf("Sound Card is UltraSound PnP\n"); |
||
714 | return(true); |
||
715 | } |
||
716 | |||
717 | //============================================================================= |
||
718 | // Get UltraSound MAX configuration if any |
||
719 | //============================================================================= |
||
720 | static qboolean GUS_GetMAXData(void) |
||
721 | { |
||
722 | char *Ultrasnd,*Ultra16; |
||
723 | int i; |
||
724 | int GusBase,Dma1,Dma2,Irq1,Irq2; |
||
725 | int CodecBase,CodecDma,CodecIrq,CodecType; |
||
726 | BYTE MaxVal; |
||
727 | |||
728 | Ultrasnd=getenv("ULTRASND"); |
||
729 | Ultra16=getenv("ULTRA16"); |
||
730 | if (Ultrasnd==NULL || Ultra16==NULL) |
||
731 | return(false); |
||
732 | |||
733 | sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2); |
||
734 | sscanf(Ultra16,"%x,%i,%i,%i",&CodecBase,&CodecDma,&CodecIrq,&CodecType); |
||
735 | |||
736 | if (CodecType==0 && CodecDma!=0) |
||
737 | DmaChannel=CodecDma & 0x07; |
||
738 | else |
||
739 | DmaChannel=Dma2 & 0x07; |
||
740 | |||
741 | // Make sure there is a GUS at GUS base |
||
742 | dos_outportb(GusBase+0x08,0x55); |
||
743 | if (dos_inportb(GusBase+0x0A)!=0x55) |
||
744 | return(false); |
||
745 | dos_outportb(GusBase+0x08,0xAA); |
||
746 | if (dos_inportb(GusBase+0x0A)!=0xAA) |
||
747 | return(false); |
||
748 | |||
749 | // Program CODEC control register |
||
750 | MaxVal=((CodecBase & 0xF0)>>4) | 0x40; |
||
751 | if (Dma1 > 3) |
||
752 | MaxVal|=0x10; |
||
753 | if (Dma2 > 3) |
||
754 | MaxVal|=0x20; |
||
755 | dos_outportb(GusBase+0x106,MaxVal); |
||
756 | |||
757 | CodecRegisterSelect=CodecBase; |
||
758 | CodecData=CodecBase+1; |
||
759 | CodecStatus=CodecBase+2; |
||
760 | |||
761 | // Make sure there is a CODEC at the CODEC base |
||
762 | |||
763 | // Clear any pending IRQs |
||
764 | dos_inportb(CodecStatus); |
||
765 | dos_outportb(CodecStatus,0); |
||
766 | |||
767 | // Wait for 'INIT' bit to clear |
||
768 | for (i=0;i<0xFFFF;i++) |
||
769 | if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0) |
||
770 | break; |
||
771 | if (i==0xFFFF) |
||
772 | return(false); |
||
773 | |||
774 | // Get chip revision - can not be zero |
||
775 | dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); |
||
776 | if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) |
||
777 | return(false); |
||
778 | if ((dos_inportb(CodecData) & 0x0F) == 0) |
||
779 | return(false); |
||
780 | |||
781 | HaveCodec=1; |
||
782 | Con_Printf("Sound Card is UltraSound MAX\n"); |
||
783 | return(true); |
||
784 | } |
||
785 | |||
786 | //============================================================================= |
||
787 | // Get regular UltraSound configuration if any |
||
788 | //============================================================================= |
||
789 | static qboolean GUS_GetGUSData(void) |
||
790 | { |
||
791 | char *Ultrasnd; |
||
792 | int GusBase,Dma1,Dma2,Irq1,Irq2,i; |
||
793 | |||
794 | Ultrasnd=getenv("ULTRASND"); |
||
795 | if (Ultrasnd==NULL) |
||
796 | return(false); |
||
797 | |||
798 | sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2); |
||
799 | |||
800 | DmaChannel=Dma1 & 0x07; |
||
801 | |||
802 | // Make sure there is a GUS at GUS base |
||
803 | dos_outportb(GusBase+0x08,0x55); |
||
804 | if (dos_inportb(GusBase+0x0A)!=0x55) |
||
805 | return(false); |
||
806 | dos_outportb(GusBase+0x08,0xAA); |
||
807 | if (dos_inportb(GusBase+0x0A)!=0xAA) |
||
808 | return(false); |
||
809 | |||
810 | Gf1TimerControl = GusBase+0x008; |
||
811 | Gf1PageRegister = GusBase+0x102; |
||
812 | Gf1RegisterSelect = GusBase+0x103; |
||
813 | Gf1DataLow = GusBase+0x104; |
||
814 | Gf1DataHigh = GusBase+0x105; |
||
815 | |||
816 | // Reset the GUS |
||
817 | SetGf18(MASTER_RESET,0x00); |
||
818 | Gf1Delay(); |
||
819 | Gf1Delay(); |
||
820 | SetGf18(MASTER_RESET,0x01); |
||
821 | Gf1Delay(); |
||
822 | Gf1Delay(); |
||
823 | |||
824 | // Set to max (32) voices |
||
825 | SetGf18(SET_VOICES,0xDF); |
||
826 | |||
827 | // Clear any pending IRQ's |
||
828 | ClearGf1Ints(); |
||
829 | |||
830 | // Set all registers to known values |
||
831 | for (i=0;i<32;i++) |
||
832 | { |
||
833 | dos_outportb(Gf1PageRegister,i); |
||
834 | SetGf18(SET_CONTROL,0x03); |
||
835 | SetGf18(SET_VOLUME_CONTROL,0x03); |
||
836 | Gf1Delay(); |
||
837 | SetGf18(SET_CONTROL,0x03); |
||
838 | SetGf18(SET_VOLUME_CONTROL,0x03); |
||
839 | SetGf116(SET_START_HIGH,0); |
||
840 | SetGf116(SET_START_LOW,0); |
||
841 | SetGf116(SET_END_HIGH,0); |
||
842 | SetGf116(SET_END_LOW,0); |
||
843 | SetGf116(SET_ACC_HIGH,0); |
||
844 | SetGf116(SET_ACC_LOW,0); |
||
845 | SetGf18(SET_VOLUME_RATE,63); |
||
846 | SetGf18(SET_VOLUME_START,5); |
||
847 | SetGf18(SET_VOLUME_END,251); |
||
848 | SetGf116(SET_VOLUME,5<<8); |
||
849 | } |
||
850 | |||
851 | // Clear any pending IRQ's |
||
852 | ClearGf1Ints(); |
||
853 | |||
854 | // Enable DAC etc. |
||
855 | SetGf18(MASTER_RESET,0x07); |
||
856 | |||
857 | // Enable line output so we can hear something |
||
858 | dos_outportb(GusBase,0x08); |
||
859 | |||
860 | HaveCodec=0; |
||
861 | Con_Printf("Sound Card is UltraSound\n"); |
||
862 | return(true); |
||
863 | } |
||
864 | |||
865 | |||
866 | //============================================================================= |
||
867 | // Programs the DMA controller to start DMAing in Auto-init mode |
||
868 | //============================================================================= |
||
869 | static void GUS_StartDMA(BYTE DmaChannel,short *dma_buffer,int count) |
||
870 | { |
||
871 | int mode; |
||
872 | int RealAddr; |
||
873 | |||
874 | RealAddr = ptr2real(dma_buffer); |
||
875 | |||
876 | if (DmaChannel <= 3) |
||
877 | { |
||
878 | ModeReg = 0x0B; |
||
879 | DisableReg = 0x0A; |
||
880 | ClearReg = 0x0E; |
||
881 | } |
||
882 | else |
||
883 | { |
||
884 | ModeReg = 0xD6; |
||
885 | DisableReg = 0xD4; |
||
886 | ClearReg = 0xDC; |
||
887 | } |
||
888 | CountReg=CountRegs[DmaChannel]; |
||
889 | AddrReg=AddrRegs[DmaChannel]; |
||
890 | |||
891 | dos_outportb(DisableReg, DmaChannel | 4); // disable channel |
||
892 | |||
893 | // set mode- see "undocumented pc", p.876 |
||
894 | mode = (1<<6) // single-cycle |
||
895 | +(0<<5) // address increment |
||
896 | +(1<<4) // auto-init dma |
||
897 | +(2<<2) // read |
||
898 | +(DmaChannel & 0x03); // channel # |
||
899 | dos_outportb(ModeReg, mode); |
||
900 | |||
901 | // set page |
||
902 | dos_outportb(PageRegs[DmaChannel], RealAddr >> 16); |
||
903 | |||
904 | if (DmaChannel <= 3) |
||
905 | { // address is in bytes |
||
906 | dos_outportb(0x0C, 0); // prepare to send 16-bit value |
||
907 | dos_outportb(AddrReg, RealAddr & 0xff); |
||
908 | dos_outportb(AddrReg, (RealAddr>>8) & 0xff); |
||
909 | |||
910 | dos_outportb(0x0C, 0); // prepare to send 16-bit value |
||
911 | dos_outportb(CountReg, (count-1) & 0xff); |
||
912 | dos_outportb(CountReg, (count-1) >> 8); |
||
913 | } |
||
914 | else |
||
915 | { // address is in words |
||
916 | dos_outportb(0xD8, 0); // prepare to send 16-bit value |
||
917 | dos_outportb(AddrReg, (RealAddr>>1) & 0xff); |
||
918 | dos_outportb(AddrReg, (RealAddr>>9) & 0xff); |
||
919 | |||
920 | dos_outportb(0xD8, 0); // prepare to send 16-bit value |
||
921 | dos_outportb(CountReg, ((count>>1)-1) & 0xff); |
||
922 | dos_outportb(CountReg, ((count>>1)-1) >> 8); |
||
923 | } |
||
924 | |||
925 | dos_outportb(ClearReg, 0); // clear write mask |
||
926 | dos_outportb(DisableReg, DmaChannel & ~4); |
||
927 | } |
||
928 | |||
929 | //============================================================================= |
||
930 | // Starts the CODEC playing |
||
931 | //============================================================================= |
||
932 | static void GUS_StartCODEC(int count,BYTE FSVal) |
||
933 | { |
||
934 | int i,j; |
||
935 | |||
936 | // Clear any pending IRQs |
||
937 | dos_inportb(CodecStatus); |
||
938 | dos_outportb(CodecStatus,0); |
||
939 | |||
940 | // Set mode to 2 |
||
941 | dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); |
||
942 | dos_outportb(CodecData,0xC0); |
||
943 | |||
944 | // Stop any playback or capture which may be happening |
||
945 | dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); |
||
946 | dos_outportb(CodecData,dos_inportb(CodecData) & 0xFC); |
||
947 | |||
948 | // Set FS |
||
949 | dos_outportb(CodecRegisterSelect,CODEC_FS_FORMAT | 0x40); |
||
950 | dos_outportb(CodecData,FSVal | 0x50); // Or in stereo and 16 bit bits |
||
951 | |||
952 | // Wait a bit |
||
953 | for (i=0;i<10;i++) |
||
954 | dos_inportb(CodecData); |
||
955 | |||
956 | // Routine 1 to counter CODEC bug - wait for init bit to clear and then a |
||
957 | // bit longer (i=min loop count, j=timeout |
||
958 | for (i=0,j=0;i<1000 && j<0x7FFFF;j++) |
||
959 | if ((dos_inportb(CodecRegisterSelect) & 0x80)==0) |
||
960 | i++; |
||
961 | |||
962 | // Routine 2 to counter CODEC bug - this is from Forte's code. For me it |
||
963 | // does not seem to cure the problem, but is added security |
||
964 | // Waits till we can modify index register |
||
965 | for (j=0;j<0x7FFFF;j++) |
||
966 | { |
||
967 | dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); |
||
968 | if (dos_inportb(CodecRegisterSelect)==(CODEC_INTERFACE_CONFIG | 0x40)) |
||
969 | break; |
||
970 | } |
||
971 | |||
972 | // Perform ACAL |
||
973 | dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); |
||
974 | dos_outportb(CodecData,0x08); |
||
975 | |||
976 | // Clear MCE bit - this makes ACAL happen |
||
977 | dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); |
||
978 | |||
979 | // Wait for ACAL to finish |
||
980 | for (j=0;j<0x7FFFF;j++) |
||
981 | { |
||
982 | if ((dos_inportb(CodecRegisterSelect) & 0x80) != 0) |
||
983 | continue; |
||
984 | dos_outportb(CodecRegisterSelect,CODEC_ERROR_STATUS_AND_INIT); |
||
985 | if ((dos_inportb(CodecData) & 0x20) == 0) |
||
986 | break; |
||
987 | } |
||
988 | |||
989 | // Clear ACAL bit |
||
990 | dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); |
||
991 | dos_outportb(CodecData,0x00); |
||
992 | dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); |
||
993 | |||
994 | // Set some other junk |
||
995 | dos_outportb(CodecRegisterSelect,CODEC_LOOPBACK_CONTROL); |
||
996 | dos_outportb(CodecData,0x00); |
||
997 | dos_outportb(CodecRegisterSelect,CODEC_PIN_CONTROL); |
||
998 | dos_outportb(CodecData,0x08); // IRQ is disabled in PIN control |
||
999 | |||
1000 | // Set count (it doesn't really matter what value we stuff in here |
||
1001 | dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_LOWER_BASE_COUNT); |
||
1002 | dos_outportb(CodecData,count & 0xFF); |
||
1003 | dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_UPPER_BASE_COUNT); |
||
1004 | dos_outportb(CodecData,count >> 8); |
||
1005 | |||
1006 | // Start playback |
||
1007 | dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); |
||
1008 | dos_outportb(CodecData,0x01); |
||
1009 | } |
||
1010 | |||
1011 | //============================================================================= |
||
1012 | // Starts the GF1 playing |
||
1013 | //============================================================================= |
||
1014 | static void GUS_StartGf1(int count,BYTE Voices) |
||
1015 | { |
||
1016 | DWORD StartAddressL,EndAddressL,StartAddressR,EndAddressR; |
||
1017 | |||
1018 | // Set number of voices to give us the sampling rate we want |
||
1019 | SetGf18(SET_VOICES,0xC0 | (Voices-1)); |
||
1020 | |||
1021 | // Figure out addresses |
||
1022 | StartAddressL=ConvertTo16(0); |
||
1023 | EndAddressL=ConvertTo16(count-2-2); |
||
1024 | StartAddressR=ConvertTo16(2); |
||
1025 | EndAddressR=ConvertTo16(count-2); |
||
1026 | |||
1027 | // Set left voice addresses |
||
1028 | dos_outportb(Gf1PageRegister,0); |
||
1029 | SetGf116(SET_START_LOW,StartAddressL<<9); |
||
1030 | SetGf116(SET_START_HIGH,StartAddressL>>7); |
||
1031 | SetGf116(SET_ACC_LOW,StartAddressL<<9); |
||
1032 | SetGf116(SET_ACC_HIGH,StartAddressL>>7); |
||
1033 | SetGf116(SET_END_LOW,EndAddressL<<9); |
||
1034 | SetGf116(SET_END_HIGH,EndAddressL>>7); |
||
1035 | // Set balance to full left |
||
1036 | SetGf18(SET_BALANCE,0); |
||
1037 | // Set volume to full |
||
1038 | SetGf116(SET_VOLUME,0xFFF0); |
||
1039 | // Set FC to 2 (so we play every second sample) |
||
1040 | SetGf116(SET_FREQUENCY,0x0800); |
||
1041 | |||
1042 | // Set right voice addresses |
||
1043 | dos_outportb(Gf1PageRegister,1); |
||
1044 | SetGf116(SET_START_LOW,StartAddressR<<9); |
||
1045 | SetGf116(SET_START_HIGH,StartAddressR>>7); |
||
1046 | SetGf116(SET_ACC_LOW,StartAddressR<<9); |
||
1047 | SetGf116(SET_ACC_HIGH,StartAddressR>>7); |
||
1048 | SetGf116(SET_END_LOW,EndAddressR<<9); |
||
1049 | SetGf116(SET_END_HIGH,EndAddressR>>7); |
||
1050 | // Set balance to full right |
||
1051 | SetGf18(SET_BALANCE,15); |
||
1052 | // Set volume to full |
||
1053 | SetGf116(SET_VOLUME,0xFFF0); |
||
1054 | // Set FC to 2 (so we play every second sample) |
||
1055 | SetGf116(SET_FREQUENCY,0x0800); |
||
1056 | |||
1057 | // Start voices |
||
1058 | dos_outportb(Gf1PageRegister,0); |
||
1059 | SetGf18(SET_CONTROL,0x0C); |
||
1060 | dos_outportb(Gf1PageRegister,1); |
||
1061 | SetGf18(SET_CONTROL,0x0C); |
||
1062 | Gf1Delay(); |
||
1063 | dos_outportb(Gf1PageRegister,0); |
||
1064 | SetGf18(SET_CONTROL,0x0C); |
||
1065 | dos_outportb(Gf1PageRegister,1); |
||
1066 | SetGf18(SET_CONTROL,0x0C); |
||
1067 | } |
||
1068 | |||
1069 | |||
1070 | //============================================================================= |
||
1071 | // Figures out what kind of UltraSound we have, if any, and starts it playing |
||
1072 | //============================================================================= |
||
1073 | qboolean GUS_Init(void) |
||
1074 | { |
||
1075 | int rc; |
||
1076 | int RealAddr; |
||
1077 | BYTE FSVal,Voices; |
||
1078 | struct CodecRateStruct *CodecRate; |
||
1079 | struct Gf1RateStruct *Gf1Rate; |
||
1080 | |||
1081 | // See what kind of UltraSound we have, if any |
||
1082 | if (GUS_GetIWData()==false) |
||
1083 | if (GUS_GetMAXData()==false) |
||
1084 | if (GUS_GetGUSData()==false) |
||
1085 | return(false); |
||
1086 | |||
1087 | shm = &sn; |
||
1088 | |||
1089 | if (HaveCodec) |
||
1090 | { |
||
1091 | // do 11khz sampling rate unless command line parameter wants different |
||
1092 | shm->speed = 11025; |
||
1093 | FSVal = 0x03; |
||
1094 | rc = COM_CheckParm("-sspeed"); |
||
1095 | if (rc) |
||
1096 | { |
||
1097 | shm->speed = Q_atoi(com_argv[rc+1]); |
||
1098 | |||
1099 | // Make sure rate not too high |
||
1100 | if (shm->speed>48000) |
||
1101 | shm->speed=48000; |
||
1102 | |||
1103 | // Adjust speed to match one of the possible CODEC rates |
||
1104 | for (CodecRate=CodecRates;CodecRate->Rate!=0;CodecRate++) |
||
1105 | { |
||
1106 | if (shm->speed <= CodecRate->Rate) |
||
1107 | { |
||
1108 | shm->speed=CodecRate->Rate; |
||
1109 | FSVal=CodecRate->FSVal; |
||
1110 | break; |
||
1111 | } |
||
1112 | } |
||
1113 | } |
||
1114 | |||
1115 | |||
1116 | // Always do 16 bit stereo |
||
1117 | shm->channels = 2; |
||
1118 | shm->samplebits = 16; |
||
1119 | |||
1120 | // allocate buffer twice the size we need so we can get aligned buffer |
||
1121 | dma_buffer = dos_getmemory(BUFFER_SIZE*2); |
||
1122 | if (dma_buffer==NULL) |
||
1123 | { |
||
1124 | Con_Printf("Couldn't allocate sound dma buffer"); |
||
1125 | return false; |
||
1126 | } |
||
1127 | |||
1128 | RealAddr = ptr2real(dma_buffer); |
||
1129 | RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1); |
||
1130 | dma_buffer = (short *) real2ptr(RealAddr); |
||
1131 | |||
1132 | // Zero off DMA buffer |
||
1133 | memset(dma_buffer, 0, BUFFER_SIZE); |
||
1134 | |||
1135 | shm->soundalive = true; |
||
1136 | shm->splitbuffer = false; |
||
1137 | |||
1138 | shm->samplepos = 0; |
||
1139 | shm->submission_chunk = 1; |
||
1140 | shm->buffer = (unsigned char *) dma_buffer; |
||
1141 | shm->samples = BUFFER_SIZE/(shm->samplebits/8); |
||
1142 | |||
1143 | GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); |
||
1144 | GUS_StartCODEC(BUFFER_SIZE,FSVal); |
||
1145 | } |
||
1146 | else |
||
1147 | { |
||
1148 | // do 19khz sampling rate unless command line parameter wants different |
||
1149 | shm->speed = 19293; |
||
1150 | Voices=32; |
||
1151 | rc = COM_CheckParm("-sspeed"); |
||
1152 | if (rc) |
||
1153 | { |
||
1154 | shm->speed = Q_atoi(com_argv[rc+1]); |
||
1155 | |||
1156 | // Make sure rate not too high |
||
1157 | if (shm->speed>44100) |
||
1158 | shm->speed=44100; |
||
1159 | |||
1160 | // Adjust speed to match one of the possible GF1 rates |
||
1161 | for (Gf1Rate=Gf1Rates;Gf1Rate->Rate!=0;Gf1Rate++) |
||
1162 | { |
||
1163 | if (shm->speed <= Gf1Rate->Rate) |
||
1164 | { |
||
1165 | shm->speed=Gf1Rate->Rate; |
||
1166 | Voices=Gf1Rate->Voices; |
||
1167 | break; |
||
1168 | } |
||
1169 | } |
||
1170 | } |
||
1171 | |||
1172 | // Always do 16 bit stereo |
||
1173 | shm->channels = 2; |
||
1174 | shm->samplebits = 16; |
||
1175 | |||
1176 | // allocate buffer twice the size we need so we can get aligned buffer |
||
1177 | dma_buffer = dos_getmemory(BUFFER_SIZE*2); |
||
1178 | if (dma_buffer==NULL) |
||
1179 | { |
||
1180 | Con_Printf("Couldn't allocate sound dma buffer"); |
||
1181 | return false; |
||
1182 | } |
||
1183 | |||
1184 | RealAddr = ptr2real(dma_buffer); |
||
1185 | RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1); |
||
1186 | dma_buffer = (short *) real2ptr(RealAddr); |
||
1187 | |||
1188 | // Zero off DMA buffer |
||
1189 | memset(dma_buffer, 0, BUFFER_SIZE); |
||
1190 | |||
1191 | shm->soundalive = true; |
||
1192 | shm->splitbuffer = false; |
||
1193 | |||
1194 | shm->samplepos = 0; |
||
1195 | shm->submission_chunk = 1; |
||
1196 | shm->buffer = (unsigned char *) dma_buffer; |
||
1197 | shm->samples = BUFFER_SIZE/(shm->samplebits/8); |
||
1198 | |||
1199 | GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); |
||
1200 | SetGf116(SET_DMA_ADDRESS,0x0000); |
||
1201 | if (DmaChannel<=3) |
||
1202 | SetGf18(DMA_CONTROL,0x41); |
||
1203 | else |
||
1204 | SetGf18(DMA_CONTROL,0x45); |
||
1205 | GUS_StartGf1(BUFFER_SIZE,Voices); |
||
1206 | } |
||
1207 | return(true); |
||
1208 | } |
||
1209 | |||
1210 | //============================================================================= |
||
1211 | // Returns the current playback position |
||
1212 | //============================================================================= |
||
1213 | int GUS_GetDMAPos(void) |
||
1214 | { |
||
1215 | int count; |
||
1216 | |||
1217 | if (HaveCodec) |
||
1218 | { |
||
1219 | // clear 16-bit reg flip-flop |
||
1220 | // load the current dma count register |
||
1221 | if (DmaChannel < 4) |
||
1222 | { |
||
1223 | dos_outportb(0x0C, 0); |
||
1224 | count = dos_inportb(CountReg); |
||
1225 | count += dos_inportb(CountReg) << 8; |
||
1226 | if (shm->samplebits == 16) |
||
1227 | count /= 2; |
||
1228 | count = shm->samples - (count+1); |
||
1229 | } |
||
1230 | else |
||
1231 | { |
||
1232 | dos_outportb(0xD8, 0); |
||
1233 | count = dos_inportb(CountReg); |
||
1234 | count += dos_inportb(CountReg) << 8; |
||
1235 | if (shm->samplebits == 8) |
||
1236 | count *= 2; |
||
1237 | count = shm->samples - (count+1); |
||
1238 | } |
||
1239 | |||
1240 | } |
||
1241 | else |
||
1242 | { |
||
1243 | // Read current position from GF1 |
||
1244 | dos_outportb(Gf1PageRegister,0); |
||
1245 | count=(GetGf116(GET_ACC_HIGH)<<7) & 0xFFFF; |
||
1246 | // See which half of buffer we are in. Note that since this is 16 bit |
||
1247 | // data we are playing, position is in 16 bit samples |
||
1248 | if (GetGf18(DMA_CONTROL) & 0x40) |
||
1249 | { |
||
1250 | GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); |
||
1251 | SetGf116(SET_DMA_ADDRESS,0x0000); |
||
1252 | if (DmaChannel<=3) |
||
1253 | SetGf18(DMA_CONTROL,0x41); |
||
1254 | else |
||
1255 | SetGf18(DMA_CONTROL,0x45); |
||
1256 | } |
||
1257 | } |
||
1258 | |||
1259 | shm->samplepos = count & (shm->samples-1); |
||
1260 | return(shm->samplepos); |
||
1261 | } |
||
1262 | |||
1263 | //============================================================================= |
||
1264 | // Stops the UltraSound playback |
||
1265 | //============================================================================= |
||
1266 | void GUS_Shutdown (void) |
||
1267 | { |
||
1268 | if (HaveCodec) |
||
1269 | { |
||
1270 | // Stop CODEC |
||
1271 | dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); |
||
1272 | dos_outportb(CodecData,0x01); |
||
1273 | } |
||
1274 | else |
||
1275 | { |
||
1276 | // Stop Voices |
||
1277 | dos_outportb(Gf1PageRegister,0); |
||
1278 | SetGf18(SET_CONTROL,0x03); |
||
1279 | dos_outportb(Gf1PageRegister,1); |
||
1280 | SetGf18(SET_CONTROL,0x03); |
||
1281 | Gf1Delay(); |
||
1282 | dos_outportb(Gf1PageRegister,0); |
||
1283 | SetGf18(SET_CONTROL,0x03); |
||
1284 | dos_outportb(Gf1PageRegister,1); |
||
1285 | SetGf18(SET_CONTROL,0x03); |
||
1286 | |||
1287 | // Stop any DMA |
||
1288 | SetGf18(DMA_CONTROL,0x00); |
||
1289 | GetGf18(DMA_CONTROL); |
||
1290 | } |
||
1291 | |||
1292 | dos_outportb(DisableReg, DmaChannel | 4); // disable dma channel |
||
1293 | }=3) |