141 lines
3.4 KiB
C++
141 lines
3.4 KiB
C++
#include "audiostream.h"
|
|
#include <memory>
|
|
|
|
struct SoundFrame { s16 l; s16 r; };
|
|
|
|
static SoundFrame Buffer[SAMPLE_COUNT];
|
|
static u32 writePtr; // next sample index
|
|
|
|
static AudioBackend *currentBackend;
|
|
std::vector<AudioBackend *> *AudioBackend::backends;
|
|
|
|
static bool audio_recording_started;
|
|
static bool eight_khz;
|
|
|
|
AudioBackend *AudioBackend::getBackend(const std::string& slug)
|
|
{
|
|
if (backends == nullptr)
|
|
return nullptr;
|
|
if (slug == "auto")
|
|
{
|
|
// Prefer sdl2 if available and avoid the null driver
|
|
AudioBackend *sdlBackend = nullptr;
|
|
AudioBackend *autoBackend = nullptr;
|
|
for (auto backend : *backends)
|
|
{
|
|
if (backend->slug == "sdl2")
|
|
sdlBackend = backend;
|
|
if (backend->slug != "null" && autoBackend == nullptr)
|
|
autoBackend = backend;
|
|
}
|
|
if (sdlBackend != nullptr)
|
|
autoBackend = sdlBackend;
|
|
if (autoBackend == nullptr)
|
|
autoBackend = backends->front();
|
|
INFO_LOG(AUDIO, "Auto-selected audio backend \"%s\" (%s).", autoBackend->slug.c_str(), autoBackend->name.c_str());
|
|
|
|
return autoBackend;
|
|
}
|
|
for (auto backend : *backends)
|
|
{
|
|
if (backend->slug == slug)
|
|
return backend;
|
|
}
|
|
WARN_LOG(AUDIO, "WARNING: Audio backend \"%s\" not found!", slug.c_str());
|
|
return nullptr;
|
|
}
|
|
|
|
void WriteSample(s16 r, s16 l)
|
|
{
|
|
Buffer[writePtr].r = r * config::AudioVolume.dbPower();
|
|
Buffer[writePtr].l = l * config::AudioVolume.dbPower();
|
|
|
|
if (++writePtr == SAMPLE_COUNT)
|
|
{
|
|
if (currentBackend != nullptr)
|
|
currentBackend->push(Buffer, SAMPLE_COUNT, config::LimitFPS);
|
|
writePtr = 0;
|
|
}
|
|
}
|
|
|
|
void InitAudio()
|
|
{
|
|
TermAudio();
|
|
|
|
std::string slug = config::AudioBackend;
|
|
currentBackend = AudioBackend::getBackend(slug);
|
|
if (currentBackend == nullptr && slug != "auto")
|
|
{
|
|
slug = "auto";
|
|
currentBackend = AudioBackend::getBackend(slug);
|
|
}
|
|
if (currentBackend != nullptr)
|
|
{
|
|
INFO_LOG(AUDIO, "Initializing audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->name.c_str());
|
|
if (!currentBackend->init())
|
|
{
|
|
currentBackend = nullptr;
|
|
if (slug != "auto")
|
|
{
|
|
WARN_LOG(AUDIO, "Audio driver %s failed to initialize. Defaulting to 'auto'", slug.c_str());
|
|
slug = "auto";
|
|
currentBackend = AudioBackend::getBackend(slug);
|
|
if (!currentBackend->init())
|
|
currentBackend = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (currentBackend == nullptr)
|
|
{
|
|
WARN_LOG(AUDIO, "Running without audio!");
|
|
return;
|
|
}
|
|
|
|
if (audio_recording_started)
|
|
{
|
|
// Restart recording
|
|
audio_recording_started = false;
|
|
StartAudioRecording(eight_khz);
|
|
}
|
|
}
|
|
|
|
void TermAudio()
|
|
{
|
|
if (currentBackend == nullptr)
|
|
return;
|
|
|
|
// Save recording state before stopping
|
|
bool rec_started = audio_recording_started;
|
|
StopAudioRecording();
|
|
audio_recording_started = rec_started;
|
|
currentBackend->term();
|
|
INFO_LOG(AUDIO, "Terminating audio backend \"%s\" (%s)...", currentBackend->slug.c_str(), currentBackend->name.c_str());
|
|
currentBackend = nullptr;
|
|
}
|
|
|
|
void StartAudioRecording(bool eight_khz)
|
|
{
|
|
::eight_khz = eight_khz;
|
|
if (currentBackend != nullptr)
|
|
audio_recording_started = currentBackend->initRecord(eight_khz ? 8000 : 11025);
|
|
else
|
|
// might be called between TermAudio/InitAudio
|
|
audio_recording_started = true;
|
|
}
|
|
|
|
u32 RecordAudio(void *buffer, u32 samples)
|
|
{
|
|
if (!audio_recording_started || currentBackend == nullptr)
|
|
return 0;
|
|
return currentBackend->record(buffer, samples);
|
|
}
|
|
|
|
void StopAudioRecording()
|
|
{
|
|
// might be called between TermAudio/InitAudio
|
|
if (audio_recording_started && currentBackend != nullptr)
|
|
currentBackend->termRecord();
|
|
audio_recording_started = false;
|
|
}
|