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 (19 years, 7 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.1: +1 -1 lines
Log Message:
updated copyright date

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * SID_WIN32.h - 6581 emulation, WIN32 specific stuff
3     *
4 cebix 1.2 * Frodo (C) 1994-1997,2002-2003 Christian Bauer
5 cebix 1.1 *
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