#include #include "cfg/cfg.h" #include "oslib/oslib.h" #include "audiostream.h" struct SoundFrame { s16 l;s16 r; }; #define SAMPLE_COUNT 512 SoundFrame RingBuffer[SAMPLE_COUNT]; const u32 RingBufferByteSize = sizeof(RingBuffer); const u32 RingBufferSampleCount = SAMPLE_COUNT; volatile u32 WritePtr; //last WRITEN sample volatile u32 ReadPtr; //next sample to read u32 gen_samples=0; double time_diff = 128/44100.0; double time_last; #ifdef LOG_SOUND // TODO Only works on Windows! WaveWriter rawout("d:\\aica_out.wav"); #endif static unsigned int audiobackends_num_max = 1; static unsigned int audiobackends_num_registered = 0; static audiobackend_t **audiobackends = NULL; static audiobackend_t *audiobackend_current = NULL; u32 GetAudioBackendCount() { return audiobackends_num_registered; } audiobackend_t* GetAudioBackend(int num) { return audiobackends[num]; } bool RegisterAudioBackend(audiobackend_t *backend) { /* This function announces the availability of an audio backend to reicast. */ // Check if backend is valid if (backend == NULL) { printf("ERROR: Tried to register invalid audio backend (NULL pointer).\n"); return false; } if (backend->slug == "auto" || backend->slug == "none") { printf("ERROR: Tried to register invalid audio backend (slug \"%s\" is a reserved keyword).\n", backend->slug.c_str()); return false; } // First call to RegisterAudioBackend(), create the backend structure; if (audiobackends == NULL) audiobackends = static_cast(calloc(audiobackends_num_max, sizeof(audiobackend_t*))); // Check if we need to allocate addition memory for storing the pointers and allocate if neccessary if (audiobackends_num_registered == audiobackends_num_max) { // Check for integer overflows if (audiobackends_num_max == UINT_MAX) { printf("ERROR: Registering audio backend \"%s\" (%s) failed. Cannot register more than %u backends\n", backend->slug.c_str(), backend->name.c_str(), audiobackends_num_max); return false; } audiobackends_num_max++; audiobackend_t **new_ptr = static_cast(realloc(audiobackends, audiobackends_num_max*sizeof(audiobackend_t*))); // Make sure that allocation worked if (new_ptr == NULL) { printf("ERROR: Registering audio backend \"%s\" (%s) failed. Cannot allocate additional memory.\n", backend->slug.c_str(), backend->name.c_str()); return false; } audiobackends = new_ptr; } audiobackends[audiobackends_num_registered] = backend; audiobackends_num_registered++; return true; } audiobackend_t* GetAudioBackend(std::string slug) { if (slug == "none") { printf("WARNING: Audio backend set to \"none\"!\n"); } else if (audiobackends_num_registered > 0) { if (slug == "auto") { /* FIXME: At some point, one might want to insert some intelligent algorithm for autoselecting the approriate audio backend here. I'm too lazy right now. */ printf("Auto-selected audio backend \"%s\" (%s).\n", audiobackends[0]->slug.c_str(), audiobackends[0]->name.c_str()); return audiobackends[0]; } else { for(unsigned int i = 0; i < audiobackends_num_registered; i++) { if(audiobackends[i]->slug == slug) { return audiobackends[i]; } } printf("WARNING: Audio backend \"%s\" not found!\n", slug.c_str()); } } else { printf("WARNING: No audio backends available!\n"); } return NULL; } u32 PushAudio(void* frame, u32 amt, bool wait) { if (audiobackend_current != NULL) { return audiobackend_current->push(frame, amt, wait); } return 0; } u32 asRingUsedCount() { if (WritePtr>ReadPtr) return WritePtr-ReadPtr; else return RingBufferSampleCount-(ReadPtr-WritePtr); //s32 sz=(WritePtr+1)%RingBufferSampleCount-ReadPtr; //return sz<0?sz+RingBufferSampleCount:sz; } u32 asRingFreeCount() { return RingBufferSampleCount-asRingUsedCount(); } extern double mspdf; void WriteSample(s16 r, s16 l) { const u32 ptr=(WritePtr+1)%RingBufferSampleCount; RingBuffer[ptr].r=r; RingBuffer[ptr].l=l; WritePtr=ptr; if (WritePtr==(SAMPLE_COUNT-1)) { bool do_wait = settings.aica.LimitFPS == LimitFPSEnabled || (settings.aica.LimitFPS == LimitFPSAuto && mspdf <= 11); PushAudio(RingBuffer,SAMPLE_COUNT, do_wait); } } static bool backends_sorted = false; void SortAudioBackends() { if (backends_sorted) return; // Sort backends by slug for (int n = audiobackends_num_registered; n > 0; n--) { for (int i = 0; i < n-1; i++) { if (audiobackends[i]->slug > audiobackends[i+1]->slug) { audiobackend_t* swap = audiobackends[i]; audiobackends[i] = audiobackends[i+1]; audiobackends[i+1] = swap; } } } } void InitAudio() { if (cfgLoadInt("audio", "disable", 0)) { printf("WARNING: Audio disabled in config!\n"); return; } cfgSaveInt("audio", "disable", 0); if (audiobackend_current != NULL) { printf("ERROR: The audio backend \"%s\" (%s) has already been initialized, you need to terminate it before you can call audio_init() again!\n", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str()); return; } SortAudioBackends(); string audiobackend_slug = settings.audio.backend; audiobackend_current = GetAudioBackend(audiobackend_slug); if (audiobackend_current == NULL) { printf("WARNING: Running without audio!\n"); return; } printf("Initializing audio backend \"%s\" (%s)...\n", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str()); audiobackend_current->init(); } void TermAudio() { if (audiobackend_current != NULL) { audiobackend_current->term(); printf("Terminating audio backend \"%s\" (%s)...\n", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str()); audiobackend_current = NULL; } }