/* 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: Core sound related functionality * * Author: Tom Charlesworth */ /* Adaption for Linux+SDL done by beom beotiger. Peace! LLL */ #include "stdafx.h" // for Assertion #include bool g_bDSAvailable = false; // forward decls ------------------------ void SDLSoundDriverUninit(); // for DSInit() bool SDLSoundDriverInit(unsigned wantedFreq, unsigned wantedSamples); // for DSUninit? bool DSInit() { if(g_bDSAvailable) return true; // do not need to repeat all process?? --bb // const DWORD SPKR_SAMPLE_RATE = 44100; - defined in Common.h g_bDSAvailable = SDLSoundDriverInit(SPKR_SAMPLE_RATE, 4096);// I just do not know what number of samples use. return g_bDSAvailable; // } //----------------------------------------------------------------------------- void DSUninit() { if(!g_bDSAvailable) return; SDLSoundDriverUninit(); // using code from OpenMSX // SDL_CloseAudio(); } void SoundCore_SetFade(int how) // { if(how == FADE_OUT) SDL_PauseAudio(1); //stop playing sound else SDL_PauseAudio(0); //start playing } /////////////////////////////////////////////////////////////////////////////////// ///////////////////////// Code from OpenMSX (http://openmsx.sourceforge.net) //// /////////////////////////////////////////////////////////////////////////////////// // Definitions void mute(); void unmute(); unsigned getFrequency(); unsigned getSamples(); double uploadBuffer(short* buffer, unsigned len); void reInit(); unsigned getBufferFilled(); unsigned getBufferFree(); static void audioCallbackHelper(void* userdata, BYTE* strm, int len); void audioCallback(short* stream, unsigned len); unsigned frequency; short* mixBuffer; short* mockBuffer; unsigned fragmentSize; unsigned bufferSize; unsigned readIdx, writeIdx, readIdx2, writeIdx2; double filledStat; /**< average filled status, 1.0 means filled exactly the right amount, less than 1.0 mean under filled, more than 1.0 means overfilled. */ bool muted; /////////////////// Main part ////////////////////// bool SDLSoundDriverInit(unsigned wantedFreq, unsigned wantedSamples) { SDL_AudioSpec desired; desired.freq = wantedFreq; desired.samples = wantedSamples; desired.channels = 2; // stereo(2) or mono(1) // be courteous with BIG_Endian systems, please! --bb #if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) desired.format = AUDIO_S16MSB; #else desired.format = AUDIO_S16LSB; #endif desired.callback = audioCallbackHelper; // must be a static method desired.userdata = NULL; /* if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) { printf("Unable to initialize SDL audio subsystem: %s", SDL_GetError()); return false; } */ SDL_AudioSpec audioSpec; if (SDL_OpenAudio(&desired, &audioSpec) != 0) { // SDL_QuitSubSystem(SDL_INIT_AUDIO); printf("Unable to open SDL audio: %s", SDL_GetError()); return false; } //std::cerr << "DEBUG wanted: " << wantedSamples // << " actual: " << audioSpec.size / 4 << std::endl; frequency = audioSpec.freq; bufferSize = 4 * (audioSpec.size / sizeof(short)); // bufferSize = SPKR_SAMPLE_RATE * 2 * sizeof(short); // 1 second of stereo short data /* fragmentSize = 256; while ((bufferSize / fragmentSize) >= 32) { fragmentSize *= 2; } while ((bufferSize / fragmentSize) < 8) { fragmentSize /= 2; } */ mixBuffer = new short[bufferSize]; // buffer for Apple2 speakers mockBuffer = new short[bufferSize]; // buffer for Mockingboard reInit(); printf("SDL_MIX_MAXVOLUME=%d\n",SDL_MIX_MAXVOLUME); printf("Freq=%d,format=%d,channels=%d,silence=%d\n", audioSpec.freq,audioSpec.format,audioSpec.channels,audioSpec.silence); printf("samples=%d,size=%d,bufferSize=%d\n",audioSpec.samples,audioSpec.size,bufferSize); // SDL_PauseAudio(0); return true; } void SDLSoundDriverUninit() { delete[] mixBuffer; delete[] mockBuffer; SDL_CloseAudio(); // SDL_QuitSubSystem(SDL_INIT_AUDIO); } void reInit() { memset(mixBuffer, 0, bufferSize * sizeof(short)); memset(mockBuffer, 0, bufferSize * sizeof(short)); readIdx = readIdx2 = 0; writeIdx = writeIdx2 = 0;//(5 * bufferSize) / 8; filledStat = 1.0; } void mute() { if (!muted) { muted = true; SDL_PauseAudio(1); } } void unmute() { if (muted) { muted = false; reInit(); SDL_PauseAudio(0); } } unsigned getFrequency() { return frequency; } unsigned getSamples() { return fragmentSize; } void audioCallbackHelper(void* userdata, BYTE* strm, int len) { assert((len & 3) == 0); // stereo, 16-bit audioCallback((short*)strm, len / sizeof(short)); } unsigned getBufferFilled() { int tmp = writeIdx - readIdx; int result = (0 <= tmp) ? tmp : tmp + bufferSize; assert((0 <= result) && (unsigned(result) < bufferSize)); return result; } unsigned getBufferFree() { // we can't distinguish completely filled from completely empty // (in both cases readIx would be equal to writeIdx), so instead // we define full as '(writeIdx + 2) == readIdx' (note that index // increases in steps of 2 (stereo)). int result = bufferSize - 2 - getBufferFilled(); assert((0 <= result) && (unsigned(result) < bufferSize)); return result; } ////////////////////////////////////////////////////////////////// //////// for Mockingboard support using another buffer ////////// ////////////////////////////////////////////////////////////////// unsigned getBuffer2Filled() { int tmp = writeIdx2 - readIdx2; int result = (0 <= tmp) ? tmp : tmp + bufferSize; assert((0 <= result) && (unsigned(result) < bufferSize)); return result; } unsigned getBuffer2Free() { // we can't distinguish completely filled from completely empty // (in both cases readIx would be equal to writeIdx), so instead // we define full as '(writeIdx + 2) == readIdx' (note that index // increases in steps of 2 (stereo)). int result = bufferSize - 2 - getBuffer2Filled(); assert((0 <= result) && (unsigned(result) < bufferSize)); return result; } ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// void audioCallback(short* stream, unsigned len) { // clear Audio buffer memset(stream,0,len * sizeof(short)); assert((len & 1) == 0); // stereo unsigned len1, len2; unsigned available = getBufferFilled(); //std::cerr << "DEBUG callback: " << available << std::endl; unsigned num = std::min(len, available); if ((readIdx + num) < bufferSize) { memcpy(stream, &mixBuffer[readIdx], num * sizeof(short)); readIdx += num; } else { len1 = bufferSize - readIdx; memcpy(stream, &mixBuffer[readIdx], len1 * sizeof(short)); len2 = num - len1; memcpy(&stream[len1], mixBuffer, len2 * sizeof(short)); readIdx = len2; } /* Note: cleared at the begininng of the func int missing = len - available; if (missing > 0) { // buffer underrun //std::cerr << "DEBUG underrun: " << missing << std::endl; memset(&stream[available], 0, missing * sizeof(short)); } */ unsigned target = (5 * bufferSize) / 8; double factor = double(available) / target; filledStat = (63 * filledStat + factor) / 64; //std::cerr << "DEBUG filledStat: " << filledStat << std::endl; #ifdef MOCKINGBOARD // And add Mockingboard sound data to the stream available = getBuffer2Filled(); //std::cerr << "DEBUG callback: " << available << std::endl; num = std::min(len, available); unsigned i; if ((readIdx2 + num) < bufferSize) { // memcpy(stream, &mixBuffer[readIdx], num * sizeof(short)); for(i = 0; i < num; i++) stream[i] |= mockBuffer[readIdx2 + i]; readIdx2 += num; } else { len1 = bufferSize - readIdx2; // memcpy(stream, &mixBuffer[readIdx], len1 * sizeof(short)); for(i = 0; i < len1; i++) stream[i] |= mockBuffer[readIdx2 + i]; len2 = num - len1; // memcpy(&stream[len1], mixBuffer, len2 * sizeof(short)); for(i = 0; i < len2; i++) stream[len1 + i] |= mockBuffer[i]; readIdx2 = len2; } #endif // normalization /* const short MY_MAX_VOLUME = (short)SDL_MIX_MAXVOLUME / 2; for(unsigned k=0;k MY_MAX_VOLUME) stream[k] = MY_MAX_VOLUME; */ } // audioCallback double DSUploadBuffer(short* buffer, unsigned len) { SDL_LockAudio(); // len *= 2; // stereo unsigned free = getBufferFree(); if (len > free) { // std::cerr << "DEBUG overrun: " << len - free << std::endl; printf("DEBUG overrun: len=%d,free=%d\n\n", len, free); } unsigned num = std::min(len, free); // ignore overrun (drop samples) if ((writeIdx + num) < bufferSize) { memcpy(&mixBuffer[writeIdx], buffer, num * sizeof(short)); writeIdx += num; } else { // printf("DEBUG split: writeIdx=%d,num=%d\n\n", writeIdx, num); unsigned len1 = bufferSize - writeIdx; memcpy(&mixBuffer[writeIdx], buffer, len1 * sizeof(short)); unsigned len2 = num - len1; memcpy(mixBuffer, &buffer[len1], len2 * sizeof(short)); writeIdx = len2; } //unsigned available = getBufferFilled(); //std::cerr << "DEBUG upload: " << available << " (" << len << ")" << std::endl; double result = filledStat; filledStat = 1.0; // only report difference once SDL_UnlockAudio(); return result; } ///// Uploading sound data for Mockingboard buffer void /*double*/ DSUploadMockBuffer(short* buffer, unsigned len) { SDL_LockAudio(); // len *= 2; // stereo unsigned free = getBuffer2Free(); //if (len > free) { // std::cerr << "DEBUG overrun: " << len - free << std::endl; //} unsigned num = std::min(len, free); // ignore overrun (drop samples) if ((writeIdx2 + num) < bufferSize) { memcpy(&mockBuffer[writeIdx2], buffer, num * sizeof(short)); writeIdx2 += num; } else { unsigned len1 = bufferSize - writeIdx2; memcpy(&mockBuffer[writeIdx2], buffer, len1 * sizeof(short)); unsigned len2 = num - len1; memcpy(mockBuffer, &buffer[len1], len2 * sizeof(short)); writeIdx2 = len2; } SDL_UnlockAudio(); // return 1.0; // do not use result? }