2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/hardware/sound/speaker.c
5 * PURPOSE: PC Speaker emulation
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9 /* INCLUDES *******************************************************************/
17 #include "hardware/pit.h"
19 /* Extra PSDK/NDK Headers */
20 #include <ndk/iofuncs.h>
22 /* DDK Driver Headers */
25 /* PRIVATE VARIABLES **********************************************************/
27 static HANDLE hBeep
= NULL
;
29 static LARGE_INTEGER FreqCount
, CountStart
;
30 static ULONG PulseTickCount
= 0, FreqPulses
= 0;
32 #define SPEAKER_RESPONSE 200 // in milliseconds
34 #define MIN_AUDIBLE_FREQ 20 // BEEP_FREQUENCY_MINIMUM
35 #define MAX_AUDIBLE_FREQ 20000 // BEEP_FREQUENCY_MAXIMUM
36 #define CLICK_FREQ 100
39 /* PRIVATE FUNCTIONS **********************************************************/
43 MakeBeep(ULONG Frequency
,
46 static ULONG LastFrequency
= 0, LastDuration
= 0;
48 IO_STATUS_BLOCK IoStatusBlock
;
49 BEEP_SET_PARAMETERS BeepSetParameters
;
51 /* A null frequency means we stop beeping */
52 if (Frequency
== 0) Duration
= 0;
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;
61 * For small durations we automatically reset the beep so
62 * that we can replay short beeps like clicks immediately.
71 LastFrequency
= Frequency
;
72 LastDuration
= Duration
;
75 /* Set the data and do the beep */
76 BeepSetParameters
.Frequency
= Frequency
;
77 BeepSetParameters
.Duration
= Duration
;
79 NtDeviceIoControlFile(hBeep
,
86 sizeof(BeepSetParameters
),
92 VOID
PulseSample(VOID
)
94 static ULONG Pulses
= 0, CountStartTick
= 0, LastPulsesFreq
= 0;
95 ULONG LastPulseTickCount
, CurrPulsesFreq
;
96 LARGE_INTEGER Counter
;
100 * Check how far away was the previous pulse and
101 * if it was >= 200ms away then restart counting.
103 LastPulseTickCount
= PulseTickCount
;
104 PulseTickCount
= GetTickCount();
105 if (PulseTickCount
- LastPulseTickCount
>= SPEAKER_RESPONSE
)
107 CountStart
.QuadPart
= 0;
113 /* We have closely spaced pulses. Start counting. */
114 if (CountStart
.QuadPart
== 0)
116 NtQueryPerformanceCounter(&CountStart
, NULL
);
117 CountStartTick
= PulseTickCount
;
123 /* A pulse is ongoing */
126 /* We require some pulses to have some statistics */
127 if (PulseTickCount
- CountStartTick
<= (SPEAKER_RESPONSE
>> 1)) return;
130 NtQueryPerformanceCounter(&Counter
, NULL
);
133 * Get the number of speaker hundreds of microseconds that have passed
134 * since we started counting.
136 Elapsed
= (Counter
.QuadPart
- CountStart
.QuadPart
) * 10000 / FreqCount
.QuadPart
;
137 if (Elapsed
== 0) ++Elapsed
;
139 /* Update counting for next pulses */
140 CountStart
= Counter
;
141 CountStartTick
= PulseTickCount
;
143 // HACKHACK!! I need to check why we need to double the number
144 // of pulses in order to have the correct frequency...
147 /* Get the current pulses frequency */
148 CurrPulsesFreq
= 10000 * Pulses
/ Elapsed
;
150 /* Round the current pulses frequency up and align */
151 if ((CurrPulsesFreq
& 0x0F) > 7) CurrPulsesFreq
+= 0x10;
152 CurrPulsesFreq
&= ~0x0F;
154 /* Reinitialize frequency counters if necessary */
155 if (LastPulsesFreq
== 0) LastPulsesFreq
= CurrPulsesFreq
;
156 if (FreqPulses
== 0) FreqPulses
= LastPulsesFreq
;
158 /* Fix up the current pulses frequency if needed */
159 if (LastPulsesFreq
!= 0 && CurrPulsesFreq
== 0)
160 CurrPulsesFreq
= LastPulsesFreq
;
163 * Magic begins there...
165 #define UABS(x) (ULONG)((LONG)(x) < 0 ? -(LONG)(x) : (x))
166 if (UABS(CurrPulsesFreq
- LastPulsesFreq
) > 7)
169 * This can be a "large" fluctuation so ignore it for now, but take
170 * it into account if it happens to be a real frequency change.
172 CurrPulsesFreq
= (CurrPulsesFreq
+ LastPulsesFreq
) >> 1;
176 // FreqPulses = ((FreqPulses << 2) + LastPulsesFreq + CurrPulsesFreq) / 6;
177 FreqPulses
= ((FreqPulses
<< 1) + LastPulsesFreq
+ CurrPulsesFreq
) >> 2;
180 /* Round the pulses frequency up and align */
181 if ((FreqPulses
& 0x0F) > 7) FreqPulses
+= 0x10;
184 DPRINT("FreqPulses = %d, LastPulsesFreq = %d, CurrPulsesFreq = %d, Pulses = %d, Elapsed = %d\n",
185 FreqPulses
, LastPulsesFreq
, CurrPulsesFreq
, Pulses
, Elapsed
);
187 LastPulsesFreq
= CurrPulsesFreq
;
192 /* PUBLIC FUNCTIONS ***********************************************************/
195 VOID
SpeakerChange(UCHAR Port61hValue
)
197 static BOOLEAN OldSpeakerOff
= TRUE
;
199 BOOLEAN Timer2Gate
= !!(Port61hValue
& 0x01);
200 BOOLEAN SpeakerOn
= !!(Port61hValue
& 0x02);
202 DPRINT("SpeakerChange -- Timer2Gate == %s ; SpeakerOn == %s\n",
203 Timer2Gate
? "true" : "false", SpeakerOn
? "true" : "false");
210 ULONG Frequency
= (PIT_BASE_FREQUENCY
/ PitGetReloadValue(2));
211 if (Frequency
< MIN_AUDIBLE_FREQ
|| MAX_AUDIBLE_FREQ
< Frequency
)
214 MakeBeep(Frequency
, INFINITE
);
228 OldSpeakerOff
= FALSE
;
232 if (FreqPulses
>= MIN_AUDIBLE_FREQ
)
233 MakeBeep(FreqPulses
, INFINITE
);
234 else if (CountStart
.QuadPart
!= 0)
235 MakeBeep(CLICK_FREQ
, 1); /* Click */
237 MakeBeep(0, 0); /* Stop beeping */
241 OldSpeakerOff
= TRUE
;
244 * Check how far away was the previous pulse and if
245 * it was >= (200 + eps) ms away then stop beeping.
247 if (GetTickCount() - PulseTickCount
>= SPEAKER_RESPONSE
+ (SPEAKER_RESPONSE
>> 3))
249 CountStart
.QuadPart
= 0;
259 VOID
SpeakerInitialize(VOID
)
262 UNICODE_STRING BeepDevice
;
263 OBJECT_ATTRIBUTES ObjectAttributes
;
264 IO_STATUS_BLOCK IoStatusBlock
;
266 /* Retrieve the performance frequency and initialize the timer ticks */
267 NtQueryPerformanceCounter(&CountStart
, &FreqCount
);
268 if (FreqCount
.QuadPart
== 0)
270 wprintf(L
"FATAL: Performance counter not available\n");
273 /* Open the BEEP device */
274 RtlInitUnicodeString(&BeepDevice
, L
"\\Device\\Beep");
275 InitializeObjectAttributes(&ObjectAttributes
, &BeepDevice
, 0, NULL
, NULL
);
276 Status
= NtCreateFile(&hBeep
,
277 FILE_READ_DATA
| FILE_WRITE_DATA
,
282 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
287 if (!NT_SUCCESS(Status
))
289 DPRINT1("Failed to open the Beep driver, Status 0x%08lx\n", Status
);
290 // hBeep = INVALID_HANDLE_VALUE;
294 VOID
SpeakerCleanup(VOID
)