ReactOS  0.4.15-dev-3720-g4cf9b79
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 
28 static 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 
42 static
43 VOID
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 
92 static
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  {
108  CountStart.QuadPart = 0;
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
196 VOID SpeakerChange(UCHAR Port61hValue)
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  {
250  CountStart.QuadPart = 0;
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");
280  &IoStatusBlock,
281  NULL,
282  0,
284  FILE_OPEN_IF,
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 */
IN PUNICODE_STRING IN POBJECT_ATTRIBUTES ObjectAttributes
Definition: conport.c:35
static LARGE_INTEGER FreqCount
Definition: speaker.c:30
#define FILE_OPEN_IF
Definition: from_kernel.h:56
static VOID PulseSample(VOID)
Definition: speaker.c:93
#define TRUE
Definition: types.h:120
LONG NTSTATUS
Definition: precomp.h:26
DWORD WINAPI GetTickCount(VOID)
Definition: time.c:455
NTSTATUS NTAPI NtQueryPerformanceCounter(OUT PLARGE_INTEGER PerformanceCounter, OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
Definition: profile.c:272
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
if(dx==0 &&dy==0)
Definition: linetemp.h:174
#define FILE_SHARE_READ
Definition: compat.h:136
#define MIN_AUDIBLE_FREQ
Definition: speaker.c:35
#define L(x)
Definition: ntvdm.h:50
#define FALSE
Definition: types.h:117
VOID SpeakerChange(UCHAR Port61hValue)
Definition: speaker.c:196
#define FILE_READ_DATA
Definition: nt_native.h:628
unsigned char BOOLEAN
#define UABS(x)
#define FILE_WRITE_DATA
Definition: nt_native.h:631
#define SPEAKER_RESPONSE
Definition: speaker.c:33
Status
Definition: gdiplustypes.h:24
int64_t LONGLONG
Definition: typedefs.h:68
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
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
static ULONG FreqPulses
Definition: speaker.c:31
NTSTATUS NTAPI NtClose(IN HANDLE Handle)
Definition: obhandle.c:3401
unsigned char UCHAR
Definition: xmlstorage.h:181
#define CLICK_FREQ
Definition: speaker.c:37
static LARGE_INTEGER CountStart
Definition: speaker.c:30
static VOID MakeBeep(ULONG Frequency, ULONG Duration)
Definition: speaker.c:44
#define PIT_BASE_FREQUENCY
Definition: pit.h:17
static ULONG PulseTickCount
Definition: speaker.c:31
VOID SpeakerCleanup(VOID)
Definition: speaker.c:295
static OUT PIO_STATUS_BLOCK IoStatusBlock
Definition: pipe.c:75
#define NULL
Definition: types.h:112
#define MAX_AUDIBLE_FREQ
Definition: speaker.c:36
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:28
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:260
#define InitializeObjectAttributes(p, n, a, r, s)
Definition: reg.c:106
#define DPRINT
Definition: sndvol32.h:71
#define INFINITE
Definition: serial.h:102
LONGLONG QuadPart
Definition: typedefs.h:114