ReactOS 0.4.15-dev-7906-g1b85a5f
speaker.c
Go to the documentation of this file.
1/*
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)
7 */
8
9/* INCLUDES *******************************************************************/
10
11#include "ntvdm.h"
12
13#define NDEBUG
14#include <debug.h>
15
16#include "speaker.h"
17#include "hardware/pit.h"
18
19/* Extra PSDK/NDK Headers */
20#include <ndk/iofuncs.h>
21#include <ndk/obfuncs.h>
22
23/* DDK Driver Headers */
24#include <ntddbeep.h>
25
26/* PRIVATE VARIABLES **********************************************************/
27
28static HANDLE hBeep = NULL;
29
32
33#define SPEAKER_RESPONSE 200 // in milliseconds
34
35#define MIN_AUDIBLE_FREQ 20 // BEEP_FREQUENCY_MINIMUM
36#define MAX_AUDIBLE_FREQ 20000 // BEEP_FREQUENCY_MAXIMUM
37#define CLICK_FREQ 100
38
39
40/* PRIVATE FUNCTIONS **********************************************************/
41
42static
43VOID
45 ULONG Duration)
46{
47 static ULONG LastFrequency = 0, LastDuration = 0;
48
50 BEEP_SET_PARAMETERS BeepSetParameters;
51
52 /* A null frequency means we stop beeping */
53 if (Frequency == 0) Duration = 0;
54
55 /*
56 * Do nothing if we are replaying exactly the same sound
57 * (this avoids hiccups due to redoing the same beeps).
58 */
59 if (Frequency == LastFrequency && Duration == LastDuration) return;
60
61 /*
62 * For small durations we automatically reset the beep so
63 * that we can replay short beeps like clicks immediately.
64 */
65 if (Duration < 10)
66 {
67 LastFrequency = 0;
68 LastDuration = 0;
69 }
70 else
71 {
72 LastFrequency = Frequency;
73 LastDuration = Duration;
74 }
75
76 /* Set the data and do the beep */
77 BeepSetParameters.Frequency = Frequency;
78 BeepSetParameters.Duration = Duration;
79
81 NULL,
82 NULL,
83 NULL,
86 &BeepSetParameters,
87 sizeof(BeepSetParameters),
88 NULL,
89 0);
90}
91
92static
94{
95 static ULONG Pulses = 0, CountStartTick = 0, LastPulsesFreq = 0;
96 ULONG LastPulseTickCount, CurrPulsesFreq;
98 LONGLONG Elapsed;
99
100 /*
101 * Check how far away was the previous pulse and
102 * if it was >= 200ms away then restart counting.
103 */
104 LastPulseTickCount = PulseTickCount;
106 if (PulseTickCount - LastPulseTickCount >= SPEAKER_RESPONSE)
107 {
109 Pulses = 0;
110 FreqPulses = 0;
111 return;
112 }
113
114 /* We have closely spaced pulses. Start counting. */
115 if (CountStart.QuadPart == 0)
116 {
118 CountStartTick = PulseTickCount;
119 Pulses = 0;
120 FreqPulses = 0;
121 return;
122 }
123
124 /* A pulse is ongoing */
125 ++Pulses;
126
127 /* We require some pulses to have some statistics */
128 if (PulseTickCount - CountStartTick <= (SPEAKER_RESPONSE >> 1)) return;
129
130 /* Get count time */
132
133 /*
134 * Get the number of speaker hundreds of microseconds that have passed
135 * since we started counting.
136 */
137 Elapsed = (Counter.QuadPart - CountStart.QuadPart) * 10000 / FreqCount.QuadPart;
138 if (Elapsed == 0) ++Elapsed;
139
140 /* Update counting for next pulses */
142 CountStartTick = PulseTickCount;
143
144 // HACKHACK!! I need to check why we need to double the number
145 // of pulses in order to have the correct frequency...
146 Pulses <<= 1;
147
148 /* Get the current pulses frequency */
149 CurrPulsesFreq = 10000 * Pulses / Elapsed;
150
151 /* Round the current pulses frequency up and align */
152 if ((CurrPulsesFreq & 0x0F) > 7) CurrPulsesFreq += 0x10;
153 CurrPulsesFreq &= ~0x0F;
154
155 /* Reinitialize frequency counters if necessary */
156 if (LastPulsesFreq == 0) LastPulsesFreq = CurrPulsesFreq;
157 if (FreqPulses == 0) FreqPulses = LastPulsesFreq;
158
159 /* Fix up the current pulses frequency if needed */
160 if (LastPulsesFreq != 0 && CurrPulsesFreq == 0)
161 CurrPulsesFreq = LastPulsesFreq;
162
163 /*
164 * Magic begins there...
165 */
166#define UABS(x) (ULONG)((LONG)(x) < 0 ? -(LONG)(x) : (x))
167 if (UABS(CurrPulsesFreq - LastPulsesFreq) > 7)
168 {
169 /*
170 * This can be a "large" fluctuation so ignore it for now, but take
171 * it into account if it happens to be a real frequency change.
172 */
173 CurrPulsesFreq = (CurrPulsesFreq + LastPulsesFreq) >> 1;
174 }
175 else
176 {
177 // FreqPulses = ((FreqPulses << 2) + LastPulsesFreq + CurrPulsesFreq) / 6;
178 FreqPulses = ((FreqPulses << 1) + LastPulsesFreq + CurrPulsesFreq) >> 2;
179 }
180
181 /* Round the pulses frequency up and align */
182 if ((FreqPulses & 0x0F) > 7) FreqPulses += 0x10;
183 FreqPulses &= ~0x0F;
184
185 DPRINT("FreqPulses = %d, LastPulsesFreq = %d, CurrPulsesFreq = %d, Pulses = %d, Elapsed = %d\n",
186 FreqPulses, LastPulsesFreq, CurrPulsesFreq, Pulses, Elapsed);
187
188 LastPulsesFreq = CurrPulsesFreq;
189 Pulses = 0;
190}
191
192
193/* PUBLIC FUNCTIONS ***********************************************************/
194
195// SpeakerPulse
197{
198 static BOOLEAN OldSpeakerOff = TRUE;
199
200 BOOLEAN Timer2Gate = !!(Port61hValue & 0x01);
201 BOOLEAN SpeakerOn = !!(Port61hValue & 0x02);
202
203 DPRINT("SpeakerChange -- Timer2Gate == %s ; SpeakerOn == %s\n",
204 Timer2Gate ? "true" : "false", SpeakerOn ? "true" : "false");
205
206 if (Timer2Gate)
207 {
208 if (SpeakerOn)
209 {
210 /* Start beeping */
213 Frequency = 0;
214
216 }
217 else
218 {
219 /* Stop beeping */
220 MakeBeep(0, 0);
221 }
222 }
223 else
224 {
225 if (SpeakerOn)
226 {
227 if (OldSpeakerOff)
228 {
229 OldSpeakerOff = FALSE;
230 PulseSample();
231 }
232
235 else if (CountStart.QuadPart != 0)
236 MakeBeep(CLICK_FREQ, 1); /* Click */
237 else
238 MakeBeep(0, 0); /* Stop beeping */
239 }
240 else
241 {
242 OldSpeakerOff = TRUE;
243
244 /*
245 * Check how far away was the previous pulse and if
246 * it was >= (200 + eps) ms away then stop beeping.
247 */
249 {
251 FreqPulses = 0;
252
253 /* Stop beeping */
254 MakeBeep(0, 0);
255 }
256 }
257 }
258}
259
261{
263 UNICODE_STRING BeepDevice;
266
267 /* Retrieve the performance frequency and initialize the timer ticks */
269 if (FreqCount.QuadPart == 0)
270 {
271 wprintf(L"FATAL: Performance counter not available\n");
272 }
273
274 /* Open the BEEP device */
275 RtlInitUnicodeString(&BeepDevice, L"\\Device\\Beep");
281 NULL,
282 0,
285 0,
286 NULL,
287 0);
288 if (!NT_SUCCESS(Status))
289 {
290 DPRINT1("Failed to open the Beep driver, Status 0x%08lx\n", Status);
291 // hBeep = INVALID_HANDLE_VALUE;
292 }
293}
294
296{
297 NtClose(hBeep);
298}
299
300/* EOF */
unsigned char BOOLEAN
LONG NTSTATUS
Definition: precomp.h:26
#define DPRINT1
Definition: precomp.h:8
IN PUNICODE_STRING IN POBJECT_ATTRIBUTES ObjectAttributes
Definition: conport.c:36
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
#define FILE_SHARE_READ
Definition: compat.h:136
DWORD WINAPI GetTickCount(VOID)
Definition: time.c:455
#define INFINITE
Definition: serial.h:102
#define FILE_OPEN_IF
Definition: from_kernel.h:56
Status
Definition: gdiplustypes.h:25
if(dx< 0)
Definition: linetemp.h:194
static OUT PIO_STATUS_BLOCK IoStatusBlock
Definition: pipe.c:75
#define InitializeObjectAttributes(p, n, a, r, s)
Definition: reg.c:106
#define FILE_SHARE_WRITE
Definition: nt_native.h:681
#define FILE_WRITE_DATA
Definition: nt_native.h:631
#define FILE_READ_DATA
Definition: nt_native.h:628
NTSYSAPI VOID NTAPI RtlInitUnicodeString(PUNICODE_STRING DestinationString, PCWSTR SourceString)
NTSYSAPI NTSTATUS NTAPI NtDeviceIoControlFile(IN HANDLE hFile, IN HANDLE hEvent OPTIONAL, IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL, IN PVOID IoApcContext OPTIONAL, OUT PIO_STATUS_BLOCK pIoStatusBlock, IN ULONG DeviceIoControlCode, IN PVOID InBuffer OPTIONAL, IN ULONG InBufferLength, OUT PVOID OutBuffer OPTIONAL, IN ULONG OutBufferLength)
NTSTATUS NTAPI NtClose(IN HANDLE Handle)
Definition: obhandle.c:3402
NTSTATUS NTAPI NtCreateFile(OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PLARGE_INTEGER AllocationSize OPTIONAL, IN ULONG FileAttributes, IN ULONG ShareAccess, IN ULONG CreateDisposition, IN ULONG CreateOptions, IN PVOID EaBuffer OPTIONAL, IN ULONG EaLength)
#define IOCTL_BEEP_SET
Definition: ntddbeep.h:31
NTSTATUS NTAPI NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter, OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
Definition: profile.c:272
#define L(x)
Definition: ntvdm.h:50
WORD PitGetReloadValue(BYTE Channel)
Definition: pit.c:493
#define DPRINT
Definition: sndvol32.h:71
static LARGE_INTEGER Frequency
Definition: clock.c:41
static LARGE_INTEGER Counter
Definition: clock.c:43
#define PIT_BASE_FREQUENCY
Definition: pit.h:17
static LARGE_INTEGER FreqCount
Definition: speaker.c:30
#define SPEAKER_RESPONSE
Definition: speaker.c:33
#define MAX_AUDIBLE_FREQ
Definition: speaker.c:36
#define MIN_AUDIBLE_FREQ
Definition: speaker.c:35
static VOID MakeBeep(ULONG Frequency, ULONG Duration)
Definition: speaker.c:44
static ULONG FreqPulses
Definition: speaker.c:31
#define CLICK_FREQ
Definition: speaker.c:37
VOID SpeakerChange(UCHAR Port61hValue)
Definition: speaker.c:196
static VOID PulseSample(VOID)
Definition: speaker.c:93
static LARGE_INTEGER CountStart
Definition: speaker.c:30
static ULONG PulseTickCount
Definition: speaker.c:31
VOID SpeakerCleanup(VOID)
Definition: speaker.c:295
static HANDLE hBeep
Definition: speaker.c:28
VOID SpeakerInitialize(VOID)
Definition: speaker.c:260
#define UABS(x)
int64_t LONGLONG
Definition: typedefs.h:68
uint32_t ULONG
Definition: typedefs.h:59
LONGLONG QuadPart
Definition: typedefs.h:114
#define wprintf(...)
Definition: whoami.c:18
unsigned char UCHAR
Definition: xmlstorage.h:181