flycast/core/oslib/audiostream.cpp

216 lines
5.6 KiB
C++
Raw Normal View History

#include <limits.h>
#include "cfg/cfg.h"
2013-12-19 17:10:14 +00:00
#include "oslib/oslib.h"
#include "audiostream.h"
2013-12-19 17:10:14 +00:00
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
2013-12-19 17:10:14 +00:00
u32 gen_samples=0;
double time_diff = 128/44100.0;
double time_last;
2013-12-19 17:10:14 +00:00
#ifdef LOG_SOUND
// TODO Only works on Windows!
2013-12-19 17:10:14 +00:00
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;
2019-04-05 20:22:46 +00:00
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<audiobackend_t**>(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<audiobackend_t**>(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;
}
2019-04-05 20:22:46 +00:00
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;
}
2019-05-01 16:01:45 +00:00
extern double full_rps;
u32 PushAudio(void* frame, u32 amt, bool wait)
{
2019-05-01 16:01:45 +00:00
bool do_wait = (full_rps<50.f)?false:wait;
if (audiobackend_current != NULL) {
2019-05-01 16:01:45 +00:00
return audiobackend_current->push(frame, amt, do_wait);
}
return 0;
}
2013-12-19 17:10:14 +00:00
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;
}
2013-12-19 17:10:14 +00:00
u32 asRingFreeCount()
{
return RingBufferSampleCount-asRingUsedCount();
}
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))
{
PushAudio(RingBuffer,SAMPLE_COUNT,settings.aica.LimitFPS);
2013-12-19 17:10:14 +00:00
}
}
2019-04-05 19:14:42 +00:00
static bool backends_sorted = false;
2019-04-05 20:22:46 +00:00
void SortAudioBackends()
2019-04-05 19:14:42 +00:00
{
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;
}
}
}
}
2013-12-19 17:10:14 +00:00
void InitAudio()
{
if (cfgLoadInt("audio", "disable", 0)) {
printf("WARNING: Audio disabled in config!\n");
return;
}
2019-04-05 19:14:42 +00:00
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;
}
2019-04-05 20:22:46 +00:00
SortAudioBackends();
2019-04-05 19:14:42 +00:00
2019-04-05 20:22:46 +00:00
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();
2013-12-19 17:10:14 +00:00
}
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;
}
}