Mercurial > foo_out_sdl
comparison foo_out_sdl.cc @ 0:e9bb126753e7
*: initial commit
totally untested besides the wrapper; need to get msvc running
| author | Paper <paper@tflc.us> |
|---|---|
| date | Sat, 03 Jan 2026 23:52:56 -0500 |
| parents | |
| children | 20d02a178406 |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:e9bb126753e7 |
|---|---|
| 1 /* SDL output for foobar2000. | |
| 2 * Somewhat scuffed since SDL does not provide latency */ | |
| 3 | |
| 4 #include <SDL3/SDL.h> | |
| 5 | |
| 6 #if audio_sample_size == 32 | |
| 7 # define OUTSDL_USE_NATIVE_F32 1 | |
| 8 # define OUTSDL_FORMAT SDL_AUDIO_F32 | |
| 9 #else | |
| 10 # define OUTSDL_FORMAT SDL_AUDIO_S32 | |
| 11 #endif | |
| 12 | |
| 13 class OutputSDL : public output { | |
| 14 public: | |
| 15 OutputSDL(); | |
| 16 ~OutputSDL(); | |
| 17 | |
| 18 virtual double get_latency() override; | |
| 19 virtual void process_samples(const audio_chunk &p_chunk) override; | |
| 20 virtual void update(bool &p_ready) override; | |
| 21 virtual void pause(bool p_state) override; | |
| 22 virtual void flush() override; | |
| 23 virtual void force_play() override; | |
| 24 virtual void volume_set(double p_val) override; | |
| 25 | |
| 26 private: | |
| 27 void ReinitStream(unsigned int channels, unsigned int freq); | |
| 28 | |
| 29 SDL_AudioStream *stream_; | |
| 30 #ifndef OUTSDL_USE_NATIVE_F32 | |
| 31 std::vector<t_int32> buffer_; | |
| 32 #endif | |
| 33 SDL_AudioSpec spec_; | |
| 34 HANDLE sdl_; | |
| 35 | |
| 36 #define FUNC(type, x, params, callparams) decltype(&SDL_##x) sdl3_##x; | |
| 37 #include "foo_out_sdl_funcs.h" | |
| 38 }; | |
| 39 | |
| 40 OutputSDL::OutputSDL() | |
| 41 : sdl_(nullptr) | |
| 42 , stream_(nullptr) | |
| 43 #ifndef OUTSDL_USE_NATIVE_F32 | |
| 44 /* sane default size */ | |
| 45 , buffer_(8096) | |
| 46 #endif | |
| 47 { | |
| 48 sdl_ = LoadLibraryA("foo_out_sdl_wrapper.dll"); | |
| 49 if (!sdl3_) | |
| 50 return; | |
| 51 | |
| 52 #define FUNC(type, x, params, callparams) \ | |
| 53 sdl3_##x = (decltype(&SDL_##x))GetProcAddress(sdl_, "SDL_" #x); \ | |
| 54 if (!sdl3_##x) \ | |
| 55 return; | |
| 56 #include "foo_out_sdl_funcs.h" | |
| 57 | |
| 58 // increment subsystem counter | |
| 59 // hope this succeeds!!! | |
| 60 sdl3_InitSubSystem(SDL_INIT_AUDIO); | |
| 61 | |
| 62 /* make a guess */ | |
| 63 spec_.format = OUTSDL_FORMAT; | |
| 64 spec_.channels = 2; /* Stereo is most likely. Who cares about surround? :) */ | |
| 65 spec_.freq = 44100; /* guess CD quality */ | |
| 66 | |
| 67 // TODO supply output devices | |
| 68 stream_ = sdl3_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec_, nullptr, nullptr); | |
| 69 } | |
| 70 | |
| 71 OutputSDL::~OutputSDL() | |
| 72 { | |
| 73 if (sdl_ && stream_ && sdl3_DestroyAudioStream) | |
| 74 sdl3_DestroyAudioStream(stream_); | |
| 75 if (sdl_ && sdl3_QuitSubSystem) | |
| 76 sdl3_QuitSubSystem(SDL_INIT_AUDIO); | |
| 77 if (sdl_) | |
| 78 FreeLibrary(sdl_); | |
| 79 } | |
| 80 | |
| 81 void OutputSDL::ReinitStream(unsigned int channels, unsigned int freq) | |
| 82 { | |
| 83 // Fast path; no change | |
| 84 // I'm assuming that the channel input config is the same for fb2k | |
| 85 // and for SDL. This may not be the case. Whatever.. | |
| 86 if (spec_.channels == channels && spec_.freq == freq) | |
| 87 return; | |
| 88 | |
| 89 spec_.channels = channels; | |
| 90 spec_.freq = freq; | |
| 91 | |
| 92 // tell SDL about our change | |
| 93 sdl3_SetAudioStreamFormat(stream_, &spec_, nullptr); | |
| 94 } | |
| 95 | |
| 96 virtual double OutputSDL::get_latency() | |
| 97 { | |
| 98 // TODO assume that one buffer's worth is queued | |
| 99 return 0.016; // i guess | |
| 100 } | |
| 101 | |
| 102 virtual void OutputSDL::process_samples(const audio_chunk &p_chunk) | |
| 103 { | |
| 104 // Reinitialize stream with possibly new values for channels and frequency | |
| 105 ReinitStream(p_chunk.get_channels(), p_chunk.get_srate()); | |
| 106 | |
| 107 /* NOTE this is only actually tested with stereo */ | |
| 108 #ifdef OUTSDL_USE_NATIVE_F32 | |
| 109 /* audio_sample is 32-bit floating point; SDL can use this directly */ | |
| 110 sdl3_PutAudioStreamData(stream_, p_chunk.get_data(), p_chunk.get_data_size()); | |
| 111 #else | |
| 112 /* Expand the buffer if necessary */ | |
| 113 t_size sz = p_chunk.get_data_size(); | |
| 114 | |
| 115 if (sz > buffer_.size()) | |
| 116 buffer_.resize(sz); | |
| 117 | |
| 118 /* Convert to int32 */ | |
| 119 audio_math::convert_to_int32(p_chunk.get_data(), sz, buffer_.data(), buffer_.size()); | |
| 120 | |
| 121 /* Add int32 audio to stream */ | |
| 122 sdl3_PutAudioStreamData(stream_, buffer_.data(), buffer_.size()); | |
| 123 #endif | |
| 124 } | |
| 125 | |
| 126 virtual void OutputSDL::update(bool &p_ready) | |
| 127 { | |
| 128 /* seems legit */ | |
| 129 p_ready = (sdl3_GetAudioStreamQueued(stream_) == 0); | |
| 130 } | |
| 131 | |
| 132 virtual void OutputSDL::pause(bool p_state) | |
| 133 { | |
| 134 if (p_state) { | |
| 135 sdl3_PauseAudioStreamDevice(stream_); | |
| 136 } else { | |
| 137 sdl3_ResumeAudioStreamDevice(stream_); | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 // . these are easy | |
| 142 virtual void OutputSDL::flush() | |
| 143 { | |
| 144 sdl3_ClearAudioStream(stream_); | |
| 145 } | |
| 146 | |
| 147 virtual void OutputSDL::force_play() | |
| 148 { | |
| 149 sdl3_FlushAudioStream(stream_); | |
| 150 } | |
| 151 | |
| 152 virtual void OutputSDL::volume_set(double p_val) | |
| 153 { | |
| 154 sdl3_SetAudioStreamGain(stream_, p_val); | |
| 155 } | |
| 156 | |
| 157 static output_factory_t<OutputSDL> g_output_sdl_factory; | |
| 158 | |
| 159 //////////////////////////////////////////////////////////////////////////////// | |
| 160 | |
| 161 const char *about = "Copyright (c) paper <paper@tflc.us>, 2026\n"; | |
| 162 | |
| 163 // very beta ;) | |
| 164 DECLARE_COMPONENT_VERSION("SDL output", "0.1", about); | |
| 165 VALIDATE_COMPONENT_FILENAME("foo_out_sdl.dll"); |
