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