DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 /* FIXME: This code... well... it could be done better. 00019 * It was written back when all mixer callbacks were potentially done from 00020 * the SDL audio thread. DOSBox-X has since moved to a model where within 00021 * the timer tick, audio can be rendered up to the "current time" at any 00022 * time. So while the event queue idea was appropriate at the time, it's 00023 * no longer needed and just like the GUS and SB code could be written to 00024 * trigger a mixer render "up-to" for every change instead. --J.C. */ 00025 00026 //#define SPKR_DEBUGGING 00027 #include <math.h> 00028 #include "dosbox.h" 00029 #include "mixer.h" 00030 #include "timer.h" 00031 #include "setup.h" 00032 #include "pic.h" 00033 #include "control.h" 00034 00035 #ifdef SPKR_DEBUGGING 00036 FILE* PCSpeakerLog = NULL; 00037 FILE* PCSpeakerState = NULL; 00038 FILE* PCSpeakerOutput = NULL; 00039 FILE* PCSpeakerOutputLevelLog = NULL; 00040 00041 typedef enum { 00042 LABEL_CONTROL, 00043 LABEL_COUNTER, 00044 LABEL_OUTPUT 00045 } state_label_e; 00046 00047 typedef struct { 00048 bool pit_output_enabled; 00049 bool pit_clock_gate_enabled; 00050 } output_state_t; 00051 00052 typedef union { 00053 unsigned char pit_mode; 00054 Bit32u counter; 00055 output_state_t output_state; 00056 } state_specific_data_u; 00057 00058 typedef struct { 00059 double timestamp; 00060 state_label_e state_label; 00061 state_specific_data_u state_specific_data; 00062 } speaker_state_change_t; 00063 00064 #endif 00065 00066 #define SPKR_ENTRIES 8192 00067 #define SPKR_VOLUME 10000 00068 //#define SPKR_SHIFT 8 00069 pic_tickindex_t SPKR_SPEED = 1.0; 00070 00071 struct DelayEntry { 00072 pic_tickindex_t index; 00073 bool output_level; 00074 }; 00075 00076 static struct { 00077 MixerChannel * chan; 00078 Bitu pit_mode; 00079 Bitu rate; 00080 00081 bool pit_output_enabled; 00082 bool pit_clock_gate_enabled; 00083 bool pit_output_level; 00084 pic_tickindex_t pit_new_max,pit_new_half; 00085 pic_tickindex_t pit_max,pit_half; 00086 pic_tickindex_t pit_index; 00087 bool pit_mode1_waiting_for_counter; 00088 bool pit_mode1_waiting_for_trigger; 00089 pic_tickindex_t pit_mode1_pending_max; 00090 00091 bool pit_mode3_counting; 00092 pic_tickindex_t volwant,volcur; 00093 Bitu last_ticks; 00094 pic_tickindex_t last_index; 00095 Bitu minimum_counter; 00096 DelayEntry entries[SPKR_ENTRIES]; 00097 Bitu used; 00098 } spkr; 00099 00100 inline static void AddDelayEntry(pic_tickindex_t index, bool new_output_level) { 00101 #ifdef SPKR_DEBUGGING 00102 if (index < 0 || index > 1) { 00103 LOG_MSG("AddDelayEntry: index out of range %f at %f", index, PIC_FullIndex()); 00104 } 00105 #endif 00106 static bool previous_output_level = 0; 00107 if (new_output_level == previous_output_level) { 00108 return; 00109 } 00110 previous_output_level = new_output_level; 00111 if (spkr.used == SPKR_ENTRIES) { 00112 LOG(LOG_MISC,LOG_WARN)("PC speaker delay entry queue overrun"); 00113 return; 00114 } 00115 spkr.entries[spkr.used].index=index; 00116 spkr.entries[spkr.used].output_level=new_output_level; 00117 spkr.used++; 00118 } 00119 00120 inline static void AddPITOutput(pic_tickindex_t index) { 00121 if (spkr.pit_output_enabled) { 00122 AddDelayEntry(index, spkr.pit_output_level); 00123 } 00124 } 00125 00126 pic_tickindex_t speaker_pit_delta(void); 00127 void speaker_pit_update(void); 00128 00129 static void CheckPITSynchronization(void) { 00130 if (spkr.pit_clock_gate_enabled) { 00131 speaker_pit_update(); 00132 00133 const pic_tickindex_t now_rel = speaker_pit_delta(); 00134 pic_tickindex_t delta = spkr.pit_index - now_rel; 00135 00136 if (spkr.pit_mode == 3) { 00137 if (delta >= (spkr.pit_half/2)) 00138 delta -= spkr.pit_half; 00139 } 00140 else { 00141 if (delta >= spkr.pit_half) 00142 delta -= spkr.pit_max; 00143 } 00144 00145 // FIXME: This code is also detecting many sync errors regarding Mode 0 aka the 00146 // "Realsound" (PWM) method of playing digitized speech, though ironically 00147 // this bludgeon seems to vastly improve the sound quality! 00148 // FIXME: This code maintains good synchronization EXCEPT in the case of Mode 3 00149 // with rapid changes to the counter WITHOUT writing port 43h (new_mode) first. 00150 // This bludgeon is here to correct that. This is the problem with timer.cpp 00151 // and pcspeaker.cpp tracking the counter separately. 00152 if (fabs(delta) >= (4.1 / CPU_CycleMax)) { 00153 #if 0//enable this when debugging PC speaker code 00154 LOG_MSG("PIT speaker synchronization error %.9f",delta); 00155 #endif 00156 spkr.pit_index = now_rel; 00157 } 00158 else { 00159 spkr.pit_index -= delta * 0.005; 00160 } 00161 } 00162 } 00163 00164 static void ForwardPIT(pic_tickindex_t newindex) { 00165 #ifdef SPKR_DEBUGGING 00166 if (newindex < 0 || newindex > 1) { 00167 LOG_MSG("ForwardPIT: index out of range %f at %f", newindex, PIC_FullIndex()); 00168 } 00169 #endif 00170 pic_tickindex_t passed=(newindex-spkr.last_index); 00171 pic_tickindex_t delay_base=spkr.last_index; 00172 spkr.last_index=newindex; 00173 switch (spkr.pit_mode) { 00174 case 6: // dummy 00175 return; 00176 case 0: 00177 if (spkr.pit_index >= spkr.pit_max) { 00178 return; // counter reached zero before previous call so do nothing 00179 } 00180 spkr.pit_index += passed; 00181 if (spkr.pit_index >= spkr.pit_max) { 00182 // counter reached zero between previous and this call 00183 pic_tickindex_t delay = delay_base; 00184 delay += spkr.pit_max - spkr.pit_index + passed; 00185 spkr.pit_output_level = 1; 00186 AddPITOutput(delay); 00187 } 00188 return; 00189 case 1: 00190 if (spkr.pit_mode1_waiting_for_counter) { 00191 // assert output level is high 00192 return; // counter not written yet 00193 } 00194 if (spkr.pit_mode1_waiting_for_trigger) { 00195 // assert output level is high 00196 return; // no pulse yet 00197 } 00198 if (spkr.pit_index >= spkr.pit_max) { 00199 return; // counter reached zero before previous call so do nothing 00200 } 00201 spkr.pit_index += passed; 00202 if (spkr.pit_index >= spkr.pit_max) { 00203 // counter reached zero between previous and this call 00204 pic_tickindex_t delay = delay_base; 00205 delay += spkr.pit_max - spkr.pit_index + passed; 00206 spkr.pit_output_level = 1; 00207 AddPITOutput(delay); 00208 // finished with this pulse 00209 spkr.pit_mode1_waiting_for_trigger = 1; 00210 } 00211 return; 00212 case 2: 00213 while (passed>0) { 00214 /* passed the initial low cycle? */ 00215 if (spkr.pit_index>=spkr.pit_half) { 00216 /* Start a new low cycle */ 00217 if ((spkr.pit_index+passed)>=spkr.pit_max) { 00218 pic_tickindex_t delay=spkr.pit_max-spkr.pit_index; 00219 delay_base+=delay;passed-=delay; 00220 spkr.pit_output_level = 0; 00221 AddPITOutput(delay_base); 00222 spkr.pit_index=0; 00223 } else { 00224 spkr.pit_index+=passed; 00225 return; 00226 } 00227 } else { 00228 if ((spkr.pit_index+passed)>=spkr.pit_half) { 00229 pic_tickindex_t delay=spkr.pit_half-spkr.pit_index; 00230 delay_base+=delay;passed-=delay; 00231 spkr.pit_output_level = 1; 00232 AddPITOutput(delay_base); 00233 spkr.pit_index=spkr.pit_half; 00234 } else { 00235 spkr.pit_index+=passed; 00236 return; 00237 } 00238 } 00239 } 00240 break; 00241 //END CASE 2 00242 case 3: 00243 /* this version will only count up to pit_half. pit_max is ignored */ 00244 if (!spkr.pit_mode3_counting) break; 00245 while (passed>0) { 00246 /* Determine where in the wave we're located */ 00247 if ((spkr.pit_index+passed)>=spkr.pit_half) { 00248 pic_tickindex_t delay=spkr.pit_half-spkr.pit_index; 00249 delay_base+=delay;passed-=delay; 00250 spkr.pit_output_level = !spkr.pit_output_level; 00251 AddPITOutput(delay_base); 00252 spkr.pit_index=0; 00253 /* Load the new count */ 00254 spkr.pit_half=spkr.pit_new_half; 00255 spkr.pit_max=spkr.pit_new_max; 00256 } else { 00257 spkr.pit_index+=passed; 00258 return; 00259 } 00260 } 00261 break; 00262 //END CASE 3 00263 case 4: 00264 if (spkr.pit_index<spkr.pit_max) { 00265 /* Check if we're gonna pass the end this block */ 00266 if (spkr.pit_index+passed>=spkr.pit_max) { 00267 pic_tickindex_t delay=spkr.pit_max-spkr.pit_index; 00268 delay_base+=delay;passed-=delay; 00269 spkr.pit_output_level = 0; 00270 AddPITOutput(delay_base); //No new events unless reprogrammed 00271 spkr.pit_index=spkr.pit_max; 00272 } else spkr.pit_index+=passed; 00273 } 00274 break; 00275 //END CASE 4 00276 } 00277 } 00278 00279 void PCSPEAKER_SetPITControl(Bitu mode) { 00280 if (spkr.chan == NULL) 00281 return; 00282 00283 pic_tickindex_t newindex = PIC_TickIndex(); 00284 ForwardPIT(newindex); 00285 #ifdef SPKR_DEBUGGING 00286 fprintf(PCSpeakerLog, "%f pit command: %u\n", PIC_FullIndex(), mode); 00287 speaker_state_change_t temp; 00288 memset(&temp, 0, sizeof(speaker_state_change_t)); 00289 temp.timestamp = PIC_FullIndex(); 00290 temp.state_label = LABEL_CONTROL; 00291 temp.state_specific_data.pit_mode = mode; 00292 if (fwrite(&temp, sizeof(temp), 1, PCSpeakerState) != 1) 00293 LOG_MSG(__FILE__ ": unable to write to pc speaker log"); 00294 #endif 00295 // TODO: implement all modes 00296 switch(mode) { 00297 case 0: 00298 spkr.pit_mode = 0; 00299 break; 00300 case 1: 00301 spkr.pit_mode = 1; 00302 spkr.pit_mode1_waiting_for_counter = 1; 00303 spkr.pit_mode1_waiting_for_trigger = 0; 00304 spkr.pit_output_level = 1; 00305 break; 00306 case 3: 00307 spkr.pit_mode = 3; 00308 spkr.pit_mode3_counting = 0; 00309 spkr.pit_output_level = 1; 00310 break; 00311 default: 00312 return; 00313 } 00314 AddPITOutput(newindex); 00315 } 00316 00317 // new mode WITHOUT writing port 43h 00318 void PCSPEAKER_SetCounter_NoNewMode(Bitu cntr) { 00319 if (spkr.chan == NULL) 00320 return; 00321 00322 if (!spkr.last_ticks) { 00323 if(spkr.chan) spkr.chan->Enable(true); 00324 spkr.last_index=0; 00325 } 00326 spkr.last_ticks=PIC_Ticks; 00327 pic_tickindex_t newindex=PIC_TickIndex(); 00328 ForwardPIT(newindex); 00329 switch (spkr.pit_mode) { 00330 case 0: /* Mode 0 one shot, used with "realsound" (PWM) */ 00331 //FIXME 00332 break; 00333 case 1: // retriggerable one-shot, used by Star Control 1 00334 //FIXME 00335 break; 00336 case 2: /* Single cycle low, rest low high generator */ 00337 //FIXME 00338 break; 00339 case 3: /* Square wave generator */ 00340 if (cntr < spkr.minimum_counter) { 00341 //#ifdef SPKR_DEBUGGING 00342 // LOG_MSG( 00343 // "SetCounter: too high frequency %u (cntr %u) at %f", 00344 // PIT_TICK_RATE/cntr, 00345 // cntr, 00346 // PIC_FullIndex()); 00347 //#endif 00348 // hack to save CPU cycles 00349 cntr = spkr.minimum_counter; 00350 //spkr.pit_output_level = 1; // avoid breaking digger music 00351 //spkr.pit_mode = 6; // dummy mode with constant output 00352 //AddPITOutput(newindex); 00353 //return; 00354 } 00355 spkr.pit_new_max = (1000.0f/PIT_TICK_RATE)*cntr; 00356 spkr.pit_new_half=spkr.pit_new_max/2; 00357 if (!spkr.pit_mode3_counting) { 00358 spkr.pit_index = 0; 00359 spkr.pit_max = spkr.pit_new_max; 00360 spkr.pit_half = spkr.pit_new_half; 00361 } 00362 break; 00363 case 4: /* Software triggered strobe */ 00364 //FIXME 00365 break; 00366 default: 00367 #ifdef SPKR_DEBUGGING 00368 LOG_MSG("Unhandled speaker mode %d at %f", mode, PIC_FullIndex()); 00369 #endif 00370 return; 00371 } 00372 } 00373 00374 static bool pit_raw_clock_gate_enabled = false; 00375 00376 bool TIMER2_ClockGateEnabled(void); 00377 00378 void PCSPEAKER_UpdateType(void) { 00379 PCSPEAKER_SetType(pit_raw_clock_gate_enabled,spkr.pit_output_enabled); 00380 } 00381 00382 void PCSPEAKER_SetCounter(Bitu cntr, Bitu mode) { 00383 if (spkr.chan == NULL) 00384 return; 00385 00386 #ifdef SPKR_DEBUGGING 00387 fprintf(PCSpeakerLog, "%f counter: %u, mode: %u\n", PIC_FullIndex(), cntr, mode); 00388 speaker_state_change_t temp; 00389 memset(&temp, 0, sizeof(speaker_state_change_t)); 00390 temp.timestamp = PIC_FullIndex(); 00391 temp.state_label = LABEL_COUNTER; 00392 temp.state_specific_data.counter = cntr; 00393 if (fwrite(&temp, sizeof(temp), 1, PCSpeakerState) != 1) 00394 LOG_MSG(__FILE__ ": unable to write to pc speaker log"); 00395 #endif 00396 if (!spkr.last_ticks) { 00397 if(spkr.chan) spkr.chan->Enable(true); 00398 spkr.last_index=0; 00399 } 00400 spkr.last_ticks=PIC_Ticks; 00401 pic_tickindex_t newindex=PIC_TickIndex(); 00402 ForwardPIT(newindex); 00403 spkr.pit_index = 0; // THIS function is always called on the port 43h reset case 00404 switch (mode) { 00405 case 0: /* Mode 0 one shot, used with "realsound" (PWM) */ 00406 //if (cntr>80) { 00407 // cntr=80; 00408 //} 00409 //spkr.pit_output_level=((pic_tickindex_t)cntr-40)*(SPKR_VOLUME/40.0f); 00410 spkr.pit_output_level = 0; 00411 spkr.pit_max = (1000.0f / PIT_TICK_RATE) * cntr; 00412 AddPITOutput(newindex); 00413 break; 00414 case 1: // retriggerable one-shot, used by Star Control 1 00415 spkr.pit_mode1_pending_max = (1000.0f / PIT_TICK_RATE) * cntr; 00416 if (spkr.pit_mode1_waiting_for_counter) { 00417 // assert output level is high 00418 spkr.pit_mode1_waiting_for_counter = 0; 00419 spkr.pit_mode1_waiting_for_trigger = 1; 00420 } 00421 break; 00422 case 2: /* Single cycle low, rest low high generator */ 00423 spkr.pit_output_level = 0; 00424 AddPITOutput(newindex); 00425 spkr.pit_half=(1000.0f/PIT_TICK_RATE)*1; 00426 spkr.pit_max=(1000.0f/PIT_TICK_RATE)*cntr; 00427 break; 00428 case 3: /* Square wave generator */ 00429 if (cntr < spkr.minimum_counter) { 00430 //#ifdef SPKR_DEBUGGING 00431 // LOG_MSG( 00432 // "SetCounter: too high frequency %u (cntr %u) at %f", 00433 // PIT_TICK_RATE/cntr, 00434 // cntr, 00435 // PIC_FullIndex()); 00436 //#endif 00437 // hack to save CPU cycles 00438 cntr = spkr.minimum_counter; 00439 //spkr.pit_output_level = 1; // avoid breaking digger music 00440 //spkr.pit_mode = 6; // dummy mode with constant output 00441 //AddPITOutput(newindex); 00442 //return; 00443 } 00444 spkr.pit_new_max = (1000.0f/PIT_TICK_RATE)*cntr; 00445 spkr.pit_new_half=spkr.pit_new_max/2; 00446 if (!spkr.pit_mode3_counting) { 00447 spkr.pit_max = spkr.pit_new_max; 00448 spkr.pit_half = spkr.pit_new_half; 00449 if (spkr.pit_clock_gate_enabled) { 00450 spkr.pit_mode3_counting = 1; 00451 spkr.pit_output_level = 1; // probably not necessary 00452 AddPITOutput(newindex); 00453 } 00454 } 00455 break; 00456 case 4: /* Software triggered strobe */ 00457 spkr.pit_output_level = 1; 00458 AddPITOutput(newindex); 00459 spkr.pit_max=(1000.0f/PIT_TICK_RATE)*cntr; 00460 break; 00461 default: 00462 #ifdef SPKR_DEBUGGING 00463 LOG_MSG("Unhandled speaker mode %d at %f", mode, PIC_FullIndex()); 00464 #endif 00465 return; 00466 } 00467 spkr.pit_mode = mode; 00468 00469 /* writing the counter enables the clock again */ 00470 PCSPEAKER_UpdateType(); 00471 00472 CheckPITSynchronization(); 00473 } 00474 00475 void PCSPEAKER_SetType(bool pit_clock_gate_enabled, bool pit_output_enabled) { 00476 if (spkr.chan == NULL) 00477 return; 00478 00479 pit_raw_clock_gate_enabled = pit_clock_gate_enabled; 00480 00481 #ifdef SPKR_DEBUGGING 00482 fprintf( 00483 PCSpeakerLog, 00484 "%f output: %s, clock gate %s\n", 00485 PIC_FullIndex(), 00486 pit_output_enabled ? "pit" : "forced low", 00487 pit_clock_gate_enabled ? "on" : "off" 00488 ); 00489 speaker_state_change_t temp; 00490 memset(&temp, 0, sizeof(speaker_state_change_t)); 00491 temp.timestamp = PIC_FullIndex(); 00492 temp.state_label = LABEL_OUTPUT; 00493 temp.state_specific_data.output_state.pit_clock_gate_enabled = pit_clock_gate_enabled; 00494 temp.state_specific_data.output_state.pit_output_enabled = pit_output_enabled; 00495 if (fwrite(&temp, sizeof(temp), 1, PCSpeakerState) != 1) 00496 LOG_MSG(__FILE__ ": unable to write to pc speaker log"); 00497 #endif 00498 if (!spkr.last_ticks) { 00499 if(spkr.chan) spkr.chan->Enable(true); 00500 spkr.last_index=0; 00501 } 00502 spkr.last_ticks=PIC_Ticks; 00503 pic_tickindex_t newindex=PIC_TickIndex(); 00504 ForwardPIT(newindex); 00505 // pit clock gate enable rising edge is a trigger 00506 00507 bool p_cg_en = spkr.pit_clock_gate_enabled; 00508 00509 spkr.pit_clock_gate_enabled = pit_clock_gate_enabled && TIMER2_ClockGateEnabled(); 00510 spkr.pit_output_enabled = pit_output_enabled; 00511 00512 bool pit_trigger = !p_cg_en && spkr.pit_clock_gate_enabled; 00513 00514 if (pit_trigger) { 00515 switch (spkr.pit_mode) { 00516 case 1: 00517 if (spkr.pit_mode1_waiting_for_counter) { 00518 // assert output level is high 00519 break; 00520 } 00521 spkr.pit_output_level = 0; 00522 spkr.pit_index = 0; 00523 spkr.pit_max = spkr.pit_mode1_pending_max; 00524 spkr.pit_mode1_waiting_for_trigger = 0; 00525 break; 00526 case 3: 00527 spkr.pit_mode3_counting = 1; 00528 spkr.pit_new_half=spkr.pit_new_max/2; 00529 spkr.pit_index = 0; 00530 spkr.pit_max = spkr.pit_new_max; 00531 spkr.pit_half = spkr.pit_new_half; 00532 spkr.pit_output_level = 1; 00533 break; 00534 default: 00535 // TODO: implement other modes 00536 break; 00537 } 00538 } else if (!spkr.pit_clock_gate_enabled) { 00539 switch (spkr.pit_mode) { 00540 case 1: 00541 // gate level does not affect mode1 00542 break; 00543 case 3: 00544 // low gate forces pit output high 00545 spkr.pit_output_level = 1; 00546 spkr.pit_mode3_counting = 0; 00547 break; 00548 default: 00549 // TODO: implement other modes 00550 break; 00551 } 00552 } 00553 if (pit_output_enabled) { 00554 AddDelayEntry(newindex, spkr.pit_output_level); 00555 } else { 00556 AddDelayEntry(newindex, 0); 00557 } 00558 00559 CheckPITSynchronization(); 00560 } 00561 00562 /* NTS: This code stinks. Sort of. The way it handles the delay entry queue 00563 * could have been done better. The event queue idea isn't needed anymore because 00564 * DOSBox-X allows any code to render audio "up to" the current time. 00565 * 00566 * Second, looking at this code tells me why it didn't work properly with the 00567 * "sample accurate" mixer mode. This code assumes that whatever length of 00568 * audio there is to render, that all events within the 1ms tick interval are 00569 * to be squashed and stretched to fill it. In the new DOSBox-X model mixer 00570 * callback code must not assume the call is for 1ms of audio, because any 00571 * code at any time can trigger a mixer render "up to" the current time with 00572 * the tick. */ 00573 static void PCSPEAKER_CallBack(Bitu len) { 00574 bool ultrasonic = false; 00575 Bit16s * stream=(Bit16s*)MixTemp; 00576 ForwardPIT(1.0 + PIC_TickIndex()); 00577 CheckPITSynchronization(); 00578 spkr.last_index = PIC_TickIndex(); 00579 Bitu count=len; 00580 Bitu pos=0; 00581 pic_tickindex_t sample_base=0; 00582 pic_tickindex_t sample_add=(pic_tickindex_t)(1.0001/len); 00583 while (count--) { 00584 pic_tickindex_t index=sample_base; 00585 sample_base+=sample_add; 00586 pic_tickindex_t end=sample_base; 00587 double value=0; 00588 while(index<end) { 00589 /* Check if there is an upcoming event */ 00590 if (spkr.used && spkr.entries[pos].index<=index) { 00591 spkr.volwant=SPKR_VOLUME*(pic_tickindex_t)spkr.entries[pos].output_level; 00592 00593 /* A change in PC speaker output means to keep rendering. 00594 * Do not allow timeout to occur unless speaker is idle too long. */ 00595 if (spkr.pit_mode == 3 && spkr.pit_max < (1000.0/spkr.rate)) { 00596 /* Unless the speaker is cycling at ultrasonic frequencies, meaning games 00597 * that "silence" the output by setting the counter way above audible frequencies. */ 00598 ultrasonic = true; 00599 } 00600 else { 00601 spkr.last_ticks=PIC_Ticks; 00602 } 00603 00604 #ifdef SPKR_DEBUGGING 00605 fprintf( 00606 PCSpeakerOutputLevelLog, 00607 "%f %u\n", 00608 PIC_Ticks + spkr.entries[pos].index, 00609 spkr.entries[pos].output_level); 00610 double tempIndex = PIC_Ticks + spkr.entries[pos].index; 00611 unsigned char tempOutputLevel = spkr.entries[pos].output_level; 00612 fwrite(&tempIndex, sizeof(double), 1, PCSpeakerOutput); 00613 fwrite(&tempOutputLevel, sizeof(unsigned char), 1, PCSpeakerOutput); 00614 #endif 00615 pos++;spkr.used--; 00616 continue; 00617 } 00618 pic_tickindex_t vol_end; 00619 if (spkr.used && spkr.entries[pos].index<end) { 00620 vol_end=spkr.entries[pos].index; 00621 } else vol_end=end; 00622 pic_tickindex_t vol_len=vol_end-index; 00623 /* Check if we have to slide the volume */ 00624 pic_tickindex_t vol_diff=spkr.volwant-spkr.volcur; 00625 if (vol_diff == 0) { 00626 value+=spkr.volcur*vol_len; 00627 index+=vol_len; 00628 } else { 00629 /* Check how long it will take to goto new level */ 00630 pic_tickindex_t vol_time=fabs(vol_diff)/SPKR_SPEED; 00631 if (vol_time<=vol_len) { 00632 /* Volume reaches endpoint in this block, calc until that point */ 00633 value+=vol_time*spkr.volcur; 00634 value+=vol_time*vol_diff/2; 00635 index+=vol_time; 00636 spkr.volcur=spkr.volwant; 00637 } else { 00638 /* Volume still not reached in this block */ 00639 value+=spkr.volcur*vol_len; 00640 if (vol_diff<0) { 00641 value-=(SPKR_SPEED*vol_len*vol_len)/2; 00642 spkr.volcur-=SPKR_SPEED*vol_len; 00643 } else { 00644 value+=(SPKR_SPEED*vol_len*vol_len)/2; 00645 spkr.volcur+=SPKR_SPEED*vol_len; 00646 } 00647 index+=vol_len; 00648 } 00649 } 00650 } 00651 *stream++=(Bit16s)(value/sample_add); 00652 } 00653 if(spkr.chan) spkr.chan->AddSamples_m16(len,(Bit16s*)MixTemp); 00654 00655 //Turn off speaker after 10 seconds of idle or one second idle when in off mode 00656 bool turnoff = false; 00657 Bitu test_ticks = PIC_Ticks; 00658 if ((spkr.last_ticks + 10000) < test_ticks) turnoff = true; 00659 if((!spkr.pit_output_enabled) && ((spkr.last_ticks + 1000) < test_ticks)) turnoff = true; 00660 00661 if(turnoff){ 00662 if(spkr.volwant == 0) { 00663 spkr.last_ticks = 0; 00664 if(spkr.chan) { 00665 if (!spkr.pit_output_enabled) 00666 LOG(LOG_MISC,LOG_DEBUG)("Silencing PC speaker output (output disabled)"); 00667 else if (ultrasonic) 00668 LOG(LOG_MISC,LOG_DEBUG)("Silencing PC speaker output (timeout and ultrasonic frequency)"); 00669 else 00670 LOG(LOG_MISC,LOG_DEBUG)("Silencing PC speaker output (timeout and non-changing output)"); 00671 00672 spkr.chan->Enable(false); 00673 } 00674 } else { 00675 if(spkr.volwant > 0) spkr.volwant--; else spkr.volwant++; 00676 00677 } 00678 } 00679 if (spkr.used != 0) { 00680 if (pos != 0) { 00681 /* well then roll the queue back */ 00682 for (Bitu i=0;i < spkr.used;i++) 00683 spkr.entries[i] = spkr.entries[pos+i]; 00684 } 00685 00686 /* hack: some indexes come out at 1.001, fix that for the next round. 00687 * this is a consequence of DOSBox-X allowing the CPU cycles 00688 * count use to overrun slightly for accuracy. if we DONT fix 00689 * this the delay queue will get stuck and PC speaker output 00690 * will stop. */ 00691 for (Bitu i=0;i < spkr.used;i++) { 00692 if (spkr.entries[i].index >= 1.000) 00693 spkr.entries[i].index -= 1.000; 00694 else 00695 break; 00696 } 00697 00698 LOG(LOG_MISC,LOG_DEBUG)("PC speaker queue render, %u entries left, %u rendered",(unsigned int)spkr.used,(unsigned int)pos); 00699 LOG(LOG_MISC,LOG_DEBUG)("Next entry waits for index %.3f, stopped at %.3f",spkr.entries[0].index,sample_base); 00700 } 00701 } 00702 class PCSPEAKER:public Module_base { 00703 private: 00704 MixerObject MixerChan; 00705 public: 00706 PCSPEAKER(Section* configuration):Module_base(configuration){ 00707 spkr.chan=0; 00708 Section_prop * section=static_cast<Section_prop *>(configuration); 00709 if(!section->Get_bool("pcspeaker")) return; 00710 spkr.pit_output_enabled = 0; 00711 spkr.pit_clock_gate_enabled = 0; 00712 spkr.pit_mode1_waiting_for_trigger = 1; 00713 spkr.last_ticks=0; 00714 spkr.last_index=0; 00715 spkr.rate=(unsigned int)section->Get_int("pcrate"); 00716 00717 // PIT initially in mode 3 at ~903 Hz 00718 spkr.pit_mode = 3; 00719 spkr.pit_mode3_counting = 0; 00720 spkr.pit_output_level = 1; 00721 spkr.pit_max=(1000.0f/PIT_TICK_RATE)*1320; 00722 spkr.pit_half=spkr.pit_max/2; 00723 spkr.pit_new_max=spkr.pit_max; 00724 spkr.pit_new_half=spkr.pit_half; 00725 spkr.pit_index=0; 00726 00727 /* set minimum counter value with some headroom so that when games "silence" the PC speaker 00728 * by setting the counter to an ultrasonic frequency, it averages out into a quiet hiss rather 00729 * than noisy aliasing noise. */ 00730 spkr.minimum_counter = PIT_TICK_RATE/(spkr.rate*10); 00731 SPKR_SPEED = (pic_tickindex_t)((SPKR_VOLUME*2*44100)/(0.050*spkr.rate)); /* calibrated around DOSBox-X default rate 44100h */ 00732 spkr.used=0; 00733 /* Register the sound channel */ 00734 spkr.chan=MixerChan.Install(&PCSPEAKER_CallBack,spkr.rate,"SPKR"); 00735 if (!spkr.chan) { 00736 E_Exit(__FILE__ ": Unable to register channel with mixer."); 00737 } 00738 spkr.chan->SetLowpassFreq(14000); 00739 spkr.chan->Enable(true); 00740 #ifdef SPKR_DEBUGGING 00741 PCSpeakerLog = fopen("PCSpeakerLog.txt", "w"); 00742 if (PCSpeakerLog == NULL) { 00743 E_Exit(__FILE__ ": Unable to create a PC speaker log for debugging."); 00744 } 00745 PCSpeakerState = fopen("PCSpeakerState.dat", "wb"); 00746 if (PCSpeakerState == NULL) { 00747 E_Exit(__FILE__ ": Unable to create a PC speaker state file for debugging."); 00748 } 00749 PCSpeakerOutput = fopen("PCSpeakerOutput.dat", "wb"); 00750 if (PCSpeakerOutput == NULL) { 00751 E_Exit(__FILE__ ": Unable to create a PC speaker output file for debugging."); 00752 } 00753 PCSpeakerOutputLevelLog = fopen("PCSpeakerOutputLevelLog.txt", "w"); 00754 if (PCSpeakerOutputLevelLog == NULL) { 00755 E_Exit(__FILE__ ": Unable to create a PC speaker output level log for debugging."); 00756 } 00757 #endif 00758 } 00759 }; 00760 00761 static PCSPEAKER* test; 00762 00763 void PCSPEAKER_ShutDown(Section* sec){ 00764 (void)sec;//UNUSED 00765 delete test; 00766 } 00767 00768 void PCSPEAKER_OnReset(Section* sec) { 00769 (void)sec;//UNUSED 00770 if (test == NULL) { 00771 LOG(LOG_MISC,LOG_DEBUG)("Allocating PC speaker emulation"); 00772 test = new PCSPEAKER(control->GetSection("speaker")); 00773 } 00774 } 00775 00776 void PCSPEAKER_Init() { 00777 LOG(LOG_MISC,LOG_DEBUG)("Initializing PC speaker"); 00778 00779 AddExitFunction(AddExitFunctionFuncPair(PCSPEAKER_ShutDown),true); 00780 AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(PCSPEAKER_OnReset)); 00781 } 00782 00783 // save state support 00784 void POD_Save_PCSpeaker( std::ostream& stream ) 00785 { 00786 const char pod_name[32] = "PCSpeaker"; 00787 00788 if( stream.fail() ) return; 00789 if( !test ) return; 00790 if( !spkr.chan ) return; 00791 00792 00793 WRITE_POD( &pod_name, pod_name ); 00794 00795 //******************************************* 00796 //******************************************* 00797 //******************************************* 00798 00799 // - near-pure data 00800 WRITE_POD( &spkr, spkr ); 00801 00802 //******************************************* 00803 //******************************************* 00804 //******************************************* 00805 00806 spkr.chan->SaveState(stream); 00807 } 00808 00809 00810 void POD_Load_PCSpeaker( std::istream& stream ) 00811 { 00812 char pod_name[32] = {0}; 00813 00814 if( stream.fail() ) return; 00815 if( !test ) return; 00816 if( !spkr.chan ) return; 00817 00818 00819 // error checking 00820 READ_POD( &pod_name, pod_name ); 00821 if( strcmp( pod_name, "PCSpeaker" ) ) { 00822 stream.clear( std::istream::failbit | std::istream::badbit ); 00823 return; 00824 } 00825 00826 //************************************************ 00827 //************************************************ 00828 //************************************************ 00829 00830 MixerChannel *chan_old; 00831 00832 00833 // - save static ptrs 00834 chan_old = spkr.chan; 00835 00836 //******************************************* 00837 //******************************************* 00838 //******************************************* 00839 00840 // - near-pure data 00841 READ_POD( &spkr, spkr ); 00842 00843 //******************************************* 00844 //******************************************* 00845 //******************************************* 00846 00847 // - restore static ptrs 00848 spkr.chan = chan_old; 00849 00850 00851 spkr.chan->LoadState(stream); 00852 }