DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/pcspeaker.cpp
00001 /*
00002  *  Copyright (C) 2002-2019  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, 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 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_max = spkr.pit_new_max;
00529                         spkr.pit_new_half=spkr.pit_new_max/2;
00530                         spkr.pit_index = 0;
00531                         spkr.pit_max = spkr.pit_new_max;
00532                         spkr.pit_half = spkr.pit_new_half;
00533                         spkr.pit_output_level = 1;
00534                         break;
00535                 default:
00536                         // TODO: implement other modes
00537                         break;
00538                 }
00539         } else if (!spkr.pit_clock_gate_enabled) {
00540                 switch (spkr.pit_mode) {
00541                 case 1:
00542                         // gate level does not affect mode1
00543                         break;
00544                 case 3:
00545                         // low gate forces pit output high
00546                         spkr.pit_output_level   = 1;
00547                         spkr.pit_mode3_counting = 0;
00548                         break;
00549                 default:
00550                         // TODO: implement other modes
00551                         break;
00552                 }
00553         }
00554         if (pit_output_enabled) {
00555                 AddDelayEntry(newindex, spkr.pit_output_level);
00556         } else {
00557                 AddDelayEntry(newindex, 0);
00558         }
00559 
00560     CheckPITSynchronization();
00561 }
00562 
00563 /* NTS: This code stinks. Sort of. The way it handles the delay entry queue
00564  *      could have been done better. The event queue idea isn't needed anymore because
00565  *      DOSBox-X allows any code to render audio "up to" the current time.
00566  *
00567  *      Second, looking at this code tells me why it didn't work properly with the
00568  *      "sample accurate" mixer mode. This code assumes that whatever length of
00569  *      audio there is to render, that all events within the 1ms tick interval are
00570  *      to be squashed and stretched to fill it. In the new DOSBox-X model mixer
00571  *      callback code must not assume the call is for 1ms of audio, because any
00572  *      code at any time can trigger a mixer render "up to" the current time with
00573  *      the tick. */
00574 static void PCSPEAKER_CallBack(Bitu len) {
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 #ifdef SPKR_DEBUGGING
00593                                 fprintf(
00594                                                 PCSpeakerOutputLevelLog,
00595                                                 "%f %u\n",
00596                                                 PIC_Ticks + spkr.entries[pos].index,
00597                                                 spkr.entries[pos].output_level);
00598                                 double tempIndex = PIC_Ticks + spkr.entries[pos].index;
00599                                 unsigned char tempOutputLevel = spkr.entries[pos].output_level;
00600                                 fwrite(&tempIndex, sizeof(double), 1, PCSpeakerOutput);
00601                                 fwrite(&tempOutputLevel, sizeof(unsigned char), 1, PCSpeakerOutput);
00602 #endif
00603                                 pos++;spkr.used--;
00604                                 continue;
00605                         }
00606                         pic_tickindex_t vol_end;
00607                         if (spkr.used && spkr.entries[pos].index<end) {
00608                                 vol_end=spkr.entries[pos].index;
00609                         } else vol_end=end;
00610                         pic_tickindex_t vol_len=vol_end-index;
00611             /* Check if we have to slide the volume */
00612                         pic_tickindex_t vol_diff=spkr.volwant-spkr.volcur;
00613                         if (vol_diff == 0) {
00614                                 value+=spkr.volcur*vol_len;
00615                                 index+=vol_len;
00616                         } else {
00617                                 /* Check how long it will take to goto new level */
00618                                 pic_tickindex_t vol_time=fabs(vol_diff)/SPKR_SPEED;
00619                                 if (vol_time<=vol_len) {
00620                                         /* Volume reaches endpoint in this block, calc until that point */
00621                                         value+=vol_time*spkr.volcur;
00622                                         value+=vol_time*vol_diff/2;
00623                                         index+=vol_time;
00624                                         spkr.volcur=spkr.volwant;
00625                                 } else {
00626                                         /* Volume still not reached in this block */
00627                                         value+=spkr.volcur*vol_len;
00628                                         if (vol_diff<0) {
00629                                                 value-=(SPKR_SPEED*vol_len*vol_len)/2;
00630                                                 spkr.volcur-=SPKR_SPEED*vol_len;
00631                                         } else {
00632                                                 value+=(SPKR_SPEED*vol_len*vol_len)/2;
00633                                                 spkr.volcur+=SPKR_SPEED*vol_len;
00634                                         }
00635                                         index+=vol_len;
00636                                 }
00637                         }
00638                 }
00639                 *stream++=(Bit16s)(value/sample_add);
00640         }
00641         if(spkr.chan) spkr.chan->AddSamples_m16(len,(Bit16s*)MixTemp);
00642 
00643         //Turn off speaker after 10 seconds of idle or one second idle when in off mode
00644         bool turnoff = false;
00645         Bitu test_ticks = PIC_Ticks;
00646         if ((spkr.last_ticks + 10000) < test_ticks) turnoff = true;
00647         if((!spkr.pit_output_enabled) && ((spkr.last_ticks + 1000) < test_ticks)) turnoff = true;
00648 
00649         if(turnoff){
00650                 if(spkr.volwant == 0) { 
00651                         spkr.last_ticks = 0;
00652                         if(spkr.chan) spkr.chan->Enable(false);
00653                 } else {
00654                         if(spkr.volwant > 0) spkr.volwant--; else spkr.volwant++;
00655                 
00656                 }
00657         }
00658         if (spkr.used != 0) {
00659         if (pos != 0) {
00660             /* well then roll the queue back */
00661             for (Bitu i=0;i < spkr.used;i++)
00662                 spkr.entries[i] = spkr.entries[pos+i];
00663         }
00664 
00665         /* hack: some indexes come out at 1.001, fix that for the next round.
00666          *       this is a consequence of DOSBox-X allowing the CPU cycles
00667          *       count use to overrun slightly for accuracy. if we DONT fix
00668          *       this the delay queue will get stuck and PC speaker output
00669          *       will stop. */
00670         for (Bitu i=0;i < spkr.used;i++) {
00671             if (spkr.entries[i].index >= 1.000)
00672                 spkr.entries[i].index -= 1.000;
00673             else
00674                 break;
00675         }
00676 
00677         LOG(LOG_MISC,LOG_DEBUG)("PC speaker queue render, %u entries left, %u rendered",(unsigned int)spkr.used,(unsigned int)pos);
00678         LOG(LOG_MISC,LOG_DEBUG)("Next entry waits for index %.3f, stopped at %.3f",spkr.entries[0].index,sample_base);
00679         }
00680 }
00681 class PCSPEAKER:public Module_base {
00682 private:
00683         MixerObject MixerChan;
00684 public:
00685         PCSPEAKER(Section* configuration):Module_base(configuration){
00686                 spkr.chan=0;
00687                 Section_prop * section=static_cast<Section_prop *>(configuration);
00688                 if(!section->Get_bool("pcspeaker")) return;
00689                 spkr.pit_output_enabled = 0;
00690                 spkr.pit_clock_gate_enabled = 0;
00691                 spkr.pit_mode1_waiting_for_trigger = 1;
00692                 spkr.last_ticks=0;
00693                 spkr.last_index=0;
00694                 spkr.rate=(unsigned int)section->Get_int("pcrate");
00695 
00696                 // PIT initially in mode 3 at ~903 Hz
00697                 spkr.pit_mode = 3;
00698                 spkr.pit_mode3_counting = 0;
00699                 spkr.pit_output_level = 1;
00700                 spkr.pit_max=(1000.0f/PIT_TICK_RATE)*1320;
00701                 spkr.pit_half=spkr.pit_max/2;
00702                 spkr.pit_new_max=spkr.pit_max;
00703                 spkr.pit_new_half=spkr.pit_half;
00704                 spkr.pit_index=0;
00705 
00706                 //spkr.minimum_counter = (PIT_TICK_RATE + spkr.rate/2-1)/(spkr.rate/2);
00707                 spkr.minimum_counter = 2*PIT_TICK_RATE/spkr.rate;
00708                 spkr.used=0;
00709                 /* Register the sound channel */
00710                 spkr.chan=MixerChan.Install(&PCSPEAKER_CallBack,spkr.rate,"SPKR");
00711                 if (!spkr.chan) {
00712                         E_Exit(__FILE__ ": Unable to register channel with mixer.");
00713                 }
00714                 spkr.chan->Enable(true);
00715 #ifdef SPKR_DEBUGGING
00716                 PCSpeakerLog = fopen("PCSpeakerLog.txt", "w");
00717                 if (PCSpeakerLog == NULL) {
00718                         E_Exit(__FILE__ ": Unable to create a PC speaker log for debugging.");
00719                 }
00720                 PCSpeakerState = fopen("PCSpeakerState.dat", "wb");
00721                 if (PCSpeakerState == NULL) {
00722                         E_Exit(__FILE__ ": Unable to create a PC speaker state file for debugging.");
00723                 }
00724                 PCSpeakerOutput = fopen("PCSpeakerOutput.dat", "wb");
00725                 if (PCSpeakerOutput == NULL) {
00726                         E_Exit(__FILE__ ": Unable to create a PC speaker output file for debugging.");
00727                 }
00728                 PCSpeakerOutputLevelLog = fopen("PCSpeakerOutputLevelLog.txt", "w");
00729                 if (PCSpeakerOutputLevelLog == NULL) {
00730                         E_Exit(__FILE__ ": Unable to create a PC speaker output level log for debugging.");
00731                 }
00732 #endif
00733         }
00734 };
00735 
00736 static PCSPEAKER* test;
00737 
00738 void PCSPEAKER_ShutDown(Section* sec){
00739     (void)sec;//UNUSED
00740         delete test;
00741 }
00742 
00743 void PCSPEAKER_OnReset(Section* sec) {
00744     (void)sec;//UNUSED
00745         if (test == NULL) {
00746                 LOG(LOG_MISC,LOG_DEBUG)("Allocating PC speaker emulation");
00747                 test = new PCSPEAKER(control->GetSection("speaker"));
00748         }
00749 }
00750 
00751 void PCSPEAKER_Init() {
00752         LOG(LOG_MISC,LOG_DEBUG)("Initializing PC speaker");
00753 
00754         AddExitFunction(AddExitFunctionFuncPair(PCSPEAKER_ShutDown),true);
00755         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(PCSPEAKER_OnReset));
00756 }
00757