oboe.cpp 8.0 KB

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