/* AppleWin : An Apple //e emulator for Windows Copyright (C) 1994-1996, Michael O'Brien Copyright (C) 1999-2001, Oliver Schmidt Copyright (C) 2002-2005, Tom Charlesworth Copyright (C) 2006-2007, Tom Charlesworth, Michael Pohoreski AppleWin is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. AppleWin is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with AppleWin; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Description: Speaker emulation * * Author: Various */ /* Remake for SDL Audio for Linux (or other SDL-compliant OSes) by beom beotiger --bb */ #include "stdafx.h" // for _ASSERT ion. (here _ASSERT means Unix(tm) assert) --bb #include // Notes: // // [OLD: 23.191 Apple CLKs == 44100Hz (CLK_6502/44100)] // 23 Apple CLKS per PC sample (played back at 44.1KHz) // // // The speaker's wave output drives how much 6502 emulation is done in real-time, eg: // If the speaker's wave buffer is running out of sample-data, then more 6502 cycles // need to be executed to top-up the wave buffer. // This is in contrast to the AY8910 voices, which can simply generate more data if // their buffers are running low. // number of channels and buffer size for Apple][ Speakers static const unsigned short g_nSPKR_NumChannels = 1; //------------------------------------- // Globals (SOUND_WAVE) const short SPKR_DATA_INIT = (short)0x8000; // data written to speakers buffer static short g_nSpeakerData = SPKR_DATA_INIT; static short *g_pSpeakerBuffer = NULL; static UINT g_nBufferIdx = 0; static short *g_pStereoBuffer = NULL; // buffer for stereo samples static short *g_pRemainderBuffer = NULL; // Remainder buffer static UINT g_nRemainderBufferSize; // Setup in SpkrInitialize() static UINT g_nRemainderBufferIdx; // Setup in SpkrInitialize() // Application-wide globals: DWORD soundtype = SOUND_WAVE; //default double g_fClksPerSpkrSample; // Setup in SetClksPerSpkrSample() // Globals static unsigned __int64 g_nSpkrQuietCycleCount = 0; static unsigned __int64 g_nSpkrLastCycle = 0; static bool g_bSpkrToggleFlag = false; static bool g_bSpkrAvailable = false; static bool g_bSpkrRecentlyActive = false; //----------------------------------------------------------------------------- // Forward refs: //ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSamples); static ULONG Spkr_SubmitWaveBuffer(short* pSpeakerBuffer, ULONG nNumSamples); static void Spkr_SetActive(bool bActive); //============================================================================= // Let us leave benchmark for the near future --bb ^_^ #if 0 static void DisplayBenchmarkResults () { DWORD totaltime = GetTickCount() - extbench; VideoRedrawScreen(); TCHAR buffer[64]; sprintf(buffer, TEXT("This benchmark took %u.%02u seconds."), (unsigned)(totaltime / 1000), (unsigned)((totaltime / 10) % 100)); /* MessageBox(g_hFrameWindow, buffer, TEXT("Benchmark Results"), MB_ICONINFORMATION | MB_SETFOREGROUND);*/ printf("This benchmark took %u.%02u seconds.", (unsigned)(totaltime / 1000), (unsigned)((totaltime / 10) % 100)); } #endif //============================================================================= static void SetClksPerSpkrSample() { // // 23.191 clks for 44.1Khz (when 6502 CLK=1.0Mhz) // g_fClksPerSpkrSample = g_fCurrentCLK6502 / (double)SPKR_SAMPLE_RATE; // Use integer value: Better for MJ Mahon's RT.SYNTH.DSK (integer multiples of 1.023MHz Clk) // . 23 clks @ 1.023MHz SPKR_SAMPLE_RATE = 44100Hz!? g_fClksPerSpkrSample = (double) (UINT) (g_fCurrentCLK6502 / (double)SPKR_SAMPLE_RATE); } //============================================================================= static void InitRemainderBuffer() { delete [] g_pRemainderBuffer; SetClksPerSpkrSample(); g_nRemainderBufferSize = (UINT) g_fClksPerSpkrSample; if ((double)g_nRemainderBufferSize != g_fClksPerSpkrSample) g_nRemainderBufferSize++; g_pRemainderBuffer = new short [g_nRemainderBufferSize]; memset(g_pRemainderBuffer, 0, g_nRemainderBufferSize); g_nRemainderBufferIdx = 0; } // // ----- ALL GLOBALLY ACCESSIBLE FUNCTIONS ARE BELOW THIS LINE ----- // //============================================================================= void SpkrDestroy () { Spkr_DSUninit(); if(soundtype == SOUND_WAVE) { delete [] g_pSpeakerBuffer; delete [] g_pStereoBuffer; delete [] g_pRemainderBuffer; g_pSpeakerBuffer = NULL; g_pStereoBuffer = NULL; g_pRemainderBuffer = NULL; } } //============================================================================= void SpkrInitialize () { if(g_fh) { fprintf(g_fh, "Spkr Config: soundtype = %d ", (int)soundtype); switch(soundtype) { case SOUND_NONE: fprintf(g_fh, "(NONE)\n"); break; case SOUND_WAVE: fprintf(g_fh, "(WAVE)\n"); break; default: fprintf(g_fh, "(UNDEFINED!)\n"); break; } } if(g_bDisableDirectSound) { // SpeakerVoice.bMute = true; } else { //DSInit(); g_bSpkrAvailable = Spkr_DSInit(); } if (soundtype == SOUND_WAVE) { InitRemainderBuffer(); // Buffer can hold a max of 1 seconds worth of samples g_pSpeakerBuffer = new short [SPKR_SAMPLE_RATE]; g_pStereoBuffer = new short [SPKR_SAMPLE_RATE * 2]; // doubled for stereo } } //============================================================================= // NB. Called when /g_fCurrentCLK6502/ changes void SpkrReinitialize () { if (soundtype == SOUND_WAVE) { InitRemainderBuffer(); } } //============================================================================= void SpkrReset() { g_nBufferIdx = 0; g_nSpkrQuietCycleCount = 0; g_bSpkrToggleFlag = false; InitRemainderBuffer(); Spkr_SubmitWaveBuffer(NULL, 0); Spkr_SetActive(false); Spkr_Demute(); } //=================2012 AD ========================================================= #if 0 BOOL SpkrSetEmulationType (DWORD newtype) { if (soundtype != SOUND_NONE) SpkrDestroy(); soundtype = newtype; if (soundtype != SOUND_NONE) SpkrInitialize(); if (soundtype != newtype) switch (newtype) { // some fault occured case SOUND_WAVE: /* MessageBox(window, TEXT("The emulator is unable to initialize a waveform ") TEXT("output device. Make sure you have a sound card ") TEXT("and a driver installed and that windows is ") TEXT("correctly configured to use the driver. Also ") TEXT("ensure that no other program is currently using ") TEXT("the device."), TEXT("Configuration"), MB_ICONEXCLAMATION | MB_SETFOREGROUND);*/ // Need to tuck SDL_GetError() hhere? ------------------------------------------ fprintf(stderr, "Unable to initialize a waveform output device.\n"); return 0; } return 1; } #endif //============================================================================= static void ReinitRemainderBuffer(UINT nCyclesRemaining) { if(nCyclesRemaining == 0) return; for(g_nRemainderBufferIdx=0; g_nRemainderBufferIdx (unsigned __int64)g_fCurrentCLK6502/5) { // After 0.2 sec of Apple time, deactivate spkr voice // . This allows emulator to auto-switch to full-speed g_nAppMode for fast disk access Spkr_SetActive(false); } } else { g_nSpkrQuietCycleCount = 0; g_bSpkrToggleFlag = false; } if (soundtype == SOUND_WAVE) { UpdateSpkr(); ULONG nSamplesUsed; if(g_bFullSpeed) g_nBufferIdx = 0; // try this --bb // nSamplesUsed = Spkr_SubmitWaveBuffer/*_FullSpeed*/(g_pSpeakerBuffer, g_nBufferIdx); else { nSamplesUsed = Spkr_SubmitWaveBuffer(g_pSpeakerBuffer, g_nBufferIdx); _ASSERT(nSamplesUsed <= g_nBufferIdx); if(nSamplesUsed == 0) return; memmove(g_pSpeakerBuffer, &g_pSpeakerBuffer[nSamplesUsed], g_nBufferIdx-nSamplesUsed); g_nBufferIdx -= nSamplesUsed; } } } //============================================================================= //----------------------------------------------------------------------------- static ULONG Spkr_SubmitWaveBuffer(short* pSpeakerBuffer, ULONG nNumSamples) { // submit nNumSamples (== 2bytes long each (sizeof short))?? // from pSpeakerBuffer to pDSSpkrBuf for callback DSPlaySnd if(!g_bSpkrRecentlyActive) return nNumSamples;//if not active, just return? if(pSpeakerBuffer == NULL) { // just init sound buffer and cursors?? return 0; } // conver mono Speakers sounds to stereo (mainly for Mockingboard support) UINT len = nNumSamples * 2; // stereo = 2 * mono UINT i; for(i = 0; i < len; i += 2) g_pStereoBuffer[i] = g_pStereoBuffer[i + 1] = pSpeakerBuffer[i >> 1]; // use code from OpenMSX // DSUploadBuffer(pSpeakerBuffer, nNumSamples); DSUploadBuffer(g_pStereoBuffer, len); // submit stereo wave data return nNumSamples; // always return as if we've filled everything!? --bb } ///////////// Mute - set volume to MINIMUM, Demute - set volume to NORMAL STATE? -bb void Spkr_Mute() { SDL_PauseAudio(1); // dangerous functiouse - will mute Mockingboard, too. Need to be changed } void Spkr_Demute() { SDL_PauseAudio(0); } //----------------------------------------------------------------------------- static void Spkr_SetActive(bool bActive) { // yes, I know the right way is: g_bSpkrRecentlyActive = bActive;, but... ^_^ --bb if(bActive) { // Called by SpkrToggle() or SpkrReset() g_bSpkrRecentlyActive = true; } else { // Called by SpkrUpdate() after 0.2s of speaker inactivity g_bSpkrRecentlyActive = false; } } bool Spkr_IsActive() { return g_bSpkrRecentlyActive; } //----------------------------------------------------------------------------- // How to deal with volume in SDL Audio // may be need to go to SDL Mixer? DWORD SpkrGetVolume() { // return SpeakerVoice.dwUserVolume; return 0; } void SpkrSetVolume(DWORD dwVolume, DWORD dwVolumeMax) { /* SpeakerVoice.dwUserVolume = dwVolume; SpeakerVoice.nVolume = NewVolume(dwVolume, dwVolumeMax); if(SpeakerVoice.bActive) SpeakerVoice.lpDSBvoice->SetVolume(SpeakerVoice.nVolume);*/ } //============================================================================= bool Spkr_DSInit() { // // Create single Apple speaker voice // if(!g_bDSAvailable) return false; // do not have DirectSound? Sorry, SDL Audio! ^_^ --bb return true; } void Spkr_DSUninit() { } //============================================================================= DWORD SpkrGetSnapshot(SS_IO_Speaker* pSS) { pSS->g_nSpkrLastCycle = g_nSpkrLastCycle; return 0; } DWORD SpkrSetSnapshot(SS_IO_Speaker* pSS) { g_nSpkrLastCycle = pSS->g_nSpkrLastCycle; return 0; }