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