DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/pcspeaker.cpp
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 }