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>
20 /* DDK Driver Headers */
23 /* PRIVATE VARIABLES **********************************************************/
25 static HANDLE hBeep
= NULL
;
27 static LARGE_INTEGER FreqCount
, CountStart
;
28 static ULONG PulseTickCount
= 0, FreqPulses
= 0;
30 #define SPEAKER_RESPONSE 200 // in milliseconds
32 #define MIN_AUDIBLE_FREQ 20 // BEEP_FREQUENCY_MINIMUM
33 #define MAX_AUDIBLE_FREQ 20000 // BEEP_FREQUENCY_MAXIMUM
34 #define CLICK_FREQ 100
37 /* PRIVATE FUNCTIONS **********************************************************/
41 MakeBeep(ULONG Frequency
,
44 static ULONG LastFrequency
= 0, LastDuration
= 0;
46 IO_STATUS_BLOCK IoStatusBlock
;
47 BEEP_SET_PARAMETERS BeepSetParameters
;
49 /* A null frequency means we stop beeping */
50 if (Frequency
== 0) Duration
= 0;
53 * Do nothing if we are replaying exactly the same sound
54 * (this avoids hiccups due to redoing the same beeps).
56 if (Frequency
== LastFrequency
&& Duration
== LastDuration
) return;
59 * For small durations we automatically reset the beep so
60 * that we can replay short beeps like clicks immediately.
69 LastFrequency
= Frequency
;
70 LastDuration
= Duration
;
73 /* Set the data and do the beep */
74 BeepSetParameters
.Frequency
= Frequency
;
75 BeepSetParameters
.Duration
= Duration
;
77 NtDeviceIoControlFile(hBeep
,
84 sizeof(BeepSetParameters
),
90 VOID
PulseSample(VOID
)
92 static ULONG Pulses
= 0, CountStartTick
= 0, LastPulsesFreq
= 0;
93 ULONG LastPulseTickCount
, CurrPulsesFreq
;
94 LARGE_INTEGER Counter
;
98 * Check how far away was the previous pulse and
99 * if it was >= 200ms away then restart counting.
101 LastPulseTickCount
= PulseTickCount
;
102 PulseTickCount
= GetTickCount();
103 if (PulseTickCount
- LastPulseTickCount
>= SPEAKER_RESPONSE
)
105 CountStart
.QuadPart
= 0;
111 /* We have closely spaced pulses. Start counting. */
112 if (CountStart
.QuadPart
== 0)
114 NtQueryPerformanceCounter(&CountStart
, NULL
);
115 CountStartTick
= PulseTickCount
;
121 /* A pulse is ongoing */
124 /* We require some pulses to have some statistics */
125 if (PulseTickCount
- CountStartTick
<= (SPEAKER_RESPONSE
>> 1)) return;
128 NtQueryPerformanceCounter(&Counter
, NULL
);
131 * Get the number of speaker hundreds of microseconds that have passed
132 * since we started counting.
134 Elapsed
= (Counter
.QuadPart
- CountStart
.QuadPart
) * 10000 / FreqCount
.QuadPart
;
135 if (Elapsed
== 0) ++Elapsed
;
137 /* Update counting for next pulses */
138 CountStart
= Counter
;
139 CountStartTick
= PulseTickCount
;
141 // HACKHACK!! I need to check why we need to double the number
142 // of pulses in order to have the correct frequency...
145 /* Get the current pulses frequency */
146 CurrPulsesFreq
= 10000 * Pulses
/ Elapsed
;
148 /* Round the current pulses frequency up and align */
149 if ((CurrPulsesFreq
& 0x0F) > 7) CurrPulsesFreq
+= 0x10;
150 CurrPulsesFreq
&= ~0x0F;
152 /* Reinitialize frequency counters if necessary */
153 if (LastPulsesFreq
== 0) LastPulsesFreq
= CurrPulsesFreq
;
154 if (FreqPulses
== 0) FreqPulses
= LastPulsesFreq
;
156 /* Fix up the current pulses frequency if needed */
157 if (LastPulsesFreq
!= 0 && CurrPulsesFreq
== 0)
158 CurrPulsesFreq
= LastPulsesFreq
;
161 * Magic begins there...
163 #define UABS(x) (ULONG)((LONG)(x) < 0 ? -(LONG)(x) : (x))
164 if (UABS(CurrPulsesFreq
- LastPulsesFreq
) > 7)
167 * This can be a "large" fluctuation so ignore it for now, but take
168 * it into account if it happens to be a real frequency change.
170 CurrPulsesFreq
= (CurrPulsesFreq
+ LastPulsesFreq
) >> 1;
174 // FreqPulses = ((FreqPulses << 2) + LastPulsesFreq + CurrPulsesFreq) / 6;
175 FreqPulses
= ((FreqPulses
<< 1) + LastPulsesFreq
+ CurrPulsesFreq
) >> 2;
178 /* Round the pulses frequency up and align */
179 if ((FreqPulses
& 0x0F) > 7) FreqPulses
+= 0x10;
182 DPRINT("FreqPulses = %d, LastPulsesFreq = %d, CurrPulsesFreq = %d, Pulses = %d, Elapsed = %d\n",
183 FreqPulses
, LastPulsesFreq
, CurrPulsesFreq
, Pulses
, Elapsed
);
185 LastPulsesFreq
= CurrPulsesFreq
;
190 /* PUBLIC FUNCTIONS ***********************************************************/
193 VOID
SpeakerChange(UCHAR Port61hValue
)
195 static BOOLEAN OldSpeakerOff
= TRUE
;
197 BOOLEAN Timer2Gate
= !!(Port61hValue
& 0x01);
198 BOOLEAN SpeakerOn
= !!(Port61hValue
& 0x02);
200 DPRINT("SpeakerChange -- Timer2Gate == %s ; SpeakerOn == %s\n",
201 Timer2Gate
? "true" : "false", SpeakerOn
? "true" : "false");
208 ULONG Frequency
= (PIT_BASE_FREQUENCY
/ PitGetReloadValue(2));
209 if (Frequency
< MIN_AUDIBLE_FREQ
|| MAX_AUDIBLE_FREQ
< Frequency
)
212 MakeBeep(Frequency
, INFINITE
);
226 OldSpeakerOff
= FALSE
;
230 if (FreqPulses
>= MIN_AUDIBLE_FREQ
)
231 MakeBeep(FreqPulses
, INFINITE
);
232 else if (CountStart
.QuadPart
!= 0)
233 MakeBeep(CLICK_FREQ
, 1); /* Click */
235 MakeBeep(0, 0); /* Stop beeping */
239 OldSpeakerOff
= TRUE
;
242 * Check how far away was the previous pulse and if
243 * it was >= (200 + eps) ms away then stop beeping.
245 if (GetTickCount() - PulseTickCount
>= SPEAKER_RESPONSE
+ (SPEAKER_RESPONSE
>> 3))
247 CountStart
.QuadPart
= 0;
257 VOID
SpeakerInitialize(VOID
)
260 UNICODE_STRING BeepDevice
;
261 OBJECT_ATTRIBUTES ObjectAttributes
;
262 IO_STATUS_BLOCK IoStatusBlock
;
264 /* Retrieve the performance frequency and initialize the timer ticks */
265 NtQueryPerformanceCounter(&CountStart
, &FreqCount
);
266 if (FreqCount
.QuadPart
== 0)
268 wprintf(L
"FATAL: Performance counter not available\n");
271 /* Open the BEEP device */
272 RtlInitUnicodeString(&BeepDevice
, L
"\\Device\\Beep");
273 InitializeObjectAttributes(&ObjectAttributes
, &BeepDevice
, 0, NULL
, NULL
);
274 Status
= NtCreateFile(&hBeep
,
275 FILE_READ_DATA
| FILE_WRITE_DATA
,
280 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
285 if (!NT_SUCCESS(Status
))
287 DPRINT1("Failed to open the Beep driver, Status 0x%08lx\n", Status
);
288 // hBeep = INVALID_HANDLE_VALUE;
292 VOID
SpeakerCleanup(VOID
)