Main Page | Class Hierarchy | Compound List | File List | Compound Members | File Members | Related Pages

SID_WIN32.i

Go to the documentation of this file.
00001 /*
00002  *  SID_WIN32.i - 6581 emulation, WIN32 specific stuff
00003  *
00004  *  Frodo (C) 1994-1997 Christian Bauer
00005  *  WIN32 code by J. Richard Sladkey <jrs@world.std.com>
00006  */
00007 
00008 #include <dsound.h>
00009 
00010 #include "VIC.h"
00011 #include "main.h"
00012 
00013 #define FRAG_FREQ SCREEN_FREQ                           // one frag per frame
00014 
00015 #define FRAGMENT_SIZE (SAMPLE_FREQ/FRAG_FREQ)           // samples, not bytes
00016 #define FRAG_INTERVAL (1000/FRAG_FREQ)                  // in milliseconds
00017 #define BUFFER_FRAGS FRAG_FREQ                          // frags the in buffer
00018 #define BUFFER_SIZE  (2*FRAGMENT_SIZE*BUFFER_FRAGS)     // bytes, not samples
00019 #define MAX_LEAD_AVG BUFFER_FRAGS                       // lead average count
00020 
00021 // This won't hurt DirectX 2 but it will help when using the DirectX 3 runtime.
00022 #if !defined(DSBCAPS_GETCURRENTPOSITION2)
00023 #define DSBCAPS_GETCURRENTPOSITION2 0x00010000
00024 #endif
00025 
00026 class DigitalPlayer {
00027 
00028 public:
00029         virtual ~DigitalPlayer() = 0;
00030         virtual BOOL Ready() = 0;
00031         virtual int GetCurrentPosition() = 0;
00032         virtual void Write(void *buffer, int position, int length) = 0;
00033         virtual void Pause() = 0;
00034         virtual void Resume() = 0;
00035 };
00036 
00037 DigitalPlayer::~DigitalPlayer()
00038 {
00039 }
00040 
00041 class DirectSound: public DigitalPlayer {
00042 
00043 public:
00044         DirectSound();
00045         ~DirectSound();
00046         BOOL Ready();
00047         int GetCurrentPosition();
00048         void Write(void *buffer, int position, int length);
00049         void Pause();
00050         void Resume();
00051 
00052 private:
00053         BOOL ready;
00054         LPDIRECTSOUND pDS;
00055         LPDIRECTSOUNDBUFFER pPrimaryBuffer;
00056         LPDIRECTSOUNDBUFFER pSoundBuffer;
00057 };
00058 
00059 class WaveOut: public DigitalPlayer {
00060 
00061 public:
00062         WaveOut();
00063         ~WaveOut();
00064         BOOL Ready();
00065         int GetCurrentPosition();
00066         void Write(void *buffer, int position, int length);
00067         void Pause();
00068         void Resume();
00069 
00070 private:
00071         void UnprepareHeader(int index);
00072         void UnprepareHeaders();
00073 
00074 private:
00075         BOOL ready;
00076         HWAVEOUT hWaveOut;
00077         char wave_buffer[BUFFER_SIZE];
00078         WAVEHDR wave_header[SCREEN_FREQ];
00079         int last_unprepared;
00080 };
00081 
00082 void DigitalRenderer::init_sound()
00083 {
00084         ready = FALSE;
00085         sound_buffer = new SWORD[2*FRAGMENT_SIZE];
00086         ThePlayer = 0;
00087         to_output = 0;
00088         divisor = 0;
00089         lead = new int[MAX_LEAD_AVG];
00090 
00091         StartPlayer();
00092 }
00093 
00094 DigitalRenderer::~DigitalRenderer()
00095 {
00096         StopPlayer();
00097 
00098         delete[] sound_buffer;
00099         delete[] lead;
00100 }
00101 
00102 void DigitalRenderer::StartPlayer()
00103 {
00104         direct_sound = ThePrefs.DirectSound;
00105         if (ThePrefs.DirectSound)
00106                 ThePlayer = new DirectSound;
00107         else
00108                 ThePlayer = new WaveOut;
00109         ready = ThePlayer->Ready();
00110         sb_pos = 0;
00111         memset(lead, 0, sizeof(lead));
00112         lead_pos = 0;
00113 }
00114 
00115 void DigitalRenderer::StopPlayer()
00116 {
00117         delete ThePlayer;
00118         ready = FALSE;
00119 }
00120 
00121 void DigitalRenderer::EmulateLine()
00122 {
00123         if (!ready)
00124                 return;
00125 
00126         sample_buf[sample_in_ptr] = volume;
00127         sample_in_ptr = (sample_in_ptr + 1) % SAMPLE_BUF_SIZE;
00128 
00129 #if 0
00130         // Now see how many samples have to be added for this line.
00131         // XXX: This is too much computation here, precompute it.
00132         divisor += SAMPLE_FREQ;
00133         while (divisor >= 0)
00134                 divisor -= TOTAL_RASTERS*SCREEN_FREQ, to_output++;
00135 
00136         // Calculate the sound data only when we have enough to fill
00137         // the buffer entirely.
00138         if (to_output < FRAGMENT_SIZE)
00139                 return;
00140         to_output -= FRAGMENT_SIZE;
00141 
00142         VBlank();
00143 #endif
00144 }
00145 
00146 void DigitalRenderer::VBlank()
00147 {
00148         if (!ready)
00149                 return;
00150 
00151         // Delete and recreate the player if preferences have changed.
00152         if (direct_sound != ThePrefs.DirectSound) {
00153                 StopPlayer();
00154                 StartPlayer();
00155         }
00156 
00157         // Convert latency preferences from milliseconds to frags.
00158         int lead_smooth = ThePrefs.LatencyAvg/FRAG_INTERVAL;
00159         int lead_hiwater = ThePrefs.LatencyMax/FRAG_INTERVAL;
00160         int lead_lowater = ThePrefs.LatencyMin/FRAG_INTERVAL;
00161 
00162         // Compute the current lead in frags.
00163         int current_position = ThePlayer->GetCurrentPosition();
00164         if (current_position == -1)
00165                 return;
00166         int lead_in_bytes = (sb_pos - current_position + BUFFER_SIZE) % BUFFER_SIZE;
00167         if (lead_in_bytes >= BUFFER_SIZE/2)
00168                 lead_in_bytes -= BUFFER_SIZE;
00169         int lead_in_frags = lead_in_bytes / int(2*FRAGMENT_SIZE);
00170         lead[lead_pos++] = lead_in_frags;
00171         if (lead_pos == lead_smooth)
00172                 lead_pos = 0;
00173 
00174         // Compute the average lead in frags.
00175         int avg_lead = 0;
00176         for (int i = 0; i < lead_smooth; i++)
00177                 avg_lead += lead[i];
00178         avg_lead /= lead_smooth;
00179         //Debug("lead = %d, avg = %d\n", lead_in_frags, avg_lead);
00180 
00181         // If we're getting too far ahead of the audio skip a frag.
00182         if (avg_lead > lead_hiwater) {
00183                 for (int i = 0; i < lead_smooth; i++)
00184                         lead[i]--;
00185                 Debug("Skipping a frag...\n");
00186                 return;
00187         }
00188 
00189         // Calculate one frag.
00190         int nsamples = FRAGMENT_SIZE;
00191         calc_buffer(sound_buffer, 2*FRAGMENT_SIZE);
00192 
00193         // If we're getting too far behind the audio add an extra frag.
00194         if (avg_lead < lead_lowater) {
00195                 for (int i = 0; i < lead_smooth; i++)
00196                         lead[i]++;
00197                 Debug("Adding an extra frag...\n");
00198                 calc_buffer(sound_buffer + FRAGMENT_SIZE, 2*FRAGMENT_SIZE);
00199                 nsamples += FRAGMENT_SIZE;
00200         }
00201 
00202         // Write the frags to the player and update out write position. 
00203         ThePlayer->Write(sound_buffer, sb_pos, 2*nsamples);
00204         sb_pos = (sb_pos + 2*nsamples) % BUFFER_SIZE;
00205 }
00206 
00207 void DigitalRenderer::Pause()
00208 {
00209         if (!ready)
00210                 return;
00211         ThePlayer->Pause();
00212 }
00213 
00214 void DigitalRenderer::Resume()
00215 {
00216         if (!ready)
00217                 return;
00218         ThePlayer->Resume();
00219 }
00220 
00221 // Direct sound implemenation.
00222 
00223 DirectSound::DirectSound()
00224 {
00225         ready = FALSE;
00226         pDS = NULL;
00227         pPrimaryBuffer = NULL;
00228         pSoundBuffer = NULL;
00229 
00230         HRESULT dsrval = DirectSoundCreate(NULL, &pDS, NULL);
00231         if (dsrval != DS_OK) {
00232                 DebugResult("DirectSoundCreate failed", dsrval);
00233                 return;
00234         }
00235 
00236         // Set cooperative level trying to get exclusive or normal mode.
00237         DWORD level = ThePrefs.ExclusiveSound ? DSSCL_EXCLUSIVE : DSSCL_NORMAL;
00238         dsrval = pDS->SetCooperativeLevel(hwnd, level);
00239         if (dsrval != DS_OK) {
00240                 DebugResult("SetCooperativeLevel failed", dsrval);
00241                 return;
00242         }
00243 
00244         WAVEFORMATEX wfx;
00245         memset(&wfx, 0, sizeof(wfx));
00246         wfx.wFormatTag = WAVE_FORMAT_PCM;
00247         wfx.nChannels = 1;
00248         wfx.nSamplesPerSec = 44100;
00249         wfx.wBitsPerSample = 16;
00250         wfx.nBlockAlign = wfx.nChannels*wfx.wBitsPerSample/8;
00251         wfx.nAvgBytesPerSec = wfx.nBlockAlign*wfx.nSamplesPerSec;
00252         wfx.cbSize = 0;
00253 
00254         DSBUFFERDESC dsbd;
00255         memset(&dsbd, 0, sizeof(dsbd));
00256         dsbd.dwSize = sizeof(dsbd);
00257         dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
00258         dsbd.dwBufferBytes = 0;
00259         dsbd.lpwfxFormat = NULL;
00260 
00261         dsrval = pDS->CreateSoundBuffer(&dsbd, &pPrimaryBuffer, NULL);
00262         if (dsrval != DS_OK) {
00263                 DebugResult("CreateSoundBuffer for primary failed", dsrval);
00264                 return;
00265         }
00266 
00267         dsrval = pPrimaryBuffer->SetFormat(&wfx);
00268         if (dsrval != DS_OK) {
00269                 DebugResult("SetFormat on primary failed", dsrval);
00270                 //return;
00271         }
00272 
00273         dsrval = pPrimaryBuffer->Play(0, 0, DSBPLAY_LOOPING);
00274         if (dsrval != DS_OK) {
00275                 DebugResult("Play primary failed", dsrval);
00276                 return;
00277         }
00278 
00279         dsbd.dwSize = sizeof(dsbd);
00280         dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
00281         dsbd.dwBufferBytes = BUFFER_SIZE;
00282         dsbd.lpwfxFormat = &wfx;
00283 
00284         dsrval = pDS->CreateSoundBuffer(&dsbd, &pSoundBuffer, NULL);
00285         if (dsrval != DS_OK) {
00286                 DebugResult("CreateSoundBuffer failed", dsrval);
00287                 return;
00288         }
00289 
00290         ready = TRUE;
00291 }
00292 
00293 DirectSound::~DirectSound()
00294 {
00295         if (pDS != NULL) {
00296                 if (pSoundBuffer != NULL) {
00297                         pSoundBuffer->Release();
00298                         pSoundBuffer = NULL;
00299                 }
00300                 if (pPrimaryBuffer != NULL) {
00301                         pPrimaryBuffer->Release();
00302                         pPrimaryBuffer = NULL;
00303                 }
00304                 pDS->Release();
00305                 pDS = NULL;
00306         }
00307 }
00308 
00309 BOOL DirectSound::Ready()
00310 {
00311         return ready;
00312 }
00313 
00314 int DirectSound::GetCurrentPosition()
00315 {
00316         DWORD dwPlayPos, dwWritePos;
00317         HRESULT dsrval = pSoundBuffer->GetCurrentPosition(&dwPlayPos, &dwWritePos);
00318         if (dsrval != DS_OK) {
00319                 DebugResult("GetCurrentPostion failed", dsrval);
00320                 return -1;
00321         }
00322         return dwWritePos;
00323 }
00324 
00325 void DirectSound::Write(void *buffer, int position, int length)
00326 {
00327         // Lock sound buffer.
00328         LPVOID pMem1, pMem2;
00329         DWORD dwSize1, dwSize2;
00330         HRESULT dsrval = pSoundBuffer->Lock(position, length,
00331                 &pMem1, &dwSize1, &pMem2, &dwSize2, 0);
00332         if (dsrval != DS_OK) {
00333                 DebugResult("Sound Lock failed", dsrval);
00334                 return;
00335         }
00336 
00337         // Copy the sample buffer into the sound buffer.
00338         BYTE *pSample = (BYTE *) buffer;
00339         memcpy(pMem1, pSample, dwSize1);
00340         if (dwSize2 != 0)
00341                 memcpy(pMem2, pSample + dwSize1, dwSize2);
00342 
00343         // Unlock the sound buffer.
00344         dsrval = pSoundBuffer->Unlock(pMem1, dwSize1, pMem2, dwSize2);
00345         if (dsrval != DS_OK) {
00346                 DebugResult("Unlock failed\n", dsrval);
00347                 return;
00348         }
00349 
00350         // Play the sound buffer.
00351         dsrval = pSoundBuffer->Play(0, 0, DSBPLAY_LOOPING);
00352         if (dsrval != DS_OK) {
00353                 DebugResult("Play failed", dsrval);
00354                 return;
00355         }
00356 }
00357 
00358 void DirectSound::Pause()
00359 {
00360         HRESULT dsrval = pSoundBuffer->Stop();
00361         if (dsrval != DS_OK)
00362                 DebugResult("Stop failed", dsrval);
00363         dsrval = pPrimaryBuffer->Stop();
00364         if (dsrval != DS_OK)
00365                 DebugResult("Stop primary failed", dsrval);
00366 }
00367 
00368 void DirectSound::Resume()
00369 {
00370         HRESULT dsrval = pPrimaryBuffer->Play(0, 0, DSBPLAY_LOOPING);
00371         if (dsrval != DS_OK)
00372                 DebugResult("Play primary failed", dsrval);
00373         dsrval = pSoundBuffer->Play(0, 0, DSBPLAY_LOOPING);
00374         if (dsrval != DS_OK)
00375                 DebugResult("Play failed", dsrval);
00376 }
00377 
00378 // Wave output implemenation.
00379 
00380 WaveOut::WaveOut()
00381 {
00382         ready = FALSE;
00383 
00384         WAVEFORMATEX wfx;
00385         memset(&wfx, 0, sizeof(wfx));
00386         wfx.wFormatTag = WAVE_FORMAT_PCM;
00387         wfx.nChannels = 1;
00388         wfx.nSamplesPerSec = 44100;
00389         wfx.wBitsPerSample = 16;
00390         wfx.nBlockAlign = wfx.nChannels*wfx.wBitsPerSample/8;
00391         wfx.nAvgBytesPerSec = wfx.nBlockAlign*wfx.nSamplesPerSec;
00392         wfx.cbSize = 0;
00393         MMRESULT worval = waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, 0);
00394         if (worval != MMSYSERR_NOERROR) {
00395                 Debug("waveOutOpen returned %d\n", worval);
00396                 return;
00397         }
00398         memset(wave_header, 0, sizeof(wave_header));
00399 
00400         last_unprepared = 0;
00401 
00402         ready = TRUE;
00403 }
00404 
00405 WaveOut::~WaveOut()
00406 {
00407         waveOutReset(hWaveOut);
00408         UnprepareHeaders();
00409         waveOutClose(hWaveOut);
00410 }
00411 
00412 BOOL WaveOut::Ready()
00413 {
00414         return ready;
00415 }
00416 
00417 int WaveOut::GetCurrentPosition()
00418 {
00419         MMTIME mmtime;
00420         memset(&mmtime, 0, sizeof(mmtime));
00421         mmtime.wType = TIME_BYTES;
00422         MMRESULT worval = waveOutGetPosition(hWaveOut, &mmtime, sizeof(mmtime));
00423         if (worval != MMSYSERR_NOERROR) {
00424                 Debug("waveOutGetPosition(%d) returned %d\n", worval);
00425                 return -1;
00426         }
00427         int current_position = mmtime.u.cb % BUFFER_SIZE;
00428         return current_position;
00429 }
00430 
00431 void WaveOut::Write(void *buffer, int position, int length)
00432 {
00433         // If we are called for a double length buffer split it in half.
00434         if (length == 4*FRAGMENT_SIZE) {
00435                 int half = length/2;
00436                 Write(buffer, position, half);
00437                 Write(((char *) buffer) + half, position + half, half);
00438                 return;
00439         }
00440 
00441         // Free up as many previous frags as possible.
00442         for (;;) {
00443                 WAVEHDR &wh = wave_header[last_unprepared];
00444                 if (!(wh.dwFlags & WHDR_DONE))
00445                         break;
00446                 UnprepareHeader(last_unprepared);
00447                 last_unprepared++;
00448                 if (last_unprepared == BUFFER_FRAGS)
00449                         last_unprepared = 0;
00450         }
00451 
00452         // Make sure the current header isn't in use.
00453         int index = position/(2*FRAGMENT_SIZE);
00454         WAVEHDR &wh = wave_header[index];
00455         if (wh.dwFlags & WHDR_DONE)
00456                 UnprepareHeader(index);
00457         if (wh.dwFlags != 0) {
00458                 Debug("wave header %d is in use!\n", index);
00459                 return;
00460         }
00461         
00462         // Prepare the header and write the sound data.
00463         wh.lpData = wave_buffer + position;
00464         wh.dwBufferLength = length;
00465         wh.dwFlags = 0;
00466         memcpy(wh.lpData, buffer, length);
00467         MMRESULT worval = waveOutPrepareHeader(hWaveOut, &wh, sizeof(wh));
00468         if (worval != MMSYSERR_NOERROR) {
00469                 Debug("waveOutPrepareHeader(%d) returned %d\n", index, worval);
00470                 return;
00471         }
00472         worval = waveOutWrite(hWaveOut, &wh, sizeof(wh));
00473         if (worval != MMSYSERR_NOERROR) {
00474                 Debug("waveOutWrite(%d) returned %d\n", index, worval);
00475                 return;
00476         }
00477 }
00478 
00479 void WaveOut::Pause()
00480 {
00481         waveOutPause(hWaveOut);
00482 }
00483 
00484 void WaveOut::Resume()
00485 {
00486         waveOutRestart(hWaveOut);
00487 }
00488 
00489 void WaveOut::UnprepareHeader(int index)
00490 {
00491         WAVEHDR &wh = wave_header[index];
00492         MMRESULT worval = waveOutUnprepareHeader(hWaveOut, &wh, sizeof(wh));
00493         if (worval != MMSYSERR_NOERROR) {
00494                 Debug("waveOutUnprepareHeader(%d) returned %d\n", index, worval);
00495                 return;
00496         }
00497         memset(&wh, 0, sizeof(wh));
00498 }
00499 
00500 void WaveOut::UnprepareHeaders()
00501 {
00502         for (int i = 0; i < BUFFER_FRAGS; i++) {
00503                 WAVEHDR &wh = wave_header[i];
00504                 if (wh.dwFlags & WHDR_DONE)
00505                         UnprepareHeader(i);
00506         }
00507 }
00508 
00509 #if 0
00510 
00511 // Log player implemenation.
00512 
00513 void Log::Write()
00514 {
00515         // Dump the sound output to the log for debugging.
00516         {
00517                 int last = sound_buffer[0];
00518                 int count = 1;
00519                 for (int i = 1; i < nsamples; i++) {
00520                         if (sound_buffer[i] == last) {
00521                                 count++;
00522                                 continue;
00523                         }
00524                         Debug("[%dx%d] ", count, last);
00525                         count = 1;
00526                         last = sound_buffer[i];
00527                 }
00528                 Debug("[%dx%d] ", count, last);
00529         }
00530 }
00531 
00532 #endif
00533 
00534 #if 0
00535 
00536 // File player implemenation.
00537 
00538 void Log::Write()
00539 {
00540         // Log the sound output to a file for debugging.
00541         {
00542                 static FILE *ofp = NULL;
00543                 if (ofp == NULL) {
00544                         ofp = fopen("debug.sid", "wb");
00545                         if (ofp == NULL)
00546                                 Debug("fopen failed\n");
00547                 }
00548                 if (ofp != NULL) {
00549                         Debug("Write sound data to file\n");
00550                         if (fwrite(sound_buffer, 1, 2*nsamples, ofp) != 2*nsamples)
00551                                 Debug("fwrite failed\n");
00552                 }
00553         }
00554 }
00555 
00556 #endif

Generated on Tue Feb 8 04:08:13 2005 for E32frodo by doxygen 1.3.3