ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/SID_WIN32.h
Revision: 1.2
Committed: 2003-07-01T17:51:17Z (20 years, 9 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.1: +1 -1 lines
Log Message:
updated copyright date

File Contents

# Content
1 /*
2 * SID_WIN32.h - 6581 emulation, WIN32 specific stuff
3 *
4 * Frodo (C) 1994-1997,2002-2003 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include <dsound.h>
22
23 #include "VIC.h"
24 #include "main.h"
25
26 #define FRAG_FREQ SCREEN_FREQ // one frag per frame
27
28 #define FRAGMENT_SIZE (SAMPLE_FREQ/FRAG_FREQ) // samples, not bytes
29 #define FRAG_INTERVAL (1000/FRAG_FREQ) // in milliseconds
30 #define BUFFER_FRAGS FRAG_FREQ // frags the in buffer
31 #define BUFFER_SIZE (2*FRAGMENT_SIZE*BUFFER_FRAGS) // bytes, not samples
32 #define MAX_LEAD_AVG BUFFER_FRAGS // lead average count
33
34 // This won't hurt DirectX 2 but it will help when using the DirectX 3 runtime.
35 #if !defined(DSBCAPS_GETCURRENTPOSITION2)
36 #define DSBCAPS_GETCURRENTPOSITION2 0x00010000
37 #endif
38
39 class DigitalPlayer {
40
41 public:
42 virtual ~DigitalPlayer() = 0;
43 virtual BOOL Ready() = 0;
44 virtual int GetCurrentPosition() = 0;
45 virtual void Write(void *buffer, int position, int length) = 0;
46 virtual void Pause() = 0;
47 virtual void Resume() = 0;
48 };
49
50 DigitalPlayer::~DigitalPlayer()
51 {
52 }
53
54 class DirectSound: public DigitalPlayer {
55
56 public:
57 DirectSound();
58 ~DirectSound();
59 BOOL Ready();
60 int GetCurrentPosition();
61 void Write(void *buffer, int position, int length);
62 void Pause();
63 void Resume();
64
65 private:
66 BOOL ready;
67 LPDIRECTSOUND pDS;
68 LPDIRECTSOUNDBUFFER pPrimaryBuffer;
69 LPDIRECTSOUNDBUFFER pSoundBuffer;
70 };
71
72 class WaveOut: public DigitalPlayer {
73
74 public:
75 WaveOut();
76 ~WaveOut();
77 BOOL Ready();
78 int GetCurrentPosition();
79 void Write(void *buffer, int position, int length);
80 void Pause();
81 void Resume();
82
83 private:
84 void UnprepareHeader(int index);
85 void UnprepareHeaders();
86
87 private:
88 BOOL ready;
89 HWAVEOUT hWaveOut;
90 char wave_buffer[BUFFER_SIZE];
91 WAVEHDR wave_header[SCREEN_FREQ];
92 int last_unprepared;
93 };
94
95 void DigitalRenderer::init_sound()
96 {
97 ready = FALSE;
98 sound_buffer = new SWORD[2*FRAGMENT_SIZE];
99 ThePlayer = 0;
100 to_output = 0;
101 divisor = 0;
102 lead = new int[MAX_LEAD_AVG];
103
104 StartPlayer();
105 }
106
107 DigitalRenderer::~DigitalRenderer()
108 {
109 StopPlayer();
110
111 delete[] sound_buffer;
112 delete[] lead;
113 }
114
115 void DigitalRenderer::StartPlayer()
116 {
117 direct_sound = ThePrefs.DirectSound;
118 if (ThePrefs.DirectSound)
119 ThePlayer = new DirectSound;
120 else
121 ThePlayer = new WaveOut;
122 ready = ThePlayer->Ready();
123 sb_pos = 0;
124 memset(lead, 0, sizeof(lead));
125 lead_pos = 0;
126 }
127
128 void DigitalRenderer::StopPlayer()
129 {
130 delete ThePlayer;
131 ready = FALSE;
132 }
133
134 void DigitalRenderer::EmulateLine()
135 {
136 if (!ready)
137 return;
138
139 sample_buf[sample_in_ptr] = volume;
140 sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE;
141
142 #if 0
143 // Now see how many samples have to be added for this line.
144 // XXX: This is too much computation here, precompute it.
145 divisor += SAMPLE_FREQ;
146 while (divisor >= 0)
147 divisor -= TOTAL_RASTERS*SCREEN_FREQ, to_output++;
148
149 // Calculate the sound data only when we have enough to fill
150 // the buffer entirely.
151 if (to_output < FRAGMENT_SIZE)
152 return;
153 to_output -= FRAGMENT_SIZE;
154
155 VBlank();
156 #endif
157 }
158
159 void DigitalRenderer::VBlank()
160 {
161 if (!ready)
162 return;
163
164 // Delete and recreate the player if preferences have changed.
165 if (direct_sound != ThePrefs.DirectSound) {
166 StopPlayer();
167 StartPlayer();
168 }
169
170 // Convert latency preferences from milliseconds to frags.
171 int lead_smooth = ThePrefs.LatencyAvg/FRAG_INTERVAL;
172 int lead_hiwater = ThePrefs.LatencyMax/FRAG_INTERVAL;
173 int lead_lowater = ThePrefs.LatencyMin/FRAG_INTERVAL;
174
175 // Compute the current lead in frags.
176 int current_position = ThePlayer->GetCurrentPosition();
177 if (current_position == -1)
178 return;
179 int lead_in_bytes = (sb_pos - current_position + BUFFER_SIZE) % BUFFER_SIZE;
180 if (lead_in_bytes >= BUFFER_SIZE/2)
181 lead_in_bytes -= BUFFER_SIZE;
182 int lead_in_frags = lead_in_bytes / int(2*FRAGMENT_SIZE);
183 lead[lead_pos++] = lead_in_frags;
184 if (lead_pos == lead_smooth)
185 lead_pos = 0;
186
187 // Compute the average lead in frags.
188 int avg_lead = 0;
189 for (int i = 0; i < lead_smooth; i++)
190 avg_lead += lead[i];
191 avg_lead /= lead_smooth;
192 //Debug("lead = %d, avg = %d\n", lead_in_frags, avg_lead);
193
194 // If we're getting too far ahead of the audio skip a frag.
195 if (avg_lead > lead_hiwater) {
196 for (int i = 0; i < lead_smooth; i++)
197 lead[i]--;
198 Debug("Skipping a frag...\n");
199 return;
200 }
201
202 // Calculate one frag.
203 int nsamples = FRAGMENT_SIZE;
204 calc_buffer(sound_buffer, 2*FRAGMENT_SIZE);
205
206 // If we're getting too far behind the audio add an extra frag.
207 if (avg_lead < lead_lowater) {
208 for (int i = 0; i < lead_smooth; i++)
209 lead[i]++;
210 Debug("Adding an extra frag...\n");
211 calc_buffer(sound_buffer + FRAGMENT_SIZE, 2*FRAGMENT_SIZE);
212 nsamples += FRAGMENT_SIZE;
213 }
214
215 // Write the frags to the player and update out write position.
216 ThePlayer->Write(sound_buffer, sb_pos, 2*nsamples);
217 sb_pos = (sb_pos + 2*nsamples) % BUFFER_SIZE;
218 }
219
220 void DigitalRenderer::Pause()
221 {
222 if (!ready)
223 return;
224 ThePlayer->Pause();
225 }
226
227 void DigitalRenderer::Resume()
228 {
229 if (!ready)
230 return;
231 ThePlayer->Resume();
232 }
233
234 // Direct sound implemenation.
235
236 DirectSound::DirectSound()
237 {
238 ready = FALSE;
239 pDS = NULL;
240 pPrimaryBuffer = NULL;
241 pSoundBuffer = NULL;
242
243 HRESULT dsrval = DirectSoundCreate(NULL, &pDS, NULL);
244 if (dsrval != DS_OK) {
245 DebugResult("DirectSoundCreate failed", dsrval);
246 return;
247 }
248
249 // Set cooperative level trying to get exclusive or normal mode.
250 DWORD level = ThePrefs.ExclusiveSound ? DSSCL_EXCLUSIVE : DSSCL_NORMAL;
251 dsrval = pDS->SetCooperativeLevel(hwnd, level);
252 if (dsrval != DS_OK) {
253 DebugResult("SetCooperativeLevel failed", dsrval);
254 return;
255 }
256
257 WAVEFORMATEX wfx;
258 memset(&wfx, 0, sizeof(wfx));
259 wfx.wFormatTag = WAVE_FORMAT_PCM;
260 wfx.nChannels = 1;
261 wfx.nSamplesPerSec = 44100;
262 wfx.wBitsPerSample = 16;
263 wfx.nBlockAlign = wfx.nChannels*wfx.wBitsPerSample/8;
264 wfx.nAvgBytesPerSec = wfx.nBlockAlign*wfx.nSamplesPerSec;
265 wfx.cbSize = 0;
266
267 DSBUFFERDESC dsbd;
268 memset(&dsbd, 0, sizeof(dsbd));
269 dsbd.dwSize = sizeof(dsbd);
270 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
271 dsbd.dwBufferBytes = 0;
272 dsbd.lpwfxFormat = NULL;
273
274 dsrval = pDS->CreateSoundBuffer(&dsbd, &pPrimaryBuffer, NULL);
275 if (dsrval != DS_OK) {
276 DebugResult("CreateSoundBuffer for primary failed", dsrval);
277 return;
278 }
279
280 dsrval = pPrimaryBuffer->SetFormat(&wfx);
281 if (dsrval != DS_OK) {
282 DebugResult("SetFormat on primary failed", dsrval);
283 //return;
284 }
285
286 dsrval = pPrimaryBuffer->Play(0, 0, DSBPLAY_LOOPING);
287 if (dsrval != DS_OK) {
288 DebugResult("Play primary failed", dsrval);
289 return;
290 }
291
292 dsbd.dwSize = sizeof(dsbd);
293 dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
294 dsbd.dwBufferBytes = BUFFER_SIZE;
295 dsbd.lpwfxFormat = &wfx;
296
297 dsrval = pDS->CreateSoundBuffer(&dsbd, &pSoundBuffer, NULL);
298 if (dsrval != DS_OK) {
299 DebugResult("CreateSoundBuffer failed", dsrval);
300 return;
301 }
302
303 ready = TRUE;
304 }
305
306 DirectSound::~DirectSound()
307 {
308 if (pDS != NULL) {
309 if (pSoundBuffer != NULL) {
310 pSoundBuffer->Release();
311 pSoundBuffer = NULL;
312 }
313 if (pPrimaryBuffer != NULL) {
314 pPrimaryBuffer->Release();
315 pPrimaryBuffer = NULL;
316 }
317 pDS->Release();
318 pDS = NULL;
319 }
320 }
321
322 BOOL DirectSound::Ready()
323 {
324 return ready;
325 }
326
327 int DirectSound::GetCurrentPosition()
328 {
329 DWORD dwPlayPos, dwWritePos;
330 HRESULT dsrval = pSoundBuffer->GetCurrentPosition(&dwPlayPos, &dwWritePos);
331 if (dsrval != DS_OK) {
332 DebugResult("GetCurrentPostion failed", dsrval);
333 return -1;
334 }
335 return dwWritePos;
336 }
337
338 void DirectSound::Write(void *buffer, int position, int length)
339 {
340 // Lock sound buffer.
341 LPVOID pMem1, pMem2;
342 DWORD dwSize1, dwSize2;
343 HRESULT dsrval = pSoundBuffer->Lock(position, length,
344 &pMem1, &dwSize1, &pMem2, &dwSize2, 0);
345 if (dsrval != DS_OK) {
346 DebugResult("Sound Lock failed", dsrval);
347 return;
348 }
349
350 // Copy the sample buffer into the sound buffer.
351 BYTE *pSample = (BYTE *) buffer;
352 memcpy(pMem1, pSample, dwSize1);
353 if (dwSize2 != 0)
354 memcpy(pMem2, pSample + dwSize1, dwSize2);
355
356 // Unlock the sound buffer.
357 dsrval = pSoundBuffer->Unlock(pMem1, dwSize1, pMem2, dwSize2);
358 if (dsrval != DS_OK) {
359 DebugResult("Unlock failed\n", dsrval);
360 return;
361 }
362
363 // Play the sound buffer.
364 dsrval = pSoundBuffer->Play(0, 0, DSBPLAY_LOOPING);
365 if (dsrval != DS_OK) {
366 DebugResult("Play failed", dsrval);
367 return;
368 }
369 }
370
371 void DirectSound::Pause()
372 {
373 HRESULT dsrval = pSoundBuffer->Stop();
374 if (dsrval != DS_OK)
375 DebugResult("Stop failed", dsrval);
376 dsrval = pPrimaryBuffer->Stop();
377 if (dsrval != DS_OK)
378 DebugResult("Stop primary failed", dsrval);
379 }
380
381 void DirectSound::Resume()
382 {
383 HRESULT dsrval = pPrimaryBuffer->Play(0, 0, DSBPLAY_LOOPING);
384 if (dsrval != DS_OK)
385 DebugResult("Play primary failed", dsrval);
386 dsrval = pSoundBuffer->Play(0, 0, DSBPLAY_LOOPING);
387 if (dsrval != DS_OK)
388 DebugResult("Play failed", dsrval);
389 }
390
391 // Wave output implemenation.
392
393 WaveOut::WaveOut()
394 {
395 ready = FALSE;
396
397 WAVEFORMATEX wfx;
398 memset(&wfx, 0, sizeof(wfx));
399 wfx.wFormatTag = WAVE_FORMAT_PCM;
400 wfx.nChannels = 1;
401 wfx.nSamplesPerSec = 44100;
402 wfx.wBitsPerSample = 16;
403 wfx.nBlockAlign = wfx.nChannels*wfx.wBitsPerSample/8;
404 wfx.nAvgBytesPerSec = wfx.nBlockAlign*wfx.nSamplesPerSec;
405 wfx.cbSize = 0;
406 MMRESULT worval = waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, 0);
407 if (worval != MMSYSERR_NOERROR) {
408 Debug("waveOutOpen returned %d\n", worval);
409 return;
410 }
411 memset(wave_header, 0, sizeof(wave_header));
412
413 last_unprepared = 0;
414
415 ready = TRUE;
416 }
417
418 WaveOut::~WaveOut()
419 {
420 waveOutReset(hWaveOut);
421 UnprepareHeaders();
422 waveOutClose(hWaveOut);
423 }
424
425 BOOL WaveOut::Ready()
426 {
427 return ready;
428 }
429
430 int WaveOut::GetCurrentPosition()
431 {
432 MMTIME mmtime;
433 memset(&mmtime, 0, sizeof(mmtime));
434 mmtime.wType = TIME_BYTES;
435 MMRESULT worval = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime));
436 if (worval != MMSYSERR_NOERROR) {
437 Debug("waveOutGetPosition(%d) returned %d\n", worval);
438 return -1;
439 }
440 int current_position = mmtime.u.cb % BUFFER_SIZE;
441 return current_position;
442 }
443
444 void WaveOut::Write(void *buffer, int position, int length)
445 {
446 // If we are called for a double length buffer split it in half.
447 if (length == 4*FRAGMENT_SIZE) {
448 int half = length/2;
449 Write(buffer, position, half);
450 Write(((char *) buffer) + half, position + half, half);
451 return;
452 }
453
454 // Free up as many previous frags as possible.
455 for (;;) {
456 WAVEHDR &wh = wave_header[last_unprepared];
457 if (!(wh.dwFlags & WHDR_DONE))
458 break;
459 UnprepareHeader(last_unprepared);
460 last_unprepared++;
461 if (last_unprepared == BUFFER_FRAGS)
462 last_unprepared = 0;
463 }
464
465 // Make sure the current header isn't in use.
466 int index = position/(2*FRAGMENT_SIZE);
467 WAVEHDR &wh = wave_header[index];
468 if (wh.dwFlags & WHDR_DONE)
469 UnprepareHeader(index);
470 if (wh.dwFlags != 0) {
471 Debug("wave header %d is in use!\n", index);
472 return;
473 }
474
475 // Prepare the header and write the sound data.
476 wh.lpData = wave_buffer + position;
477 wh.dwBufferLength = length;
478 wh.dwFlags = 0;
479 memcpy(wh.lpData, buffer, length);
480 MMRESULT worval = waveOutPrepareHeader(hWaveOut, &wh, sizeof(wh));
481 if (worval != MMSYSERR_NOERROR) {
482 Debug("waveOutPrepareHeader(%d) returned %d\n", index, worval);
483 return;
484 }
485 worval = waveOutWrite(hWaveOut, &wh, sizeof(wh));
486 if (worval != MMSYSERR_NOERROR) {
487 Debug("waveOutWrite(%d) returned %d\n", index, worval);
488 return;
489 }
490 }
491
492 void WaveOut::Pause()
493 {
494 waveOutPause(hWaveOut);
495 }
496
497 void WaveOut::Resume()
498 {
499 waveOutRestart(hWaveOut);
500 }
501
502 void WaveOut::UnprepareHeader(int index)
503 {
504 WAVEHDR &wh = wave_header[index];
505 MMRESULT worval = waveOutUnprepareHeader(hWaveOut, &wh, sizeof(wh));
506 if (worval != MMSYSERR_NOERROR) {
507 Debug("waveOutUnprepareHeader(%d) returned %d\n", index, worval);
508 return;
509 }
510 memset(&wh, 0, sizeof(wh));
511 }
512
513 void WaveOut::UnprepareHeaders()
514 {
515 for (int i = 0; i < BUFFER_FRAGS; i++) {
516 WAVEHDR &wh = wave_header[i];
517 if (wh.dwFlags & WHDR_DONE)
518 UnprepareHeader(i);
519 }
520 }
521
522 #if 0
523
524 // Log player implemenation.
525
526 void Log::Write()
527 {
528 // Dump the sound output to the log for debugging.
529 {
530 int last = sound_buffer[0];
531 int count = 1;
532 for (int i = 1; i < nsamples; i++) {
533 if (sound_buffer[i] == last) {
534 count++;
535 continue;
536 }
537 Debug("[%dx%d] ", count, last);
538 count = 1;
539 last = sound_buffer[i];
540 }
541 Debug("[%dx%d] ", count, last);
542 }
543 }
544
545 #endif
546
547 #if 0
548
549 // File player implemenation.
550
551 void Log::Write()
552 {
553 // Log the sound output to a file for debugging.
554 {
555 static FILE *ofp = NULL;
556 if (ofp == NULL) {
557 ofp = fopen("debug.sid", "wb");
558 if (ofp == NULL)
559 Debug("fopen failed\n");
560 }
561 if (ofp != NULL) {
562 Debug("Write sound data to file\n");
563 if (fwrite(sound_buffer, 1, 2*nsamples, ofp) != 2*nsamples)
564 Debug("fwrite failed\n");
565 }
566 }
567 }
568
569 #endif