1 /* Project 16 Source Code~
\r
2 * Copyright (C) 2012-2022 sparky4 & pngwen & andrius4669 & joncampbell123 & yakui-lover
\r
4 * This file is part of Project 16.
\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
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
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
23 #include "src/lib/16_snd.h"
\r
25 static void interrupt (*t0OldService)(void);
\r
27 void opl2out(word reg, word data)
\r
32 mov dx,word ptr [ADLIB_FM_ADDRESS]
\r
50 void opl3out(word reg, word data)
\r
55 mov dx,word ptr [ADLIB_FM_ADDRESS]
\r
70 void opl3exp(word data)
\r
75 mov dx,word ptr [ADLIB_FM_ADDRESS]
\r
90 /* Function: FMResest *******************************************************
\r
92 * Description: quick and dirty sound card reset (zeros all
\r
96 void FMReset(void/*int percusiveMode*/)
\r
100 /* zero all registers */
\r
101 for(i = MIN_REGISTER; i < MAX_REGISTER+1; i++) opl2out(i, 0);
\r
103 /* allow FM chips to control the waveform of each operator */
\r
104 opl2out(0x01, 0x20);
\r
106 /* set rhythm enabled (6 melodic voices, 5 percussive) */
\r
107 opl2out(0xBD, 0x20);
\r
109 //FMSetPercusiveMode(percusiveMode);
\r
110 } /* End of FMReset */
\r
112 /* Function: FMKeyOff *******************************************************
\r
114 * Parameters: voice - which voice to turn off.
\r
116 * Description: turns off the specified voice.
\r
119 void FMKeyOff(int voice)
\r
123 /* turn voice off */
\r
124 regNum = 0xB0 + voice % 11;//NUMVOICE;
\r
125 opl2out(regNum, 0x0E);
\r
126 } /* End of FMKeyOff */
\r
128 /* Function: FMKeyOn *******************************************************
\r
130 * Parameters: voice - which voice to turn on.
\r
131 * freq - its frequency (note).
\r
132 * octave - its octave.
\r
134 * Description: turns on a voice of specfied frequency and
\r
138 void FMKeyOn(int voice, int freq, int octave)
\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
149 /* Function: FMSetVoice *****************************************************
\r
151 * Parameters: voiceNum - which voice to set.
\r
152 * ins - instrument to set voice.
\r
154 * Description: sets the instrument of a voice.
\r
157 void FMSetVoice(int voiceNum, FMInstrument *ins){
\r
158 int opCellNum, cellOffset;
\r
160 voiceNum %= 11;//NUMVOICE;
\r
161 cellOffset = voiceNum % 3 + ((voiceNum / 3) << 3);
\r
163 /* set sound characteristic */
\r
164 opCellNum = 0x20 + (char)cellOffset;
\r
165 opl2out(opCellNum, ins->SoundCharacteristic[0]);
\r
167 opl2out(opCellNum, ins->SoundCharacteristic[1]);
\r
169 /* set level/output */
\r
170 opCellNum = 0x40 + (char)cellOffset;
\r
171 opl2out(opCellNum, ins->Level[0]);
\r
173 opl2out(opCellNum, ins->Level[1]);
\r
175 /* set Attack/Decay */
\r
176 opCellNum = 0x60 + (char)cellOffset;
\r
177 opl2out(opCellNum, ins->AttackDecay[0]);
\r
179 opl2out(opCellNum, ins->AttackDecay[1]);
\r
181 /* set Sustain/Release */
\r
182 opCellNum = 0x80 + (char)cellOffset;
\r
183 opl2out(opCellNum, ins->SustainRelease[0]);
\r
185 opl2out(opCellNum, ins->SustainRelease[1]);
\r
187 /* set Wave Select */
\r
188 opCellNum = 0xE0 + (char)cellOffset;
\r
189 opl2out(opCellNum, ins->WaveSelect[0]);
\r
191 opl2out(opCellNum, ins->WaveSelect[1]);
\r
193 /* set Feedback/Selectivity */
\r
194 opCellNum = (byte)0xC0 + (byte)voiceNum;
\r
195 opl2out(opCellNum, ins->Feedback);
\r
196 } /* End of FMSetVoice */
\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
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
212 p8259_OCW2(0,P8259_OCW2_NON_SPECIFIC_EOI);
\r
216 void SD_Initimf(global_game_variables_t *gvar)
\r
218 if (!init_adlib()) {
\r
219 printf("Cannot init library\n");
\r
222 if (!probe_8254()) { /* we need the timer to keep time with the music */
\r
223 printf("8254 timer not found\n");
\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
232 SD_adlib_shut_up();
\r
233 shutdown_adlib_opl3(); // NTS: Apparently the music won't play otherwise
\r
236 void SD_imf_reset_music(global_game_variables_t *gvar)
\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
242 void SD_StartupTimer(global_game_variables_t *gvar)
\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
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
255 gvar->ca.sd.irq0_ticks = gvar->ca.sd.ptick = 0;
\r
259 void SD_ShutdownTimer()
\r
261 _dos_setvect(8,t0OldService);
\r
264 void SD_imf_free_music(global_game_variables_t *gvar)
\r
266 #ifndef SD_USESCAMMPM
\r
267 if (gvar->ca.sd.imf_music) free(gvar->ca.sd.imf_music);
\r
269 MM_FreePtr(MEMPTRCONV gvar->ca.audiosegs[0], gvar); //TODO make behave like id engine
\r
271 SD_imf_reset_music(gvar);
\r
274 int SD_imf_load_music(const char *path, global_game_variables_t *gvar)
\r
277 unsigned char buf[8];
\r
280 #ifndef SD_USESCAMMPM
\r
281 SD_imf_free_music(gvar);
\r
283 SD_imf_reset_music(gvar);
\r
286 fd = open(path,O_RDONLY|O_BINARY);
\r
287 if (fd < 0) return 0;
\r
289 len = lseek(fd,0,SEEK_END);
\r
290 lseek(fd,0,SEEK_SET);
\r
292 if (buf[0] != 0 || buf[1] != 0) // type 1 IMF
\r
293 len = *((uint16_t*)buf);
\r
295 lseek(fd,0,SEEK_SET);
\r
297 if (len == 0 || len > 65535UL) {
\r
303 #ifndef SD_USESCAMMPM
\r
304 gvar->ca.sd.imf_music = malloc(len);
\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
309 if (gvar->ca.sd.imf_music == NULL) {
\r
313 read(fd,gvar->ca.sd.imf_music,len);
\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
321 void SD_imf_tick(global_game_variables_t *gvar)
\r
323 if (gvar->ca.sd.imf_delay_countdown == 0) {
\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
330 // printf("replay\n");
\r
331 gvar->ca.sd.imf_play_ptr = gvar->ca.sd.imf_music;
\r
333 } while (gvar->ca.sd.imf_delay_countdown == 0);
\r
336 gvar->ca.sd.imf_delay_countdown--;
\r
340 void SD_adlib_shut_up() {
\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
353 for (i=0;i < adlib_fm_voices;i++) {
\r
354 struct adlib_fm_operator *f;
\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
361 f->sustain_level = 0;
\r
362 f->release_rate = 8;
\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
373 f->sustain_level = 0;
\r
374 f->release_rate = 8;
\r