2015-04-14 14:58:41 +00:00
|
|
|
#include "audiostream.h"
|
2020-03-28 16:58:01 +00:00
|
|
|
#include <memory>
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2021-03-23 15:27:57 +00:00
|
|
|
struct SoundFrame { s16 l; s16 r; };
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2021-03-23 15:27:57 +00:00
|
|
|
static SoundFrame Buffer[SAMPLE_COUNT];
|
|
|
|
static u32 writePtr; // next sample index
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2020-03-14 21:46:40 +00:00
|
|
|
static audiobackend_t *audiobackend_current = nullptr;
|
|
|
|
static std::unique_ptr<std::vector<audiobackend_t *>> audiobackends; // Using a pointer to avoid out of order init
|
2015-04-14 14:58:41 +00:00
|
|
|
|
2020-12-05 17:47:09 +00:00
|
|
|
static bool audio_recording_started;
|
|
|
|
static bool eight_khz;
|
|
|
|
|
2019-04-05 20:22:46 +00:00
|
|
|
u32 GetAudioBackendCount()
|
|
|
|
{
|
2021-03-01 09:13:40 +00:00
|
|
|
return audiobackends != nullptr ? (u32)audiobackends->size() : 0;
|
2019-04-05 20:22:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
audiobackend_t* GetAudioBackend(int num)
|
|
|
|
{
|
2020-03-14 21:46:40 +00:00
|
|
|
return audiobackends->at(num);
|
2019-04-05 20:22:46 +00:00
|
|
|
}
|
|
|
|
|
2020-03-14 21:46:40 +00:00
|
|
|
static void SortAudioBackends()
|
2015-04-14 14:58:41 +00:00
|
|
|
{
|
2020-03-14 21:46:40 +00:00
|
|
|
if (audiobackends != nullptr)
|
|
|
|
std::sort(audiobackends->begin(), audiobackends->end(), [](audiobackend_t *b1, audiobackend_t *b2) { return b1->slug < b2->slug; });
|
|
|
|
}
|
2019-04-05 19:05:18 +00:00
|
|
|
|
2020-03-14 21:46:40 +00:00
|
|
|
bool RegisterAudioBackend(audiobackend_t *backend)
|
|
|
|
{
|
|
|
|
verify(backend != nullptr);
|
|
|
|
verify(!backend->slug.empty() && backend->slug != "auto");
|
2019-04-05 19:05:18 +00:00
|
|
|
|
2020-03-14 21:46:40 +00:00
|
|
|
if (audiobackends == nullptr)
|
|
|
|
audiobackends = std::unique_ptr<std::vector<audiobackend_t *>>(new std::vector<audiobackend_t *>());
|
|
|
|
audiobackends->push_back(backend);
|
|
|
|
SortAudioBackends();
|
2019-04-05 19:05:18 +00:00
|
|
|
|
2015-04-14 14:58:41 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-09-07 12:37:39 +00:00
|
|
|
audiobackend_t* GetAudioBackend(const std::string& slug)
|
2015-04-14 14:58:41 +00:00
|
|
|
{
|
2020-03-14 21:46:40 +00:00
|
|
|
if (audiobackends != nullptr && !audiobackends->empty())
|
2015-04-14 14:58:41 +00:00
|
|
|
{
|
|
|
|
if (slug == "auto")
|
|
|
|
{
|
2021-04-30 17:57:11 +00:00
|
|
|
// Don't select the null or OpenSL/Oboe drivers
|
2020-03-14 21:46:40 +00:00
|
|
|
audiobackend_t *autoselection = nullptr;
|
|
|
|
for (auto backend : *audiobackends)
|
2021-04-30 17:57:11 +00:00
|
|
|
if (backend->slug != "null" && backend->slug != "OpenSL" && backend->slug != "Oboe")
|
2020-03-14 21:46:40 +00:00
|
|
|
{
|
|
|
|
autoselection = backend;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (autoselection == nullptr)
|
|
|
|
autoselection = audiobackends->front();
|
|
|
|
INFO_LOG(AUDIO, "Auto-selected audio backend \"%s\" (%s).", autoselection->slug.c_str(), autoselection->name.c_str());
|
|
|
|
return autoselection;
|
2015-04-14 14:58:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-14 21:46:40 +00:00
|
|
|
for (auto backend : *audiobackends)
|
2015-04-14 14:58:41 +00:00
|
|
|
{
|
2020-03-14 21:46:40 +00:00
|
|
|
if (backend->slug == slug)
|
|
|
|
return backend;
|
2015-04-14 14:58:41 +00:00
|
|
|
}
|
2019-07-01 15:17:08 +00:00
|
|
|
WARN_LOG(AUDIO, "WARNING: Audio backend \"%s\" not found!", slug.c_str());
|
2015-04-14 14:58:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-07-01 15:17:08 +00:00
|
|
|
WARN_LOG(AUDIO, "WARNING: No audio backends available!");
|
2015-04-14 14:58:41 +00:00
|
|
|
}
|
2020-03-14 21:46:40 +00:00
|
|
|
return nullptr;
|
2015-04-14 14:58:41 +00:00
|
|
|
}
|
|
|
|
|
2013-12-19 17:10:14 +00:00
|
|
|
void WriteSample(s16 r, s16 l)
|
|
|
|
{
|
2021-08-26 11:30:23 +00:00
|
|
|
Buffer[writePtr].r = r * config::AudioVolume.dbPower();
|
|
|
|
Buffer[writePtr].l = l * config::AudioVolume.dbPower();
|
2013-12-19 17:10:14 +00:00
|
|
|
|
2021-03-23 15:27:57 +00:00
|
|
|
if (++writePtr == SAMPLE_COUNT)
|
|
|
|
{
|
|
|
|
if (audiobackend_current != nullptr)
|
|
|
|
audiobackend_current->push(Buffer, SAMPLE_COUNT, config::LimitFPS);
|
|
|
|
writePtr = 0;
|
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void InitAudio()
|
|
|
|
{
|
2021-09-30 12:31:51 +00:00
|
|
|
TermAudio();
|
2015-04-14 14:58:41 +00:00
|
|
|
|
2019-04-05 20:22:46 +00:00
|
|
|
SortAudioBackends();
|
2019-04-05 19:14:42 +00:00
|
|
|
|
2021-03-01 09:13:40 +00:00
|
|
|
std::string audiobackend_slug = config::AudioBackend;
|
2015-04-14 14:58:41 +00:00
|
|
|
audiobackend_current = GetAudioBackend(audiobackend_slug);
|
2020-03-14 21:46:40 +00:00
|
|
|
if (audiobackend_current == nullptr) {
|
2019-07-01 15:17:08 +00:00
|
|
|
INFO_LOG(AUDIO, "WARNING: Running without audio!");
|
2015-04-14 14:58:41 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-04-05 19:05:18 +00:00
|
|
|
|
2019-07-01 15:17:08 +00:00
|
|
|
INFO_LOG(AUDIO, "Initializing audio backend \"%s\" (%s)...", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str());
|
2019-04-05 19:05:18 +00:00
|
|
|
audiobackend_current->init();
|
2020-12-05 17:47:09 +00:00
|
|
|
if (audio_recording_started)
|
|
|
|
{
|
|
|
|
// Restart recording
|
|
|
|
audio_recording_started = false;
|
|
|
|
StartAudioRecording(eight_khz);
|
|
|
|
}
|
2013-12-19 17:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TermAudio()
|
|
|
|
{
|
2020-03-14 21:46:40 +00:00
|
|
|
if (audiobackend_current != nullptr) {
|
2020-12-05 17:47:09 +00:00
|
|
|
// Save recording state before stopping
|
|
|
|
bool rec_started = audio_recording_started;
|
|
|
|
StopAudioRecording();
|
|
|
|
audio_recording_started = rec_started;
|
2015-04-14 14:58:41 +00:00
|
|
|
audiobackend_current->term();
|
2019-07-01 15:17:08 +00:00
|
|
|
INFO_LOG(AUDIO, "Terminating audio backend \"%s\" (%s)...", audiobackend_current->slug.c_str(), audiobackend_current->name.c_str());
|
2020-03-14 21:46:40 +00:00
|
|
|
audiobackend_current = nullptr;
|
2019-04-05 19:05:18 +00:00
|
|
|
}
|
2015-04-15 09:39:12 +00:00
|
|
|
}
|
2020-12-05 17:47:09 +00:00
|
|
|
|
|
|
|
void StartAudioRecording(bool eight_khz)
|
|
|
|
{
|
|
|
|
::eight_khz = eight_khz;
|
|
|
|
if (audiobackend_current != nullptr)
|
|
|
|
{
|
|
|
|
audio_recording_started = false;
|
|
|
|
if (audiobackend_current->init_record != nullptr)
|
|
|
|
audio_recording_started = audiobackend_current->init_record(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 || audiobackend_current == nullptr)
|
|
|
|
return 0;
|
|
|
|
return audiobackend_current->record(buffer, samples);
|
|
|
|
}
|
|
|
|
|
|
|
|
void StopAudioRecording()
|
|
|
|
{
|
|
|
|
// might be called between TermAudio/InitAudio
|
|
|
|
if (audio_recording_started && audiobackend_current != nullptr && audiobackend_current->term_record != nullptr)
|
|
|
|
audiobackend_current->term_record();
|
|
|
|
audio_recording_started = false;
|
|
|
|
}
|