您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

247 行
7.6 KiB

  1. //
  2. // Lol Engine
  3. //
  4. // Copyright © 2010—2019 Sam Hocevar <sam@hocevar.net>
  5. //
  6. // Lol Engine is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #include <lol/engine-internal.h>
  13. #include <array>
  14. #include <unordered_set>
  15. #include <functional>
  16. #if LOL_USE_SDL_MIXER
  17. # if HAVE_SDL2_SDL_H
  18. # include <SDL2/SDL.h>
  19. # include <SDL2/SDL_mixer.h>
  20. # elif HAVE_SDL_H
  21. # include <SDL.h>
  22. # include <SDL_mixer.h>
  23. # endif
  24. #endif
  25. // Buffer size, in samples (https://wiki.libsdl.org/SDL_AudioSpec)
  26. // “[…] refers to the size of the audio buffer in sample frames. A sample frame
  27. // is a chunk of audio data of the size specified in format multiplied by the
  28. // number of channels.”
  29. #define LOL_AUDIO_DEFAULT_FRAMES 1024
  30. #define LOL_AUDIO_DEFAULT_TRACKS 8
  31. #define LOL_AUDIO_DEFAULT_CHANNELS 2
  32. #define LOL_AUDIO_DEFAULT_FORMAT AUDIO_S16
  33. #define LOL_AUDIO_DEFAULT_RATE 22050
  34. namespace lol
  35. {
  36. #if defined LOL_USE_SDL_MIXER
  37. struct audio_streamer
  38. {
  39. int m_track = -1;
  40. std::function<void(void *, int)> m_callback;
  41. // Buffer where the streaming client will write, and where the SDL
  42. // audio conversion may happen.
  43. std::vector<uint8_t> m_convert_buffer;
  44. // Remaining bytes in the conversion buffer.
  45. int m_extra_bytes = 0;
  46. // Buffer used to write the converted audio data for mixing.
  47. std::vector<uint8_t> m_output_buffer;
  48. SDL_AudioCVT m_convert;
  49. Mix_Chunk *m_chunk = nullptr;
  50. };
  51. static std::unordered_set<std::shared_ptr<audio_streamer>> g_streamers;
  52. // The global audio format
  53. static int g_frequency, g_channels;
  54. static audio::format g_format;
  55. static audio::format sdl2lol_format(Uint16 sdl_format)
  56. {
  57. switch (sdl_format)
  58. {
  59. case AUDIO_U8: return audio::format::uint8;
  60. case AUDIO_S8: return audio::format::sint8;
  61. case AUDIO_U16LSB: return audio::format::uint16le;
  62. case AUDIO_U16MSB: return audio::format::uint16be;
  63. case AUDIO_S16LSB: return audio::format::sint16le;
  64. case AUDIO_S16MSB: return audio::format::sint16be;
  65. case AUDIO_S32LSB: return audio::format::sint32le;
  66. case AUDIO_S32MSB: return audio::format::sint32be;
  67. case AUDIO_F32LSB: return audio::format::float32le;
  68. case AUDIO_F32MSB: return audio::format::float32be;
  69. default: return audio::format::unknown;
  70. }
  71. }
  72. static int lol2sdl_format(audio::format format)
  73. {
  74. switch (format)
  75. {
  76. case audio::format::uint8: return AUDIO_U8;
  77. case audio::format::sint8: return AUDIO_S8;
  78. case audio::format::uint16le: return AUDIO_U16LSB;
  79. case audio::format::uint16be: return AUDIO_U16MSB;
  80. case audio::format::sint16le: return AUDIO_S16LSB;
  81. case audio::format::sint16be: return AUDIO_S16MSB;
  82. case audio::format::sint32le: return AUDIO_S32LSB;
  83. case audio::format::sint32be: return AUDIO_S32MSB;
  84. case audio::format::float32le: return AUDIO_F32LSB;
  85. case audio::format::float32be: return AUDIO_F32MSB;
  86. default: return 0;
  87. }
  88. }
  89. #endif
  90. /*
  91. * Public audio class
  92. */
  93. #if defined LOL_USE_SDL_MIXER
  94. void audio::init()
  95. {
  96. if (Mix_OpenAudio(LOL_AUDIO_DEFAULT_RATE, LOL_AUDIO_DEFAULT_FORMAT, LOL_AUDIO_DEFAULT_CHANNELS, LOL_AUDIO_DEFAULT_FRAMES) < 0)
  97. {
  98. msg::error("error opening audio: %s\n", Mix_GetError());
  99. return;
  100. }
  101. set_tracks(LOL_AUDIO_DEFAULT_TRACKS);
  102. Uint16 sdl_format;
  103. if (Mix_QuerySpec(&g_frequency, &sdl_format, &g_channels) == 0)
  104. {
  105. msg::error("error querying audio: %s\n", Mix_GetError());
  106. return;
  107. }
  108. g_format = sdl2lol_format(sdl_format);
  109. char const *u = (SDL_AUDIO_ISFLOAT(sdl_format) || SDL_AUDIO_ISSIGNED(sdl_format)) ? "" : "u";
  110. char const *t = SDL_AUDIO_ISFLOAT(sdl_format) ? "float" : "int";
  111. int b = SDL_AUDIO_BITSIZE(sdl_format);
  112. char const *e = b <= 8 ? "" : SDL_AUDIO_ISLITTLEENDIAN(sdl_format) ? "le" : "be";
  113. msg::info("audio initialised: freq=%dHz format=%s%s%d%s channels=%d\n",
  114. g_frequency, u, t, b, e, g_channels);
  115. }
  116. void audio::shutdown()
  117. {
  118. }
  119. void audio::set_tracks(int tracks)
  120. {
  121. Mix_AllocateChannels(tracks);
  122. }
  123. void audio::set_volume(int track, int volume)
  124. {
  125. Mix_Volume(track, volume);
  126. }
  127. void audio::mute_all()
  128. {
  129. Mix_Volume(-1, 0);
  130. }
  131. void audio::unmute_all()
  132. {
  133. Mix_Volume(-1, MIX_MAX_VOLUME);
  134. }
  135. int audio::start_streaming(std::function<void(void *, int)> const &f,
  136. enum audio::format format /* = audio::format::sint16le */,
  137. int frequency /* = 22050 */,
  138. int channels /* = 2 */)
  139. {
  140. static auto trampoline = [](int, void *stream, int bytes, void *udata)
  141. {
  142. auto s = (audio_streamer *)udata;
  143. // If there were still bytes from a previous conversion, copy them.
  144. if (s->m_extra_bytes)
  145. {
  146. int tocopy = lol::min(bytes, s->m_extra_bytes);
  147. memcpy(stream, s->m_convert.buf + s->m_convert.len_cvt - s->m_extra_bytes, tocopy);
  148. s->m_extra_bytes -= tocopy;
  149. bytes -= tocopy;
  150. stream = (void *)((uint8_t *)stream + tocopy);
  151. }
  152. // Ask the callback for more bytes as long as we need them.
  153. while (bytes > 0)
  154. {
  155. s->m_callback(s->m_convert.buf, s->m_convert.len);
  156. if (s->m_convert.needed)
  157. SDL_ConvertAudio(&s->m_convert);
  158. int tocopy = lol::min(bytes, s->m_convert.len_cvt);
  159. s->m_extra_bytes = s->m_convert.len_cvt - tocopy;
  160. memcpy(stream, s->m_convert.buf, tocopy);
  161. bytes -= tocopy;
  162. stream = (void *)((uint8_t *)stream + tocopy);
  163. }
  164. };
  165. auto s = std::make_shared<audio_streamer>();
  166. g_streamers.insert(s);
  167. // Build an audio converter that converts from a given streaming format
  168. // to the format SDL was currently initialised with, if necessary.
  169. Uint16 sdl_format = lol2sdl_format(format);
  170. Uint16 sdl_g_format = lol2sdl_format(g_format);
  171. SDL_BuildAudioCVT(&s->m_convert, sdl_format, channels, frequency, sdl_g_format, g_channels, g_frequency);
  172. // This is how many bytes we will ask the streaming callback
  173. s->m_convert.len = LOL_AUDIO_DEFAULT_FRAMES * channels * SDL_AUDIO_BITSIZE(sdl_format) / 8;
  174. s->m_convert_buffer.resize(s->m_convert.len * s->m_convert.len_mult);
  175. s->m_convert.buf = s->m_convert_buffer.data();
  176. // This is how many bytes we will send to the SDL mixer
  177. Uint32 output_bytes = LOL_AUDIO_DEFAULT_FRAMES * g_channels * SDL_AUDIO_BITSIZE(sdl_g_format) / 8;
  178. s->m_output_buffer.resize(output_bytes);
  179. Uint8* audio_data = (Uint8*)s->m_output_buffer.data();
  180. s->m_chunk = Mix_QuickLoad_RAW(audio_data, output_bytes);
  181. s->m_track = Mix_PlayChannel(-1, s->m_chunk, -1);
  182. s->m_callback = f;
  183. Mix_RegisterEffect(s->m_track, trampoline, nullptr, s.get());
  184. return s->m_track;
  185. }
  186. void audio::stop_streaming(int track)
  187. {
  188. for (auto streamer : g_streamers)
  189. {
  190. if (streamer->m_track == track)
  191. {
  192. Mix_HaltChannel(track);
  193. g_streamers.erase(streamer);
  194. break;
  195. }
  196. }
  197. }
  198. #elif __NX__
  199. #else
  200. void audio::init() {}
  201. void audio::shutdown() {}
  202. void audio::set_tracks(int) {}
  203. void audio::set_volume(int, int) {}
  204. void audio::mute_all() {}
  205. void audio::unmute_all() {}
  206. int audio::start_streaming(std::function<void(void *, int)> const &,
  207. enum audio::format, int, int) { return -1; }
  208. void audio::stop_streaming(int) {}
  209. #endif
  210. } /* namespace lol */