diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foo_out_sdl.cc	Sat Jan 03 23:52:56 2026 -0500
@@ -0,0 +1,165 @@
+/* SDL output for foobar2000.
+ * Somewhat scuffed since SDL does not provide latency */
+
+#include <SDL3/SDL.h>
+
+#if audio_sample_size == 32
+# define OUTSDL_USE_NATIVE_F32 1
+# define OUTSDL_FORMAT SDL_AUDIO_F32
+#else
+# define OUTSDL_FORMAT SDL_AUDIO_S32
+#endif
+
+class OutputSDL : public output {
+public:
+	OutputSDL();
+	~OutputSDL();
+
+	virtual double get_latency() override;
+	virtual void process_samples(const audio_chunk &p_chunk) override;
+	virtual void update(bool &p_ready) override;
+	virtual void pause(bool p_state) override;
+	virtual void flush() override;
+	virtual void force_play() override;
+	virtual void volume_set(double p_val) override;
+
+private:
+	void ReinitStream(unsigned int channels, unsigned int freq);
+
+	SDL_AudioStream *stream_;
+#ifndef OUTSDL_USE_NATIVE_F32
+	std::vector<t_int32> buffer_;
+#endif
+	SDL_AudioSpec spec_;
+	HANDLE sdl_;
+
+#define FUNC(type, x, params, callparams) decltype(&SDL_##x) sdl3_##x;
+#include "foo_out_sdl_funcs.h"
+};
+
+OutputSDL::OutputSDL()
+	: sdl_(nullptr)
+	, stream_(nullptr)
+#ifndef OUTSDL_USE_NATIVE_F32
+	/* sane default size */
+	, buffer_(8096)
+#endif
+{
+	sdl_ = LoadLibraryA("foo_out_sdl_wrapper.dll");
+	if (!sdl3_)
+		return;
+
+#define FUNC(type, x, params, callparams) \
+	sdl3_##x = (decltype(&SDL_##x))GetProcAddress(sdl_, "SDL_" #x); \
+	if (!sdl3_##x) \
+		return;
+#include "foo_out_sdl_funcs.h"
+
+	// increment subsystem counter
+	// hope this succeeds!!!
+	sdl3_InitSubSystem(SDL_INIT_AUDIO);
+
+	/* make a guess */
+	spec_.format = OUTSDL_FORMAT;
+	spec_.channels = 2; /* Stereo is most likely. Who cares about surround? :) */
+	spec_.freq = 44100; /* guess CD quality */
+
+	// TODO supply output devices
+	stream_ = sdl3_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec_, nullptr, nullptr);
+}
+
+OutputSDL::~OutputSDL()
+{
+	if (sdl_ && stream_ && sdl3_DestroyAudioStream)
+		sdl3_DestroyAudioStream(stream_);
+	if (sdl_ && sdl3_QuitSubSystem)
+		sdl3_QuitSubSystem(SDL_INIT_AUDIO);
+	if (sdl_)
+		FreeLibrary(sdl_);
+}
+
+void OutputSDL::ReinitStream(unsigned int channels, unsigned int freq)
+{
+	// Fast path; no change
+	// I'm assuming that the channel input config is the same for fb2k
+	// and for SDL. This may not be the case. Whatever..
+	if (spec_.channels == channels && spec_.freq == freq)
+		return;
+
+	spec_.channels = channels;
+	spec_.freq = freq;
+
+	// tell SDL about our change
+	sdl3_SetAudioStreamFormat(stream_, &spec_, nullptr);
+}
+
+virtual double OutputSDL::get_latency()
+{
+	// TODO assume that one buffer's worth is queued
+	return 0.016; // i guess
+}
+
+virtual void OutputSDL::process_samples(const audio_chunk &p_chunk)
+{
+	// Reinitialize stream with possibly new values for channels and frequency
+	ReinitStream(p_chunk.get_channels(), p_chunk.get_srate());
+
+	/* NOTE this is only actually tested with stereo */
+#ifdef OUTSDL_USE_NATIVE_F32
+	/* audio_sample is 32-bit floating point; SDL can use this directly */
+	sdl3_PutAudioStreamData(stream_, p_chunk.get_data(), p_chunk.get_data_size());
+#else
+	/* Expand the buffer if necessary */
+	t_size sz = p_chunk.get_data_size();
+
+	if (sz > buffer_.size())
+		buffer_.resize(sz);
+
+	/* Convert to int32 */
+	audio_math::convert_to_int32(p_chunk.get_data(), sz, buffer_.data(), buffer_.size());
+
+	/* Add int32 audio to stream */
+	sdl3_PutAudioStreamData(stream_, buffer_.data(), buffer_.size());
+#endif
+}
+
+virtual void OutputSDL::update(bool &p_ready)
+{
+	/* seems legit */
+	p_ready = (sdl3_GetAudioStreamQueued(stream_) == 0);
+}
+
+virtual void OutputSDL::pause(bool p_state)
+{
+	if (p_state) {
+		sdl3_PauseAudioStreamDevice(stream_);
+	} else {
+		sdl3_ResumeAudioStreamDevice(stream_);
+	}
+}
+
+// . these are easy
+virtual void OutputSDL::flush()
+{
+	sdl3_ClearAudioStream(stream_);
+}
+
+virtual void OutputSDL::force_play()
+{
+	sdl3_FlushAudioStream(stream_);
+}
+
+virtual void OutputSDL::volume_set(double p_val)
+{
+	sdl3_SetAudioStreamGain(stream_, p_val);
+}
+
+static output_factory_t<OutputSDL> g_output_sdl_factory;
+
+////////////////////////////////////////////////////////////////////////////////
+
+const char *about = "Copyright (c) paper <paper@tflc.us>, 2026\n";
+
+// very beta ;)
+DECLARE_COMPONENT_VERSION("SDL output", "0.1", about);
+VALIDATE_COMPONENT_FILENAME("foo_out_sdl.dll");