Limit convolution processing to the output ambisonic order
[openal-soft.git] / alc / backends / oboe.cpp
blobf4a9877a15c894f18de51c22268c7d8fa2bd88ed
2 #include "config.h"
4 #include "oboe.h"
6 #include <cassert>
7 #include <cstring>
9 #include "alu.h"
10 #include "logging.h"
12 #include "oboe/Oboe.h"
15 namespace {
17 constexpr char device_name[] = "Oboe Default";
20 struct OboePlayback final : public BackendBase, public oboe::AudioStreamCallback {
21 OboePlayback(ALCdevice *device) : BackendBase{device} { }
23 oboe::ManagedStream mStream;
25 oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
26 int32_t numFrames) override;
28 void open(const ALCchar *name) override;
29 bool reset() override;
30 void start() override;
31 void stop() override;
35 oboe::DataCallbackResult OboePlayback::onAudioReady(oboe::AudioStream *oboeStream, void *audioData,
36 int32_t numFrames)
38 assert(numFrames > 0);
39 const int32_t numChannels{oboeStream->getChannelCount()};
41 if UNLIKELY(numChannels > 2 && mDevice->FmtChans == DevFmtStereo)
43 /* If the device is only mixing stereo but there's more than two
44 * output channels, there are unused channels that need to be silenced.
46 if(mStream->getFormat() == oboe::AudioFormat::Float)
47 memset(audioData, 0, static_cast<uint32_t>(numFrames*numChannels)*sizeof(float));
48 else
49 memset(audioData, 0, static_cast<uint32_t>(numFrames*numChannels)*sizeof(int16_t));
52 mDevice->renderSamples(audioData, static_cast<uint32_t>(numFrames),
53 static_cast<uint32_t>(numChannels));
54 return oboe::DataCallbackResult::Continue;
58 void OboePlayback::open(const ALCchar *name)
60 if(!name)
61 name = device_name;
62 else if(std::strcmp(name, device_name) != 0)
63 throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
65 /* Open a basic output stream, just to ensure it can work. */
66 oboe::Result result{oboe::AudioStreamBuilder{}.setDirection(oboe::Direction::Output)
67 ->setPerformanceMode(oboe::PerformanceMode::LowLatency)
68 ->openManagedStream(mStream)};
69 if(result != oboe::Result::OK)
70 throw al::backend_exception{ALC_INVALID_VALUE, "Failed to create stream: %s",
71 oboe::convertToText(result)};
73 mDevice->DeviceName = name;
76 bool OboePlayback::reset()
78 oboe::AudioStreamBuilder builder;
79 builder.setDirection(oboe::Direction::Output);
80 builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
81 /* Don't let Oboe convert. We should be able to handle anything it gives
82 * back.
84 builder.setSampleRateConversionQuality(oboe::SampleRateConversionQuality::None);
85 builder.setChannelConversionAllowed(false);
86 builder.setFormatConversionAllowed(false);
87 builder.setCallback(this);
89 if(mDevice->Flags.get<FrequencyRequest>())
90 builder.setSampleRate(static_cast<int32_t>(mDevice->Frequency));
91 if(mDevice->Flags.get<ChannelsRequest>())
93 /* Only use mono or stereo at user request. There's no telling what
94 * other counts may be inferred as.
96 builder.setChannelCount((mDevice->FmtChans==DevFmtMono) ? oboe::ChannelCount::Mono
97 : (mDevice->FmtChans==DevFmtStereo) ? oboe::ChannelCount::Stereo
98 : oboe::ChannelCount::Unspecified);
100 if(mDevice->Flags.get<SampleTypeRequest>())
102 oboe::AudioFormat format{oboe::AudioFormat::Unspecified};
103 switch(mDevice->FmtType)
105 case DevFmtByte:
106 case DevFmtUByte:
107 case DevFmtShort:
108 case DevFmtUShort:
109 format = oboe::AudioFormat::I16;
110 break;
111 case DevFmtInt:
112 case DevFmtUInt:
113 case DevFmtFloat:
114 format = oboe::AudioFormat::Float;
115 break;
117 builder.setFormat(format);
120 oboe::Result result{builder.openManagedStream(mStream)};
121 /* If the format failed, try asking for the defaults. */
122 while(result == oboe::Result::ErrorInvalidFormat)
124 if(builder.getFormat() != oboe::AudioFormat::Unspecified)
125 builder.setFormat(oboe::AudioFormat::Unspecified);
126 else if(builder.getSampleRate() != oboe::kUnspecified)
127 builder.setSampleRate(oboe::kUnspecified);
128 else if(builder.getChannelCount() != oboe::ChannelCount::Unspecified)
129 builder.setChannelCount(oboe::ChannelCount::Unspecified);
130 else
131 break;
132 result = builder.openManagedStream(mStream);
134 if(result != oboe::Result::OK)
135 throw al::backend_exception{ALC_INVALID_DEVICE, "Failed to create stream: %s",
136 oboe::convertToText(result)};
137 TRACE("Got stream with properties:\n%s", oboe::convertToText(mStream.get()));
139 switch(mStream->getChannelCount())
141 case oboe::ChannelCount::Mono:
142 mDevice->FmtChans = DevFmtMono;
143 break;
144 case oboe::ChannelCount::Stereo:
145 mDevice->FmtChans = DevFmtStereo;
146 break;
147 /* Other potential configurations. Could be wrong, but better than failing.
148 * Assume WFX channel order.
150 case 4:
151 mDevice->FmtChans = DevFmtQuad;
152 break;
153 case 6:
154 mDevice->FmtChans = DevFmtX51Rear;
155 break;
156 case 7:
157 mDevice->FmtChans = DevFmtX61;
158 break;
159 case 8:
160 mDevice->FmtChans = DevFmtX71;
161 break;
162 default:
163 if(mStream->getChannelCount() < 1)
164 throw al::backend_exception{ALC_INVALID_DEVICE, "Got unhandled channel count: %d",
165 mStream->getChannelCount()};
166 /* Assume first two channels are front left/right. We can do a stereo
167 * mix and keep the other channels silent.
169 mDevice->FmtChans = DevFmtStereo;
170 break;
172 setDefaultWFXChannelOrder();
174 switch(mStream->getFormat())
176 case oboe::AudioFormat::I16:
177 mDevice->FmtType = DevFmtShort;
178 break;
179 case oboe::AudioFormat::Float:
180 mDevice->FmtType = DevFmtFloat;
181 break;
182 case oboe::AudioFormat::Unspecified:
183 case oboe::AudioFormat::Invalid:
184 throw al::backend_exception{ALC_INVALID_DEVICE, "Got unhandled sample type: %s",
185 oboe::convertToText(mStream->getFormat())};
187 mDevice->Frequency = static_cast<uint32_t>(mStream->getSampleRate());
189 /* Ensure the period size is no less than 10ms. It's possible for FramesPerCallback to be 0
190 * indicating variable updates, but OpenAL should have a reasonable minimum update size set.
191 * FramesPerBurst may not necessarily be correct, but hopefully it can act as a minimum
192 * update size.
194 mDevice->UpdateSize = maxu(mDevice->Frequency / 100,
195 static_cast<uint32_t>(mStream->getFramesPerBurst()));
196 mDevice->BufferSize = maxu(mDevice->UpdateSize * 2,
197 static_cast<uint32_t>(mStream->getBufferSizeInFrames()));
199 return true;
202 void OboePlayback::start()
204 const oboe::Result result{mStream->start()};
205 if(result != oboe::Result::OK)
206 throw al::backend_exception{ALC_INVALID_DEVICE, "Failed to start stream: %s",
207 oboe::convertToText(result)};
210 void OboePlayback::stop()
212 oboe::Result result{mStream->stop()};
213 if(result != oboe::Result::OK)
214 throw al::backend_exception{ALC_INVALID_DEVICE, "Failed to stop stream: %s",
215 oboe::convertToText(result)};
218 } // namespace
220 bool OboeBackendFactory::init() { return true; }
222 bool OboeBackendFactory::querySupport(BackendType type)
223 { return type == BackendType::Playback; }
225 std::string OboeBackendFactory::probe(BackendType type)
227 switch(type)
229 case BackendType::Playback:
230 /* Includes null char. */
231 return std::string{device_name, sizeof(device_name)};
232 case BackendType::Capture:
233 break;
235 return std::string{};
238 BackendPtr OboeBackendFactory::createBackend(ALCdevice *device, BackendType type)
240 if(type == BackendType::Playback)
241 return BackendPtr{new OboePlayback{device}};
242 return nullptr;
245 BackendFactory &OboeBackendFactory::getFactory()
247 static OboeBackendFactory factory{};
248 return factory;