flycast/core/oslib/audiostream.h

130 lines
3.0 KiB
C++

#pragma once
#include "types.h"
#include "cfg/option.h"
#include <vector>
#include <algorithm>
#include <atomic>
class AudioBackend
{
public:
virtual ~AudioBackend() = default;
virtual bool init() = 0;
virtual u32 push(const void *data, u32 frames, bool wait) = 0;
virtual void term() {}
struct Option {
std::string name;
std::string caption;
enum { integer, checkbox, list } type;
int minValue;
int maxValue;
std::vector<std::string> values;
};
virtual const Option *getOptions(int *count) {
*count = 0;
return nullptr;
}
virtual bool initRecord(u32 sampling_freq) { return false; }
virtual u32 record(void *, u32) { return 0; }
virtual void termRecord() {}
std::string slug;
std::string name;
static size_t getCount() { return backends == nullptr ? 0 : backends->size(); }
static AudioBackend *getBackend(size_t index) { return backends == nullptr ? nullptr : (*backends)[index]; }
static AudioBackend *getBackend(const std::string& slug);
protected:
AudioBackend(const std::string& slug, const std::string& name)
: slug(slug), name(name) {
registerAudioBackend(this);
}
private:
static void registerAudioBackend(AudioBackend *backend)
{
if (backends == nullptr)
backends = new std::vector<AudioBackend *>();
backends->push_back(backend);
std::sort(backends->begin(), backends->end(), [](AudioBackend *b1, AudioBackend *b2) { return b1->slug < b2->slug; });
}
static std::vector<AudioBackend *> *backends;
};
void InitAudio();
void TermAudio();
void WriteSample(s16 right, s16 left);
void StartAudioRecording(bool eight_khz);
u32 RecordAudio(void *buffer, u32 samples);
void StopAudioRecording();
constexpr u32 SAMPLE_COUNT = 512; // AudioBackend::push() is always called with that many frames
class RingBuffer
{
std::vector<u8> buffer;
std::atomic_int readCursor { 0 };
std::atomic_int writeCursor { 0 };
u32 readSize() {
return (u32)((writeCursor - readCursor + buffer.size()) % buffer.size());
}
u32 writeSize() {
return (u32)((readCursor - writeCursor + buffer.size() - 1) % buffer.size());
}
public:
bool write(const u8 *data, u32 size)
{
if (size > writeSize())
return false;
u32 wc = writeCursor;
u32 chunkSize = std::min<u32>(size, (u32)buffer.size() - wc);
memcpy(&buffer[wc], data, chunkSize);
wc = (wc + chunkSize) % buffer.size();
size -= chunkSize;
if (size > 0)
{
data += chunkSize;
memcpy(&buffer[wc], data, size);
wc = (wc + size) % buffer.size();
}
writeCursor = wc;
return true;
}
bool read(u8 *data, u32 size)
{
if (size > readSize())
return false;
u32 rc = readCursor;
u32 chunkSize = std::min<u32>(size, (u32)buffer.size() - rc);
memcpy(data, &buffer[rc], chunkSize);
rc = (rc + chunkSize) % buffer.size();
size -= chunkSize;
if (size > 0)
{
data += chunkSize;
memcpy(data, &buffer[rc], size);
rc = (rc + size) % buffer.size();
}
readCursor = rc;
return true;
}
void setCapacity(size_t size)
{
std::fill(buffer.begin(), buffer.end(), 0);
buffer.resize(size);
readCursor = 0;
writeCursor = 0;
}
};