2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: PC Speaker emulation
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9 /* INCLUDES *******************************************************************/
15 #include "hardware/pit.h"
17 /* Extra PSDK/NDK Headers */
18 #include <ndk/iofuncs.h>
19 #include <ndk/obfuncs.h>
20 #include <ndk/rtlfuncs.h>
22 /* Extra PSDK/NDK Headers */
23 #include <ndk/kefuncs.h>
25 /* DDK Driver Headers */
28 /* PRIVATE VARIABLES **********************************************************/
30 static HANDLE hBeep
= NULL
;
32 static LARGE_INTEGER FreqCount
, CountStart
;
33 static ULONG PulseTickCount
= 0, FreqPulses
= 0;
35 #define SPEAKER_RESPONSE 200 // in milliseconds
37 #define MIN_AUDIBLE_FREQ 20 // BEEP_FREQUENCY_MINIMUM
38 #define MAX_AUDIBLE_FREQ 20000 // BEEP_FREQUENCY_MAXIMUM
39 #define CLICK_FREQ 100
42 /* PRIVATE FUNCTIONS **********************************************************/
46 MakeBeep(ULONG Frequency
,
49 static ULONG LastFrequency
= 0, LastDuration
= 0;
51 IO_STATUS_BLOCK IoStatusBlock
;
52 BEEP_SET_PARAMETERS BeepSetParameters
;
55 * Do nothing if we are replaying exactly the same sound
56 * (this avoids hiccups due to redoing the same beeps).
58 if (Frequency
== LastFrequency
&& Duration
== LastDuration
) return;
60 /* A null frequency means we stop beeping */
61 if (Frequency
== 0) Duration
= 0;
64 * For small durations we automatically reset the beep so
65 * that we can replay short beeps like clicks immediately.
74 LastFrequency
= Frequency
;
75 LastDuration
= Duration
;
78 /* Set the data and do the beep */
79 BeepSetParameters
.Frequency
= Frequency
;
80 BeepSetParameters
.Duration
= Duration
;
82 NtDeviceIoControlFile(hBeep
,
89 sizeof(BeepSetParameters
),
95 VOID
PulseSample(VOID
)
97 static ULONG Pulses
= 0, CountStartTick
= 0, LastPulsesFreq
= 0;
98 ULONG LastPulseTickCount
, CurrPulsesFreq
;
99 LARGE_INTEGER Counter
;
103 * Check how far away was the previous pulse and
104 * if it was >= 200ms away then restart counting.
106 LastPulseTickCount
= PulseTickCount
;
107 PulseTickCount
= GetTickCount();
108 if (PulseTickCount
- LastPulseTickCount
>= SPEAKER_RESPONSE
)
110 CountStart
.QuadPart
= 0;
116 /* We have closely spaced pulses. Start counting. */
117 if (CountStart
.QuadPart
== 0)
119 NtQueryPerformanceCounter(&CountStart
, NULL
);
120 CountStartTick
= PulseTickCount
;
126 /* A pulse is ongoing */
129 /* We require some pulses to have some statistics */
130 if (PulseTickCount
- CountStartTick
<= (SPEAKER_RESPONSE
>> 1)) return;
133 NtQueryPerformanceCounter(&Counter
, NULL
);
136 * Get the number of speaker hundreds of microseconds that have passed
137 * since we started counting.
139 Elapsed
= (Counter
.QuadPart
- CountStart
.QuadPart
) * 10000 / FreqCount
.QuadPart
;
140 if (Elapsed
== 0) ++Elapsed
;
142 /* Update counting for next pulses */
143 CountStart
= Counter
;
144 CountStartTick
= PulseTickCount
;
146 // HACKHACK!! I need to check why we need to double the number
147 // of pulses in order to have the correct frequency...
150 /* Get the current pulses frequency */
151 CurrPulsesFreq
= 10000 * Pulses
/ Elapsed
;
153 /* Round the current pulses frequency up and align */
154 if ((CurrPulsesFreq
& 0x0F) > 7) CurrPulsesFreq
+= 0x10;
155 CurrPulsesFreq
&= ~0x0F;
157 /* Reinitialize frequency counters if necessary */
158 if (LastPulsesFreq
== 0) LastPulsesFreq
= CurrPulsesFreq
;
159 if (FreqPulses
== 0) FreqPulses
= LastPulsesFreq
;
161 /* Fix up the current pulses frequency if needed */
162 if (LastPulsesFreq
!= 0 && CurrPulsesFreq
== 0)
163 CurrPulsesFreq
= LastPulsesFreq
;
166 * Magic begins there...
169 #define ABS(x) ((x) < 0 ? -(x) : (x))
171 if (ABS(CurrPulsesFreq
- LastPulsesFreq
) > 7)
174 * This can be a "large" fluctuation so ignore it for now, but take
175 * it into account if it happens to be a real frequency change.
177 CurrPulsesFreq
= (CurrPulsesFreq
+ LastPulsesFreq
) >> 1;
181 // FreqPulses = ((FreqPulses << 2) + LastPulsesFreq + CurrPulsesFreq) / 6;
182 FreqPulses
= ((FreqPulses
<< 1) + LastPulsesFreq
+ CurrPulsesFreq
) >> 2;
185 /* Round the pulses frequency up and align */
186 if ((FreqPulses
& 0x0F) > 7) FreqPulses
+= 0x10;
189 DPRINT("FreqPulses = %d, LastPulsesFreq = %d, CurrPulsesFreq = %d, Pulses = %d, Elapsed = %d\n",
190 FreqPulses
, LastPulsesFreq
, CurrPulsesFreq
, Pulses
, Elapsed
);
192 LastPulsesFreq
= CurrPulsesFreq
;
197 /* PUBLIC FUNCTIONS ***********************************************************/
200 VOID
SpeakerChange(UCHAR Port61hValue
)
202 static BOOLEAN OldSpeakerOff
= TRUE
;
204 BOOLEAN Timer2Gate
= !!(Port61hValue
& 0x01);
205 BOOLEAN SpeakerOn
= !!(Port61hValue
& 0x02);
207 DPRINT("SpeakerChange -- Timer2Gate == %s ; SpeakerOn == %s\n",
208 Timer2Gate
? "true" : "false", SpeakerOn
? "true" : "false");
215 ULONG Frequency
= (PIT_BASE_FREQUENCY
/ PitGetReloadValue(2));
216 if (Frequency
< MIN_AUDIBLE_FREQ
|| MAX_AUDIBLE_FREQ
< Frequency
)
219 MakeBeep(Frequency
, INFINITE
);
233 OldSpeakerOff
= FALSE
;
237 if (FreqPulses
>= MIN_AUDIBLE_FREQ
)
238 MakeBeep(FreqPulses
, INFINITE
);
239 else if (CountStart
.QuadPart
!= 0)
240 MakeBeep(CLICK_FREQ
, 1); /* Click */
242 MakeBeep(0, 0); /* Stop beeping */
246 OldSpeakerOff
= TRUE
;
249 * Check how far away was the previous pulse and if
250 * it was >= (200 + eps) ms away then stop beeping.
252 if (GetTickCount() - PulseTickCount
>= SPEAKER_RESPONSE
+ (SPEAKER_RESPONSE
>> 3))
254 CountStart
.QuadPart
= 0;
264 VOID
SpeakerInitialize(VOID
)
267 UNICODE_STRING BeepDevice
;
268 OBJECT_ATTRIBUTES ObjectAttributes
;
269 IO_STATUS_BLOCK IoStatusBlock
;
271 /* Retrieve the performance frequency and initialize the timer ticks */
272 NtQueryPerformanceCounter(&CountStart
, &FreqCount
);
273 if (FreqCount
.QuadPart
== 0)
275 wprintf(L
"FATAL: Performance counter not available\n");
278 /* Open the BEEP device */
279 RtlInitUnicodeString(&BeepDevice
, L
"\\Device\\Beep");
280 InitializeObjectAttributes(&ObjectAttributes
, &BeepDevice
, 0, NULL
, NULL
);
281 Status
= NtCreateFile(&hBeep
,
282 FILE_READ_DATA
| FILE_WRITE_DATA
,
287 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
292 if (!NT_SUCCESS(Status
))
294 DPRINT1("Failed to open the Beep driver, Status 0x%08lx\n", Status
);
295 // hBeep = INVALID_HANDLE_VALUE;
299 VOID
SpeakerCleanup(VOID
)