sdl2.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. /**
  2. * OpenAL cross platform audio library
  3. * Copyright (C) 2018 by authors.
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Library General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Library General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Library General Public
  15. * License along with this library; if not, write to the
  16. * Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18. * Or go to http://www.gnu.org/copyleft/lgpl.html
  19. */
  20. #include "config.h"
  21. #include "sdl2.h"
  22. #include <cassert>
  23. #include <cstdlib>
  24. #include <cstring>
  25. #include <string>
  26. #include <string_view>
  27. #include "alnumeric.h"
  28. #include "core/device.h"
  29. _Pragma("GCC diagnostic push")
  30. _Pragma("GCC diagnostic ignored \"-Wold-style-cast\"")
  31. #include "SDL.h"
  32. _Pragma("GCC diagnostic pop")
  33. namespace {
  34. using namespace std::string_view_literals;
  35. [[nodiscard]] constexpr auto getDefaultDeviceName() noexcept -> std::string_view
  36. { return "Default Device"sv; }
  37. struct Sdl2Backend final : public BackendBase {
  38. explicit Sdl2Backend(DeviceBase *device) noexcept : BackendBase{device} { }
  39. ~Sdl2Backend() override;
  40. void audioCallback(Uint8 *stream, int len) noexcept;
  41. void open(std::string_view name) override;
  42. bool reset() override;
  43. void start() override;
  44. void stop() override;
  45. std::string mSDLName;
  46. SDL_AudioDeviceID mDeviceID{0u};
  47. uint mFrameSize{0};
  48. };
  49. Sdl2Backend::~Sdl2Backend()
  50. {
  51. if(mDeviceID)
  52. SDL_CloseAudioDevice(mDeviceID);
  53. mDeviceID = 0;
  54. }
  55. void Sdl2Backend::audioCallback(Uint8 *stream, int len) noexcept
  56. {
  57. const auto ulen = static_cast<unsigned int>(len);
  58. assert((ulen % mFrameSize) == 0);
  59. mDevice->renderSamples(stream, ulen / mFrameSize, mDevice->channelsFromFmt());
  60. }
  61. void Sdl2Backend::open(std::string_view name)
  62. {
  63. SDL_AudioSpec want{}, have{};
  64. want.freq = static_cast<int>(mDevice->mSampleRate);
  65. switch(mDevice->FmtType)
  66. {
  67. case DevFmtUByte: want.format = AUDIO_U8; break;
  68. case DevFmtByte: want.format = AUDIO_S8; break;
  69. case DevFmtUShort: want.format = AUDIO_U16SYS; break;
  70. case DevFmtShort: want.format = AUDIO_S16SYS; break;
  71. case DevFmtUInt: /* fall-through */
  72. case DevFmtInt: want.format = AUDIO_S32SYS; break;
  73. case DevFmtFloat: want.format = AUDIO_F32; break;
  74. }
  75. want.channels = static_cast<Uint8>(std::min<uint>(mDevice->channelsFromFmt(),
  76. std::numeric_limits<Uint8>::max()));
  77. want.samples = static_cast<Uint16>(std::min(mDevice->mUpdateSize, 8192u));
  78. want.callback = [](void *ptr, Uint8 *stream, int len) noexcept
  79. { return static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); };
  80. want.userdata = this;
  81. /* Passing nullptr to SDL_OpenAudioDevice opens a default, which isn't
  82. * necessarily the first in the list.
  83. */
  84. const auto defaultDeviceName = getDefaultDeviceName();
  85. if(name.empty() || name == defaultDeviceName)
  86. {
  87. name = defaultDeviceName;
  88. mSDLName.clear();
  89. mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have,
  90. SDL_AUDIO_ALLOW_ANY_CHANGE);
  91. }
  92. else
  93. {
  94. mSDLName = name;
  95. mDeviceID = SDL_OpenAudioDevice(mSDLName.c_str(), SDL_FALSE, &want, &have,
  96. SDL_AUDIO_ALLOW_ANY_CHANGE);
  97. }
  98. if(!mDeviceID)
  99. throw al::backend_exception{al::backend_error::NoDevice, "{}", SDL_GetError()};
  100. DevFmtType devtype{};
  101. switch(have.format)
  102. {
  103. case AUDIO_U8: devtype = DevFmtUByte; break;
  104. case AUDIO_S8: devtype = DevFmtByte; break;
  105. case AUDIO_U16SYS: devtype = DevFmtUShort; break;
  106. case AUDIO_S16SYS: devtype = DevFmtShort; break;
  107. case AUDIO_S32SYS: devtype = DevFmtInt; break;
  108. case AUDIO_F32SYS: devtype = DevFmtFloat; break;
  109. default:
  110. throw al::backend_exception{al::backend_error::DeviceError,
  111. "Unhandled SDL format: {:#04x}", have.format};
  112. }
  113. mFrameSize = BytesFromDevFmt(devtype) * have.channels;
  114. mDeviceName = name;
  115. }
  116. bool Sdl2Backend::reset()
  117. {
  118. if(mDeviceID)
  119. SDL_CloseAudioDevice(mDeviceID);
  120. mDeviceID = 0;
  121. auto want = SDL_AudioSpec{};
  122. want.freq = static_cast<int>(mDevice->mSampleRate);
  123. switch(mDevice->FmtType)
  124. {
  125. case DevFmtUByte: want.format = AUDIO_U8; break;
  126. case DevFmtByte: want.format = AUDIO_S8; break;
  127. case DevFmtUShort: want.format = AUDIO_U16SYS; break;
  128. case DevFmtShort: want.format = AUDIO_S16SYS; break;
  129. case DevFmtUInt: [[fallthrough]];
  130. case DevFmtInt: want.format = AUDIO_S32SYS; break;
  131. case DevFmtFloat: want.format = AUDIO_F32; break;
  132. }
  133. want.channels = static_cast<Uint8>(std::min<uint>(mDevice->channelsFromFmt(),
  134. std::numeric_limits<Uint8>::max()));
  135. want.samples = static_cast<Uint16>(std::min(mDevice->mUpdateSize, 8192u));
  136. want.callback = [](void *ptr, Uint8 *stream, int len) noexcept
  137. { return static_cast<Sdl2Backend*>(ptr)->audioCallback(stream, len); };
  138. want.userdata = this;
  139. auto have = SDL_AudioSpec{};
  140. if(mSDLName.empty())
  141. {
  142. mDeviceID = SDL_OpenAudioDevice(nullptr, SDL_FALSE, &want, &have,
  143. SDL_AUDIO_ALLOW_ANY_CHANGE);
  144. }
  145. else
  146. {
  147. mDeviceID = SDL_OpenAudioDevice(mSDLName.c_str(), SDL_FALSE, &want, &have,
  148. SDL_AUDIO_ALLOW_ANY_CHANGE);
  149. }
  150. if(!mDeviceID)
  151. throw al::backend_exception{al::backend_error::NoDevice, "{}", SDL_GetError()};
  152. if(have.channels != mDevice->channelsFromFmt())
  153. {
  154. /* SDL guarantees these layouts for the given channel count. */
  155. if(have.channels == 8)
  156. mDevice->FmtChans = DevFmtX71;
  157. else if(have.channels == 7)
  158. mDevice->FmtChans = DevFmtX61;
  159. else if(have.channels == 6)
  160. mDevice->FmtChans = DevFmtX51;
  161. else if(have.channels == 4)
  162. mDevice->FmtChans = DevFmtQuad;
  163. else if(have.channels >= 2)
  164. mDevice->FmtChans = DevFmtStereo;
  165. else if(have.channels == 1)
  166. mDevice->FmtChans = DevFmtMono;
  167. else
  168. throw al::backend_exception{al::backend_error::DeviceError,
  169. "Unhandled SDL channel count: {}", int{have.channels}};
  170. mDevice->mAmbiOrder = 0;
  171. }
  172. switch(have.format)
  173. {
  174. case AUDIO_U8: mDevice->FmtType = DevFmtUByte; break;
  175. case AUDIO_S8: mDevice->FmtType = DevFmtByte; break;
  176. case AUDIO_U16SYS: mDevice->FmtType = DevFmtUShort; break;
  177. case AUDIO_S16SYS: mDevice->FmtType = DevFmtShort; break;
  178. case AUDIO_S32SYS: mDevice->FmtType = DevFmtInt; break;
  179. case AUDIO_F32SYS: mDevice->FmtType = DevFmtFloat; break;
  180. default:
  181. throw al::backend_exception{al::backend_error::DeviceError,
  182. "Unhandled SDL format: {:#04x}", have.format};
  183. }
  184. mFrameSize = BytesFromDevFmt(mDevice->FmtType) * have.channels;
  185. if(have.freq < int{MinOutputRate})
  186. throw al::backend_exception{al::backend_error::DeviceError,
  187. "Unhandled SDL sample rate: {}", have.freq};
  188. mDevice->mSampleRate = static_cast<uint>(have.freq);
  189. mDevice->mUpdateSize = have.samples;
  190. mDevice->mBufferSize = std::max(have.size/mFrameSize, mDevice->mUpdateSize*2u);
  191. setDefaultWFXChannelOrder();
  192. return true;
  193. }
  194. void Sdl2Backend::start()
  195. { SDL_PauseAudioDevice(mDeviceID, 0); }
  196. void Sdl2Backend::stop()
  197. { SDL_PauseAudioDevice(mDeviceID, 1); }
  198. } // namespace
  199. BackendFactory &SDL2BackendFactory::getFactory()
  200. {
  201. static SDL2BackendFactory factory{};
  202. return factory;
  203. }
  204. bool SDL2BackendFactory::init()
  205. { return (SDL_InitSubSystem(SDL_INIT_AUDIO) == 0); }
  206. bool SDL2BackendFactory::querySupport(BackendType type)
  207. { return type == BackendType::Playback; }
  208. auto SDL2BackendFactory::enumerate(BackendType type) -> std::vector<std::string>
  209. {
  210. std::vector<std::string> outnames;
  211. if(type != BackendType::Playback)
  212. return outnames;
  213. int num_devices{SDL_GetNumAudioDevices(SDL_FALSE)};
  214. if(num_devices <= 0)
  215. return outnames;
  216. outnames.reserve(static_cast<unsigned int>(num_devices)+1_uz);
  217. outnames.emplace_back(getDefaultDeviceName());
  218. for(int i{0};i < num_devices;++i)
  219. {
  220. if(const char *name = SDL_GetAudioDeviceName(i, SDL_FALSE))
  221. outnames.emplace_back(name);
  222. else
  223. outnames.emplace_back("Unknown Device Name #"+std::to_string(i));
  224. }
  225. return outnames;
  226. }
  227. BackendPtr SDL2BackendFactory::createBackend(DeviceBase *device, BackendType type)
  228. {
  229. if(type == BackendType::Playback)
  230. return BackendPtr{new Sdl2Backend{device}};
  231. return nullptr;
  232. }