DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/pcspeaker.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  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
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 1024
00067 #define SPKR_VOLUME 10000
00068 //#define SPKR_SHIFT 8
00069 #define SPKR_SPEED (pic_tickindex_t)((SPKR_VOLUME*2)/0.050) // TODO: replace with runtime value
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 static void ForwardPIT(pic_tickindex_t newindex) {
00127 #ifdef SPKR_DEBUGGING
00128         if (newindex < 0 || newindex > 1) {
00129                 LOG_MSG("ForwardPIT: index out of range %f at %f", newindex, PIC_FullIndex());
00130         }
00131 #endif
00132         pic_tickindex_t passed=(newindex-spkr.last_index);
00133         pic_tickindex_t delay_base=spkr.last_index;
00134         spkr.last_index=newindex;
00135         switch (spkr.pit_mode) {
00136         case 6: // dummy
00137                 return;
00138         case 0:
00139                 if (spkr.pit_index >= spkr.pit_max) {
00140                         return; // counter reached zero before previous call so do nothing
00141                 }
00142                 spkr.pit_index += passed;
00143                 if (spkr.pit_index >= spkr.pit_max) {
00144                         // counter reached zero between previous and this call
00145                         pic_tickindex_t delay = delay_base;
00146                         delay += spkr.pit_max - spkr.pit_index + passed;
00147                         spkr.pit_output_level = 1;
00148                         AddPITOutput(delay);
00149                 }
00150                 return;
00151         case 1:
00152                 if (spkr.pit_mode1_waiting_for_counter) {
00153                         // assert output level is high
00154                         return; // counter not written yet
00155                 }
00156                 if (spkr.pit_mode1_waiting_for_trigger) {
00157                         // assert output level is high
00158                         return; // no pulse yet
00159                 }
00160                 if (spkr.pit_index >= spkr.pit_max) {
00161                         return; // counter reached zero before previous call so do nothing
00162                 }
00163                 spkr.pit_index += passed;
00164                 if (spkr.pit_index >= spkr.pit_max) {
00165                         // counter reached zero between previous and this call
00166                         pic_tickindex_t delay = delay_base;
00167                         delay += spkr.pit_max - spkr.pit_index + passed;
00168                         spkr.pit_output_level = 1;
00169                         AddPITOutput(delay);
00170                         // finished with this pulse
00171                         spkr.pit_mode1_waiting_for_trigger = 1;
00172                 }
00173                 return;
00174         case 2:
00175                 while (passed>0) {
00176                         /* passed the initial low cycle? */
00177                         if (spkr.pit_index>=spkr.pit_half) {
00178                                 /* Start a new low cycle */
00179                                 if ((spkr.pit_index+passed)>=spkr.pit_max) {
00180                                         pic_tickindex_t delay=spkr.pit_max-spkr.pit_index;
00181                                         delay_base+=delay;passed-=delay;
00182                                         spkr.pit_output_level = 0;
00183                                         AddPITOutput(delay_base);
00184                                         spkr.pit_index=0;
00185                                 } else {
00186                                         spkr.pit_index+=passed;
00187                                         return;
00188                                 }
00189                         } else {
00190                                 if ((spkr.pit_index+passed)>=spkr.pit_half) {
00191                                         pic_tickindex_t delay=spkr.pit_half-spkr.pit_index;
00192                                         delay_base+=delay;passed-=delay;
00193                                         spkr.pit_output_level = 1;
00194                                         AddPITOutput(delay_base);
00195                                         spkr.pit_index=spkr.pit_half;
00196                                 } else {
00197                                         spkr.pit_index+=passed;
00198                                         return;
00199                                 }
00200                         }
00201                 }
00202                 break;
00203                 //END CASE 2
00204         case 3:
00205                 if (!spkr.pit_mode3_counting) break;
00206                 while (passed>0) {
00207                         /* Determine where in the wave we're located */
00208                         if (spkr.pit_index>=spkr.pit_half) {
00209                                 if ((spkr.pit_index+passed)>=spkr.pit_max) {
00210                                         pic_tickindex_t delay=spkr.pit_max-spkr.pit_index;
00211                                         delay_base+=delay;passed-=delay;
00212                                         spkr.pit_output_level = 1;
00213                                         AddPITOutput(delay_base);
00214                                         spkr.pit_index=0;
00215                                         /* Load the new count */
00216                                         spkr.pit_half=spkr.pit_new_half;
00217                                         spkr.pit_max=spkr.pit_new_max;
00218                                 } else {
00219                                         spkr.pit_index+=passed;
00220                                         return;
00221                                 }
00222                         } else {
00223                                 if ((spkr.pit_index+passed)>=spkr.pit_half) {
00224                                         pic_tickindex_t delay=spkr.pit_half-spkr.pit_index;
00225                                         delay_base+=delay;passed-=delay;
00226                                         spkr.pit_output_level = 0;
00227                                         AddPITOutput(delay_base);
00228                                         spkr.pit_index=spkr.pit_half;
00229                                         /* Load the new count */
00230                                         spkr.pit_half=spkr.pit_new_half;
00231                                         spkr.pit_max=spkr.pit_new_max;
00232                                 } else {
00233                                         spkr.pit_index+=passed;
00234                                         return;
00235                                 }
00236                         }
00237                 }
00238                 break;
00239                 //END CASE 3
00240         case 4:
00241                 if (spkr.pit_index<spkr.pit_max) {
00242                         /* Check if we're gonna pass the end this block */
00243                         if (spkr.pit_index+passed>=spkr.pit_max) {
00244                                 pic_tickindex_t delay=spkr.pit_max-spkr.pit_index;
00245                                 delay_base+=delay;passed-=delay;
00246                                 spkr.pit_output_level = 0;
00247                                 AddPITOutput(delay_base); //No new events unless reprogrammed
00248                                 spkr.pit_index=spkr.pit_max;
00249                         } else spkr.pit_index+=passed;
00250                 }
00251                 break;
00252                 //END CASE 4
00253         }
00254 }
00255 
00256 void PCSPEAKER_SetPITControl(Bitu mode) {
00257         pic_tickindex_t newindex = PIC_TickIndex();
00258         ForwardPIT(newindex);
00259 #ifdef SPKR_DEBUGGING
00260         fprintf(PCSpeakerLog, "%f pit command: %u\n", PIC_FullIndex(), mode);
00261         speaker_state_change_t temp;
00262         memset(&temp, 0, sizeof(speaker_state_change_t));
00263         temp.timestamp = PIC_FullIndex();
00264         temp.state_label = LABEL_CONTROL;
00265         temp.state_specific_data.pit_mode = mode;
00266         if (fwrite(&temp, sizeof(temp), 1, PCSpeakerState) != 1)
00267                 LOG_MSG(__FILE__ ": unable to write to pc speaker log");
00268 #endif
00269         // TODO: implement all modes
00270         switch(mode) {
00271     case 0:
00272                 spkr.pit_mode = 0;
00273         break;
00274         case 1:
00275                 spkr.pit_mode = 1;
00276                 spkr.pit_mode1_waiting_for_counter = 1;
00277                 spkr.pit_mode1_waiting_for_trigger = 0;
00278                 spkr.pit_output_level = 1;
00279                 break;
00280         case 3:
00281                 spkr.pit_mode = 3;
00282                 spkr.pit_mode3_counting = 0;
00283                 spkr.pit_output_level = 1;
00284                 break;
00285         default:
00286                 return;
00287         }
00288         AddPITOutput(newindex);
00289 }
00290 
00291 void PCSPEAKER_SetCounter(Bitu cntr, Bitu mode) {
00292 #ifdef SPKR_DEBUGGING
00293         fprintf(PCSpeakerLog, "%f counter: %u, mode: %u\n", PIC_FullIndex(), cntr, mode);
00294         speaker_state_change_t temp;
00295         memset(&temp, 0, sizeof(speaker_state_change_t));
00296         temp.timestamp = PIC_FullIndex();
00297         temp.state_label = LABEL_COUNTER;
00298         temp.state_specific_data.counter = cntr;
00299         if (fwrite(&temp, sizeof(temp), 1, PCSpeakerState) != 1)
00300                 LOG_MSG(__FILE__ ": unable to write to pc speaker log");
00301 #endif
00302         if (!spkr.last_ticks) {
00303                 if(spkr.chan) spkr.chan->Enable(true);
00304                 spkr.last_index=0;
00305         }
00306         spkr.last_ticks=PIC_Ticks;
00307         pic_tickindex_t newindex=PIC_TickIndex();
00308         ForwardPIT(newindex);
00309         switch (mode) {
00310         case 0:         /* Mode 0 one shot, used with "realsound" (PWM) */
00311                 //if (cntr>80) { 
00312                 //      cntr=80;
00313                 //}
00314                 //spkr.pit_output_level=((pic_tickindex_t)cntr-40)*(SPKR_VOLUME/40.0f);
00315                 spkr.pit_output_level = 0;
00316                 spkr.pit_index = 0;
00317                 spkr.pit_max = (1000.0f / PIT_TICK_RATE) * cntr;
00318                 AddPITOutput(newindex);
00319                 break;
00320         case 1: // retriggerable one-shot, used by Star Control 1
00321                 spkr.pit_mode1_pending_max = (1000.0f / PIT_TICK_RATE) * cntr;
00322                 if (spkr.pit_mode1_waiting_for_counter) {
00323                         // assert output level is high
00324                         spkr.pit_mode1_waiting_for_counter = 0;
00325                         spkr.pit_mode1_waiting_for_trigger = 1;
00326                 }
00327                 break;
00328         case 2:                 /* Single cycle low, rest low high generator */
00329                 spkr.pit_index=0;
00330                 spkr.pit_output_level = 0;
00331                 AddPITOutput(newindex);
00332                 spkr.pit_half=(1000.0f/PIT_TICK_RATE)*1;
00333                 spkr.pit_max=(1000.0f/PIT_TICK_RATE)*cntr;
00334                 break;
00335         case 3:         /* Square wave generator */
00336                 if (cntr < spkr.minimum_counter) {
00337 //#ifdef SPKR_DEBUGGING
00338 //                      LOG_MSG(
00339 //                              "SetCounter: too high frequency %u (cntr %u) at %f",
00340 //                              PIT_TICK_RATE/cntr,
00341 //                              cntr,
00342 //                              PIC_FullIndex());
00343 //#endif
00344                         // hack to save CPU cycles
00345                         cntr = spkr.minimum_counter;
00346                         //spkr.pit_output_level = 1; // avoid breaking digger music
00347                         //spkr.pit_mode = 6; // dummy mode with constant output
00348                         //AddPITOutput(newindex);
00349                         //return;
00350                 }
00351                 spkr.pit_new_max = (1000.0f/PIT_TICK_RATE)*cntr;
00352                 spkr.pit_new_half=spkr.pit_new_max/2;
00353                 if (!spkr.pit_mode3_counting) {
00354                         spkr.pit_index = 0;
00355                         spkr.pit_max = spkr.pit_new_max;
00356                         spkr.pit_half = spkr.pit_new_half;
00357                         if (spkr.pit_clock_gate_enabled) {
00358                                 spkr.pit_mode3_counting = 1;
00359                                 spkr.pit_output_level = 1; // probably not necessary
00360                                 AddPITOutput(newindex);
00361                         }
00362                 }
00363                 break;
00364         case 4:         /* Software triggered strobe */
00365                 spkr.pit_output_level = 1;
00366                 AddPITOutput(newindex);
00367                 spkr.pit_index=0;
00368                 spkr.pit_max=(1000.0f/PIT_TICK_RATE)*cntr;
00369                 break;
00370         default:
00371 #ifdef SPKR_DEBUGGING
00372                 LOG_MSG("Unhandled speaker mode %d at %f", mode, PIC_FullIndex());
00373 #endif
00374                 return;
00375         }
00376         spkr.pit_mode = mode;
00377 }
00378 
00379 void PCSPEAKER_SetType(bool pit_clock_gate_enabled, bool pit_output_enabled) {
00380 #ifdef SPKR_DEBUGGING
00381         fprintf(
00382                         PCSpeakerLog,
00383                         "%f output: %s, clock gate %s\n",
00384                         PIC_FullIndex(),
00385                         pit_output_enabled ? "pit" : "forced low",
00386                         pit_clock_gate_enabled ? "on" : "off"
00387                         );
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_OUTPUT;
00392         temp.state_specific_data.output_state.pit_clock_gate_enabled = pit_clock_gate_enabled;
00393         temp.state_specific_data.output_state.pit_output_enabled     = pit_output_enabled;
00394         if (fwrite(&temp, sizeof(temp), 1, PCSpeakerState) != 1)
00395                 LOG_MSG(__FILE__ ": unable to write to pc speaker log");
00396 #endif
00397         if (!spkr.last_ticks) {
00398                 if(spkr.chan) spkr.chan->Enable(true);
00399                 spkr.last_index=0;
00400         }
00401         spkr.last_ticks=PIC_Ticks;
00402         pic_tickindex_t newindex=PIC_TickIndex();
00403         ForwardPIT(newindex);
00404         // pit clock gate enable rising edge is a trigger
00405         bool pit_trigger = pit_clock_gate_enabled && !spkr.pit_clock_gate_enabled;
00406         spkr.pit_clock_gate_enabled = pit_clock_gate_enabled;
00407         spkr.pit_output_enabled     = pit_output_enabled;
00408         if (pit_trigger) {
00409                 switch (spkr.pit_mode) {
00410                 case 1:
00411                         if (spkr.pit_mode1_waiting_for_counter) {
00412                                 // assert output level is high
00413                                 break;
00414                         }
00415                         spkr.pit_output_level = 0;
00416                         spkr.pit_index = 0;
00417                         spkr.pit_max = spkr.pit_mode1_pending_max;
00418                         spkr.pit_mode1_waiting_for_trigger = 0;
00419                         break;
00420                 case 3:
00421                         spkr.pit_mode3_counting = 1;
00422                         spkr.pit_new_max = spkr.pit_new_max;
00423                         spkr.pit_new_half=spkr.pit_new_max/2;
00424                         spkr.pit_index = 0;
00425                         spkr.pit_max = spkr.pit_new_max;
00426                         spkr.pit_half = spkr.pit_new_half;
00427                         spkr.pit_output_level = 1;
00428                         break;
00429                 default:
00430                         // TODO: implement other modes
00431                         break;
00432                 }
00433         } else if (!pit_clock_gate_enabled) {
00434                 switch (spkr.pit_mode) {
00435                 case 1:
00436                         // gate level does not affect mode1
00437                         break;
00438                 case 3:
00439                         // low gate forces pit output high
00440                         spkr.pit_output_level   = 1;
00441                         spkr.pit_mode3_counting = 0;
00442                         break;
00443                 default:
00444                         // TODO: implement other modes
00445                         break;
00446                 }
00447         }
00448         if (pit_output_enabled) {
00449                 AddDelayEntry(newindex, spkr.pit_output_level);
00450         } else {
00451                 AddDelayEntry(newindex, 0);
00452         }
00453 }
00454 
00455 /* NTS: This code stinks. Sort of. The way it handles the delay entry queue
00456  *      could have been done better. The event queue idea isn't needed anymore because
00457  *      DOSBox-X allows any code to render audio "up to" the current time.
00458  *
00459  *      Second, looking at this code tells me why it didn't work properly with the
00460  *      "sample accurate" mixer mode. This code assumes that whatever length of
00461  *      audio there is to render, that all events within the 1ms tick interval are
00462  *      to be squashed and stretched to fill it. In the new DOSBox-X model mixer
00463  *      callback code must not assume the call is for 1ms of audio, because any
00464  *      code at any time can trigger a mixer render "up to" the current time with
00465  *      the tick. */
00466 static void PCSPEAKER_CallBack(Bitu len) {
00467         Bit16s * stream=(Bit16s*)MixTemp;
00468         ForwardPIT(1);
00469         spkr.last_index=0;
00470         Bitu count=len;
00471         Bitu pos=0;
00472         pic_tickindex_t sample_base=0;
00473         pic_tickindex_t sample_add=(pic_tickindex_t)(1.0001/len);
00474         while (count--) {
00475                 pic_tickindex_t index=sample_base;
00476                 sample_base+=sample_add;
00477                 pic_tickindex_t end=sample_base;
00478                 double value=0;
00479                 while(index<end) {
00480                         /* Check if there is an upcoming event */
00481                         if (spkr.used && spkr.entries[pos].index<=index) {
00482                                 spkr.volwant=SPKR_VOLUME*(pic_tickindex_t)spkr.entries[pos].output_level;
00483 #ifdef SPKR_DEBUGGING
00484                                 fprintf(
00485                                                 PCSpeakerOutputLevelLog,
00486                                                 "%f %u\n",
00487                                                 PIC_Ticks + spkr.entries[pos].index,
00488                                                 spkr.entries[pos].output_level);
00489                                 double tempIndex = PIC_Ticks + spkr.entries[pos].index;
00490                                 unsigned char tempOutputLevel = spkr.entries[pos].output_level;
00491                                 fwrite(&tempIndex, sizeof(double), 1, PCSpeakerOutput);
00492                                 fwrite(&tempOutputLevel, sizeof(unsigned char), 1, PCSpeakerOutput);
00493 #endif
00494                                 pos++;spkr.used--;
00495                                 continue;
00496                         }
00497                         pic_tickindex_t vol_end;
00498                         if (spkr.used && spkr.entries[pos].index<end) {
00499                                 vol_end=spkr.entries[pos].index;
00500                         } else vol_end=end;
00501                         pic_tickindex_t vol_len=vol_end-index;
00502             /* Check if we have to slide the volume */
00503                         pic_tickindex_t vol_diff=spkr.volwant-spkr.volcur;
00504                         if (vol_diff == 0) {
00505                                 value+=spkr.volcur*vol_len;
00506                                 index+=vol_len;
00507                         } else {
00508                                 /* Check how long it will take to goto new level */
00509                                 pic_tickindex_t vol_time=fabs(vol_diff)/SPKR_SPEED;
00510                                 if (vol_time<=vol_len) {
00511                                         /* Volume reaches endpoint in this block, calc until that point */
00512                                         value+=vol_time*spkr.volcur;
00513                                         value+=vol_time*vol_diff/2;
00514                                         index+=vol_time;
00515                                         spkr.volcur=spkr.volwant;
00516                                 } else {
00517                                         /* Volume still not reached in this block */
00518                                         value+=spkr.volcur*vol_len;
00519                                         if (vol_diff<0) {
00520                                                 value-=(SPKR_SPEED*vol_len*vol_len)/2;
00521                                                 spkr.volcur-=SPKR_SPEED*vol_len;
00522                                         } else {
00523                                                 value+=(SPKR_SPEED*vol_len*vol_len)/2;
00524                                                 spkr.volcur+=SPKR_SPEED*vol_len;
00525                                         }
00526                                         index+=vol_len;
00527                                 }
00528                         }
00529                 }
00530                 *stream++=(Bit16s)(value/sample_add);
00531         }
00532         if(spkr.chan) spkr.chan->AddSamples_m16(len,(Bit16s*)MixTemp);
00533 
00534         //Turn off speaker after 10 seconds of idle or one second idle when in off mode
00535         bool turnoff = false;
00536         Bitu test_ticks = PIC_Ticks;
00537         if ((spkr.last_ticks + 10000) < test_ticks) turnoff = true;
00538         if((!spkr.pit_output_enabled) && ((spkr.last_ticks + 1000) < test_ticks)) turnoff = true;
00539 
00540         if(turnoff){
00541                 if(spkr.volwant == 0) { 
00542                         spkr.last_ticks = 0;
00543                         if(spkr.chan) spkr.chan->Enable(false);
00544                 } else {
00545                         if(spkr.volwant > 0) spkr.volwant--; else spkr.volwant++;
00546                 
00547                 }
00548         }
00549         if (spkr.used != 0) {
00550         if (pos != 0) {
00551             /* well then roll the queue back */
00552             for (Bitu i=0;i < spkr.used;i++)
00553                 spkr.entries[i] = spkr.entries[pos+i];
00554         }
00555 
00556         /* hack: some indexes come out at 1.001, fix that for the next round.
00557          *       this is a consequence of DOSBox-X allowing the CPU cycles
00558          *       count use to overrun slightly for accuracy. if we DONT fix
00559          *       this the delay queue will get stuck and PC speaker output
00560          *       will stop. */
00561         for (Bitu i=0;i < spkr.used;i++) {
00562             if (spkr.entries[i].index >= 1.000)
00563                 spkr.entries[i].index -= 1.000;
00564             else
00565                 break;
00566         }
00567 
00568         LOG(LOG_MISC,LOG_DEBUG)("PC speaker queue render, %u entries left, %u rendered",(unsigned int)spkr.used,(unsigned int)pos);
00569         LOG(LOG_MISC,LOG_DEBUG)("Next entry waits for index %.3f, stopped at %.3f",spkr.entries[0].index,sample_base);
00570         }
00571 }
00572 class PCSPEAKER:public Module_base {
00573 private:
00574         MixerObject MixerChan;
00575 public:
00576         PCSPEAKER(Section* configuration):Module_base(configuration){
00577                 spkr.chan=0;
00578                 Section_prop * section=static_cast<Section_prop *>(configuration);
00579                 if(!section->Get_bool("pcspeaker")) return;
00580                 spkr.pit_output_enabled = 0;
00581                 spkr.pit_clock_gate_enabled = 0;
00582                 spkr.pit_mode1_waiting_for_trigger = 1;
00583                 spkr.last_ticks=0;
00584                 spkr.last_index=0;
00585                 spkr.rate=(unsigned int)section->Get_int("pcrate");
00586 
00587                 // PIT initially in mode 3 at ~903 Hz
00588                 spkr.pit_mode = 3;
00589                 spkr.pit_mode3_counting = 0;
00590                 spkr.pit_output_level = 1;
00591                 spkr.pit_max=(1000.0f/PIT_TICK_RATE)*1320;
00592                 spkr.pit_half=spkr.pit_max/2;
00593                 spkr.pit_new_max=spkr.pit_max;
00594                 spkr.pit_new_half=spkr.pit_half;
00595                 spkr.pit_index=0;
00596 
00597                 //spkr.minimum_counter = (PIT_TICK_RATE + spkr.rate/2-1)/(spkr.rate/2);
00598                 spkr.minimum_counter = 2*PIT_TICK_RATE/spkr.rate;
00599                 spkr.used=0;
00600                 /* Register the sound channel */
00601                 spkr.chan=MixerChan.Install(&PCSPEAKER_CallBack,spkr.rate,"SPKR");
00602                 if (!spkr.chan) {
00603                         E_Exit(__FILE__ ": Unable to register channel with mixer.");
00604                 }
00605                 spkr.chan->Enable(true);
00606 #ifdef SPKR_DEBUGGING
00607                 PCSpeakerLog = fopen("PCSpeakerLog.txt", "w");
00608                 if (PCSpeakerLog == NULL) {
00609                         E_Exit(__FILE__ ": Unable to create a PC speaker log for debugging.");
00610                 }
00611                 PCSpeakerState = fopen("PCSpeakerState.dat", "wb");
00612                 if (PCSpeakerState == NULL) {
00613                         E_Exit(__FILE__ ": Unable to create a PC speaker state file for debugging.");
00614                 }
00615                 PCSpeakerOutput = fopen("PCSpeakerOutput.dat", "wb");
00616                 if (PCSpeakerOutput == NULL) {
00617                         E_Exit(__FILE__ ": Unable to create a PC speaker output file for debugging.");
00618                 }
00619                 PCSpeakerOutputLevelLog = fopen("PCSpeakerOutputLevelLog.txt", "w");
00620                 if (PCSpeakerOutputLevelLog == NULL) {
00621                         E_Exit(__FILE__ ": Unable to create a PC speaker output level log for debugging.");
00622                 }
00623 #endif
00624         }
00625 };
00626 
00627 static PCSPEAKER* test;
00628 
00629 void PCSPEAKER_ShutDown(Section* sec){
00630     (void)sec;//UNUSED
00631         delete test;
00632 }
00633 
00634 void PCSPEAKER_OnReset(Section* sec) {
00635     (void)sec;//UNUSED
00636         if (test == NULL) {
00637                 LOG(LOG_MISC,LOG_DEBUG)("Allocating PC speaker emulation");
00638                 test = new PCSPEAKER(control->GetSection("speaker"));
00639         }
00640 }
00641 
00642 void PCSPEAKER_Init() {
00643         LOG(LOG_MISC,LOG_DEBUG)("Initializing PC speaker");
00644 
00645         AddExitFunction(AddExitFunctionFuncPair(PCSPEAKER_ShutDown),true);
00646         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(PCSPEAKER_OnReset));
00647 }
00648