aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--alc/backends/oboe.cpp189
1 files changed, 185 insertions, 4 deletions
diff --git a/alc/backends/oboe.cpp b/alc/backends/oboe.cpp
index 54fd0f1c..c999df6f 100644
--- a/alc/backends/oboe.cpp
+++ b/alc/backends/oboe.cpp
@@ -3,21 +3,202 @@
#include "oboe.h"
+#include <cassert>
+#include <cstring>
+
+#include "alu.h"
+#include "logging.h"
+
#include "oboe/Oboe.h"
+namespace {
+
+constexpr char device_name[] = "Oboe Default";
+
+
+struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
+ OboePlayback(ALCdevice *device) : BackendBase{device} { }
+
+ oboe::ManagedStream mStream;
+
+ oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
+ int32_t numFrames) override;
+
+ void open(const ALCchar *name) override;
+ bool reset() override;
+ bool start() override;
+ void stop() override;
+};
+
+
+oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
+ int32_t numFrames)
+{
+ assert(numFrames > 0);
+ aluMixData(mDevice, audioData, static_cast<uint32_t>(numFrames),
+ static_cast<uint32_t>(oboeStream->getChannelCount()));
+ return oboe::DataCallbackResult::Continue;
+}
+
+
+void OboePlayback::open(const ALCchar *name)
+{
+ if(!name)
+ name = device_name;
+ else if(std::strcmp(name, device_name) != 0)
+ throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
+
+ oboe::AudioStreamBuilder builder;
+ builder.setDirection(oboe::Direction::Output);
+ builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
+
+ oboe::Result result{builder.openManagedStream(mStream)};
+ if(result != oboe::Result::OK)
+ throw al::backend_exception{ALC_INVALID_VALUE, "Failed to create stream. Error: %s",
+ oboe::convertToText(result)};
+}
+
+bool OboePlayback::reset()
+{
+ mStream = nullptr;
+
+ oboe::AudioStreamBuilder builder;
+ builder.setDirection(oboe::Direction::Output);
+ builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
+ builder.setCallback(this);
+
+ if(mDevice->Flags.get<FrequencyRequest>())
+ builder.setSampleRate(static_cast<int32_t>(mDevice->Frequency));
+ if(mDevice->Flags.get<ChannelsRequest>())
+ builder.setChannelCount((mDevice->FmtChans==DevFmtMono) ? oboe::ChannelCount::Mono
+ : oboe::ChannelCount::Stereo);
+ if(mDevice->Flags.get<SampleTypeRequest>())
+ {
+ oboe::AudioFormat format{oboe::AudioFormat::Unspecified};
+ switch(mDevice->FmtType)
+ {
+ case DevFmtByte:
+ case DevFmtUByte:
+ case DevFmtShort:
+ case DevFmtUShort:
+ format = oboe::AudioFormat::I16;
+ break;
+ case DevFmtInt:
+ case DevFmtUInt:
+ case DevFmtFloat:
+ format = oboe::AudioFormat::Float;
+ break;
+ }
+ builder.setFormat(format);
+ }
+
+ oboe::Result result{builder.openManagedStream(mStream)};
+ if(result == oboe::Result::ErrorInvalidFormat)
+ {
+ builder.setSampleRate(oboe::kUnspecified);
+ builder.setChannelCount(oboe::ChannelCount::Stereo);
+ builder.setFormat(oboe::AudioFormat::Unspecified);
+ result = builder.openManagedStream(mStream);
+ }
+ if(result != oboe::Result::OK)
+ throw al::backend_exception{ALC_INVALID_DEVICE, "Failed to create stream: %s",
+ oboe::convertToText(result)};
+
+ switch(mStream->getChannelCount())
+ {
+ case oboe::ChannelCount::Mono:
+ mDevice->FmtChans = DevFmtMono;
+ break;
+ case oboe::ChannelCount::Stereo:
+ mDevice->FmtChans = DevFmtStereo;
+ break;
+ /* Other potential configurations. Assume WFX channel order. */
+ case 4:
+ mDevice->FmtChans = DevFmtQuad;
+ break;
+ case 6:
+ mDevice->FmtChans = DevFmtX51Rear;
+ break;
+ case 7:
+ mDevice->FmtChans = DevFmtX61;
+ break;
+ case 8:
+ mDevice->FmtChans = DevFmtX71;
+ break;
+ default:
+ throw al::backend_exception{ALC_INVALID_DEVICE, "Got unhandled channel count: %d",
+ mStream->getChannelCount()};
+ }
+ SetDefaultWFXChannelOrder(mDevice);
+
+ switch(mStream->getFormat())
+ {
+ case oboe::AudioFormat::I16:
+ mDevice->FmtType = DevFmtShort;
+ break;
+ case oboe::AudioFormat::Float:
+ mDevice->FmtType = DevFmtFloat;
+ break;
+ case oboe::AudioFormat::Unspecified:
+ case oboe::AudioFormat::Invalid:
+ throw al::backend_exception{ALC_INVALID_DEVICE, "Got unhandled sample type: %d",
+ mStream->getFormat()};
+ }
+ mDevice->Frequency = static_cast<uint32_t>(mStream->getSampleRate());
+
+ /* Ensure the period size is no less than 10ms. It's possible for FramesPerBurst to be 0
+ * indicating variable updates, but OpenAL should have a reasonable minimum update size set.
+ */
+ mDevice->UpdateSize = maxu(mDevice->Frequency / 100,
+ static_cast<uint32_t>(mStream->getFramesPerBurst()));
+ mDevice->BufferSize = maxu(mDevice->UpdateSize * 2,
+ static_cast<uint32_t>(mStream->getBufferSizeInFrames()));
+
+ return true;
+}
+
+bool OboePlayback::start()
+{
+ oboe::Result result{mStream->start()};
+ if(result != oboe::Result::OK)
+ throw al::backend_exception{ALC_INVALID_DEVICE, "Failed to start stream: %s",
+ oboe::convertToText(result)};
+ return true;
+}
+
+void OboePlayback::stop()
+{
+ oboe::Result result{mStream->stop()};
+ if(result != oboe::Result::OK)
+ throw al::backend_exception{ALC_INVALID_DEVICE, "Failed to stop stream: %s",
+ oboe::convertToText(result)};
+}
+
+} // namespace
+
bool OboeBackendFactory::init() { return true; }
-bool OboeBackendFactory::querySupport(BackendType /*type*/)
-{ return false; }
+bool OboeBackendFactory::querySupport(BackendType type)
+{ return type == BackendType::Playback; }
-std::string OboeBackendFactory::probe(BackendType /*type*/)
+std::string OboeBackendFactory::probe(BackendType type)
{
+ switch(type)
+ {
+ case BackendType::Playback:
+ /* Includes null char. */
+ return std::string{device_name, sizeof(device_name)};
+ case BackendType::Capture:
+ break;
+ }
return std::string{};
}
-BackendPtr OboeBackendFactory::createBackend(ALCdevice* /*device*/, BackendType /*type*/)
+BackendPtr OboeBackendFactory::createBackend(ALCdevice *device, BackendType type)
{
+ if(type == BackendType::Playback)
+ return BackendPtr{new OboePlayback{device}};
return nullptr;
}