OSDN Git Service

866d9a204166c61a22e84297ba29bc2d7fcb82c1
[proj16/16.git] / src / lib / 16_snd.c
1 /* Project 16 Source Code~\r
2  * Copyright (C) 2012-2019 sparky4 & pngwen & andrius4669 & joncampbell123 & yakui-lover\r
3  *\r
4  * This file is part of Project 16.\r
5  *\r
6  * Project 16 is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 3 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * Project 16 is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>, or\r
18  * write to the Free Software Foundation, Inc., 51 Franklin Street,\r
19  * Fifth Floor, Boston, MA 02110-1301 USA.\r
20  *\r
21  */\r
22 \r
23 #include "src/lib/16_snd.h"\r
24 \r
25 static  void interrupt  (*t0OldService)(void);\r
26 \r
27 void opl2out(word reg, word data)\r
28 {\r
29         __asm\r
30         {\r
31                 mov     ax,reg\r
32                 mov     dx,word ptr [ADLIB_FM_ADDRESS]\r
33                 or      ah,ah\r
34                 jz      @@1\r
35                 add     dx,2\r
36 @@1:    out     dx,al\r
37                 mov     cx,6\r
38 @@2:    in      al,dx\r
39                 loop    @@2\r
40                 inc     dl\r
41                 mov     ax,data\r
42                 out     dx,al\r
43                 dec     dl\r
44                 mov     cx,36\r
45 @@3:    in      al,dx\r
46                 loop    @@3\r
47         }\r
48 }\r
49 \r
50 void opl3out(word reg, word data)\r
51 {\r
52         __asm\r
53         {\r
54                 mov     ax,reg\r
55                 mov     dx,word ptr [ADLIB_FM_ADDRESS]\r
56                 or      ah,ah\r
57                 jz      @@1\r
58                 add     dx,2\r
59 @@1:    out     dx,al\r
60                 inc     dl\r
61                 mov     ax,data\r
62                 out     dx,al\r
63                 dec     dl\r
64                 mov     cx,26\r
65 @@2:    in      al,dx\r
66                 loop    @@2\r
67         }\r
68 }\r
69 \r
70 void opl3exp(word data)\r
71 {\r
72         __asm\r
73         {\r
74                 mov     ax,data\r
75                 mov     dx,word ptr [ADLIB_FM_ADDRESS]\r
76                 add     dx,2\r
77                 out     dx,al\r
78                 mov     cx,6\r
79 @@1:    in      al,dx\r
80                 loop    @@1\r
81                 inc     dl\r
82                 mov     al,ah\r
83                 out     dx,al\r
84                 mov     cx,36\r
85 @@2:    in      al,dx\r
86                 loop    @@2\r
87         }\r
88 }\r
89 \r
90 /* Function: FMResest *******************************************************\r
91 *\r
92 *     Description:        quick and dirty sound card reset (zeros all\r
93 *                         registers).\r
94 *\r
95 */\r
96 void FMReset(void/*int percusiveMode*/)\r
97 {\r
98         int i;\r
99 \r
100         /* zero all registers */\r
101         for(i = MIN_REGISTER; i < MAX_REGISTER+1; i++) opl2out(i, 0);\r
102 \r
103         /* allow FM chips to control the waveform of each operator */\r
104         opl2out(0x01, 0x20);\r
105 \r
106         /* set rhythm enabled (6 melodic voices, 5 percussive) */\r
107         opl2out(0xBD, 0x20);\r
108 \r
109         //FMSetPercusiveMode(percusiveMode);\r
110 } /* End of FMReset */\r
111 \r
112 /* Function: FMKeyOff *******************************************************\r
113 *\r
114 *     Parameters:        voice - which voice to turn off.\r
115 *\r
116 *     Description:        turns off the specified voice.\r
117 *\r
118 */\r
119 void FMKeyOff(int voice)\r
120 {\r
121         int regNum;\r
122 \r
123         /* turn voice off */\r
124         regNum = 0xB0 + voice % 11;//NUMVOICE;\r
125         opl2out(regNum, 0x0E);\r
126 } /* End of FMKeyOff */\r
127 \r
128 /* Function: FMKeyOn *******************************************************\r
129 *\r
130 *     Parameters:        voice - which voice to turn on.\r
131 *                         freq - its frequency (note).\r
132 *                         octave - its octave.\r
133 *\r
134 *     Description:        turns on a voice of specfied frequency and\r
135 *                         octave.\r
136 *\r
137 */\r
138 void FMKeyOn(int voice, int freq, int octave)\r
139 {\r
140         int regNum, tmp;\r
141 \r
142         regNum = 0xA0 + voice % 11;//NUMVOICE;\r
143         opl2out(regNum, freq & 0xff);\r
144         regNum = 0xB0 + voice % 11;//NUMVOICE;\r
145         tmp = (freq >> 8) | (octave << 2) | 0x20;\r
146         opl2out(regNum, tmp);\r
147 } /* End of FMKeyOn */\r
148 \r
149 /* Function: FMSetVoice *****************************************************\r
150 *\r
151 *     Parameters:        voiceNum - which voice to set.\r
152 *                         ins - instrument to set voice.\r
153 *\r
154 *     Description:        sets the instrument of a voice.\r
155 *\r
156 */\r
157 void FMSetVoice(int voiceNum, FMInstrument *ins){\r
158         int opCellNum, cellOffset;\r
159 \r
160         voiceNum %= 11;//NUMVOICE;\r
161         cellOffset = voiceNum % 3 + ((voiceNum / 3) << 3);\r
162 \r
163         /* set sound characteristic */\r
164         opCellNum = 0x20 + (char)cellOffset;\r
165         opl2out(opCellNum, ins->SoundCharacteristic[0]);\r
166         opCellNum += 3;\r
167         opl2out(opCellNum, ins->SoundCharacteristic[1]);\r
168 \r
169         /* set level/output */\r
170         opCellNum = 0x40 + (char)cellOffset;\r
171         opl2out(opCellNum, ins->Level[0]);\r
172         opCellNum += 3;\r
173         opl2out(opCellNum, ins->Level[1]);\r
174 \r
175         /* set Attack/Decay */\r
176         opCellNum = 0x60 + (char)cellOffset;\r
177         opl2out(opCellNum, ins->AttackDecay[0]);\r
178         opCellNum += 3;\r
179         opl2out(opCellNum, ins->AttackDecay[1]);\r
180 \r
181         /* set Sustain/Release */\r
182         opCellNum = 0x80 + (char)cellOffset;\r
183         opl2out(opCellNum, ins->SustainRelease[0]);\r
184         opCellNum += 3;\r
185         opl2out(opCellNum, ins->SustainRelease[1]);\r
186 \r
187         /* set Wave Select */\r
188         opCellNum = 0xE0 + (char)cellOffset;\r
189         opl2out(opCellNum, ins->WaveSelect[0]);\r
190         opCellNum += 3;\r
191         opl2out(opCellNum, ins->WaveSelect[1]);\r
192 \r
193         /* set Feedback/Selectivity */\r
194         opCellNum = (byte)0xC0 + (byte)voiceNum;\r
195         opl2out(opCellNum, ins->Feedback);\r
196 } /* End of FMSetVoice */\r
197 \r
198 \r
199 //newer sd\r
200 \r
201 \r
202 struct glob_game_vars   *ggvv;\r
203 // WARNING: subroutine call in interrupt handler. make sure you compile with -zu flag for large/compact memory models\r
204 void interrupt SDL_irq0()\r
205 {\r
206         ggvv->ca.sd.irq0_ticks++;\r
207         if ((ggvv->ca.sd.irq0_cnt += ggvv->ca.sd.irq0_add) >= ggvv->ca.sd.irq0_max) {\r
208                 ggvv->ca.sd.irq0_cnt -= ggvv->ca.sd.irq0_max;\r
209                 t0OldService();\r
210         }\r
211         else {\r
212                 p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);\r
213         }\r
214 }\r
215 \r
216 void SD_Initimf(global_game_variables_t *gvar)\r
217 {\r
218         if (!init_adlib()) {\r
219                 printf("Cannot init library\n");\r
220                 return;\r
221         }\r
222         if (!probe_8254()) { /* we need the timer to keep time with the music */\r
223                 printf("8254 timer not found\n");\r
224                 return;\r
225         }\r
226 \r
227         gvar->ca.sd.imf_delay_countdown=0;\r
228         gvar->ca.sd.imf_music=\r
229         gvar->ca.sd.imf_play_ptr=\r
230         gvar->ca.sd.imf_music_end=NULL;\r
231 \r
232         SD_adlib_shut_up();\r
233         shutdown_adlib_opl3(); // NTS: Apparently the music won't play otherwise\r
234 }\r
235 \r
236 void SD_imf_reset_music(global_game_variables_t *gvar)\r
237 {\r
238         gvar->ca.sd.imf_music = gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music_end = NULL;\r
239         gvar->ca.sd.imf_delay_countdown = 0;\r
240 }\r
241 \r
242 void SD_StartupTimer(global_game_variables_t *gvar)\r
243 {\r
244         gvar->ca.sd.irq0_ticks=\r
245         gvar->ca.sd.irq0_cnt = 0;\r
246         gvar->ca.sd.irq0_add = 182;\r
247         gvar->ca.sd.irq0_max = 1000; /* about 18.2Hz */\r
248         gvar->ca.sd.tickrate = 700;\r
249 \r
250         write_8254_system_timer(T8254_REF_CLOCK_HZ / gvar->ca.sd.tickrate);\r
251         t0OldService = _dos_getvect(8); /*IRQ0*/\r
252         _dos_setvect(8,SDL_irq0);\r
253 \r
254         _cli();\r
255         gvar->ca.sd.irq0_ticks = gvar->ca.sd.ptick = 0;\r
256         _sti();\r
257 }\r
258 \r
259 void SD_ShutdownTimer()\r
260 {\r
261         _dos_setvect(8,t0OldService);\r
262 }\r
263 \r
264 void SD_imf_free_music(global_game_variables_t *gvar)\r
265 {\r
266 #ifndef SD_USESCAMMPM\r
267         if (gvar->ca.sd.imf_music) free(gvar->ca.sd.imf_music);\r
268 #else\r
269         MM_FreePtr(MEMPTRCONV gvar->ca.audiosegs[0], gvar);     //TODO make behave like id engine\r
270 #endif\r
271         SD_imf_reset_music(gvar);\r
272 }\r
273 \r
274 int SD_imf_load_music(const char *path, global_game_variables_t *gvar)\r
275 {\r
276         unsigned long len;\r
277         unsigned char buf[8];\r
278         int fd;\r
279 \r
280 #ifndef SD_USESCAMMPM\r
281         SD_imf_free_music(gvar);\r
282 #else\r
283         SD_imf_reset_music(gvar);\r
284 #endif\r
285 \r
286         fd = open(path,O_RDONLY|O_BINARY);\r
287         if (fd < 0) return 0;\r
288 \r
289         len = lseek(fd,0,SEEK_END);\r
290         lseek(fd,0,SEEK_SET);\r
291         read(fd,buf,2);\r
292         if (buf[0] != 0 || buf[1] != 0) // type 1 IMF\r
293                 len = *((uint16_t*)buf);\r
294         else\r
295                 lseek(fd,0,SEEK_SET);\r
296 \r
297         if (len == 0 || len > 65535UL) {\r
298                 close(fd);\r
299                 return 0;\r
300         }\r
301         len -= len & 3;\r
302 \r
303 #ifndef SD_USESCAMMPM\r
304         gvar->ca.sd.imf_music = malloc(len);\r
305 #else\r
306         MM_GetPtr(MEMPTRCONV gvar->ca.audiosegs[0],len, gvar);\r
307         gvar->ca.sd.imf_music = (struct imf_entry *)gvar->ca.audiosegs[0];\r
308 #endif\r
309         if (gvar->ca.sd.imf_music == NULL) {\r
310                 close(fd);\r
311                 return 0;\r
312         }\r
313         read(fd,gvar->ca.sd.imf_music,len);\r
314         close(fd);\r
315 \r
316         gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music;\r
317         gvar->ca.sd.imf_music_end = gvar->ca.sd.imf_music + (len >> 2UL);\r
318         return 1;\r
319 }\r
320 \r
321 void SD_imf_tick(global_game_variables_t *gvar)\r
322 {\r
323         if (gvar->ca.sd.imf_delay_countdown == 0) {\r
324                 do {\r
325                         adlib_write(gvar->ca.sd.imf_play_ptr->reg,gvar->ca.sd.imf_play_ptr->data);\r
326                         gvar->ca.sd.imf_delay_countdown = gvar->ca.sd.imf_play_ptr->delay;\r
327                         gvar->ca.sd.imf_play_ptr++;\r
328                         if (gvar->ca.sd.imf_play_ptr == gvar->ca.sd.imf_music_end)\r
329                         {\r
330 //                              printf("replay\n");\r
331                                 gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music;\r
332                         }\r
333                 } while (gvar->ca.sd.imf_delay_countdown == 0);\r
334         }\r
335         else {\r
336                 gvar->ca.sd.imf_delay_countdown--;\r
337         }\r
338 }\r
339 \r
340 void SD_adlib_shut_up() {\r
341         int i;\r
342 \r
343         memset(adlib_fm,0,sizeof(adlib_fm));\r
344         memset(&adlib_reg_bd,0,sizeof(adlib_reg_bd));\r
345         for (i=0;i < adlib_fm_voices;i++) {\r
346                 struct adlib_fm_operator *f;\r
347                 f = &adlib_fm[i].mod;\r
348                 f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;\r
349                 f = &adlib_fm[i].car;\r
350                 f->ch_a = f->ch_b = f->ch_c = f->ch_d = 1;\r
351         }\r
352 \r
353         for (i=0;i < adlib_fm_voices;i++) {\r
354                 struct adlib_fm_operator *f;\r
355 \r
356                 f = &adlib_fm[i].mod;\r
357                 f->mod_multiple = 1;\r
358                 f->total_level = 63 - 16;\r
359                 f->attack_rate = 15;\r
360                 f->decay_rate = 4;\r
361                 f->sustain_level = 0;\r
362                 f->release_rate = 8;\r
363                 f->f_number = 400;\r
364                 f->sustain = 1;\r
365                 f->octave = 4;\r
366                 f->key_on = 0;\r
367 \r
368                 f = &adlib_fm[i].car;\r
369                 f->mod_multiple = 1;\r
370                 f->total_level = 63 - 16;\r
371                 f->attack_rate = 15;\r
372                 f->decay_rate = 4;\r
373                 f->sustain_level = 0;\r
374                 f->release_rate = 8;\r
375                 f->f_number = 0;\r
376                 f->sustain = 1;\r
377                 f->octave = 0;\r
378                 f->key_on = 0;\r
379         }\r
380 \r
381         adlib_apply_all();\r
382 }\r