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");