Another 1.0 candidate before I start stripping meson options
							parent
							
								
									6075950a35
								
							
						
					
					
						commit
						077d715da0
					
				@ -1,40 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "Configured.hxx"
 | 
					 | 
				
			||||||
#include "EncoderList.hxx"
 | 
					 | 
				
			||||||
#include "EncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "config/Block.hxx"
 | 
					 | 
				
			||||||
#include "lib/fmt/RuntimeError.hxx"
 | 
					 | 
				
			||||||
#include "util/StringAPI.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const EncoderPlugin &
 | 
					 | 
				
			||||||
GetConfiguredEncoderPlugin(const ConfigBlock &block, bool shout_legacy)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *name = block.GetBlockValue("encoder", nullptr);
 | 
					 | 
				
			||||||
	if (name == nullptr && shout_legacy)
 | 
					 | 
				
			||||||
		name = block.GetBlockValue("encoding", nullptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (name == nullptr)
 | 
					 | 
				
			||||||
		name = "vorbis";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (shout_legacy) {
 | 
					 | 
				
			||||||
		if (StringIsEqual(name, "ogg"))
 | 
					 | 
				
			||||||
			name = "vorbis";
 | 
					 | 
				
			||||||
		else if (StringIsEqual(name, "mp3"))
 | 
					 | 
				
			||||||
			name = "lame";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const auto plugin = encoder_plugin_get(name);
 | 
					 | 
				
			||||||
	if (plugin == nullptr)
 | 
					 | 
				
			||||||
		throw FmtRuntimeError("No such encoder: {}", name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return *plugin;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
PreparedEncoder *
 | 
					 | 
				
			||||||
CreateConfiguredEncoder(const ConfigBlock &block, bool shout_legacy)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return encoder_init(GetConfiguredEncoderPlugin(block, shout_legacy),
 | 
					 | 
				
			||||||
			    block);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,23 +1,23 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Encoder support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_ENCODER_CONFIGURED_HXX
 | 
					#ifndef MPD_ENCODER_CONFIGURED_HXX
 | 
				
			||||||
#define MPD_ENCODER_CONFIGURED_HXX
 | 
					#define MPD_ENCODER_CONFIGURED_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "EncoderInterface.hxx"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ConfigBlock;
 | 
					struct ConfigBlock;
 | 
				
			||||||
class PreparedEncoder;
 | 
					class AudioFormat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Create a #PreparedEncoder instance from the settings in the
 | 
					 * Stub implementation - encoder support not needed for database creation
 | 
				
			||||||
 * #ConfigBlock.  Its "encoder" setting is used to choose the encoder
 | 
					 | 
				
			||||||
 * plugin.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Throws an exception on error.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param shout_legacy enable the "shout" plugin legacy configuration?
 | 
					 | 
				
			||||||
 * i.e. fall back to setting "encoding" instead of "encoder"
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
PreparedEncoder *
 | 
					inline EncoderPtr
 | 
				
			||||||
CreateConfiguredEncoder(const ConfigBlock &block, bool shout_legacy=false);
 | 
					encoder_init([[maybe_unused]] const ConfigBlock &block,
 | 
				
			||||||
 | 
						     [[maybe_unused]] AudioFormat &audio_format)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,22 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * This header is included by encoder plugins.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_ENCODER_API_HXX
 | 
					 | 
				
			||||||
#define MPD_ENCODER_API_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IWYU pragma: begin_exports
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "EncoderInterface.hxx"
 | 
					 | 
				
			||||||
#include "EncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "tag/Tag.hxx"
 | 
					 | 
				
			||||||
#include "config/Block.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// IWYU pragma: end_exports
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,122 +1,20 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Encoder support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_ENCODER_INTERFACE_HXX
 | 
					#ifndef MPD_ENCODER_INTERFACE_HXX
 | 
				
			||||||
#define MPD_ENCODER_INTERFACE_HXX
 | 
					#define MPD_ENCODER_INTERFACE_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <cstddef>
 | 
					#include <memory>
 | 
				
			||||||
#include <span>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct AudioFormat;
 | 
					 | 
				
			||||||
struct Tag;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Stub implementation - encoder support not needed for database creation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
class Encoder {
 | 
					class Encoder {
 | 
				
			||||||
	const bool implements_tag;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	explicit Encoder(bool _implements_tag) noexcept
 | 
						virtual ~Encoder() = default;
 | 
				
			||||||
		:implements_tag(_implements_tag) {}
 | 
					 | 
				
			||||||
	virtual ~Encoder() noexcept = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool ImplementsTag() const noexcept {
 | 
					 | 
				
			||||||
		return implements_tag;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Ends the stream: flushes the encoder object, generate an
 | 
					 | 
				
			||||||
	 * end-of-stream marker (if applicable), make everything which
 | 
					 | 
				
			||||||
	 * might currently be buffered available by Read().
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * After this function has been called, the encoder may not be
 | 
					 | 
				
			||||||
	 * usable for more data, and only Read() can be called.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Throws on error.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual void End() {
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Flushes an encoder object, make everything which might
 | 
					 | 
				
			||||||
	 * currently be buffered available by Read().
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Throws on error.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual void Flush() {
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Prepare for sending a tag to the encoder.  This is used by
 | 
					 | 
				
			||||||
	 * some encoders to flush the previous sub-stream, in
 | 
					 | 
				
			||||||
	 * preparation to begin a new one.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Throws on error.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual void PreTag() {
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Sends a tag to the encoder.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Instructions: call PreTag(); then obtain flushed data with
 | 
					 | 
				
			||||||
	 * Read(); finally call Tag() and again Read().
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Throws on error.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param tag the tag object
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual void SendTag([[maybe_unused]] const Tag &tag) {
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Writes raw PCM data to the encoder.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Throws on error.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param data the buffer containing PCM samples
 | 
					 | 
				
			||||||
	 * @param length the length of the buffer in bytes
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual void Write(std::span<const std::byte> src) = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Reads encoded data from the encoder.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Call this repeatedly after End(), Flush(), PreTag(), SendTag() and
 | 
					 | 
				
			||||||
	 *  Write() until no more data is returned.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param buffer a buffer that can be used to write data into
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return the portion of the buffer that was filled (but may
 | 
					 | 
				
			||||||
	 * also point to a different buffer, e.g. one owned by this object)
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual std::span<const std::byte> Read(std::span<std::byte> buffer) noexcept = 0;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PreparedEncoder {
 | 
					using EncoderPtr = std::unique_ptr<Encoder>;
 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	virtual ~PreparedEncoder() noexcept = default;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Create an #Encoder instance.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * After this function returns successfully and before the
 | 
					 | 
				
			||||||
	 * first Encoder::Write() call, you should invoke
 | 
					 | 
				
			||||||
	 * Encoder::Read() to obtain the file header.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Throws on error.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param audio_format the encoder's input audio format; the plugin
 | 
					 | 
				
			||||||
	 * may modify the struct to adapt it to its abilities
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual Encoder *Open(AudioFormat &audio_format) = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Get mime type of encoded content.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return an constant string, nullptr on failure
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual const char *GetMimeType() const noexcept {
 | 
					 | 
				
			||||||
		return nullptr;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,55 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "config.h"
 | 
					 | 
				
			||||||
#include "EncoderList.hxx"
 | 
					 | 
				
			||||||
#include "EncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "encoder/Features.h"
 | 
					 | 
				
			||||||
#include "plugins/NullEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/WaveEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/VorbisEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/OpusEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/FlacEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/ShineEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/LameEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/TwolameEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "decoder/Features.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
constinit const EncoderPlugin *const encoder_plugins[] = {
 | 
					 | 
				
			||||||
	&null_encoder_plugin,
 | 
					 | 
				
			||||||
#ifdef ENABLE_VORBISENC
 | 
					 | 
				
			||||||
	&vorbis_encoder_plugin,
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#ifdef ENABLE_OPUS
 | 
					 | 
				
			||||||
	&opus_encoder_plugin,
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#ifdef ENABLE_LAME
 | 
					 | 
				
			||||||
	&lame_encoder_plugin,
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#ifdef ENABLE_TWOLAME
 | 
					 | 
				
			||||||
	&twolame_encoder_plugin,
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#ifdef ENABLE_WAVE_ENCODER
 | 
					 | 
				
			||||||
	&wave_encoder_plugin,
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#ifdef ENABLE_FLAC_ENCODER
 | 
					 | 
				
			||||||
	&flac_encoder_plugin,
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#ifdef ENABLE_SHINE
 | 
					 | 
				
			||||||
	&shine_encoder_plugin,
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	nullptr
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const EncoderPlugin *
 | 
					 | 
				
			||||||
encoder_plugin_get(const char *name)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	for (const auto &plugin : GetAllEncoderPlugins()) {
 | 
					 | 
				
			||||||
		if (strcmp(plugin.name, name) == 0)
 | 
					 | 
				
			||||||
			return &plugin;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nullptr;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,27 +1,41 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Encoder support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#ifndef MPD_ENCODER_LIST_HXX
 | 
				
			||||||
 | 
					#define MPD_ENCODER_LIST_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "util/DereferenceIterator.hxx"
 | 
					#include <iterator>
 | 
				
			||||||
#include "util/TerminatedArray.hxx"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct EncoderPlugin;
 | 
					struct EncoderPlugin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern const EncoderPlugin *const encoder_plugins[];
 | 
					/**
 | 
				
			||||||
 | 
					 * Stub implementation - encoder support not needed for database creation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class EncoderPluginIterator {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						using iterator_category = std::forward_iterator_tag;
 | 
				
			||||||
 | 
						using value_type = const EncoderPlugin *;
 | 
				
			||||||
 | 
						using difference_type = std::ptrdiff_t;
 | 
				
			||||||
 | 
						using pointer = value_type *;
 | 
				
			||||||
 | 
						using reference = value_type &;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline auto
 | 
						bool operator==(const EncoderPluginIterator &) const noexcept { return true; }
 | 
				
			||||||
GetAllEncoderPlugins() noexcept
 | 
						bool operator!=(const EncoderPluginIterator &) const noexcept { return false; }
 | 
				
			||||||
 | 
						EncoderPluginIterator &operator++() noexcept { return *this; }
 | 
				
			||||||
 | 
						const EncoderPlugin *operator*() const noexcept { return nullptr; }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline EncoderPluginIterator
 | 
				
			||||||
 | 
					encoder_plugins_begin() noexcept
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return DereferenceContainerAdapter{TerminatedArray<const EncoderPlugin *const, nullptr>{encoder_plugins}};
 | 
						return {};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					inline EncoderPluginIterator
 | 
				
			||||||
 * Looks up an encoder plugin by its name.
 | 
					encoder_plugins_end() noexcept
 | 
				
			||||||
 *
 | 
					{
 | 
				
			||||||
 * @param name the encoder name to look for
 | 
						return {};
 | 
				
			||||||
 * @return the encoder plugin with the specified name, or nullptr if none
 | 
					}
 | 
				
			||||||
 * was found
 | 
					
 | 
				
			||||||
 */
 | 
					#endif
 | 
				
			||||||
const EncoderPlugin *
 | 
					 | 
				
			||||||
encoder_plugin_get(const char *name);
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -1,32 +1,17 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Encoder support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_ENCODER_PLUGIN_HXX
 | 
					#ifndef MPD_ENCODER_PLUGIN_HXX
 | 
				
			||||||
#define MPD_ENCODER_PLUGIN_HXX
 | 
					#define MPD_ENCODER_PLUGIN_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PreparedEncoder;
 | 
					 | 
				
			||||||
struct ConfigBlock;
 | 
					struct ConfigBlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Stub implementation - encoder support not needed for database creation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
struct EncoderPlugin {
 | 
					struct EncoderPlugin {
 | 
				
			||||||
	const char *name;
 | 
						const char *name;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Throws #std::runtime_error on error.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	PreparedEncoder *(*init)(const ConfigBlock &block);
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Creates a new encoder object.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Throws #std::runtime_error on error.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param plugin the encoder plugin
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static inline PreparedEncoder *
 | 
					 | 
				
			||||||
encoder_init(const EncoderPlugin &plugin, const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return plugin.init(block);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Encoder support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef MPD_ENCODER_FEATURES_H
 | 
				
			||||||
 | 
					#define MPD_ENCODER_FEATURES_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Encoder support disabled for database creation tool
 | 
				
			||||||
 | 
					#define ENABLE_ENCODER 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@ -1,23 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "ToOutputStream.hxx"
 | 
					 | 
				
			||||||
#include "EncoderInterface.hxx"
 | 
					 | 
				
			||||||
#include "io/OutputStream.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
EncoderToOutputStream(OutputStream &os, Encoder &encoder)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	while (true) {
 | 
					 | 
				
			||||||
		/* read from the encoder */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		std::byte buffer[32768];
 | 
					 | 
				
			||||||
		const auto r = encoder.Read(buffer);
 | 
					 | 
				
			||||||
		if (r.empty())
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* write everything to the stream */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		os.Write(r);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,16 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class OutputStream;
 | 
					 | 
				
			||||||
class Encoder;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Read all available output from the #Encoder and write it to the
 | 
					 | 
				
			||||||
 * #OutputStream.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Throws on error.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
EncoderToOutputStream(OutputStream &os, Encoder &encoder);
 | 
					 | 
				
			||||||
@ -1,50 +0,0 @@
 | 
				
			|||||||
encoder_features = configuration_data()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
encoder_features.set('ENABLE_ENCODER', need_encoder)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if not need_encoder
 | 
					 | 
				
			||||||
  if need_wave_encoder
 | 
					 | 
				
			||||||
    # Special case for the Snapcast output plugin which only needs the
 | 
					 | 
				
			||||||
    # PCM wave encoder encoder plugin
 | 
					 | 
				
			||||||
    encoder_glue = static_library(
 | 
					 | 
				
			||||||
      'encoder_glue',
 | 
					 | 
				
			||||||
      'plugins/WaveEncoderPlugin.cxx',
 | 
					 | 
				
			||||||
      include_directories: inc,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    encoder_glue_dep = declare_dependency(
 | 
					 | 
				
			||||||
      link_with: encoder_glue,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    configure_file(output: 'Features.h', configuration: encoder_features)
 | 
					 | 
				
			||||||
    subdir_done()
 | 
					 | 
				
			||||||
  endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  encoder_glue_dep = dependency('', required: false)
 | 
					 | 
				
			||||||
  configure_file(output: 'Features.h', configuration: encoder_features)
 | 
					 | 
				
			||||||
  subdir_done()
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
encoder_api_dep = declare_dependency()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
subdir('plugins')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
encoder_glue = static_library(
 | 
					 | 
				
			||||||
  'encoder_glue',
 | 
					 | 
				
			||||||
  'Configured.cxx',
 | 
					 | 
				
			||||||
  'ToOutputStream.cxx',
 | 
					 | 
				
			||||||
  'EncoderList.cxx',
 | 
					 | 
				
			||||||
  include_directories: inc,
 | 
					 | 
				
			||||||
  dependencies: [
 | 
					 | 
				
			||||||
    fmt_dep,
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
encoder_glue_dep = declare_dependency(
 | 
					 | 
				
			||||||
  link_with: encoder_glue,
 | 
					 | 
				
			||||||
  dependencies: [
 | 
					 | 
				
			||||||
    encoder_plugins_dep,
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
configure_file(output: 'Features.h', configuration: encoder_features)
 | 
					 | 
				
			||||||
@ -1,288 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "FlacEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "../EncoderAPI.hxx"
 | 
					 | 
				
			||||||
#include "tag/Names.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "pcm/Buffer.hxx"
 | 
					 | 
				
			||||||
#include "lib/fmt/RuntimeError.hxx"
 | 
					 | 
				
			||||||
#include "util/DynamicFifoBuffer.hxx"
 | 
					 | 
				
			||||||
#include "util/Serial.hxx"
 | 
					 | 
				
			||||||
#include "util/SpanCast.hxx"
 | 
					 | 
				
			||||||
#include "util/StringUtil.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <FLAC/stream_encoder.h>
 | 
					 | 
				
			||||||
#include <FLAC/metadata.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <algorithm>
 | 
					 | 
				
			||||||
#include <utility> // for std::unreachable()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class FlacEncoder final : public Encoder {
 | 
					 | 
				
			||||||
	const AudioFormat audio_format;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	FLAC__StreamEncoder *const fse;
 | 
					 | 
				
			||||||
	const unsigned compression;
 | 
					 | 
				
			||||||
	const bool oggflac;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	PcmBuffer expand_buffer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * This buffer will hold encoded data from libFLAC until it is
 | 
					 | 
				
			||||||
	 * picked up with Read().
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	DynamicFifoBuffer<std::byte> output_buffer{8192};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse, unsigned _compression, bool _oggflac, bool _oggchaining);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	~FlacEncoder() noexcept override {
 | 
					 | 
				
			||||||
		FLAC__stream_encoder_delete(fse);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	FlacEncoder(const FlacEncoder &) = delete;
 | 
					 | 
				
			||||||
	FlacEncoder &operator=(const FlacEncoder &) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Encoder */
 | 
					 | 
				
			||||||
	void End() override {
 | 
					 | 
				
			||||||
		(void) FLAC__stream_encoder_finish(fse);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void Flush() override {
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void PreTag() override {
 | 
					 | 
				
			||||||
		(void) FLAC__stream_encoder_finish(fse);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void SendTag(const Tag &tag) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void Write(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> Read(std::span<std::byte>) noexcept override {
 | 
					 | 
				
			||||||
		auto r = output_buffer.Read();
 | 
					 | 
				
			||||||
		output_buffer.Consume(r.size());
 | 
					 | 
				
			||||||
		return r;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	static FLAC__StreamEncoderWriteStatus WriteCallback(const FLAC__StreamEncoder *,
 | 
					 | 
				
			||||||
							    const FLAC__byte data[],
 | 
					 | 
				
			||||||
							    size_t bytes,
 | 
					 | 
				
			||||||
							    [[maybe_unused]] unsigned samples,
 | 
					 | 
				
			||||||
							    [[maybe_unused]] unsigned current_frame,
 | 
					 | 
				
			||||||
							    void *client_data) noexcept {
 | 
					 | 
				
			||||||
		auto &encoder = *(FlacEncoder *)client_data;
 | 
					 | 
				
			||||||
		encoder.output_buffer.Append({(const std::byte *)data, bytes});
 | 
					 | 
				
			||||||
		return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedFlacEncoder final : public PreparedEncoder {
 | 
					 | 
				
			||||||
	const unsigned compression;
 | 
					 | 
				
			||||||
	const bool oggchaining;
 | 
					 | 
				
			||||||
	const bool oggflac;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit PreparedFlacEncoder(const ConfigBlock &block);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedEncoder */
 | 
					 | 
				
			||||||
	Encoder *Open(AudioFormat &audio_format) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] const char *GetMimeType() const noexcept override {
 | 
					 | 
				
			||||||
		if(oggflac)
 | 
					 | 
				
			||||||
			return  "audio/ogg";
 | 
					 | 
				
			||||||
		return "audio/flac";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
PreparedFlacEncoder::PreparedFlacEncoder(const ConfigBlock &block)
 | 
					 | 
				
			||||||
	:compression(block.GetBlockValue("compression", 5U)),
 | 
					 | 
				
			||||||
	oggchaining(block.GetBlockValue("oggchaining",false)),
 | 
					 | 
				
			||||||
	oggflac(block.GetBlockValue("oggflac",false) || oggchaining)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static PreparedEncoder *
 | 
					 | 
				
			||||||
flac_encoder_init(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return new PreparedFlacEncoder(block);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
flac_encoder_setup(FLAC__StreamEncoder *fse, unsigned compression, bool oggflac,
 | 
					 | 
				
			||||||
		   const AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	unsigned bits_per_sample;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (audio_format.format) {
 | 
					 | 
				
			||||||
	case SampleFormat::S8:
 | 
					 | 
				
			||||||
		bits_per_sample = 8;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case SampleFormat::S16:
 | 
					 | 
				
			||||||
		bits_per_sample = 16;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		bits_per_sample = 24;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!FLAC__stream_encoder_set_compression_level(fse, compression))
 | 
					 | 
				
			||||||
		throw FmtRuntimeError("error setting flac compression to {}",
 | 
					 | 
				
			||||||
				      compression);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!FLAC__stream_encoder_set_channels(fse, audio_format.channels))
 | 
					 | 
				
			||||||
		throw FmtRuntimeError("error setting flac channels num to {}",
 | 
					 | 
				
			||||||
				      audio_format.channels);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!FLAC__stream_encoder_set_bits_per_sample(fse, bits_per_sample))
 | 
					 | 
				
			||||||
		throw FmtRuntimeError("error setting flac bit format to {}",
 | 
					 | 
				
			||||||
				      bits_per_sample);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!FLAC__stream_encoder_set_sample_rate(fse,
 | 
					 | 
				
			||||||
						  audio_format.sample_rate))
 | 
					 | 
				
			||||||
		throw FmtRuntimeError("error setting flac sample rate to {}",
 | 
					 | 
				
			||||||
				      audio_format.sample_rate);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (oggflac && !FLAC__stream_encoder_set_ogg_serial_number(fse,
 | 
					 | 
				
			||||||
						  GenerateSerial()))
 | 
					 | 
				
			||||||
		throw std::runtime_error{"error setting ogg serial number"};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
FlacEncoder::FlacEncoder(AudioFormat _audio_format, FLAC__StreamEncoder *_fse, unsigned _compression, bool _oggflac, bool _oggchaining)
 | 
					 | 
				
			||||||
	:Encoder(_oggchaining),
 | 
					 | 
				
			||||||
	 audio_format(_audio_format), fse(_fse),
 | 
					 | 
				
			||||||
	 compression(_compression),
 | 
					 | 
				
			||||||
	 oggflac(_oggflac)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* this immediately outputs data through callback */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto init_status = oggflac ?
 | 
					 | 
				
			||||||
		FLAC__stream_encoder_init_ogg_stream(fse,
 | 
					 | 
				
			||||||
						     nullptr, WriteCallback,
 | 
					 | 
				
			||||||
						     nullptr, nullptr, nullptr,
 | 
					 | 
				
			||||||
						     this)
 | 
					 | 
				
			||||||
		:
 | 
					 | 
				
			||||||
		FLAC__stream_encoder_init_stream(fse,
 | 
					 | 
				
			||||||
						 WriteCallback,
 | 
					 | 
				
			||||||
						 nullptr, nullptr, nullptr,
 | 
					 | 
				
			||||||
						 this);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
 | 
					 | 
				
			||||||
		throw FmtRuntimeError("failed to initialize encoder: {}",
 | 
					 | 
				
			||||||
				      FLAC__StreamEncoderInitStatusString[init_status]);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Encoder *
 | 
					 | 
				
			||||||
PreparedFlacEncoder::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	switch (audio_format.format) {
 | 
					 | 
				
			||||||
	case SampleFormat::S8:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case SampleFormat::S16:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case SampleFormat::S24_P32:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		audio_format.format = SampleFormat::S24_P32;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* allocate the encoder */
 | 
					 | 
				
			||||||
	auto fse = FLAC__stream_encoder_new();
 | 
					 | 
				
			||||||
	if (fse == nullptr)
 | 
					 | 
				
			||||||
		throw std::runtime_error("FLAC__stream_encoder_new() failed");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	try {
 | 
					 | 
				
			||||||
		flac_encoder_setup(fse, compression, oggflac, audio_format);
 | 
					 | 
				
			||||||
	} catch (...) {
 | 
					 | 
				
			||||||
		FLAC__stream_encoder_delete(fse);
 | 
					 | 
				
			||||||
		throw;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return new FlacEncoder(audio_format, fse, compression, oggflac, oggchaining);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
FlacEncoder::SendTag(const Tag &tag)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* re-initialize encoder since flac_encoder_finish resets everything */
 | 
					 | 
				
			||||||
	flac_encoder_setup(fse, compression, oggflac, audio_format);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	FLAC__StreamMetadata *metadata = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
 | 
					 | 
				
			||||||
	FLAC__StreamMetadata_VorbisComment_Entry entry;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (const auto &item : tag) {
 | 
					 | 
				
			||||||
		char name[64];
 | 
					 | 
				
			||||||
		ToUpperASCII(name, tag_item_names[item.type], sizeof(name));
 | 
					 | 
				
			||||||
		FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, name, item.value);
 | 
					 | 
				
			||||||
		FLAC__metadata_object_vorbiscomment_append_comment(metadata, entry, false);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	FLAC__stream_encoder_set_metadata(fse,&metadata,1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto init_status = FLAC__stream_encoder_init_ogg_stream(fse,
 | 
					 | 
				
			||||||
							        nullptr, WriteCallback,
 | 
					 | 
				
			||||||
							        nullptr, nullptr, nullptr,
 | 
					 | 
				
			||||||
							        this);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	FLAC__metadata_object_delete(metadata);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK)
 | 
					 | 
				
			||||||
		throw FmtRuntimeError("failed to initialize encoder: {}",
 | 
					 | 
				
			||||||
				      FLAC__StreamEncoderInitStatusString[init_status]);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
template<typename T>
 | 
					 | 
				
			||||||
static std::span<const FLAC__int32>
 | 
					 | 
				
			||||||
ToFlac32(PcmBuffer &buffer, std::span<const T> src) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	FLAC__int32 *dest = buffer.GetT<FLAC__int32>(src.size());
 | 
					 | 
				
			||||||
	std::copy(src.begin(), src.end(), dest);
 | 
					 | 
				
			||||||
	return {dest, src.size()};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::span<const FLAC__int32>
 | 
					 | 
				
			||||||
ToFlac32(PcmBuffer &buffer, std::span<const std::byte> src,
 | 
					 | 
				
			||||||
	 SampleFormat format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	switch (format) {
 | 
					 | 
				
			||||||
	case SampleFormat::S8:
 | 
					 | 
				
			||||||
		return ToFlac32(buffer, FromBytesStrict<const int8_t>(src));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case SampleFormat::S16:
 | 
					 | 
				
			||||||
		return ToFlac32(buffer, FromBytesStrict<const int16_t>(src));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case SampleFormat::S24_P32:
 | 
					 | 
				
			||||||
	case SampleFormat::S32:
 | 
					 | 
				
			||||||
		/* nothing need to be done; format is the same for
 | 
					 | 
				
			||||||
		   both mpd and libFLAC */
 | 
					 | 
				
			||||||
		return FromBytesStrict<const int32_t>(src);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		std::unreachable();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
FlacEncoder::Write(std::span<const std::byte> src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const auto imported = ToFlac32(expand_buffer, src,
 | 
					 | 
				
			||||||
				       audio_format.format);
 | 
					 | 
				
			||||||
	const std::size_t n_frames = imported.size() / audio_format.channels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* feed samples to encoder */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!FLAC__stream_encoder_process_interleaved(fse, imported.data(),
 | 
					 | 
				
			||||||
						      n_frames))
 | 
					 | 
				
			||||||
		throw std::runtime_error("flac encoder process failed");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const EncoderPlugin flac_encoder_plugin = {
 | 
					 | 
				
			||||||
	"flac",
 | 
					 | 
				
			||||||
	flac_encoder_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ -1,9 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_ENCODER_FLAC_HXX
 | 
					 | 
				
			||||||
#define MPD_ENCODER_FLAC_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const struct EncoderPlugin flac_encoder_plugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,187 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "LameEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "../EncoderAPI.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "lib/fmt/RuntimeError.hxx"
 | 
					 | 
				
			||||||
#include "util/CNumberParser.hxx"
 | 
					 | 
				
			||||||
#include "util/ReusableArray.hxx"
 | 
					 | 
				
			||||||
#include "util/SpanCast.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <lame/lame.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cassert>
 | 
					 | 
				
			||||||
#include <stdexcept>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class LameEncoder final : public Encoder {
 | 
					 | 
				
			||||||
	lame_global_flags *const gfp;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ReusableArray<std::byte, 32768> output_buffer;
 | 
					 | 
				
			||||||
	std::span<const std::byte> output{};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	static constexpr unsigned CHANNELS = 2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	explicit LameEncoder(lame_global_flags *_gfp) noexcept
 | 
					 | 
				
			||||||
		:Encoder(false), gfp(_gfp) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	~LameEncoder() noexcept override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LameEncoder(const LameEncoder &) = delete;
 | 
					 | 
				
			||||||
	LameEncoder &operator=(const LameEncoder &) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Encoder */
 | 
					 | 
				
			||||||
	void Write(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
	std::span<const std::byte> Read(std::span<std::byte> buffer) noexcept override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedLameEncoder final : public PreparedEncoder {
 | 
					 | 
				
			||||||
	float quality;
 | 
					 | 
				
			||||||
	int bitrate;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit PreparedLameEncoder(const ConfigBlock &block);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedEncoder */
 | 
					 | 
				
			||||||
	Encoder *Open(AudioFormat &audio_format) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] const char *GetMimeType() const noexcept override {
 | 
					 | 
				
			||||||
		return "audio/mpeg";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
PreparedLameEncoder::PreparedLameEncoder(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *value;
 | 
					 | 
				
			||||||
	char *endptr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	value = block.GetBlockValue("quality");
 | 
					 | 
				
			||||||
	if (value != nullptr) {
 | 
					 | 
				
			||||||
		/* a quality was configured (VBR) */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		quality = float(ParseDouble(value, &endptr));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (*endptr != '\0' || quality < -1.0f || quality > 10.0f)
 | 
					 | 
				
			||||||
			throw FmtRuntimeError("quality {:?} is not a number in the "
 | 
					 | 
				
			||||||
					      "range -1 to 10",
 | 
					 | 
				
			||||||
					      value);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (block.GetBlockValue("bitrate") != nullptr)
 | 
					 | 
				
			||||||
			throw std::runtime_error("quality and bitrate are both defined");
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* a bit rate was configured */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		value = block.GetBlockValue("bitrate");
 | 
					 | 
				
			||||||
		if (value == nullptr)
 | 
					 | 
				
			||||||
			throw std::runtime_error("neither bitrate nor quality defined");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		quality = -2.0;
 | 
					 | 
				
			||||||
		bitrate = ParseInt(value, &endptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (*endptr != '\0' || bitrate <= 0)
 | 
					 | 
				
			||||||
			throw std::runtime_error("bitrate should be a positive integer");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static PreparedEncoder *
 | 
					 | 
				
			||||||
lame_encoder_init(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return new PreparedLameEncoder(block);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
lame_encoder_setup(lame_global_flags *gfp, float quality, int bitrate,
 | 
					 | 
				
			||||||
		   const AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (quality >= -1.0f) {
 | 
					 | 
				
			||||||
		/* a quality was configured (VBR) */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (0 != lame_set_VBR(gfp, vbr_rh))
 | 
					 | 
				
			||||||
			throw std::runtime_error("error setting lame VBR mode");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (0 != lame_set_VBR_q(gfp, int(quality)))
 | 
					 | 
				
			||||||
			throw std::runtime_error("error setting lame VBR quality");
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* a bit rate was configured */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (0 != lame_set_brate(gfp, bitrate))
 | 
					 | 
				
			||||||
			throw std::runtime_error("error setting lame bitrate");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (0 != lame_set_num_channels(gfp, audio_format.channels))
 | 
					 | 
				
			||||||
		throw std::runtime_error("error setting lame num channels");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (0 != lame_set_in_samplerate(gfp, audio_format.sample_rate))
 | 
					 | 
				
			||||||
		throw std::runtime_error("error setting lame sample rate");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (0 != lame_set_out_samplerate(gfp, audio_format.sample_rate))
 | 
					 | 
				
			||||||
		throw std::runtime_error("error setting lame out sample rate");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (0 > lame_init_params(gfp))
 | 
					 | 
				
			||||||
		throw std::runtime_error("error initializing lame params");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Encoder *
 | 
					 | 
				
			||||||
PreparedLameEncoder::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	audio_format.format = SampleFormat::S16;
 | 
					 | 
				
			||||||
	audio_format.channels = LameEncoder::CHANNELS;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto gfp = lame_init();
 | 
					 | 
				
			||||||
	if (gfp == nullptr)
 | 
					 | 
				
			||||||
		throw std::runtime_error("lame_init() failed");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	try {
 | 
					 | 
				
			||||||
		lame_encoder_setup(gfp, quality, bitrate, audio_format);
 | 
					 | 
				
			||||||
	} catch (...) {
 | 
					 | 
				
			||||||
		lame_close(gfp);
 | 
					 | 
				
			||||||
		throw;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return new LameEncoder(gfp);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
LameEncoder::~LameEncoder() noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	lame_close(gfp);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
LameEncoder::Write(std::span<const std::byte> _src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const auto src = FromBytesStrict<const int16_t>(_src);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	assert(output.empty());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const std::size_t num_samples = src.size();
 | 
					 | 
				
			||||||
	const std::size_t num_frames = num_samples / CHANNELS;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* worst-case formula according to LAME documentation */
 | 
					 | 
				
			||||||
	const std::size_t output_buffer_size = 5 * num_samples / 4 + 7200;
 | 
					 | 
				
			||||||
	const auto dest = output_buffer.Get(output_buffer_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* this is for only 16-bit audio */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int bytes_out = lame_encode_buffer_interleaved(gfp,
 | 
					 | 
				
			||||||
						       const_cast<short *>(src.data()),
 | 
					 | 
				
			||||||
						       num_frames,
 | 
					 | 
				
			||||||
						       (unsigned char *)dest,
 | 
					 | 
				
			||||||
						       output_buffer_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (bytes_out < 0)
 | 
					 | 
				
			||||||
		throw std::runtime_error("lame encoder failed");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	output = {dest, std::size_t(bytes_out)};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
LameEncoder::Read(std::span<std::byte>) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::exchange(output, std::span<const std::byte>{});
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const EncoderPlugin lame_encoder_plugin = {
 | 
					 | 
				
			||||||
	"lame",
 | 
					 | 
				
			||||||
	lame_encoder_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,9 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_ENCODER_LAME_HXX
 | 
					 | 
				
			||||||
#define MPD_ENCODER_LAME_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const struct EncoderPlugin lame_encoder_plugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,42 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "NullEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "../EncoderAPI.hxx"
 | 
					 | 
				
			||||||
#include "util/DynamicFifoBuffer.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class NullEncoder final : public Encoder {
 | 
					 | 
				
			||||||
	DynamicFifoBuffer<std::byte> buffer{8192};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	NullEncoder()
 | 
					 | 
				
			||||||
		:Encoder(false) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Encoder */
 | 
					 | 
				
			||||||
	void Write(std::span<const std::byte> src) override {
 | 
					 | 
				
			||||||
		buffer.Append(src);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> Read(std::span<std::byte> b) noexcept override {
 | 
					 | 
				
			||||||
		return b.first(buffer.Read(b.data(), b.size()));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedNullEncoder final : public PreparedEncoder {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedEncoder */
 | 
					 | 
				
			||||||
	Encoder *Open(AudioFormat &) override {
 | 
					 | 
				
			||||||
		return new NullEncoder();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static PreparedEncoder *
 | 
					 | 
				
			||||||
null_encoder_init([[maybe_unused]] const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return new PreparedNullEncoder();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const EncoderPlugin null_encoder_plugin = {
 | 
					 | 
				
			||||||
	"null",
 | 
					 | 
				
			||||||
	null_encoder_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,9 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_ENCODER_NULL_HXX
 | 
					 | 
				
			||||||
#define MPD_ENCODER_NULL_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const struct EncoderPlugin null_encoder_plugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,55 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_OGG_ENCODER_HXX
 | 
					 | 
				
			||||||
#define MPD_OGG_ENCODER_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "../EncoderAPI.hxx"
 | 
					 | 
				
			||||||
#include "lib/xiph/OggStreamState.hxx"
 | 
					 | 
				
			||||||
#include "lib/xiph/OggPage.hxx"
 | 
					 | 
				
			||||||
#include "util/Serial.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <ogg/ogg.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * An abstract base class which contains code common to all encoders
 | 
					 | 
				
			||||||
 * with Ogg container output.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class OggEncoder : public Encoder {
 | 
					 | 
				
			||||||
	/* initialize "flush" to true, so the caller gets the full
 | 
					 | 
				
			||||||
	   headers on the first read */
 | 
					 | 
				
			||||||
	bool flush = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	OggStreamState stream;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	OggEncoder(bool _implements_tag)
 | 
					 | 
				
			||||||
		:Encoder(_implements_tag),
 | 
					 | 
				
			||||||
		 stream(GenerateSerial()) {
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Encoder */
 | 
					 | 
				
			||||||
	void Flush() final {
 | 
					 | 
				
			||||||
		flush = true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> Read(std::span<std::byte> buffer) noexcept override {
 | 
					 | 
				
			||||||
		ogg_page page;
 | 
					 | 
				
			||||||
		bool success = stream.PageOut(page);
 | 
					 | 
				
			||||||
		if (!success) {
 | 
					 | 
				
			||||||
			if (flush) {
 | 
					 | 
				
			||||||
				flush = false;
 | 
					 | 
				
			||||||
				success = stream.Flush(page);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!success)
 | 
					 | 
				
			||||||
				return {};
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return buffer.first(ReadPage(page, buffer.data(),
 | 
					 | 
				
			||||||
					     buffer.size()));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,402 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "OpusEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "OggEncoder.hxx"
 | 
					 | 
				
			||||||
#include "tag/Names.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "util/ByteOrder.hxx"
 | 
					 | 
				
			||||||
#include "util/StringUtil.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <opus.h>
 | 
					 | 
				
			||||||
#include <ogg/ogg.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cassert>
 | 
					 | 
				
			||||||
#include <stdexcept>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class OpusEncoder final : public OggEncoder {
 | 
					 | 
				
			||||||
	const AudioFormat audio_format;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const size_t frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const size_t buffer_frames, buffer_size;
 | 
					 | 
				
			||||||
	size_t buffer_position = 0;
 | 
					 | 
				
			||||||
	std::byte *const buffer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	::OpusEncoder *const enc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	unsigned char buffer2[1275 * 3 + 7];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int lookahead;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ogg_int64_t packetno = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ogg_int64_t granulepos = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc, bool _chaining);
 | 
					 | 
				
			||||||
	~OpusEncoder() noexcept override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	OpusEncoder(const OpusEncoder &) = delete;
 | 
					 | 
				
			||||||
	OpusEncoder &operator=(const OpusEncoder &) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Encoder */
 | 
					 | 
				
			||||||
	void End() override;
 | 
					 | 
				
			||||||
	void Write(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void PreTag() override;
 | 
					 | 
				
			||||||
	void SendTag(const Tag &tag) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	void DoEncode(bool eos);
 | 
					 | 
				
			||||||
	void WriteSilence(unsigned fill_frames);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void GenerateHeaders(const Tag *tag) noexcept;
 | 
					 | 
				
			||||||
	void GenerateHead() noexcept;
 | 
					 | 
				
			||||||
	void GenerateTags(const Tag *tag) noexcept;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedOpusEncoder final : public PreparedEncoder {
 | 
					 | 
				
			||||||
	opus_int32 bitrate;
 | 
					 | 
				
			||||||
	int complexity;
 | 
					 | 
				
			||||||
	int signal;
 | 
					 | 
				
			||||||
	int packet_loss;
 | 
					 | 
				
			||||||
	int vbr;
 | 
					 | 
				
			||||||
	int vbr_constraint;
 | 
					 | 
				
			||||||
	const bool chaining;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit PreparedOpusEncoder(const ConfigBlock &block);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedEncoder */
 | 
					 | 
				
			||||||
	Encoder *Open(AudioFormat &audio_format) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] const char *GetMimeType() const noexcept override {
 | 
					 | 
				
			||||||
		return "audio/ogg";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
PreparedOpusEncoder::PreparedOpusEncoder(const ConfigBlock &block)
 | 
					 | 
				
			||||||
	:chaining(block.GetBlockValue("opustags", false))
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *value = block.GetBlockValue("bitrate", "auto");
 | 
					 | 
				
			||||||
	if (strcmp(value, "auto") == 0)
 | 
					 | 
				
			||||||
		bitrate = OPUS_AUTO;
 | 
					 | 
				
			||||||
	else if (strcmp(value, "max") == 0)
 | 
					 | 
				
			||||||
		bitrate = OPUS_BITRATE_MAX;
 | 
					 | 
				
			||||||
	else {
 | 
					 | 
				
			||||||
		char *endptr;
 | 
					 | 
				
			||||||
		bitrate = strtoul(value, &endptr, 10);
 | 
					 | 
				
			||||||
		if (endptr == value || *endptr != 0 ||
 | 
					 | 
				
			||||||
		    bitrate < 500 || bitrate > 512000)
 | 
					 | 
				
			||||||
			throw std::runtime_error("Invalid bit rate");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	complexity = block.GetBlockValue("complexity", 10U);
 | 
					 | 
				
			||||||
	if (complexity > 10)
 | 
					 | 
				
			||||||
		throw std::runtime_error("Invalid complexity");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	value = block.GetBlockValue("signal", "auto");
 | 
					 | 
				
			||||||
	if (strcmp(value, "auto") == 0)
 | 
					 | 
				
			||||||
		signal = OPUS_AUTO;
 | 
					 | 
				
			||||||
	else if (strcmp(value, "voice") == 0)
 | 
					 | 
				
			||||||
		signal = OPUS_SIGNAL_VOICE;
 | 
					 | 
				
			||||||
	else if (strcmp(value, "music") == 0)
 | 
					 | 
				
			||||||
		signal = OPUS_SIGNAL_MUSIC;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		throw std::runtime_error("Invalid signal");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	value = block.GetBlockValue("vbr", "yes");
 | 
					 | 
				
			||||||
	if (strcmp(value, "yes") == 0) {
 | 
					 | 
				
			||||||
		vbr = 1U;
 | 
					 | 
				
			||||||
		vbr_constraint = 0U;
 | 
					 | 
				
			||||||
	} else if (strcmp(value, "no") == 0) {
 | 
					 | 
				
			||||||
		vbr = 0U;
 | 
					 | 
				
			||||||
		vbr_constraint = 0U;
 | 
					 | 
				
			||||||
	} else if (strcmp(value, "constrained") == 0) {
 | 
					 | 
				
			||||||
		vbr = 1U;
 | 
					 | 
				
			||||||
		vbr_constraint = 1U;
 | 
					 | 
				
			||||||
	} else
 | 
					 | 
				
			||||||
		throw std::runtime_error("Invalid vbr");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	packet_loss = block.GetBlockValue("packet_loss", 0U);
 | 
					 | 
				
			||||||
	if (packet_loss > 100)
 | 
					 | 
				
			||||||
		throw std::runtime_error("Invalid packet loss");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
PreparedEncoder *
 | 
					 | 
				
			||||||
opus_encoder_init(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return new PreparedOpusEncoder(block);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
OpusEncoder::OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc, bool _chaining)
 | 
					 | 
				
			||||||
	:OggEncoder(_chaining),
 | 
					 | 
				
			||||||
	 audio_format(_audio_format),
 | 
					 | 
				
			||||||
	 frame_size(_audio_format.GetFrameSize()),
 | 
					 | 
				
			||||||
	 buffer_frames(_audio_format.sample_rate / 50),
 | 
					 | 
				
			||||||
	 buffer_size(frame_size * buffer_frames),
 | 
					 | 
				
			||||||
	 buffer(new std::byte[buffer_size]),
 | 
					 | 
				
			||||||
	 enc(_enc)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&lookahead));
 | 
					 | 
				
			||||||
	GenerateHeaders(nullptr);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Encoder *
 | 
					 | 
				
			||||||
PreparedOpusEncoder::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* libopus supports only 48 kHz */
 | 
					 | 
				
			||||||
	audio_format.sample_rate = 48000;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (audio_format.channels > 2)
 | 
					 | 
				
			||||||
		audio_format.channels = 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (audio_format.format) {
 | 
					 | 
				
			||||||
	case SampleFormat::S16:
 | 
					 | 
				
			||||||
	case SampleFormat::FLOAT:
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case SampleFormat::S8:
 | 
					 | 
				
			||||||
		audio_format.format = SampleFormat::S16;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		audio_format.format = SampleFormat::FLOAT;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int error_code;
 | 
					 | 
				
			||||||
	auto *enc = opus_encoder_create(audio_format.sample_rate,
 | 
					 | 
				
			||||||
					audio_format.channels,
 | 
					 | 
				
			||||||
					OPUS_APPLICATION_AUDIO,
 | 
					 | 
				
			||||||
					&error_code);
 | 
					 | 
				
			||||||
	if (enc == nullptr)
 | 
					 | 
				
			||||||
		throw std::runtime_error(opus_strerror(error_code));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
 | 
					 | 
				
			||||||
	opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
 | 
					 | 
				
			||||||
	opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal));
 | 
					 | 
				
			||||||
	opus_encoder_ctl(enc, OPUS_SET_VBR(vbr));
 | 
					 | 
				
			||||||
	opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(vbr_constraint));
 | 
					 | 
				
			||||||
	opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packet_loss));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return new OpusEncoder(audio_format, enc, chaining);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
OpusEncoder::~OpusEncoder() noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	delete[] buffer;
 | 
					 | 
				
			||||||
	opus_encoder_destroy(enc);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
OpusEncoder::DoEncode(bool eos)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(buffer_position == buffer_size || eos);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	opus_int32 result =
 | 
					 | 
				
			||||||
		audio_format.format == SampleFormat::S16
 | 
					 | 
				
			||||||
		? opus_encode(enc,
 | 
					 | 
				
			||||||
		              (const opus_int16 *)buffer,
 | 
					 | 
				
			||||||
		              buffer_frames,
 | 
					 | 
				
			||||||
		              buffer2,
 | 
					 | 
				
			||||||
		              sizeof(buffer2))
 | 
					 | 
				
			||||||
		: opus_encode_float(enc,
 | 
					 | 
				
			||||||
		                    (const float *)buffer,
 | 
					 | 
				
			||||||
		                    buffer_frames,
 | 
					 | 
				
			||||||
		                    buffer2,
 | 
					 | 
				
			||||||
		                    sizeof(buffer2));
 | 
					 | 
				
			||||||
	if (result < 0)
 | 
					 | 
				
			||||||
		throw std::runtime_error("Opus encoder error");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	granulepos += buffer_position / frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ogg_packet packet;
 | 
					 | 
				
			||||||
	packet.packet = buffer2;
 | 
					 | 
				
			||||||
	packet.bytes = result;
 | 
					 | 
				
			||||||
	packet.b_o_s = false;
 | 
					 | 
				
			||||||
	packet.e_o_s = eos;
 | 
					 | 
				
			||||||
	packet.granulepos = granulepos;
 | 
					 | 
				
			||||||
	packet.packetno = packetno++;
 | 
					 | 
				
			||||||
	stream.PacketIn(packet);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	buffer_position = 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
OpusEncoder::End()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	memset(buffer + buffer_position, 0,
 | 
					 | 
				
			||||||
	       buffer_size - buffer_position);
 | 
					 | 
				
			||||||
	DoEncode(true);
 | 
					 | 
				
			||||||
	Flush();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
OpusEncoder::WriteSilence(unsigned fill_frames)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	size_t fill_bytes = fill_frames * frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	while (fill_bytes > 0) {
 | 
					 | 
				
			||||||
		size_t nbytes = buffer_size - buffer_position;
 | 
					 | 
				
			||||||
		if (nbytes > fill_bytes)
 | 
					 | 
				
			||||||
			nbytes = fill_bytes;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		memset(buffer + buffer_position, 0, nbytes);
 | 
					 | 
				
			||||||
		buffer_position += nbytes;
 | 
					 | 
				
			||||||
		fill_bytes -= nbytes;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (buffer_position == buffer_size)
 | 
					 | 
				
			||||||
			DoEncode(false);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
OpusEncoder::Write(std::span<const std::byte> src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (lookahead > 0) {
 | 
					 | 
				
			||||||
		/* generate some silence at the beginning of the
 | 
					 | 
				
			||||||
		   stream */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		assert(buffer_position == 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		WriteSilence(lookahead);
 | 
					 | 
				
			||||||
		lookahead = 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	while (!src.empty()) {
 | 
					 | 
				
			||||||
		const std::size_t nbytes = std::min(buffer_size - buffer_position,
 | 
					 | 
				
			||||||
						    src.size());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		memcpy(buffer + buffer_position, src.data(), nbytes);
 | 
					 | 
				
			||||||
		src = src.subspan(nbytes);
 | 
					 | 
				
			||||||
		buffer_position += nbytes;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (buffer_position == buffer_size)
 | 
					 | 
				
			||||||
			DoEncode(false);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
OpusEncoder::GenerateHeaders(const Tag *tag) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	GenerateHead();
 | 
					 | 
				
			||||||
	GenerateTags(tag);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
OpusEncoder::GenerateHead() noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	unsigned char header[19];
 | 
					 | 
				
			||||||
	memcpy(header, "OpusHead", 8);
 | 
					 | 
				
			||||||
	header[8] = 1;
 | 
					 | 
				
			||||||
	header[9] = audio_format.channels;
 | 
					 | 
				
			||||||
	*(uint16_t *)(header + 10) = ToLE16(lookahead);
 | 
					 | 
				
			||||||
	*(uint32_t *)(header + 12) = ToLE32(audio_format.sample_rate);
 | 
					 | 
				
			||||||
	header[16] = 0;
 | 
					 | 
				
			||||||
	header[17] = 0;
 | 
					 | 
				
			||||||
	header[18] = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ogg_packet packet;
 | 
					 | 
				
			||||||
	packet.packet = header;
 | 
					 | 
				
			||||||
	packet.bytes = sizeof(header);
 | 
					 | 
				
			||||||
	packet.b_o_s = true;
 | 
					 | 
				
			||||||
	packet.e_o_s = false;
 | 
					 | 
				
			||||||
	packet.granulepos = 0;
 | 
					 | 
				
			||||||
	packet.packetno = packetno++;
 | 
					 | 
				
			||||||
	stream.PacketIn(packet);
 | 
					 | 
				
			||||||
	// flush not needed because libogg autoflushes on b_o_s flag
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
OpusEncoder::GenerateTags(const Tag *tag) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *version = opus_get_version_string();
 | 
					 | 
				
			||||||
	size_t version_length = strlen(version);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// len("OpusTags") + 4 byte version length + len(version) + 4 byte tag count
 | 
					 | 
				
			||||||
	size_t comments_size = 8 + 4 + version_length + 4;
 | 
					 | 
				
			||||||
	uint32_t tag_count = 0;
 | 
					 | 
				
			||||||
	if (tag) {
 | 
					 | 
				
			||||||
		for (const auto &item: *tag) {
 | 
					 | 
				
			||||||
			++tag_count;
 | 
					 | 
				
			||||||
			// 4 byte length + len(tagname) + len('=') + len(value)
 | 
					 | 
				
			||||||
			comments_size += 4 + strlen(tag_item_names[item.type]) + 1 + strlen(item.value);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto *comments = new unsigned char[comments_size];
 | 
					 | 
				
			||||||
	unsigned char *p = comments;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	memcpy(comments, "OpusTags", 8);
 | 
					 | 
				
			||||||
	*(uint32_t *)(comments + 8) = ToLE32(version_length);
 | 
					 | 
				
			||||||
	p += 12;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	memcpy(p, version, version_length);
 | 
					 | 
				
			||||||
	p += version_length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	tag_count = ToLE32(tag_count);
 | 
					 | 
				
			||||||
	memcpy(p, &tag_count, 4);
 | 
					 | 
				
			||||||
	p += 4;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (tag) {
 | 
					 | 
				
			||||||
		for (const auto &item: *tag) {
 | 
					 | 
				
			||||||
			size_t tag_name_len = strlen(tag_item_names[item.type]);
 | 
					 | 
				
			||||||
			size_t tag_val_len = strlen(item.value);
 | 
					 | 
				
			||||||
			uint32_t tag_len_le = ToLE32(tag_name_len + 1 + tag_val_len);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			memcpy(p, &tag_len_le, 4);
 | 
					 | 
				
			||||||
			p += 4;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			ToUpperASCII((char *)p, tag_item_names[item.type], tag_name_len + 1);
 | 
					 | 
				
			||||||
			p += tag_name_len;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			*p++ = '=';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			memcpy(p, item.value, tag_val_len);
 | 
					 | 
				
			||||||
			p += tag_val_len;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	assert(comments + comments_size == p);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ogg_packet packet;
 | 
					 | 
				
			||||||
	packet.packet = comments;
 | 
					 | 
				
			||||||
	packet.bytes = comments_size;
 | 
					 | 
				
			||||||
	packet.b_o_s = false;
 | 
					 | 
				
			||||||
	packet.e_o_s = false;
 | 
					 | 
				
			||||||
	packet.granulepos = 0;
 | 
					 | 
				
			||||||
	packet.packetno = packetno++;
 | 
					 | 
				
			||||||
	stream.PacketIn(packet);
 | 
					 | 
				
			||||||
	Flush();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	delete[] comments;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
OpusEncoder::PreTag()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	End();
 | 
					 | 
				
			||||||
	packetno = 0;
 | 
					 | 
				
			||||||
	granulepos = 0; // not really required, but useful to prevent wraparound
 | 
					 | 
				
			||||||
	opus_encoder_ctl(enc, OPUS_RESET_STATE);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
OpusEncoder::SendTag(const Tag &tag)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	stream.Reinitialize(GenerateSerial());
 | 
					 | 
				
			||||||
	opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&lookahead));
 | 
					 | 
				
			||||||
	GenerateHeaders(&tag);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
} // namespace
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const EncoderPlugin opus_encoder_plugin = {
 | 
					 | 
				
			||||||
	"opus",
 | 
					 | 
				
			||||||
	opus_encoder_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,9 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_ENCODER_OPUS_H
 | 
					 | 
				
			||||||
#define MPD_ENCODER_OPUS_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const struct EncoderPlugin opus_encoder_plugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,193 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "ShineEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "../EncoderAPI.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "lib/fmt/RuntimeError.hxx"
 | 
					 | 
				
			||||||
#include "util/DynamicFifoBuffer.hxx"
 | 
					 | 
				
			||||||
#include "util/SpanCast.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern "C"
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
#include <shine/layer3.h>
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static constexpr size_t BUFFER_INIT_SIZE = 8192;
 | 
					 | 
				
			||||||
static constexpr unsigned CHANNELS = 2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ShineEncoder final : public Encoder {
 | 
					 | 
				
			||||||
	const AudioFormat audio_format;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const shine_t shine;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const size_t frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* workaround for bug:
 | 
					 | 
				
			||||||
	   https://github.com/savonet/shine/issues/11 */
 | 
					 | 
				
			||||||
	size_t input_pos = SHINE_MAX_SAMPLES + 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int16_t *stereo[CHANNELS];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DynamicFifoBuffer<std::byte> output_buffer{BUFFER_INIT_SIZE};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	ShineEncoder(AudioFormat _audio_format, shine_t _shine) noexcept
 | 
					 | 
				
			||||||
		:Encoder(false),
 | 
					 | 
				
			||||||
		 audio_format(_audio_format), shine(_shine),
 | 
					 | 
				
			||||||
		 frame_size(shine_samples_per_pass(shine)),
 | 
					 | 
				
			||||||
		 stereo{new int16_t[frame_size], new int16_t[frame_size]}
 | 
					 | 
				
			||||||
	{}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	~ShineEncoder() noexcept override {
 | 
					 | 
				
			||||||
		if (input_pos > SHINE_MAX_SAMPLES) {
 | 
					 | 
				
			||||||
			/* write zero chunk */
 | 
					 | 
				
			||||||
			input_pos = 0;
 | 
					 | 
				
			||||||
			WriteChunk(true);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		shine_close(shine);
 | 
					 | 
				
			||||||
		delete[] stereo[0];
 | 
					 | 
				
			||||||
		delete[] stereo[1];
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool WriteChunk(bool flush);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Encoder */
 | 
					 | 
				
			||||||
	void End() override {
 | 
					 | 
				
			||||||
		return Flush();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void Flush() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void Write(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> Read(std::span<std::byte> buffer) noexcept override {
 | 
					 | 
				
			||||||
		return buffer.first(output_buffer.Read(buffer.data(), buffer.size()));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedShineEncoder final : public PreparedEncoder {
 | 
					 | 
				
			||||||
	shine_config_t config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit PreparedShineEncoder(const ConfigBlock &block);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedEncoder */
 | 
					 | 
				
			||||||
	Encoder *Open(AudioFormat &audio_format) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] const char *GetMimeType() const noexcept override {
 | 
					 | 
				
			||||||
		return  "audio/mpeg";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
PreparedShineEncoder::PreparedShineEncoder(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	shine_set_config_mpeg_defaults(&config.mpeg);
 | 
					 | 
				
			||||||
	config.mpeg.bitr = block.GetBlockValue("bitrate", 128);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static PreparedEncoder *
 | 
					 | 
				
			||||||
shine_encoder_init(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return new PreparedShineEncoder(block);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static shine_t
 | 
					 | 
				
			||||||
SetupShine(shine_config_t config, AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	audio_format.format = SampleFormat::S16;
 | 
					 | 
				
			||||||
	audio_format.channels = CHANNELS;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	config.mpeg.mode = audio_format.channels == 2 ? STEREO : MONO;
 | 
					 | 
				
			||||||
	config.wave.samplerate = audio_format.sample_rate;
 | 
					 | 
				
			||||||
	config.wave.channels =
 | 
					 | 
				
			||||||
		audio_format.channels == 2 ? PCM_STEREO : PCM_MONO;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (shine_check_config(config.wave.samplerate, config.mpeg.bitr) < 0)
 | 
					 | 
				
			||||||
		throw FmtRuntimeError("error configuring shine. "
 | 
					 | 
				
			||||||
				      "samplerate {} and bitrate {} configuration"
 | 
					 | 
				
			||||||
				      " not supported.",
 | 
					 | 
				
			||||||
				      config.wave.samplerate,
 | 
					 | 
				
			||||||
				      config.mpeg.bitr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto shine = shine_initialise(&config);
 | 
					 | 
				
			||||||
	if (!shine)
 | 
					 | 
				
			||||||
		throw std::runtime_error("error initializing shine");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return shine;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Encoder *
 | 
					 | 
				
			||||||
PreparedShineEncoder::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	auto shine = SetupShine(config, audio_format);
 | 
					 | 
				
			||||||
	return new ShineEncoder(audio_format, shine);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool
 | 
					 | 
				
			||||||
ShineEncoder::WriteChunk(bool flush)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (flush || input_pos == frame_size) {
 | 
					 | 
				
			||||||
		if (flush) {
 | 
					 | 
				
			||||||
			/* fill remaining with 0s */
 | 
					 | 
				
			||||||
			for (; input_pos < frame_size; input_pos++) {
 | 
					 | 
				
			||||||
				stereo[0][input_pos] = stereo[1][input_pos] = 0;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		int written;
 | 
					 | 
				
			||||||
		const auto out = (const std::byte *)
 | 
					 | 
				
			||||||
			shine_encode_buffer(shine, stereo, &written);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (written > 0)
 | 
					 | 
				
			||||||
			output_buffer.Append({out, std::size_t(written)});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		input_pos = 0;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
ShineEncoder::Write(std::span<const std::byte> _src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const auto src = FromBytesStrict<const int16_t>(_src);
 | 
					 | 
				
			||||||
	const std::size_t nframes = src.size() / audio_format.channels;
 | 
					 | 
				
			||||||
	size_t written = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (input_pos > SHINE_MAX_SAMPLES)
 | 
					 | 
				
			||||||
		input_pos = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* write all data to de-interleaved buffers */
 | 
					 | 
				
			||||||
	while (written < nframes) {
 | 
					 | 
				
			||||||
		for (;
 | 
					 | 
				
			||||||
		     written < nframes && input_pos < frame_size;
 | 
					 | 
				
			||||||
		     written++, input_pos++) {
 | 
					 | 
				
			||||||
			const size_t base =
 | 
					 | 
				
			||||||
				written * audio_format.channels;
 | 
					 | 
				
			||||||
			stereo[0][input_pos] = src[base];
 | 
					 | 
				
			||||||
			stereo[1][input_pos] = src[base + 1];
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		/* write if chunk is filled */
 | 
					 | 
				
			||||||
		WriteChunk(false);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
ShineEncoder::Flush()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* flush buffers and flush shine */
 | 
					 | 
				
			||||||
	WriteChunk(true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int written;
 | 
					 | 
				
			||||||
	const auto data = (const std::byte *)shine_flush(shine, &written);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (written > 0)
 | 
					 | 
				
			||||||
		output_buffer.Append({data, std::size_t(written)});
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const EncoderPlugin shine_encoder_plugin = {
 | 
					 | 
				
			||||||
	"shine",
 | 
					 | 
				
			||||||
	shine_encoder_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,9 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_ENCODER_SHINE_HXX
 | 
					 | 
				
			||||||
#define MPD_ENCODER_SHINE_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const struct EncoderPlugin shine_encoder_plugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,209 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "TwolameEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "../EncoderAPI.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "lib/fmt/RuntimeError.hxx"
 | 
					 | 
				
			||||||
#include "util/CNumberParser.hxx"
 | 
					 | 
				
			||||||
#include "util/SpanCast.hxx"
 | 
					 | 
				
			||||||
#include "util/Domain.hxx"
 | 
					 | 
				
			||||||
#include "Log.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <twolame.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cassert>
 | 
					 | 
				
			||||||
#include <stdexcept>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class TwolameEncoder final : public Encoder {
 | 
					 | 
				
			||||||
	twolame_options *options;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::byte output_buffer[32768];
 | 
					 | 
				
			||||||
	std::size_t fill = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Call libtwolame's flush function when the output_buffer is
 | 
					 | 
				
			||||||
	 * empty?
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	bool flush = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	static constexpr unsigned CHANNELS = 2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	explicit TwolameEncoder(twolame_options *_options) noexcept
 | 
					 | 
				
			||||||
		:Encoder(false), options(_options) {}
 | 
					 | 
				
			||||||
	~TwolameEncoder() noexcept override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	TwolameEncoder(const TwolameEncoder &) = delete;
 | 
					 | 
				
			||||||
	TwolameEncoder &operator=(const TwolameEncoder &) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Encoder */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void End() override {
 | 
					 | 
				
			||||||
		flush = true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void Flush() override {
 | 
					 | 
				
			||||||
		flush = true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void Write(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
	std::span<const std::byte> Read(std::span<std::byte> buffer) noexcept override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedTwolameEncoder final : public PreparedEncoder {
 | 
					 | 
				
			||||||
	float quality;
 | 
					 | 
				
			||||||
	int bitrate;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit PreparedTwolameEncoder(const ConfigBlock &block);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedEncoder */
 | 
					 | 
				
			||||||
	Encoder *Open(AudioFormat &audio_format) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] const char *GetMimeType() const noexcept override {
 | 
					 | 
				
			||||||
		return  "audio/mpeg";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static constexpr Domain twolame_encoder_domain("twolame_encoder");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
PreparedTwolameEncoder::PreparedTwolameEncoder(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *value;
 | 
					 | 
				
			||||||
	char *endptr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	value = block.GetBlockValue("quality");
 | 
					 | 
				
			||||||
	if (value != nullptr) {
 | 
					 | 
				
			||||||
		/* a quality was configured (VBR) */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		quality = float(ParseDouble(value, &endptr));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (*endptr != '\0' || quality < -1.0f || quality > 10.0f)
 | 
					 | 
				
			||||||
			throw FmtRuntimeError("quality {:?} is not a number in the "
 | 
					 | 
				
			||||||
					      "range -1 to 10",
 | 
					 | 
				
			||||||
					      value);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (block.GetBlockValue("bitrate") != nullptr)
 | 
					 | 
				
			||||||
			throw std::runtime_error("quality and bitrate are both defined");
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* a bit rate was configured */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		value = block.GetBlockValue("bitrate");
 | 
					 | 
				
			||||||
		if (value == nullptr)
 | 
					 | 
				
			||||||
			throw std::runtime_error("neither bitrate nor quality defined");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		quality = -2.0;
 | 
					 | 
				
			||||||
		bitrate = ParseInt(value, &endptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (*endptr != '\0' || bitrate <= 0)
 | 
					 | 
				
			||||||
			throw std::runtime_error("bitrate should be a positive integer");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static PreparedEncoder *
 | 
					 | 
				
			||||||
twolame_encoder_init(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	FmtDebug(twolame_encoder_domain,
 | 
					 | 
				
			||||||
		 "libtwolame version {}", get_twolame_version());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return new PreparedTwolameEncoder(block);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
twolame_encoder_setup(twolame_options *options, float quality, int bitrate,
 | 
					 | 
				
			||||||
		      const AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (quality >= -1.0f) {
 | 
					 | 
				
			||||||
		/* a quality was configured (VBR) */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (0 != twolame_set_VBR(options, true))
 | 
					 | 
				
			||||||
			throw std::runtime_error("error setting twolame VBR mode");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (0 != twolame_set_VBR_q(options, quality))
 | 
					 | 
				
			||||||
			throw std::runtime_error("error setting twolame VBR quality");
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* a bit rate was configured */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (0 != twolame_set_brate(options, bitrate))
 | 
					 | 
				
			||||||
			throw std::runtime_error("error setting twolame bitrate");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (0 != twolame_set_num_channels(options, audio_format.channels))
 | 
					 | 
				
			||||||
		throw std::runtime_error("error setting twolame num channels");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (0 != twolame_set_in_samplerate(options,
 | 
					 | 
				
			||||||
					   audio_format.sample_rate))
 | 
					 | 
				
			||||||
		throw std::runtime_error("error setting twolame sample rate");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (0 > twolame_init_params(options))
 | 
					 | 
				
			||||||
		throw std::runtime_error("error initializing twolame params");
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Encoder *
 | 
					 | 
				
			||||||
PreparedTwolameEncoder::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	audio_format.format = SampleFormat::S16;
 | 
					 | 
				
			||||||
	audio_format.channels = TwolameEncoder::CHANNELS;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto options = twolame_init();
 | 
					 | 
				
			||||||
	if (options == nullptr)
 | 
					 | 
				
			||||||
		throw std::runtime_error("twolame_init() failed");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	try {
 | 
					 | 
				
			||||||
		twolame_encoder_setup(options, quality, bitrate,
 | 
					 | 
				
			||||||
				      audio_format);
 | 
					 | 
				
			||||||
	} catch (...) {
 | 
					 | 
				
			||||||
		twolame_close(&options);
 | 
					 | 
				
			||||||
		throw;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return new TwolameEncoder(options);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
TwolameEncoder::~TwolameEncoder() noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	twolame_close(&options);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
TwolameEncoder::Write(std::span<const std::byte> _src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const auto src = FromBytesStrict<const int16_t>(_src);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	assert(fill == 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const std::size_t num_frames = src.size() / CHANNELS;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int bytes_out = twolame_encode_buffer_interleaved(options,
 | 
					 | 
				
			||||||
							  src.data(), num_frames,
 | 
					 | 
				
			||||||
							  (unsigned char *)output_buffer,
 | 
					 | 
				
			||||||
							  sizeof(output_buffer));
 | 
					 | 
				
			||||||
	if (bytes_out < 0)
 | 
					 | 
				
			||||||
		throw std::runtime_error("twolame encoder failed");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fill = (std::size_t)bytes_out;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
TwolameEncoder::Read(std::span<std::byte>) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(fill <= sizeof(output_buffer));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (fill == 0 && flush) {
 | 
					 | 
				
			||||||
		int ret = twolame_encode_flush(options,
 | 
					 | 
				
			||||||
					       (unsigned char *)output_buffer,
 | 
					 | 
				
			||||||
					       sizeof(output_buffer));
 | 
					 | 
				
			||||||
		if (ret > 0)
 | 
					 | 
				
			||||||
			fill = (std::size_t)ret;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		flush = false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return std::span{output_buffer}.first(std::exchange(fill, 0));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const EncoderPlugin twolame_encoder_plugin = {
 | 
					 | 
				
			||||||
	"twolame",
 | 
					 | 
				
			||||||
	twolame_encoder_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,9 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_ENCODER_TWOLAME_HXX
 | 
					 | 
				
			||||||
#define MPD_ENCODER_TWOLAME_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const struct EncoderPlugin twolame_encoder_plugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,251 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "VorbisEncoderPlugin.hxx"
 | 
					 | 
				
			||||||
#include "OggEncoder.hxx"
 | 
					 | 
				
			||||||
#include "lib/fmt/RuntimeError.hxx"
 | 
					 | 
				
			||||||
#include "lib/xiph/VorbisComment.hxx"
 | 
					 | 
				
			||||||
#include "tag/Names.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "config/Domain.hxx"
 | 
					 | 
				
			||||||
#include "util/StringUtil.hxx"
 | 
					 | 
				
			||||||
#include "util/CNumberParser.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <vorbis/vorbisenc.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class VorbisEncoder final : public OggEncoder {
 | 
					 | 
				
			||||||
	AudioFormat audio_format;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vorbis_dsp_state vd;
 | 
					 | 
				
			||||||
	vorbis_block vb;
 | 
					 | 
				
			||||||
	vorbis_info vi;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	VorbisEncoder(float quality, int bitrate, AudioFormat &_audio_format);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	~VorbisEncoder() noexcept override {
 | 
					 | 
				
			||||||
		vorbis_block_clear(&vb);
 | 
					 | 
				
			||||||
		vorbis_dsp_clear(&vd);
 | 
					 | 
				
			||||||
		vorbis_info_clear(&vi);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	VorbisEncoder(const VorbisEncoder &) = delete;
 | 
					 | 
				
			||||||
	VorbisEncoder &operator=(const VorbisEncoder &) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Encoder */
 | 
					 | 
				
			||||||
	void End() override {
 | 
					 | 
				
			||||||
		PreTag();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void PreTag() override;
 | 
					 | 
				
			||||||
	void SendTag(const Tag &tag) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void Write(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	void HeaderOut(vorbis_comment &vc);
 | 
					 | 
				
			||||||
	void SendHeader();
 | 
					 | 
				
			||||||
	void BlockOut();
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedVorbisEncoder final : public PreparedEncoder {
 | 
					 | 
				
			||||||
	float quality = 3;
 | 
					 | 
				
			||||||
	int bitrate;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit PreparedVorbisEncoder(const ConfigBlock &block);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedEncoder */
 | 
					 | 
				
			||||||
	Encoder *Open(AudioFormat &audio_format) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] const char *GetMimeType() const noexcept override {
 | 
					 | 
				
			||||||
		return "audio/ogg";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
PreparedVorbisEncoder::PreparedVorbisEncoder(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *value = block.GetBlockValue("quality");
 | 
					 | 
				
			||||||
	if (value != nullptr) {
 | 
					 | 
				
			||||||
		/* a quality was configured (VBR) */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		char *endptr;
 | 
					 | 
				
			||||||
		quality = ParseDouble(value, &endptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (*endptr != '\0' || quality < -1.0f || quality > 10.0f)
 | 
					 | 
				
			||||||
			throw FmtRuntimeError("quality {:?} is not a number in the "
 | 
					 | 
				
			||||||
					      "range -1 to 10",
 | 
					 | 
				
			||||||
					      value);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (block.GetBlockValue("bitrate") != nullptr)
 | 
					 | 
				
			||||||
			throw std::runtime_error("quality and bitrate are both defined");
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* a bit rate was configured */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		value = block.GetBlockValue("bitrate");
 | 
					 | 
				
			||||||
		if (value == nullptr)
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		quality = -2.0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		char *endptr;
 | 
					 | 
				
			||||||
		bitrate = ParseInt(value, &endptr);
 | 
					 | 
				
			||||||
		if (*endptr != '\0' || bitrate <= 0)
 | 
					 | 
				
			||||||
			throw std::runtime_error("bitrate should be a positive integer");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static PreparedEncoder *
 | 
					 | 
				
			||||||
vorbis_encoder_init(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return new PreparedVorbisEncoder(block);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
VorbisEncoder::VorbisEncoder(float quality, int bitrate,
 | 
					 | 
				
			||||||
			     AudioFormat &_audio_format)
 | 
					 | 
				
			||||||
	:OggEncoder(true)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	vorbis_info_init(&vi);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_audio_format.format = SampleFormat::FLOAT;
 | 
					 | 
				
			||||||
	audio_format = _audio_format;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (quality >= -1.0f) {
 | 
					 | 
				
			||||||
		/* a quality was configured (VBR) */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (0 != vorbis_encode_init_vbr(&vi,
 | 
					 | 
				
			||||||
						audio_format.channels,
 | 
					 | 
				
			||||||
						audio_format.sample_rate,
 | 
					 | 
				
			||||||
						quality * 0.1f)) {
 | 
					 | 
				
			||||||
			vorbis_info_clear(&vi);
 | 
					 | 
				
			||||||
			throw std::runtime_error("error initializing vorbis vbr");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* a bit rate was configured */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (0 != vorbis_encode_init(&vi,
 | 
					 | 
				
			||||||
					    audio_format.channels,
 | 
					 | 
				
			||||||
					    audio_format.sample_rate, -1.0,
 | 
					 | 
				
			||||||
					    bitrate * 1000, -1.0f)) {
 | 
					 | 
				
			||||||
			vorbis_info_clear(&vi);
 | 
					 | 
				
			||||||
			throw std::runtime_error("error initializing vorbis encoder");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vorbis_analysis_init(&vd, &vi);
 | 
					 | 
				
			||||||
	vorbis_block_init(&vd, &vb);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	SendHeader();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
VorbisEncoder::HeaderOut(vorbis_comment &vc)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	ogg_packet packet, comments, codebooks;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vorbis_analysis_headerout(&vd, &vc,
 | 
					 | 
				
			||||||
				  &packet, &comments, &codebooks);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	stream.PacketIn(packet);
 | 
					 | 
				
			||||||
	stream.PacketIn(comments);
 | 
					 | 
				
			||||||
	stream.PacketIn(codebooks);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
VorbisEncoder::SendHeader()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	VorbisComment vc;
 | 
					 | 
				
			||||||
	HeaderOut(vc);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Encoder *
 | 
					 | 
				
			||||||
PreparedVorbisEncoder::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return new VorbisEncoder(quality, bitrate, audio_format);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
VorbisEncoder::BlockOut()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	while (vorbis_analysis_blockout(&vd, &vb) == 1) {
 | 
					 | 
				
			||||||
		vorbis_analysis(&vb, nullptr);
 | 
					 | 
				
			||||||
		vorbis_bitrate_addblock(&vb);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ogg_packet packet;
 | 
					 | 
				
			||||||
		while (vorbis_bitrate_flushpacket(&vd, &packet))
 | 
					 | 
				
			||||||
			stream.PacketIn(packet);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
VorbisEncoder::PreTag()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	vorbis_analysis_wrote(&vd, 0);
 | 
					 | 
				
			||||||
	BlockOut();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* reinitialize vorbis_dsp_state and vorbis_block to reset the
 | 
					 | 
				
			||||||
	   end-of-stream marker */
 | 
					 | 
				
			||||||
	vorbis_block_clear(&vb);
 | 
					 | 
				
			||||||
	vorbis_dsp_clear(&vd);
 | 
					 | 
				
			||||||
	vorbis_analysis_init(&vd, &vi);
 | 
					 | 
				
			||||||
	vorbis_block_init(&vd, &vb);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Flush();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
copy_tag_to_vorbis_comment(VorbisComment &vc, const Tag &tag)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	for (const auto &item : tag) {
 | 
					 | 
				
			||||||
		char name[64];
 | 
					 | 
				
			||||||
		ToUpperASCII(name, tag_item_names[item.type], sizeof(name));
 | 
					 | 
				
			||||||
		vc.AddTag(name, item.value);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
VorbisEncoder::SendTag(const Tag &tag)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* write the vorbis_comment object */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	VorbisComment comment;
 | 
					 | 
				
			||||||
	copy_tag_to_vorbis_comment(comment, tag);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* reset ogg_stream_state and begin a new stream */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	stream.Reinitialize(GenerateSerial());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* send that vorbis_comment to the ogg_stream_state */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	HeaderOut(comment);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
interleaved_to_vorbis_buffer(float **dest, const float *src,
 | 
					 | 
				
			||||||
			     std::size_t num_frames, std::size_t num_channels)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	for (unsigned i = 0; i < num_frames; i++)
 | 
					 | 
				
			||||||
		for (unsigned j = 0; j < num_channels; j++)
 | 
					 | 
				
			||||||
			dest[j][i] = *src++;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
VorbisEncoder::Write(std::span<const std::byte> src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	std::size_t num_frames = src.size() / audio_format.GetFrameSize();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* this is for only 16-bit audio */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	interleaved_to_vorbis_buffer(vorbis_analysis_buffer(&vd, num_frames),
 | 
					 | 
				
			||||||
				     (const float *)(const void *)src.data(),
 | 
					 | 
				
			||||||
				     num_frames,
 | 
					 | 
				
			||||||
				     audio_format.channels);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	vorbis_analysis_wrote(&vd, num_frames);
 | 
					 | 
				
			||||||
	BlockOut();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const EncoderPlugin vorbis_encoder_plugin = {
 | 
					 | 
				
			||||||
	"vorbis",
 | 
					 | 
				
			||||||
	vorbis_encoder_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,9 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_ENCODER_VORBIS_H
 | 
					 | 
				
			||||||
#define MPD_ENCODER_VORBIS_H
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const struct EncoderPlugin vorbis_encoder_plugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,217 +1,12 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Encoder support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "WaveEncoderPlugin.hxx"
 | 
					#include "WaveEncoderPlugin.hxx"
 | 
				
			||||||
#include "../EncoderAPI.hxx"
 | 
					 | 
				
			||||||
#include "tag/RiffFormat.hxx"
 | 
					 | 
				
			||||||
#include "util/ByteOrder.hxx"
 | 
					 | 
				
			||||||
#include "util/DynamicFifoBuffer.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cassert>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class WaveEncoder final : public Encoder {
 | 
					 | 
				
			||||||
	unsigned bits;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DynamicFifoBuffer<std::byte> buffer{8192};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit WaveEncoder(AudioFormat &audio_format) noexcept;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Encoder */
 | 
					 | 
				
			||||||
	void Write(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> Read(std::span<std::byte> b) noexcept override {
 | 
					 | 
				
			||||||
		return b.first(buffer.Read(b.data(), b.size()));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedWaveEncoder final : public PreparedEncoder {
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedEncoder */
 | 
					 | 
				
			||||||
	Encoder *Open(AudioFormat &audio_format) override {
 | 
					 | 
				
			||||||
		return new WaveEncoder(audio_format);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] const char *GetMimeType() const noexcept override {
 | 
					 | 
				
			||||||
		return "audio/wav";
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct WaveHeader {
 | 
					 | 
				
			||||||
	RiffFileHeader file_header;
 | 
					 | 
				
			||||||
	RiffChunkHeader fmt_header;
 | 
					 | 
				
			||||||
	RiffFmtChunk fmt;
 | 
					 | 
				
			||||||
	RiffChunkHeader data_header;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static_assert(sizeof(WaveHeader) == 44);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static WaveHeader
 | 
					 | 
				
			||||||
MakeWaveHeader(int channels, int bits,
 | 
					 | 
				
			||||||
	       int freq, int block_size) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	WaveHeader header{};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	int data_size = 0x0FFFFFFF;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* constants */
 | 
					 | 
				
			||||||
	memcpy(header.file_header.id, "RIFF", 4);
 | 
					 | 
				
			||||||
	memcpy(header.file_header.format, "WAVE", 4);
 | 
					 | 
				
			||||||
	memcpy(header.fmt_header.id, "fmt ", 4);
 | 
					 | 
				
			||||||
	memcpy(header.data_header.id, "data", 4);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* wave format */
 | 
					 | 
				
			||||||
	header.fmt.tag = ToLE16(RiffFmtChunk::TAG_PCM);
 | 
					 | 
				
			||||||
	header.fmt.channels = ToLE16(channels);
 | 
					 | 
				
			||||||
	header.fmt.bits_per_sample = ToLE16(bits);
 | 
					 | 
				
			||||||
	header.fmt.sample_rate = ToLE32(freq);
 | 
					 | 
				
			||||||
	header.fmt.block_align = ToLE16(block_size);
 | 
					 | 
				
			||||||
	header.fmt.byte_rate = ToLE32(freq * block_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* chunk sizes (fake data length) */
 | 
					 | 
				
			||||||
	header.fmt_header.size = ToLE32(sizeof(header.fmt));
 | 
					 | 
				
			||||||
	header.data_header.size = ToLE32(data_size);
 | 
					 | 
				
			||||||
	header.file_header.size = ToLE32(4 +
 | 
					 | 
				
			||||||
					 sizeof(header.fmt_header) + sizeof(header.fmt) +
 | 
					 | 
				
			||||||
					 sizeof(header.data_header) + data_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return header;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static PreparedEncoder *
 | 
					 | 
				
			||||||
wave_encoder_init([[maybe_unused]] const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return new PreparedWaveEncoder();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
WaveEncoder::WaveEncoder(AudioFormat &audio_format) noexcept
 | 
					 | 
				
			||||||
	:Encoder(false)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(audio_format.IsValid());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (audio_format.format) {
 | 
					 | 
				
			||||||
	case SampleFormat::S8:
 | 
					 | 
				
			||||||
		bits = 8;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case SampleFormat::S16:
 | 
					 | 
				
			||||||
		bits = 16;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case SampleFormat::S24_P32:
 | 
					 | 
				
			||||||
		bits = 24;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	case SampleFormat::S32:
 | 
					 | 
				
			||||||
		bits = 32;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	default:
 | 
					 | 
				
			||||||
		audio_format.format = SampleFormat::S16;
 | 
					 | 
				
			||||||
		bits = 16;
 | 
					 | 
				
			||||||
		break;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto range = buffer.Write();
 | 
					 | 
				
			||||||
	assert(range.size() >= sizeof(WaveHeader));
 | 
					 | 
				
			||||||
	auto *header = (WaveHeader *)(void *)range.data();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* create PCM wave header in initial buffer */
 | 
					 | 
				
			||||||
	*header = MakeWaveHeader(audio_format.channels,
 | 
					 | 
				
			||||||
				 bits,
 | 
					 | 
				
			||||||
				 audio_format.sample_rate,
 | 
					 | 
				
			||||||
				 (bits / 8) * audio_format.channels);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	buffer.Append(sizeof(*header));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static size_t
 | 
					 | 
				
			||||||
pcm16_to_wave(uint16_t *dst16, const uint16_t *src16, size_t length)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	size_t cnt = length >> 1;
 | 
					 | 
				
			||||||
	while (cnt > 0) {
 | 
					 | 
				
			||||||
		*dst16++ = ToLE16(*src16++);
 | 
					 | 
				
			||||||
		cnt--;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return length;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static size_t
 | 
					 | 
				
			||||||
pcm32_to_wave(uint32_t *dst32, const uint32_t *src32, size_t length) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	size_t cnt = length >> 2;
 | 
					 | 
				
			||||||
	while (cnt > 0){
 | 
					 | 
				
			||||||
		*dst32++ = ToLE32(*src32++);
 | 
					 | 
				
			||||||
		cnt--;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return length;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static size_t
 | 
					 | 
				
			||||||
pcm24_to_wave(uint8_t *dst8, const uint32_t *src32, size_t length) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	uint32_t value;
 | 
					 | 
				
			||||||
	uint8_t *dst_old = dst8;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	length = length >> 2;
 | 
					 | 
				
			||||||
	while (length > 0){
 | 
					 | 
				
			||||||
		value = *src32++;
 | 
					 | 
				
			||||||
		*dst8++ = (value) & 0xFF;
 | 
					 | 
				
			||||||
		*dst8++ = (value >> 8) & 0xFF;
 | 
					 | 
				
			||||||
		*dst8++ = (value >> 16) & 0xFF;
 | 
					 | 
				
			||||||
		length--;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	//correct buffer length
 | 
					 | 
				
			||||||
	return (dst8 - dst_old);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
WaveEncoder::Write(std::span<const std::byte> src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	std::size_t length = src.size();
 | 
					 | 
				
			||||||
	std::byte *dst = buffer.Write(length);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (IsLittleEndian()) {
 | 
					 | 
				
			||||||
		switch (bits) {
 | 
					 | 
				
			||||||
		case 8:
 | 
					 | 
				
			||||||
		case 16:
 | 
					 | 
				
			||||||
		case 32:// optimized cases
 | 
					 | 
				
			||||||
			memcpy(dst, src.data(), length);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case 24:
 | 
					 | 
				
			||||||
			length = pcm24_to_wave((uint8_t *)dst,
 | 
					 | 
				
			||||||
					       (const uint32_t *)(const void *)src.data(),
 | 
					 | 
				
			||||||
					       length);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		switch (bits) {
 | 
					 | 
				
			||||||
		case 8:
 | 
					 | 
				
			||||||
			memcpy(dst, src.data(), length);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case 16:
 | 
					 | 
				
			||||||
			length = pcm16_to_wave((uint16_t *)dst,
 | 
					 | 
				
			||||||
					       (const uint16_t *)(const void *)src.data(),
 | 
					 | 
				
			||||||
					       length);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case 24:
 | 
					 | 
				
			||||||
			length = pcm24_to_wave((uint8_t *)dst,
 | 
					 | 
				
			||||||
					       (const uint32_t *)(const void *)src.data(),
 | 
					 | 
				
			||||||
					       length);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		case 32:
 | 
					 | 
				
			||||||
			length = pcm32_to_wave((uint32_t *)dst,
 | 
					 | 
				
			||||||
					       (const uint32_t *)(const void *)src.data(),
 | 
					 | 
				
			||||||
					       length);
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	buffer.Append(length);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Stub implementation - encoder support not needed for database creation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
const EncoderPlugin wave_encoder_plugin = {
 | 
					const EncoderPlugin wave_encoder_plugin = {
 | 
				
			||||||
	"wave",
 | 
						"wave",
 | 
				
			||||||
	wave_encoder_init,
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,15 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Encoder support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_ENCODER_WAVE_HXX
 | 
					#ifndef MPD_WAVE_ENCODER_PLUGIN_HXX
 | 
				
			||||||
#define MPD_ENCODER_WAVE_HXX
 | 
					#define MPD_WAVE_ENCODER_PLUGIN_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern const struct EncoderPlugin wave_encoder_plugin;
 | 
					#include "encoder/EncoderPlugin.hxx"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Stub implementation - encoder support not needed for database creation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					extern const EncoderPlugin wave_encoder_plugin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,80 +0,0 @@
 | 
				
			|||||||
encoder_plugins_sources = [
 | 
					 | 
				
			||||||
  'NullEncoderPlugin.cxx',
 | 
					 | 
				
			||||||
]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
encoder_features.set('ENABLE_FLAC_ENCODER', flac_dep.found())
 | 
					 | 
				
			||||||
if flac_dep.found()
 | 
					 | 
				
			||||||
  encoder_plugins_sources += 'FlacEncoderPlugin.cxx'
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if libopus_dep.found()
 | 
					 | 
				
			||||||
  encoder_plugins_sources += 'OpusEncoderPlugin.cxx'
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
encoder_features.set('ENABLE_VORBISENC', libvorbisenc_dep.found())
 | 
					 | 
				
			||||||
if libvorbisenc_dep.found()
 | 
					 | 
				
			||||||
  encoder_plugins_sources += 'VorbisEncoderPlugin.cxx'
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if not get_option('lame').disabled()
 | 
					 | 
				
			||||||
  # LAME doesn't have a pkg-config file so we have to use
 | 
					 | 
				
			||||||
  # find_library()
 | 
					 | 
				
			||||||
  liblame_dep = c_compiler.find_library('mp3lame', required: false)
 | 
					 | 
				
			||||||
  if not liblame_dep.found()
 | 
					 | 
				
			||||||
    # only if that was not found, use dependency() which may use the
 | 
					 | 
				
			||||||
    # LAME subproject
 | 
					 | 
				
			||||||
    liblame_dep = dependency('mp3lame', required: get_option('lame'))
 | 
					 | 
				
			||||||
  endif
 | 
					 | 
				
			||||||
else
 | 
					 | 
				
			||||||
  liblame_dep = dependency('', required: false)
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
encoder_features.set('ENABLE_LAME', liblame_dep.found())
 | 
					 | 
				
			||||||
if liblame_dep.found()
 | 
					 | 
				
			||||||
  encoder_plugins_sources += 'LameEncoderPlugin.cxx'
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
libtwolame_dep = dependency('twolame', required: get_option('twolame'))
 | 
					 | 
				
			||||||
encoder_features.set('ENABLE_TWOLAME', libtwolame_dep.found())
 | 
					 | 
				
			||||||
if libtwolame_dep.found()
 | 
					 | 
				
			||||||
  encoder_plugins_sources += 'TwolameEncoderPlugin.cxx'
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
libshine_dep = dependency('shine', version: '>= 3.1', required: get_option('shine'))
 | 
					 | 
				
			||||||
encoder_features.set('ENABLE_SHINE', libshine_dep.found())
 | 
					 | 
				
			||||||
if libshine_dep.found()
 | 
					 | 
				
			||||||
  encoder_plugins_sources += 'ShineEncoderPlugin.cxx'
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
encoder_features.set('ENABLE_WAVE_ENCODER', get_option('wave_encoder'))
 | 
					 | 
				
			||||||
if get_option('wave_encoder') or need_wave_encoder
 | 
					 | 
				
			||||||
  encoder_plugins_sources += 'WaveEncoderPlugin.cxx'
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
encoder_plugins = static_library(
 | 
					 | 
				
			||||||
  'encoder_plugins',
 | 
					 | 
				
			||||||
  encoder_plugins_sources,
 | 
					 | 
				
			||||||
  include_directories: inc,
 | 
					 | 
				
			||||||
  dependencies: [
 | 
					 | 
				
			||||||
    pcm_basic_dep,
 | 
					 | 
				
			||||||
    flac_dep,
 | 
					 | 
				
			||||||
    ogg_dep,
 | 
					 | 
				
			||||||
    libopus_dep,
 | 
					 | 
				
			||||||
    libvorbisenc_dep,
 | 
					 | 
				
			||||||
    libvorbis_dep,
 | 
					 | 
				
			||||||
    liblame_dep,
 | 
					 | 
				
			||||||
    libtwolame_dep,
 | 
					 | 
				
			||||||
    libshine_dep,
 | 
					 | 
				
			||||||
    log_dep,
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
encoder_plugins_dep = declare_dependency(
 | 
					 | 
				
			||||||
  link_with: encoder_plugins,
 | 
					 | 
				
			||||||
  dependencies: [
 | 
					 | 
				
			||||||
    encoder_api_dep,
 | 
					 | 
				
			||||||
    tag_dep,
 | 
					 | 
				
			||||||
    pcm_dep,
 | 
					 | 
				
			||||||
    config_dep,
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
@ -1,23 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "Factory.hxx"
 | 
					 | 
				
			||||||
#include "LoadOne.hxx"
 | 
					 | 
				
			||||||
#include "Prepared.hxx"
 | 
					 | 
				
			||||||
#include "config/Data.hxx"
 | 
					 | 
				
			||||||
#include "config/Block.hxx"
 | 
					 | 
				
			||||||
#include "lib/fmt/RuntimeError.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
FilterFactory::MakeFilter(const char *name)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const auto *cfg = config.FindBlock(ConfigBlockOption::AUDIO_FILTER,
 | 
					 | 
				
			||||||
					   "name", name);
 | 
					 | 
				
			||||||
	if (cfg == nullptr)
 | 
					 | 
				
			||||||
		throw FmtRuntimeError("Filter template not found: {}",
 | 
					 | 
				
			||||||
				      name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	cfg->SetUsed();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filter_configured_new(*cfg);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,22 +1,18 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_FILTER_FACTORY_HXX
 | 
					#ifndef MPD_FILTER_FACTORY_HXX
 | 
				
			||||||
#define MPD_FILTER_FACTORY_HXX
 | 
					#define MPD_FILTER_FACTORY_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <memory>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct ConfigData;
 | 
					struct ConfigData;
 | 
				
			||||||
class PreparedFilter;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Stub implementation - filter support not needed for database creation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
class FilterFactory {
 | 
					class FilterFactory {
 | 
				
			||||||
	const ConfigData &config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	explicit FilterFactory(const ConfigData &_config) noexcept
 | 
						FilterFactory([[maybe_unused]] const ConfigData &config) {}
 | 
				
			||||||
		:config(_config) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::unique_ptr<PreparedFilter> MakeFilter(const char *name);
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Filter.hxx"
 | 
				
			||||||
 | 
					#include "pcm/AudioFormat.hxx"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const AudioFormat &
 | 
				
			||||||
 | 
					Filter::GetOutAudioFormat() const noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// Return a static dummy audio format with minimal valid initialization
 | 
				
			||||||
 | 
						static const AudioFormat dummy_format{44100, SampleFormat::S16, 2};
 | 
				
			||||||
 | 
						return dummy_format;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,84 +1,42 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_FILTER_HXX
 | 
					#ifndef MPD_FILTER_HXX
 | 
				
			||||||
#define MPD_FILTER_HXX
 | 
					#define MPD_FILTER_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cassert>
 | 
					 | 
				
			||||||
#include <cstddef>
 | 
					 | 
				
			||||||
#include <span>
 | 
					#include <span>
 | 
				
			||||||
 | 
					#include <cstddef>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Filter {
 | 
					struct AudioFormat;
 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
	AudioFormat out_audio_format;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	explicit Filter(AudioFormat _out_audio_format) noexcept
 | 
					 | 
				
			||||||
		:out_audio_format(_out_audio_format) {
 | 
					 | 
				
			||||||
		assert(out_audio_format.IsValid());
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Stub implementation - filter support not needed for database creation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class Filter {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	virtual ~Filter() noexcept = default;
 | 
						virtual ~Filter() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						virtual std::span<const std::byte> FilterPCM(std::span<const std::byte> src) {
 | 
				
			||||||
	 * Returns the #AudioFormat produced by FilterPCM().
 | 
							return src; // Pass-through stub
 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	const AudioFormat &GetOutAudioFormat() const noexcept {
 | 
					 | 
				
			||||||
		return out_audio_format;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						virtual std::span<const std::byte> Flush() {
 | 
				
			||||||
	 * Reset the filter's state, e.g. drop/flush buffers.
 | 
							return {}; // Empty stub
 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual void Reset() noexcept {
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Filters a block of PCM data.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Throws on error.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param src the input buffer
 | 
					 | 
				
			||||||
	 * @return the output buffer (will be invalidated by deleting
 | 
					 | 
				
			||||||
	 * this object or any call to Reset(), FilterPCM(), ReadMore()
 | 
					 | 
				
			||||||
	 * or Flush()); may be empty if no output is currently
 | 
					 | 
				
			||||||
	 * available
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual std::span<const std::byte> FilterPCM(std::span<const std::byte> src) = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Read more result data from the filter.  After each
 | 
					 | 
				
			||||||
	 * FilterPCM() call, this should be called repeatedly until it
 | 
					 | 
				
			||||||
	 * returns an empty span.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Throws on error.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return the output buffer (will be invalidated by deleting
 | 
					 | 
				
			||||||
	 * this object or any call to Reset(), FilterPCM(), ReadMore()
 | 
					 | 
				
			||||||
	 * or Flush()); may be empty if no output is currently
 | 
					 | 
				
			||||||
	 * available
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual std::span<const std::byte> ReadMore() {
 | 
						virtual std::span<const std::byte> ReadMore() {
 | 
				
			||||||
		return {};
 | 
							return {}; // Empty stub
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						virtual void Reset() noexcept {
 | 
				
			||||||
	 * Flush pending data and return it.  This should be called
 | 
							// No-op stub
 | 
				
			||||||
	 * repeatedly until it returns an empty span.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * After calling this method, this object cannot be used again
 | 
					 | 
				
			||||||
         * (not even Reset() is allowed).
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * Throws on error.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @return pending data (will be invalidated by deleting this
 | 
					 | 
				
			||||||
	 * object or by any call to Flush())
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual std::span<const std::byte> Flush() {
 | 
					 | 
				
			||||||
		return {};
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual const AudioFormat &GetOutAudioFormat() const noexcept;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PreparedFilter; // Forward declaration - see Prepared.hxx for full definition
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,27 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** \file
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This header declares the filter_plugin class.  It describes a
 | 
					 | 
				
			||||||
 * plugin API for objects which filter raw PCM data.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_FILTER_PLUGIN_HXX
 | 
					 | 
				
			||||||
#define MPD_FILTER_PLUGIN_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <memory>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct ConfigBlock;
 | 
					 | 
				
			||||||
class PreparedFilter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct FilterPlugin {
 | 
					 | 
				
			||||||
	const char *name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
         * Allocates and configures a filter.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	std::unique_ptr<PreparedFilter> (*init)(const ConfigBlock &block);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,40 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "LoadChain.hxx"
 | 
					 | 
				
			||||||
#include "Factory.hxx"
 | 
					 | 
				
			||||||
#include "Prepared.hxx"
 | 
					 | 
				
			||||||
#include "plugins/AutoConvertFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/TwoFilters.hxx"
 | 
					 | 
				
			||||||
#include "util/IterableSplitString.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
filter_chain_append_new(std::unique_ptr<PreparedFilter> &chain,
 | 
					 | 
				
			||||||
			FilterFactory &factory,
 | 
					 | 
				
			||||||
			std::string_view template_name)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* using the AutoConvert filter just in case the specified
 | 
					 | 
				
			||||||
	   filter plugin does not support the exact input format */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	chain = ChainFilters(std::move(chain),
 | 
					 | 
				
			||||||
			     /* unfortunately, MakeFilter() wants a
 | 
					 | 
				
			||||||
				null-terminated string, so we need to
 | 
					 | 
				
			||||||
				copy it here */
 | 
					 | 
				
			||||||
			     autoconvert_filter_new(factory.MakeFilter(std::string(template_name).c_str())),
 | 
					 | 
				
			||||||
			     template_name);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
filter_chain_parse(std::unique_ptr<PreparedFilter> &chain,
 | 
					 | 
				
			||||||
		   FilterFactory &factory,
 | 
					 | 
				
			||||||
		   const char *spec)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	for (const std::string_view i : IterableSplitString(spec, ',')) {
 | 
					 | 
				
			||||||
		if (i.empty())
 | 
					 | 
				
			||||||
			continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		filter_chain_append_new(chain, factory, i);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,28 +1,31 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_FILTER_LOAD_CHAIN_HXX
 | 
					#ifndef MPD_FILTER_LOAD_CHAIN_HXX
 | 
				
			||||||
#define MPD_FILTER_LOAD_CHAIN_HXX
 | 
					#define MPD_FILTER_LOAD_CHAIN_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Prepared.hxx"
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FilterFactory;
 | 
					class FilterFactory;
 | 
				
			||||||
class PreparedFilter;
 | 
					struct ConfigBlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Builds a filter chain from a configuration string on the form
 | 
					 * Stub implementation - filter support not needed for database creation
 | 
				
			||||||
 * "name1, name2, name3, ..." by looking up each name among the
 | 
					 | 
				
			||||||
 * configured filter sections.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Throws on error.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param chain the chain to append filters on
 | 
					 | 
				
			||||||
 * @param config the global configuration to load filter definitions from
 | 
					 | 
				
			||||||
 * @param spec the filter chain specification
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void
 | 
					inline std::unique_ptr<PreparedFilter>
 | 
				
			||||||
filter_chain_parse(std::unique_ptr<PreparedFilter> &chain,
 | 
					filter_chain_parse([[maybe_unused]] std::unique_ptr<PreparedFilter> &prepared,
 | 
				
			||||||
		   FilterFactory &factory,
 | 
							   [[maybe_unused]] FilterFactory &factory,
 | 
				
			||||||
		   const char *spec);
 | 
							   [[maybe_unused]] const char *spec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline std::unique_ptr<PreparedFilter>
 | 
				
			||||||
 | 
					filter_chain_new([[maybe_unused]] const ConfigBlock *block)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,24 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "LoadOne.hxx"
 | 
					 | 
				
			||||||
#include "FilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "Registry.hxx"
 | 
					 | 
				
			||||||
#include "Prepared.hxx"
 | 
					 | 
				
			||||||
#include "config/Block.hxx"
 | 
					 | 
				
			||||||
#include "lib/fmt/RuntimeError.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
filter_configured_new(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *plugin_name = block.GetBlockValue("plugin");
 | 
					 | 
				
			||||||
	if (plugin_name == nullptr)
 | 
					 | 
				
			||||||
		throw std::runtime_error("No filter plugin specified");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const auto *plugin = filter_plugin_by_name(plugin_name);
 | 
					 | 
				
			||||||
	if (plugin == nullptr)
 | 
					 | 
				
			||||||
		throw FmtRuntimeError("No such filter plugin: {}",
 | 
					 | 
				
			||||||
				      plugin_name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return plugin->init(block);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,23 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_FILTER_LOAD_ONE_HXX
 | 
					 | 
				
			||||||
#define MPD_FILTER_LOAD_ONE_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <memory>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct ConfigBlock;
 | 
					 | 
				
			||||||
class PreparedFilter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Creates a new filter, loads configuration and the plugin name from
 | 
					 | 
				
			||||||
 * the specified configuration section.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Throws on error.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param block the configuration section
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
filter_configured_new(const ConfigBlock &block);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,18 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_NULL_FILTER_HXX
 | 
					 | 
				
			||||||
#define MPD_NULL_FILTER_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "filter/Filter.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class NullFilter final : public Filter {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit NullFilter(const AudioFormat &af):Filter(af) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> FilterPCM(std::span<const std::byte> src) override {
 | 
					 | 
				
			||||||
		return src;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,114 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "Observer.hxx"
 | 
					 | 
				
			||||||
#include "Filter.hxx"
 | 
					 | 
				
			||||||
#include "Prepared.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cassert>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class FilterObserver::PreparedProxy final : public PreparedFilter {
 | 
					 | 
				
			||||||
	FilterObserver &observer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::unique_ptr<PreparedFilter> prepared_filter;
 | 
					 | 
				
			||||||
	Proxy *child = nullptr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	PreparedProxy(FilterObserver &_observer,
 | 
					 | 
				
			||||||
		      std::unique_ptr<PreparedFilter> _prepared_filter) noexcept
 | 
					 | 
				
			||||||
		:observer(_observer),
 | 
					 | 
				
			||||||
		 prepared_filter(std::move(_prepared_filter)) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	~PreparedProxy() noexcept override {
 | 
					 | 
				
			||||||
		assert(child == nullptr);
 | 
					 | 
				
			||||||
		assert(observer.proxy == this);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		observer.proxy = nullptr;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	PreparedProxy(const PreparedProxy &) = delete;
 | 
					 | 
				
			||||||
	PreparedProxy &operator=(const PreparedProxy &) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void Clear([[maybe_unused]] Proxy *_child) noexcept {
 | 
					 | 
				
			||||||
		assert(child == _child);
 | 
					 | 
				
			||||||
		child = nullptr;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Filter *Get() noexcept;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> Open(AudioFormat &af) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class FilterObserver::Proxy final : public Filter {
 | 
					 | 
				
			||||||
	PreparedProxy &parent;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> filter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	Proxy(PreparedProxy &_parent, std::unique_ptr<Filter> _filter) noexcept
 | 
					 | 
				
			||||||
		:Filter(_filter->GetOutAudioFormat()),
 | 
					 | 
				
			||||||
		 parent(_parent), filter(std::move(_filter)) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	~Proxy() noexcept override {
 | 
					 | 
				
			||||||
		parent.Clear(this);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Proxy(const Proxy &) = delete;
 | 
					 | 
				
			||||||
	Proxy &operator=(const Proxy &) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Filter *Get() noexcept {
 | 
					 | 
				
			||||||
		return filter.get();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void Reset() noexcept override {
 | 
					 | 
				
			||||||
		filter->Reset();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> FilterPCM(std::span<const std::byte> src) override {
 | 
					 | 
				
			||||||
		return filter->FilterPCM(src);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> ReadMore() override {
 | 
					 | 
				
			||||||
		return filter->ReadMore();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> Flush() override {
 | 
					 | 
				
			||||||
		return filter->Flush();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Filter *
 | 
					 | 
				
			||||||
FilterObserver::PreparedProxy::Get() noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return child != nullptr
 | 
					 | 
				
			||||||
		? child->Get()
 | 
					 | 
				
			||||||
		: nullptr;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
FilterObserver::PreparedProxy::Open(AudioFormat &af)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(child == nullptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto c = std::make_unique<Proxy>(*this, prepared_filter->Open(af));
 | 
					 | 
				
			||||||
	child = c.get();
 | 
					 | 
				
			||||||
	return c;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
FilterObserver::Set(std::unique_ptr<PreparedFilter> pf)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(proxy == nullptr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto p = std::make_unique<PreparedProxy>(*this, std::move(pf));
 | 
					 | 
				
			||||||
	proxy = p.get();
 | 
					 | 
				
			||||||
	return p;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Filter *
 | 
					 | 
				
			||||||
FilterObserver::Get() noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return proxy != nullptr
 | 
					 | 
				
			||||||
		? proxy->Get()
 | 
					 | 
				
			||||||
		: nullptr;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,31 +1,32 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_FILTER_OBSERVER_HXX
 | 
					#ifndef MPD_FILTER_OBSERVER_HXX
 | 
				
			||||||
#define MPD_FILTER_OBSERVER_HXX
 | 
					#define MPD_FILTER_OBSERVER_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PreparedFilter;
 | 
					 | 
				
			||||||
class Filter;
 | 
					class Filter;
 | 
				
			||||||
 | 
					class PreparedFilter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * A helper class which observes calls to a #PreparedFilter and allows
 | 
					 * Stub implementation - filter support not needed for database creation
 | 
				
			||||||
 * the caller to access the #Filter instances created by it.
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class FilterObserver {
 | 
					class FilterObserver {
 | 
				
			||||||
	class PreparedProxy;
 | 
					public:
 | 
				
			||||||
	class Proxy;
 | 
						FilterObserver() noexcept = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	PreparedProxy *proxy = nullptr;
 | 
						Filter *Get() noexcept { return nullptr; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
						void Set(Filter *) noexcept {}
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * @return a proxy object
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	std::unique_ptr<PreparedFilter> Set(std::unique_ptr<PreparedFilter> pf);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Filter *Get() noexcept;
 | 
						// Also accept PreparedFilter unique_ptr, return it for chaining
 | 
				
			||||||
 | 
						// Use template to avoid incomplete type issues
 | 
				
			||||||
 | 
						template<typename T>
 | 
				
			||||||
 | 
						std::unique_ptr<T> Set(std::unique_ptr<T> p) noexcept {
 | 
				
			||||||
 | 
							return p;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,28 +1,25 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_PREPARED_FILTER_HXX
 | 
					#ifndef MPD_FILTER_PREPARED_HXX
 | 
				
			||||||
#define MPD_PREPARED_FILTER_HXX
 | 
					#define MPD_FILTER_PREPARED_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Filter.hxx"
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct AudioFormat;
 | 
					struct AudioFormat;
 | 
				
			||||||
class Filter;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Stub implementation - filter support not needed for database creation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
class PreparedFilter {
 | 
					class PreparedFilter {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	virtual ~PreparedFilter() = default;
 | 
						virtual ~PreparedFilter() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						virtual std::unique_ptr<Filter> Open([[maybe_unused]] const AudioFormat &af) {
 | 
				
			||||||
	 * Opens the filter, preparing it for FilterPCM().
 | 
							return nullptr; // Stub returns nullptr
 | 
				
			||||||
	 *
 | 
						}
 | 
				
			||||||
	 * Throws on error.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * @param af the audio format of incoming data; the
 | 
					 | 
				
			||||||
	 * plugin may modify the object to enforce another input
 | 
					 | 
				
			||||||
	 * format
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	virtual std::unique_ptr<Filter> Open(AudioFormat &af) = 0;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,34 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "Registry.hxx"
 | 
					 | 
				
			||||||
#include "FilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/NullFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/RouteFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/NormalizeFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/FfmpegFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "plugins/HdcdFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "config.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static constinit const FilterPlugin *const filter_plugins[] = {
 | 
					 | 
				
			||||||
	&null_filter_plugin,
 | 
					 | 
				
			||||||
	&route_filter_plugin,
 | 
					 | 
				
			||||||
	&normalize_filter_plugin,
 | 
					 | 
				
			||||||
#ifdef HAVE_LIBAVFILTER
 | 
					 | 
				
			||||||
	&ffmpeg_filter_plugin,
 | 
					 | 
				
			||||||
	&hdcd_filter_plugin,
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	nullptr,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const FilterPlugin *
 | 
					 | 
				
			||||||
filter_plugin_by_name(const char *name) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	for (unsigned i = 0; filter_plugins[i] != nullptr; ++i)
 | 
					 | 
				
			||||||
		if (strcmp(filter_plugins[i]->name, name) == 0)
 | 
					 | 
				
			||||||
			return filter_plugins[i];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nullptr;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,19 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** \file
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This library manages all filter plugins which are enabled at
 | 
					 | 
				
			||||||
 * compile time.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_FILTER_REGISTRY_HXX
 | 
					 | 
				
			||||||
#define MPD_FILTER_REGISTRY_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct FilterPlugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[gnu::pure]]
 | 
					 | 
				
			||||||
const FilterPlugin *
 | 
					 | 
				
			||||||
filter_plugin_by_name(const char *name) noexcept;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,56 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "AutoConvertFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "ConvertFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "TwoFilters.hxx"
 | 
					 | 
				
			||||||
#include "filter/Filter.hxx"
 | 
					 | 
				
			||||||
#include "filter/Prepared.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cassert>
 | 
					 | 
				
			||||||
#include <memory>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedAutoConvertFilter final : public PreparedFilter {
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The underlying filter.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	std::unique_ptr<PreparedFilter> filter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit PreparedAutoConvertFilter(std::unique_ptr<PreparedFilter> _filter) noexcept
 | 
					 | 
				
			||||||
		:filter(std::move(_filter)) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> Open(AudioFormat &af) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
PreparedAutoConvertFilter::Open(AudioFormat &in_audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(in_audio_format.IsValid());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* open the "real" filter */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	AudioFormat child_audio_format = in_audio_format;
 | 
					 | 
				
			||||||
	auto new_filter = filter->Open(child_audio_format);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* need to convert? */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (in_audio_format == child_audio_format)
 | 
					 | 
				
			||||||
		/* no */
 | 
					 | 
				
			||||||
		return new_filter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* yes - create a convert_filter */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto convert = convert_filter_new(in_audio_format,
 | 
					 | 
				
			||||||
					  child_audio_format);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return std::make_unique<TwoFilters>(std::move(convert),
 | 
					 | 
				
			||||||
					    std::move(new_filter));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
autoconvert_filter_new(std::unique_ptr<PreparedFilter> filter) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::make_unique<PreparedAutoConvertFilter>(std::move(filter));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,20 +1,20 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
 | 
					#ifndef MPD_AUTO_CONVERT_FILTER_PLUGIN_HXX
 | 
				
			||||||
#define MPD_AUTOCONVERT_FILTER_PLUGIN_HXX
 | 
					#define MPD_AUTO_CONVERT_FILTER_PLUGIN_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "filter/Prepared.hxx"
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PreparedFilter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Creates a new "autoconvert" filter.  When opened, it ensures that
 | 
					 * Stub implementation - filter support not needed for database creation
 | 
				
			||||||
 * the input audio format isn't changed.  If the underlying filter
 | 
					 | 
				
			||||||
 * requests a different format, it automatically creates a
 | 
					 | 
				
			||||||
 * convert_filter.
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					inline std::unique_ptr<PreparedFilter>
 | 
				
			||||||
autoconvert_filter_new(std::unique_ptr<PreparedFilter> filter) noexcept;
 | 
					autoconvert_filter_new([[maybe_unused]] std::unique_ptr<PreparedFilter> a = nullptr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,118 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "ConvertFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "filter/Filter.hxx"
 | 
					 | 
				
			||||||
#include "filter/Prepared.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "pcm/Convert.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cassert>
 | 
					 | 
				
			||||||
#include <memory>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ConvertFilter final : public Filter {
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The input audio format; PCM data is passed to the filter()
 | 
					 | 
				
			||||||
	 * method in this format.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	const AudioFormat in_audio_format;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * This object is only "open" if #in_audio_format !=
 | 
					 | 
				
			||||||
	 * #out_audio_format.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	std::unique_ptr<PcmConvert> state;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit ConvertFilter(const AudioFormat &audio_format);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void Set(const AudioFormat &_out_audio_format);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void Reset() noexcept override {
 | 
					 | 
				
			||||||
		if (state)
 | 
					 | 
				
			||||||
			state->Reset();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> FilterPCM(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> Flush() override {
 | 
					 | 
				
			||||||
		return state
 | 
					 | 
				
			||||||
			? state->Flush()
 | 
					 | 
				
			||||||
			: std::span<const std::byte>{};
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedConvertFilter final : public PreparedFilter {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> Open(AudioFormat &af) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
ConvertFilter::Set(const AudioFormat &_out_audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(_out_audio_format.IsValid());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (_out_audio_format == out_audio_format)
 | 
					 | 
				
			||||||
		/* no change */
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (state) {
 | 
					 | 
				
			||||||
		out_audio_format = in_audio_format;
 | 
					 | 
				
			||||||
		state.reset();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (_out_audio_format == in_audio_format)
 | 
					 | 
				
			||||||
		/* optimized special case: no-op */
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	state = std::make_unique<PcmConvert>(in_audio_format,
 | 
					 | 
				
			||||||
					     _out_audio_format);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	out_audio_format = _out_audio_format;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
ConvertFilter::ConvertFilter(const AudioFormat &audio_format)
 | 
					 | 
				
			||||||
	:Filter(audio_format), in_audio_format(audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(in_audio_format.IsValid());
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
PreparedConvertFilter::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(audio_format.IsValid());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return std::make_unique<ConvertFilter>(audio_format);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
ConvertFilter::FilterPCM(std::span<const std::byte> src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return state
 | 
					 | 
				
			||||||
		? state->Convert(src)
 | 
					 | 
				
			||||||
		/* optimized special case: no-op */
 | 
					 | 
				
			||||||
		: std::span<const std::byte>{src};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
convert_filter_prepare() noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::make_unique<PreparedConvertFilter>();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
convert_filter_new(const AudioFormat in_audio_format,
 | 
					 | 
				
			||||||
		   const AudioFormat out_audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	auto filter = std::make_unique<ConvertFilter>(in_audio_format);
 | 
					 | 
				
			||||||
	filter->Set(out_audio_format);
 | 
					 | 
				
			||||||
	return filter;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
convert_filter_set(Filter *_filter, AudioFormat out_audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	auto *filter = (ConvertFilter *)_filter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	filter->Set(out_audio_format);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,31 +1,37 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_CONVERT_FILTER_PLUGIN_HXX
 | 
					#ifndef MPD_CONVERT_FILTER_PLUGIN_HXX
 | 
				
			||||||
#define MPD_CONVERT_FILTER_PLUGIN_HXX
 | 
					#define MPD_CONVERT_FILTER_PLUGIN_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "filter/Prepared.hxx"
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PreparedFilter;
 | 
					 | 
				
			||||||
class Filter;
 | 
					 | 
				
			||||||
struct AudioFormat;
 | 
					struct AudioFormat;
 | 
				
			||||||
 | 
					class Filter;
 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
convert_filter_prepare() noexcept;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
convert_filter_new(AudioFormat in_audio_format,
 | 
					 | 
				
			||||||
		   AudioFormat out_audio_format);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Sets the output audio format for the specified filter.  You must
 | 
					 * Stub implementation - filter support not needed for database creation
 | 
				
			||||||
 * call this after the filter has been opened.  Since this audio
 | 
					 | 
				
			||||||
 * format switch is a violation of the filter API, this filter must be
 | 
					 | 
				
			||||||
 * the last in a chain.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Throws on error.
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void
 | 
					inline std::unique_ptr<PreparedFilter>
 | 
				
			||||||
convert_filter_set(Filter *filter, AudioFormat out_audio_format);
 | 
					convert_filter_new([[maybe_unused]] const AudioFormat &in,
 | 
				
			||||||
 | 
							   [[maybe_unused]] const AudioFormat &out)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline void
 | 
				
			||||||
 | 
					convert_filter_set([[maybe_unused]] Filter *,
 | 
				
			||||||
 | 
							   [[maybe_unused]] const AudioFormat &) noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// No-op stub
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline std::unique_ptr<PreparedFilter>
 | 
				
			||||||
 | 
					convert_filter_prepare()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,104 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "FfmpegFilter.hxx"
 | 
					 | 
				
			||||||
#include "lib/ffmpeg/Interleave.hxx"
 | 
					 | 
				
			||||||
#include "lib/ffmpeg/SampleFormat.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern "C" {
 | 
					 | 
				
			||||||
#include <libavfilter/buffersrc.h>
 | 
					 | 
				
			||||||
#include <libavfilter/buffersink.h>
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
FfmpegFilter::FfmpegFilter(const AudioFormat &in_audio_format,
 | 
					 | 
				
			||||||
			   const AudioFormat &_out_audio_format,
 | 
					 | 
				
			||||||
			   Ffmpeg::FilterGraph &&_graph,
 | 
					 | 
				
			||||||
			   AVFilterContext &_buffer_src,
 | 
					 | 
				
			||||||
			   AVFilterContext &_buffer_sink) noexcept
 | 
					 | 
				
			||||||
	:Filter(_out_audio_format),
 | 
					 | 
				
			||||||
	 graph(std::move(_graph)),
 | 
					 | 
				
			||||||
	 buffer_src(_buffer_src),
 | 
					 | 
				
			||||||
	 buffer_sink(_buffer_sink),
 | 
					 | 
				
			||||||
	 in_format(Ffmpeg::ToFfmpegSampleFormat(in_audio_format.format)),
 | 
					 | 
				
			||||||
	 in_sample_rate(in_audio_format.sample_rate),
 | 
					 | 
				
			||||||
#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(57, 25, 100)
 | 
					 | 
				
			||||||
	 in_channels(in_audio_format.channels),
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	 in_audio_frame_size(in_audio_format.GetFrameSize()),
 | 
					 | 
				
			||||||
	 out_audio_frame_size(_out_audio_format.GetFrameSize())
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 25, 100)
 | 
					 | 
				
			||||||
	av_channel_layout_default(&in_ch_layout, in_audio_format.channels);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
inline std::span<const std::byte>
 | 
					 | 
				
			||||||
FfmpegFilter::ReadOutput()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	frame.Unref();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (int err = av_buffersink_get_frame(&buffer_sink, frame.get()); err < 0) {
 | 
					 | 
				
			||||||
		if (err == AVERROR(EAGAIN) || err == AVERROR_EOF)
 | 
					 | 
				
			||||||
			return {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		throw MakeFfmpegError(err, "av_buffersink_get_frame() failed");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return Ffmpeg::InterleaveFrame(*frame, interleave_buffer);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
FfmpegFilter::FilterPCM(std::span<const std::byte> src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(!flushed);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* submit source data into the FFmpeg audio buffer source */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	frame.Unref();
 | 
					 | 
				
			||||||
	frame->format = in_format;
 | 
					 | 
				
			||||||
	frame->sample_rate = in_sample_rate;
 | 
					 | 
				
			||||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 25, 100)
 | 
					 | 
				
			||||||
	frame->ch_layout = in_ch_layout;
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
	frame->channels = in_channels;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	frame->nb_samples = src.size() / in_audio_frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	frame->pts = pts;
 | 
					 | 
				
			||||||
	pts += frame->nb_samples;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	frame.GetBuffer();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	memcpy(frame.GetData(0), src.data(), src.size());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (int err = av_buffersrc_add_frame(&buffer_src, frame.get()); err < 0)
 | 
					 | 
				
			||||||
		throw MakeFfmpegError(err, "av_buffersrc_write_frame() failed");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* collect filtered data from the FFmpeg audio buffer sink */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* TODO: call av_buffersink_get_frame() repeatedly?  Not
 | 
					 | 
				
			||||||
	   possible with MPD's current Filter API */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ReadOutput();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
FfmpegFilter::ReadMore()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return ReadOutput();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
FfmpegFilter::Flush()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (!flushed) {
 | 
					 | 
				
			||||||
		if (int err = av_buffersrc_add_frame(&buffer_src, nullptr); err < 0)
 | 
					 | 
				
			||||||
			throw MakeFfmpegError(err, "av_buffersrc_write_frame() failed");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		flushed = true;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return ReadOutput();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,62 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#pragma once
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "filter/Filter.hxx"
 | 
					 | 
				
			||||||
#include "lib/ffmpeg/Buffer.hxx"
 | 
					 | 
				
			||||||
#include "lib/ffmpeg/Filter.hxx"
 | 
					 | 
				
			||||||
#include "lib/ffmpeg/Frame.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cstdint>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * A #Filter implementation using FFmpeg's libavfilter.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class FfmpegFilter final : public Filter {
 | 
					 | 
				
			||||||
	Ffmpeg::FilterGraph graph;
 | 
					 | 
				
			||||||
	AVFilterContext &buffer_src, &buffer_sink;
 | 
					 | 
				
			||||||
	Ffmpeg::Frame frame;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	FfmpegBuffer interleave_buffer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const int in_format, in_sample_rate;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 25, 100)
 | 
					 | 
				
			||||||
	AVChannelLayout in_ch_layout;
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
	const int in_channels;
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const size_t in_audio_frame_size;
 | 
					 | 
				
			||||||
	const size_t out_audio_frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Presentation timestamp.  A counter for `AVFrame::pts`.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	int_least64_t pts = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool flushed = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * @param _graph a checked and configured AVFilterGraph
 | 
					 | 
				
			||||||
	 * @param _buffer_src an "abuffer" filter which serves as
 | 
					 | 
				
			||||||
	 * input
 | 
					 | 
				
			||||||
	 * @param _buffer_sink an "abuffersink" filter which serves as
 | 
					 | 
				
			||||||
	 * output
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	FfmpegFilter(const AudioFormat &in_audio_format,
 | 
					 | 
				
			||||||
		     const AudioFormat &_out_audio_format,
 | 
					 | 
				
			||||||
		     Ffmpeg::FilterGraph &&_graph,
 | 
					 | 
				
			||||||
		     AVFilterContext &_buffer_src,
 | 
					 | 
				
			||||||
		     AVFilterContext &_buffer_sink) noexcept;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Filter */
 | 
					 | 
				
			||||||
	std::span<const std::byte> FilterPCM(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
	std::span<const std::byte> ReadMore() override;
 | 
					 | 
				
			||||||
	std::span<const std::byte> Flush() override;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	std::span<const std::byte> ReadOutput();
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,114 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "FfmpegFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "FfmpegFilter.hxx"
 | 
					 | 
				
			||||||
#include "filter/FilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "filter/Filter.hxx"
 | 
					 | 
				
			||||||
#include "filter/Prepared.hxx"
 | 
					 | 
				
			||||||
#include "lib/ffmpeg/Filter.hxx"
 | 
					 | 
				
			||||||
#include "lib/ffmpeg/DetectFilterFormat.hxx"
 | 
					 | 
				
			||||||
#include "config/Block.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedFfmpegFilter final : public PreparedFilter {
 | 
					 | 
				
			||||||
	const char *const graph_string;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit PreparedFfmpegFilter(const char *_graph) noexcept
 | 
					 | 
				
			||||||
		:graph_string(_graph) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedFilter */
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> Open(AudioFormat &af) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Fallback for PreparedFfmpegFilter::Open() just in case the filter's
 | 
					 | 
				
			||||||
 * native output format could not be determined.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * TODO: improve the MPD filter API to allow returning the output
 | 
					 | 
				
			||||||
 * format later, and eliminate this kludge
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static auto
 | 
					 | 
				
			||||||
OpenWithAformat(const char *graph_string, AudioFormat &in_audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	Ffmpeg::FilterGraph graph;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto &buffer_src =
 | 
					 | 
				
			||||||
		Ffmpeg::MakeAudioBufferSource(in_audio_format, *graph);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto &buffer_sink = Ffmpeg::MakeAudioBufferSink(*graph);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	AudioFormat out_audio_format = in_audio_format;
 | 
					 | 
				
			||||||
	auto &aformat = Ffmpeg::MakeAformat(out_audio_format, *graph);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (int error = avfilter_link(&aformat, 0, &buffer_sink, 0); error < 0)
 | 
					 | 
				
			||||||
		throw MakeFfmpegError(error, "avfilter_link() failed");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	graph.ParseSingleInOut(graph_string, aformat, buffer_src);
 | 
					 | 
				
			||||||
	graph.CheckAndConfigure();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return std::make_unique<FfmpegFilter>(in_audio_format,
 | 
					 | 
				
			||||||
					      out_audio_format,
 | 
					 | 
				
			||||||
					      std::move(graph),
 | 
					 | 
				
			||||||
					      buffer_src,
 | 
					 | 
				
			||||||
					      buffer_sink);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
PreparedFfmpegFilter::Open(AudioFormat &in_audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	Ffmpeg::FilterGraph graph;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto &buffer_src =
 | 
					 | 
				
			||||||
		Ffmpeg::MakeAudioBufferSource(in_audio_format, *graph);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto &buffer_sink = Ffmpeg::MakeAudioBufferSink(*graph);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* if the filter's output format is not supported by MPD, this
 | 
					 | 
				
			||||||
	   "aformat" filter is inserted at the end and takes care for
 | 
					 | 
				
			||||||
	   the required conversion */
 | 
					 | 
				
			||||||
	auto &aformat = Ffmpeg::MakeAutoAformat(*graph);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (int error = avfilter_link(&aformat, 0, &buffer_sink, 0); error < 0)
 | 
					 | 
				
			||||||
		throw MakeFfmpegError(error, "avfilter_link() failed");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	graph.ParseSingleInOut(graph_string, aformat, buffer_src);
 | 
					 | 
				
			||||||
	graph.CheckAndConfigure();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const auto out_audio_format =
 | 
					 | 
				
			||||||
		Ffmpeg::DetectFilterOutputFormat(in_audio_format, buffer_src,
 | 
					 | 
				
			||||||
						 buffer_sink);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!out_audio_format.IsDefined())
 | 
					 | 
				
			||||||
		/* the filter's native output format could not be
 | 
					 | 
				
			||||||
		   determined yet, but we need to know it now; as a
 | 
					 | 
				
			||||||
		   workaround for this MPD API deficiency, try again
 | 
					 | 
				
			||||||
		   with an "aformat" filter which forces a specific
 | 
					 | 
				
			||||||
		   output format */
 | 
					 | 
				
			||||||
		return OpenWithAformat(graph_string, in_audio_format);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return std::make_unique<FfmpegFilter>(in_audio_format,
 | 
					 | 
				
			||||||
					      out_audio_format,
 | 
					 | 
				
			||||||
					      std::move(graph),
 | 
					 | 
				
			||||||
					      buffer_src,
 | 
					 | 
				
			||||||
					      buffer_sink);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
ffmpeg_filter_init(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *graph = block.GetBlockValue("graph");
 | 
					 | 
				
			||||||
	if (graph == nullptr)
 | 
					 | 
				
			||||||
		throw std::runtime_error("Missing \"graph\" configuration");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* check if the graph can be parsed (and discard the
 | 
					 | 
				
			||||||
	   object) */
 | 
					 | 
				
			||||||
	Ffmpeg::FilterGraph().Parse(graph);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return std::make_unique<PreparedFfmpegFilter>(graph);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const FilterPlugin ffmpeg_filter_plugin = {
 | 
					 | 
				
			||||||
	"ffmpeg",
 | 
					 | 
				
			||||||
	ffmpeg_filter_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,11 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_FFMPEG_FILTER_PLUGIN_HXX
 | 
					 | 
				
			||||||
#define MPD_FFMPEG_FILTER_PLUGIN_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct FilterPlugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const FilterPlugin ffmpeg_filter_plugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,79 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "HdcdFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "FfmpegFilter.hxx"
 | 
					 | 
				
			||||||
#include "filter/FilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "filter/Filter.hxx"
 | 
					 | 
				
			||||||
#include "filter/NullFilter.hxx"
 | 
					 | 
				
			||||||
#include "filter/Prepared.hxx"
 | 
					 | 
				
			||||||
#include "lib/ffmpeg/Filter.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static constexpr const char *hdcd_graph = "hdcd";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
[[gnu::pure]]
 | 
					 | 
				
			||||||
static bool
 | 
					 | 
				
			||||||
MaybeHdcd(const AudioFormat &audio_format) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return audio_format.sample_rate == 44100 &&
 | 
					 | 
				
			||||||
		audio_format.format == SampleFormat::S16 &&
 | 
					 | 
				
			||||||
		audio_format.channels == 2;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static auto
 | 
					 | 
				
			||||||
OpenHdcdFilter(AudioFormat &in_audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	Ffmpeg::FilterGraph graph;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto &buffer_src =
 | 
					 | 
				
			||||||
		Ffmpeg::MakeAudioBufferSource(in_audio_format,
 | 
					 | 
				
			||||||
					      *graph);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto &buffer_sink = Ffmpeg::MakeAudioBufferSink(*graph);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	graph.ParseSingleInOut(hdcd_graph, buffer_sink, buffer_src);
 | 
					 | 
				
			||||||
	graph.CheckAndConfigure();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	auto out_audio_format = in_audio_format;
 | 
					 | 
				
			||||||
	// TODO: convert to 32 bit only if HDCD actually detected
 | 
					 | 
				
			||||||
	out_audio_format.format = SampleFormat::S32;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return std::make_unique<FfmpegFilter>(in_audio_format,
 | 
					 | 
				
			||||||
					      out_audio_format,
 | 
					 | 
				
			||||||
					      std::move(graph),
 | 
					 | 
				
			||||||
					      buffer_src,
 | 
					 | 
				
			||||||
					      buffer_sink);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedHdcdFilter final : public PreparedFilter {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedFilter */
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> Open(AudioFormat &af) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
PreparedHdcdFilter::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (MaybeHdcd(audio_format))
 | 
					 | 
				
			||||||
		return OpenHdcdFilter(audio_format);
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		/* this cannot be HDCD, so let's copy as-is using
 | 
					 | 
				
			||||||
		   NullFilter */
 | 
					 | 
				
			||||||
		return std::make_unique<NullFilter>(audio_format);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
hdcd_filter_init(const ConfigBlock &)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* check if the graph can be parsed (and discard the
 | 
					 | 
				
			||||||
	   object) */
 | 
					 | 
				
			||||||
	Ffmpeg::FilterGraph().Parse(hdcd_graph);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return std::make_unique<PreparedHdcdFilter>();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const FilterPlugin hdcd_filter_plugin = {
 | 
					 | 
				
			||||||
	"hdcd",
 | 
					 | 
				
			||||||
	hdcd_filter_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,11 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_HDCD_FILTER_PLUGIN_HXX
 | 
					 | 
				
			||||||
#define MPD_HDCD_FILTER_PLUGIN_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct FilterPlugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const FilterPlugin hdcd_filter_plugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,73 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "NormalizeFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "filter/FilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "filter/Filter.hxx"
 | 
					 | 
				
			||||||
#include "filter/Prepared.hxx"
 | 
					 | 
				
			||||||
#include "pcm/Buffer.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "pcm/Normalizer.hxx"
 | 
					 | 
				
			||||||
#include "util/SpanCast.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class NormalizeFilter final : public Filter {
 | 
					 | 
				
			||||||
	PcmNormalizer normalizer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	PcmBuffer buffer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit NormalizeFilter(const AudioFormat &audio_format)
 | 
					 | 
				
			||||||
		:Filter(audio_format) {
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	NormalizeFilter(const NormalizeFilter &) = delete;
 | 
					 | 
				
			||||||
	NormalizeFilter &operator=(const NormalizeFilter &) = delete;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Filter */
 | 
					 | 
				
			||||||
	void Reset() noexcept override {
 | 
					 | 
				
			||||||
		normalizer.Reset();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::span<const std::byte> FilterPCM(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedNormalizeFilter final : public PreparedFilter {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedFilter */
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> Open(AudioFormat &af) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
normalize_filter_init([[maybe_unused]] const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::make_unique<PreparedNormalizeFilter>();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
PreparedNormalizeFilter::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	audio_format.format = SampleFormat::S16;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return std::make_unique<NormalizeFilter>(audio_format);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
NormalizeFilter::FilterPCM(std::span<const std::byte> _src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const auto src = FromBytesStrict<const int16_t>(_src);
 | 
					 | 
				
			||||||
	auto *dest = (int16_t *)buffer.GetT<int16_t>(src.size());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	normalizer.ProcessS16(dest, src);
 | 
					 | 
				
			||||||
	return std::as_bytes(std::span{dest, src.size()});
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const FilterPlugin normalize_filter_plugin = {
 | 
					 | 
				
			||||||
	"normalize",
 | 
					 | 
				
			||||||
	normalize_filter_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
normalize_filter_prepare() noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::make_unique<PreparedNormalizeFilter>();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,17 +1,22 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_NORMALIZE_FILTER_PLUGIN_HXX
 | 
					#ifndef MPD_NORMALIZE_FILTER_PLUGIN_HXX
 | 
				
			||||||
#define MPD_NORMALIZE_FILTER_PLUGIN_HXX
 | 
					#define MPD_NORMALIZE_FILTER_PLUGIN_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "filter/Prepared.hxx"
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct FilterPlugin;
 | 
					struct ConfigBlock;
 | 
				
			||||||
class PreparedFilter;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern const FilterPlugin normalize_filter_plugin;
 | 
					/**
 | 
				
			||||||
 | 
					 * Stub implementation - filter support not needed for database creation
 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 */
 | 
				
			||||||
normalize_filter_prepare() noexcept;
 | 
					inline std::unique_ptr<PreparedFilter>
 | 
				
			||||||
 | 
					normalize_filter_prepare([[maybe_unused]] const ConfigBlock *block = nullptr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** \file
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This filter plugin does nothing.  That is not quite useful, except
 | 
					 | 
				
			||||||
 * for testing the filter core, or as a template for new filter
 | 
					 | 
				
			||||||
 * plugins.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "NullFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "filter/FilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "filter/NullFilter.hxx"
 | 
					 | 
				
			||||||
#include "filter/Prepared.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedNullFilter final : public PreparedFilter {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> Open(AudioFormat &af) override {
 | 
					 | 
				
			||||||
		return std::make_unique<NullFilter>(af);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
null_filter_init([[maybe_unused]] const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::make_unique<PreparedNullFilter>();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const FilterPlugin null_filter_plugin = {
 | 
					 | 
				
			||||||
	"null",
 | 
					 | 
				
			||||||
	null_filter_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,11 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_NULL_FILTER_PLUGIN_HXX
 | 
					 | 
				
			||||||
#define MPD_NULL_FILTER_PLUGIN_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct FilterPlugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const FilterPlugin null_filter_plugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,217 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "ReplayGainFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "filter/Filter.hxx"
 | 
					 | 
				
			||||||
#include "filter/Prepared.hxx"
 | 
					 | 
				
			||||||
#include "tag/ReplayGainInfo.hxx"
 | 
					 | 
				
			||||||
#include "config/ReplayGainConfig.hxx"
 | 
					 | 
				
			||||||
#include "mixer/Control.hxx"
 | 
					 | 
				
			||||||
#include "mixer/Mixer.hxx"
 | 
					 | 
				
			||||||
#include "mixer/Listener.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "pcm/Volume.hxx"
 | 
					 | 
				
			||||||
#include "util/Domain.hxx"
 | 
					 | 
				
			||||||
#include "Log.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <cassert>
 | 
					 | 
				
			||||||
#include <exception>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static constexpr Domain replay_gain_domain("replay_gain");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ReplayGainFilter final : public Filter {
 | 
					 | 
				
			||||||
	const ReplayGainConfig config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * If set, then this hardware mixer is used for applying
 | 
					 | 
				
			||||||
	 * replay gain, instead of the software volume library.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	Mixer *const mixer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The base volume level for scale=1.0, between 1 and 100
 | 
					 | 
				
			||||||
	 * (including).
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	const unsigned base;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ReplayGainMode mode = ReplayGainMode::OFF;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	ReplayGainInfo info;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * About the current volume: it is between 0 and a value that
 | 
					 | 
				
			||||||
	 * may or may not exceed #PCM_VOLUME_1.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * If the default value of true is used for replaygain_limit, the
 | 
					 | 
				
			||||||
	 * application of the volume to the signal will never cause clipping.
 | 
					 | 
				
			||||||
	 *
 | 
					 | 
				
			||||||
	 * On the other hand, if the user has set replaygain_limit to false,
 | 
					 | 
				
			||||||
	 * the chance of clipping is explicitly preferred if that's required to
 | 
					 | 
				
			||||||
	 * maintain a consistent audio level. Whether clipping will actually
 | 
					 | 
				
			||||||
	 * occur depends on what value the user is using for replaygain_preamp.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	PcmVolume pv;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	ReplayGainFilter(const ReplayGainConfig &_config, bool allow_convert,
 | 
					 | 
				
			||||||
			 const AudioFormat &audio_format,
 | 
					 | 
				
			||||||
			 Mixer *_mixer, unsigned _base)
 | 
					 | 
				
			||||||
		:Filter(audio_format),
 | 
					 | 
				
			||||||
		 config(_config),
 | 
					 | 
				
			||||||
		 mixer(_mixer), base(_base) {
 | 
					 | 
				
			||||||
		info.Clear();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		out_audio_format.format = pv.Open(out_audio_format.format,
 | 
					 | 
				
			||||||
						  allow_convert);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void SetInfo(const ReplayGainInfo *_info) {
 | 
					 | 
				
			||||||
		if (_info != nullptr)
 | 
					 | 
				
			||||||
			info = *_info;
 | 
					 | 
				
			||||||
		else
 | 
					 | 
				
			||||||
			info.Clear();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		Update();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void SetMode(ReplayGainMode _mode) {
 | 
					 | 
				
			||||||
		if (_mode == mode)
 | 
					 | 
				
			||||||
			/* no change */
 | 
					 | 
				
			||||||
			return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		FmtDebug(replay_gain_domain,
 | 
					 | 
				
			||||||
			 "replay gain mode has changed {}->{}",
 | 
					 | 
				
			||||||
			 ToString(mode), ToString(_mode));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		mode = _mode;
 | 
					 | 
				
			||||||
		Update();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Recalculates the new volume after a property was changed.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	void Update();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Filter */
 | 
					 | 
				
			||||||
	std::span<const std::byte> FilterPCM(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedReplayGainFilter final : public PreparedFilter {
 | 
					 | 
				
			||||||
	const ReplayGainConfig config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * If set, then this hardware mixer is used for applying
 | 
					 | 
				
			||||||
	 * replay gain, instead of the software volume library.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	Mixer *mixer = nullptr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Allow the class to convert to a different #SampleFormat to
 | 
					 | 
				
			||||||
	 * preserve quality?
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	const bool allow_convert;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The base volume level for scale=1.0, between 1 and 100
 | 
					 | 
				
			||||||
	 * (including).
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	unsigned base;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit PreparedReplayGainFilter(const ReplayGainConfig _config,
 | 
					 | 
				
			||||||
					  bool _allow_convert)
 | 
					 | 
				
			||||||
		:config(_config), allow_convert(_allow_convert) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void SetMixer(Mixer *_mixer, unsigned _base) {
 | 
					 | 
				
			||||||
		assert(_mixer == nullptr || (_base > 0 && _base <= 100));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		mixer = _mixer;
 | 
					 | 
				
			||||||
		base = _base;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Filter */
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> Open(AudioFormat &af) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
ReplayGainFilter::Update()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	unsigned volume = PCM_VOLUME_1;
 | 
					 | 
				
			||||||
	if (mode != ReplayGainMode::OFF) {
 | 
					 | 
				
			||||||
		const auto &tuple = info.Get(mode);
 | 
					 | 
				
			||||||
		float scale = tuple.CalculateScale(config);
 | 
					 | 
				
			||||||
		FmtDebug(replay_gain_domain, "scale={}\n", scale);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		volume = pcm_float_to_volume(scale);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (mixer != nullptr) {
 | 
					 | 
				
			||||||
		/* update the hardware mixer volume */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		unsigned _volume = (volume * base) / PCM_VOLUME_1;
 | 
					 | 
				
			||||||
		if (_volume > 100)
 | 
					 | 
				
			||||||
			_volume = 100;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		try {
 | 
					 | 
				
			||||||
			mixer->LockSetVolume(_volume);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			/* invoke the mixer's listener manually, just
 | 
					 | 
				
			||||||
			   in case the mixer implementation didn't do
 | 
					 | 
				
			||||||
			   that already (this depends on the
 | 
					 | 
				
			||||||
			   implementation) */
 | 
					 | 
				
			||||||
			mixer->listener.OnMixerVolumeChanged(*mixer, _volume);
 | 
					 | 
				
			||||||
		} catch (...) {
 | 
					 | 
				
			||||||
			LogError(std::current_exception(),
 | 
					 | 
				
			||||||
				 "Failed to update hardware mixer");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else
 | 
					 | 
				
			||||||
		pv.SetVolume(volume);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
NewReplayGainFilter(const ReplayGainConfig &config,
 | 
					 | 
				
			||||||
		    bool allow_convert) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::make_unique<PreparedReplayGainFilter>(config,
 | 
					 | 
				
			||||||
							  allow_convert);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
PreparedReplayGainFilter::Open(AudioFormat &af)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::make_unique<ReplayGainFilter>(config, allow_convert,
 | 
					 | 
				
			||||||
						  af, mixer, base);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
ReplayGainFilter::FilterPCM(std::span<const std::byte> src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return mixer != nullptr
 | 
					 | 
				
			||||||
		? std::span<const std::byte>{src}
 | 
					 | 
				
			||||||
		: pv.Apply(src);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
replay_gain_filter_set_mixer(PreparedFilter &_filter, Mixer *mixer,
 | 
					 | 
				
			||||||
			     unsigned base)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	auto &filter = (PreparedReplayGainFilter &)_filter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	filter.SetMixer(mixer, base);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
replay_gain_filter_set_info(Filter &_filter, const ReplayGainInfo *info)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	auto &filter = (ReplayGainFilter &)_filter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	filter.SetInfo(info);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
replay_gain_filter_set_mode(Filter &_filter, ReplayGainMode mode)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	auto &filter = (ReplayGainFilter &)_filter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	filter.SetMode(mode);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,49 +1,47 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX
 | 
					#ifndef MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX
 | 
				
			||||||
#define MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX
 | 
					#define MPD_REPLAY_GAIN_FILTER_PLUGIN_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "ReplayGainMode.hxx"
 | 
					#include "filter/Prepared.hxx"
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Filter;
 | 
					 | 
				
			||||||
class PreparedFilter;
 | 
					 | 
				
			||||||
class Mixer;
 | 
					 | 
				
			||||||
struct ReplayGainConfig;
 | 
					struct ReplayGainConfig;
 | 
				
			||||||
struct ReplayGainInfo;
 | 
					enum class ReplayGainMode : uint8_t;
 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * @param allow_convert allow the class to convert to a different
 | 
					 | 
				
			||||||
 * #SampleFormat to preserve quality?
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
NewReplayGainFilter(const ReplayGainConfig &config,
 | 
					 | 
				
			||||||
		    bool allow_convert) noexcept;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Enables or disables the hardware mixer for applying replay gain.
 | 
					 * Stub implementation - filter support not needed for database creation
 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param mixer the hardware mixer, or nullptr to fall back to software
 | 
					 | 
				
			||||||
 * volume
 | 
					 | 
				
			||||||
 * @param base the base volume level for scale=1.0, between 1 and 100
 | 
					 | 
				
			||||||
 * (including).
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void
 | 
					inline std::unique_ptr<PreparedFilter>
 | 
				
			||||||
replay_gain_filter_set_mixer(PreparedFilter &_filter, Mixer *mixer,
 | 
					NewReplayGainFilter([[maybe_unused]] const ReplayGainConfig &config,
 | 
				
			||||||
			     unsigned base);
 | 
							    [[maybe_unused]] bool allow_convert = false)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
/**
 | 
						return nullptr;
 | 
				
			||||||
 * Sets a new #ReplayGainInfo at the beginning of a new song.
 | 
					}
 | 
				
			||||||
 *
 | 
					
 | 
				
			||||||
 * @param info the new #ReplayGainInfo value, or nullptr if no replay
 | 
					inline void
 | 
				
			||||||
 * gain data is available for the current song
 | 
					replay_gain_filter_set_mode([[maybe_unused]] Filter &,
 | 
				
			||||||
 */
 | 
								    [[maybe_unused]] ReplayGainMode) noexcept
 | 
				
			||||||
void
 | 
					{
 | 
				
			||||||
replay_gain_filter_set_info(Filter &filter, const ReplayGainInfo *info);
 | 
						// No-op stub
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
void
 | 
					
 | 
				
			||||||
replay_gain_filter_set_mode(Filter &filter, ReplayGainMode mode);
 | 
					inline void
 | 
				
			||||||
 | 
					replay_gain_filter_set_info([[maybe_unused]] Filter &,
 | 
				
			||||||
 | 
								    [[maybe_unused]] const void *) noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// No-op stub
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline void
 | 
				
			||||||
 | 
					replay_gain_filter_set_mixer([[maybe_unused]] PreparedFilter &,
 | 
				
			||||||
 | 
								     [[maybe_unused]] class Mixer *,
 | 
				
			||||||
 | 
								     [[maybe_unused]] unsigned) noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// No-op stub
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,260 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** \file
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This filter copies audio data between channels. Useful for
 | 
					 | 
				
			||||||
 * upmixing mono/stereo audio to surround speaker configurations.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Its configuration consists of a "filter" section with a single
 | 
					 | 
				
			||||||
 * "routes" entry, formatted as: \\
 | 
					 | 
				
			||||||
 * routes "0>1, 1>0, 2>2, 3>3, 3>4" \\
 | 
					 | 
				
			||||||
 * where each pair of numbers signifies a set of channels.
 | 
					 | 
				
			||||||
 * Each source>dest pair leads to the data from channel #source
 | 
					 | 
				
			||||||
 * being copied to channel #dest in the output.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Example: \\
 | 
					 | 
				
			||||||
 * routes "0>0, 1>1, 0>2, 1>3"\\
 | 
					 | 
				
			||||||
 * upmixes stereo audio to a 4-speaker system, copying the front-left
 | 
					 | 
				
			||||||
 * (0) to front left (0) and rear left (2), copying front-right (1) to
 | 
					 | 
				
			||||||
 * front-right (1) and rear-right (3).
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * If multiple sources are copied to the same destination channel, only
 | 
					 | 
				
			||||||
 * one of them takes effect.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "RouteFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "config/Block.hxx"
 | 
					 | 
				
			||||||
#include "filter/FilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "filter/Filter.hxx"
 | 
					 | 
				
			||||||
#include "filter/Prepared.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "pcm/Buffer.hxx"
 | 
					 | 
				
			||||||
#include "pcm/Silence.hxx"
 | 
					 | 
				
			||||||
#include "lib/fmt/RuntimeError.hxx"
 | 
					 | 
				
			||||||
#include "util/StringStrip.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <array>
 | 
					 | 
				
			||||||
#include <cstdint>
 | 
					 | 
				
			||||||
#include <stdexcept>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <stdlib.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class RouteFilter final : public Filter {
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The set of copy operations to perform on each sample
 | 
					 | 
				
			||||||
	 * The index is an output channel to use, the value is
 | 
					 | 
				
			||||||
	 * a corresponding input channel from which to take the
 | 
					 | 
				
			||||||
	 * data. A -1 means "no source"
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	const std::array<int8_t, MAX_CHANNELS> sources;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The actual input format of our signal, once opened
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	const AudioFormat input_format;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The size, in bytes, of each multichannel frame in the
 | 
					 | 
				
			||||||
	 * input buffer
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	const size_t input_frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The size, in bytes, of each multichannel frame in the
 | 
					 | 
				
			||||||
	 * output buffer
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	size_t output_frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The output buffer used last time around, can be reused if the size doesn't differ.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	PcmBuffer output_buffer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	RouteFilter(const AudioFormat &audio_format, unsigned out_channels,
 | 
					 | 
				
			||||||
		    const std::array<int8_t, MAX_CHANNELS> &_sources);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Filter */
 | 
					 | 
				
			||||||
	std::span<const std::byte> FilterPCM(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedRouteFilter final : public PreparedFilter {
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The minimum number of channels we need for output
 | 
					 | 
				
			||||||
	 * to be able to perform all the copies the user has specified
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	unsigned min_output_channels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The minimum number of input channels we need to
 | 
					 | 
				
			||||||
	 * copy all the data the user has requested. If fewer
 | 
					 | 
				
			||||||
	 * than this many are supplied by the input, undefined
 | 
					 | 
				
			||||||
	 * copy operations are given zeroed sources in stead.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	unsigned min_input_channels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * The set of copy operations to perform on each sample
 | 
					 | 
				
			||||||
	 * The index is an output channel to use, the value is
 | 
					 | 
				
			||||||
	 * a corresponding input channel from which to take the
 | 
					 | 
				
			||||||
	 * data. A -1 means "no source"
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	std::array<int8_t, MAX_CHANNELS> sources;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Parse the "routes" section, a string on the form
 | 
					 | 
				
			||||||
	 *  a>b, c>d, e>f, ...
 | 
					 | 
				
			||||||
	 * where a... are non-unique, non-negative integers
 | 
					 | 
				
			||||||
	 * and input channel a gets copied to output channel b, etc.
 | 
					 | 
				
			||||||
	 * @param block the configuration block to read
 | 
					 | 
				
			||||||
	 * @param filter a route_filter whose min_channels and sources[] to set
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	explicit PreparedRouteFilter(const ConfigBlock &block);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class PreparedFilter */
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> Open(AudioFormat &af) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
PreparedRouteFilter::PreparedRouteFilter(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* TODO:
 | 
					 | 
				
			||||||
	 * With a more clever way of marking "don't copy to output N",
 | 
					 | 
				
			||||||
	 * This could easily be merged into a single loop with some
 | 
					 | 
				
			||||||
	 * dynamic realloc() instead of one count run and one malloc().
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sources.fill(-1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	min_input_channels = 0;
 | 
					 | 
				
			||||||
	min_output_channels = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// A cowardly default, just passthrough stereo
 | 
					 | 
				
			||||||
	const char *routes = block.GetBlockValue("routes", "0>0, 1>1");
 | 
					 | 
				
			||||||
	while (true) {
 | 
					 | 
				
			||||||
		routes = StripLeft(routes);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		char *endptr;
 | 
					 | 
				
			||||||
		const unsigned source = strtoul(routes, &endptr, 10);
 | 
					 | 
				
			||||||
		endptr = StripLeft(endptr);
 | 
					 | 
				
			||||||
		if (endptr == routes || *endptr != '>')
 | 
					 | 
				
			||||||
			throw std::runtime_error("Malformed 'routes' specification");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (source >= MAX_CHANNELS)
 | 
					 | 
				
			||||||
			throw FmtRuntimeError("Invalid source channel number: {}",
 | 
					 | 
				
			||||||
					      source);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (source >= min_input_channels)
 | 
					 | 
				
			||||||
			min_input_channels = source + 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		routes = StripLeft(endptr + 1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		unsigned dest = strtoul(routes, &endptr, 10);
 | 
					 | 
				
			||||||
		endptr = StripLeft(endptr);
 | 
					 | 
				
			||||||
		if (endptr == routes)
 | 
					 | 
				
			||||||
			throw std::runtime_error("Malformed 'routes' specification");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (dest >= MAX_CHANNELS)
 | 
					 | 
				
			||||||
			throw FmtRuntimeError("Invalid destination channel number: {}",
 | 
					 | 
				
			||||||
					      dest);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (dest >= min_output_channels)
 | 
					 | 
				
			||||||
			min_output_channels = dest + 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		sources[dest] = source;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		routes = endptr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (*routes == 0)
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (*routes != ',')
 | 
					 | 
				
			||||||
			throw std::runtime_error("Malformed 'routes' specification");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		++routes;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
route_filter_init(const ConfigBlock &block)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::make_unique<PreparedRouteFilter>(block);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RouteFilter::RouteFilter(const AudioFormat &audio_format,
 | 
					 | 
				
			||||||
			 unsigned out_channels,
 | 
					 | 
				
			||||||
			 const std::array<int8_t, MAX_CHANNELS> &_sources)
 | 
					 | 
				
			||||||
	:Filter(audio_format), sources(_sources), input_format(audio_format),
 | 
					 | 
				
			||||||
	 input_frame_size(input_format.GetFrameSize())
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	// Decide on an output format which has enough channels,
 | 
					 | 
				
			||||||
	// and is otherwise identical
 | 
					 | 
				
			||||||
	out_audio_format.channels = out_channels;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Precalculate this simple value, to speed up allocation later
 | 
					 | 
				
			||||||
	output_frame_size = out_audio_format.GetFrameSize();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
PreparedRouteFilter::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::make_unique<RouteFilter>(audio_format, min_output_channels,
 | 
					 | 
				
			||||||
					     sources);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
RouteFilter::FilterPCM(std::span<const std::byte> src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	size_t number_of_frames = src.size() / input_frame_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const size_t bytes_per_frame_per_channel = input_format.GetSampleSize();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// A moving pointer that always refers to channel 0 in the input, at the currently handled frame
 | 
					 | 
				
			||||||
	const auto *base_source = (const uint8_t *)src.data();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Grow our reusable buffer, if needed, and set the moving pointer
 | 
					 | 
				
			||||||
	const size_t result_size = number_of_frames * output_frame_size;
 | 
					 | 
				
			||||||
	void *const result = output_buffer.Get(result_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// A moving pointer that always refers to the currently filled channel of the currently handled frame, in the output
 | 
					 | 
				
			||||||
	auto *chan_destination = (std::byte *)result;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Perform our copy operations, with N input channels and M output channels
 | 
					 | 
				
			||||||
	for (unsigned int s=0; s<number_of_frames; ++s) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Need to perform one copy per output channel
 | 
					 | 
				
			||||||
		for (unsigned c = 0; c < out_audio_format.channels; ++c) {
 | 
					 | 
				
			||||||
			if (sources[c] == -1 ||
 | 
					 | 
				
			||||||
			    (unsigned)sources[c] >= input_format.channels) {
 | 
					 | 
				
			||||||
				// No source for this destination output,
 | 
					 | 
				
			||||||
				// give it zeroes as input
 | 
					 | 
				
			||||||
				PcmSilence({chan_destination, bytes_per_frame_per_channel},
 | 
					 | 
				
			||||||
					   input_format.format);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				// Get the data from channel sources[c]
 | 
					 | 
				
			||||||
				// and copy it to the output
 | 
					 | 
				
			||||||
				const uint8_t *data = base_source +
 | 
					 | 
				
			||||||
					(sources[c] * bytes_per_frame_per_channel);
 | 
					 | 
				
			||||||
				memcpy(chan_destination,
 | 
					 | 
				
			||||||
					  data,
 | 
					 | 
				
			||||||
					  bytes_per_frame_per_channel);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			// Move on to the next output channel
 | 
					 | 
				
			||||||
			chan_destination += bytes_per_frame_per_channel;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Go on to the next N input samples
 | 
					 | 
				
			||||||
		base_source += input_frame_size;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Here it is, ladies and gentlemen! Rerouted data!
 | 
					 | 
				
			||||||
	return { (const std::byte *)result, result_size };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const FilterPlugin route_filter_plugin = {
 | 
					 | 
				
			||||||
	"route",
 | 
					 | 
				
			||||||
	route_filter_init,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
@ -1,11 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifndef MPD_ROUTE_FILTER_PLUGIN_HXX
 | 
					 | 
				
			||||||
#define MPD_ROUTE_FILTER_PLUGIN_HXX
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct FilterPlugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
extern const FilterPlugin route_filter_plugin;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
@ -1,100 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "TwoFilters.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
#include "lib/fmt/AudioFormatFormatter.hxx"
 | 
					 | 
				
			||||||
#include "lib/fmt/RuntimeError.hxx"
 | 
					 | 
				
			||||||
#include "util/StringBuffer.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
TwoFilters::Reset() noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(first);
 | 
					 | 
				
			||||||
	assert(second);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	first->Reset();
 | 
					 | 
				
			||||||
	second->Reset();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
TwoFilters::FilterPCM(std::span<const std::byte> src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(first);
 | 
					 | 
				
			||||||
	assert(second);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (const auto dest = first->FilterPCM(src); dest.empty()) [[unlikely]]
 | 
					 | 
				
			||||||
		/* no output from the first filter; pass the empty
 | 
					 | 
				
			||||||
                   buffer on, do not call the second filter */
 | 
					 | 
				
			||||||
		return dest;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		/* pass output from the first filter to the second
 | 
					 | 
				
			||||||
                   filter and return its result */
 | 
					 | 
				
			||||||
		return second->FilterPCM(dest);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
TwoFilters::ReadMore()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(first);
 | 
					 | 
				
			||||||
	assert(second);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* first read all remaining data from the second filter */
 | 
					 | 
				
			||||||
	if (auto result = second->ReadMore(); !result.empty())
 | 
					 | 
				
			||||||
		return result;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* now read more data from the first filter and process it
 | 
					 | 
				
			||||||
           with the second filter */
 | 
					 | 
				
			||||||
	if (auto result = first->ReadMore(); !result.empty())
 | 
					 | 
				
			||||||
		/* output from the first Filter must be filtered by
 | 
					 | 
				
			||||||
		   the second Filter */
 | 
					 | 
				
			||||||
		return second->FilterPCM(result);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* both filters have been queried and there's no more data */
 | 
					 | 
				
			||||||
	return {};
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
TwoFilters::Flush()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(second);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* first read all remaining data from the second filter */
 | 
					 | 
				
			||||||
	if (auto result = second->ReadMore(); !result.empty())
 | 
					 | 
				
			||||||
		return result;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* now flush the first filter and process it with the second
 | 
					 | 
				
			||||||
           filter */
 | 
					 | 
				
			||||||
	if (first) {
 | 
					 | 
				
			||||||
		if (auto result = first->Flush(); !result.empty())
 | 
					 | 
				
			||||||
			/* output from the first Filter must be
 | 
					 | 
				
			||||||
			   filtered by the second Filter */
 | 
					 | 
				
			||||||
			return second->FilterPCM(result);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/* the first filter is flushed completely and we don't
 | 
					 | 
				
			||||||
		   need it anymore; any further method calls that
 | 
					 | 
				
			||||||
		   would use it are illegal according to the Filter
 | 
					 | 
				
			||||||
		   API docs */
 | 
					 | 
				
			||||||
		first.reset();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* finally flush the second filter */
 | 
					 | 
				
			||||||
	return second->Flush();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
PreparedTwoFilters::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	auto a = first->Open(audio_format);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	const auto &a_out_format = a->GetOutAudioFormat();
 | 
					 | 
				
			||||||
	auto b_in_format = a_out_format;
 | 
					 | 
				
			||||||
	auto b = second->Open(b_in_format);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (b_in_format != a_out_format)
 | 
					 | 
				
			||||||
		throw FmtRuntimeError("Audio format not supported by filter {:?}: {}",
 | 
					 | 
				
			||||||
				      second_name, a_out_format);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return std::make_unique<TwoFilters>(std::move(a),
 | 
					 | 
				
			||||||
					    std::move(b));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,66 +1,29 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#ifndef MPD_TWO_FILTERS_HXX
 | 
				
			||||||
 | 
					#define MPD_TWO_FILTERS_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "filter/Filter.hxx"
 | 
					 | 
				
			||||||
#include "filter/Prepared.hxx"
 | 
					#include "filter/Prepared.hxx"
 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <string>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * A #Filter implementation which chains two other filters.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
class TwoFilters final : public Filter {
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> first, second;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	template<typename F, typename S>
 | 
					 | 
				
			||||||
	TwoFilters(F &&_first, S &&_second) noexcept
 | 
					 | 
				
			||||||
		:Filter(_second->GetOutAudioFormat()),
 | 
					 | 
				
			||||||
		 first(std::forward<F>(_first)),
 | 
					 | 
				
			||||||
		 second(std::forward<S>(_second)) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// virtual methods from class Filter
 | 
					 | 
				
			||||||
	void Reset() noexcept override;
 | 
					 | 
				
			||||||
	std::span<const std::byte> FilterPCM(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
	std::span<const std::byte> ReadMore() override;
 | 
					 | 
				
			||||||
	std::span<const std::byte> Flush() override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Like #TwoFilters, but implements the #PreparedFilter interface.
 | 
					 * Stub implementation - filter support not needed for database creation
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class PreparedTwoFilters final : public PreparedFilter {
 | 
					inline std::unique_ptr<PreparedFilter>
 | 
				
			||||||
	std::unique_ptr<PreparedFilter> first, second;
 | 
					PreparedTwoFilters([[maybe_unused]] std::unique_ptr<PreparedFilter> a,
 | 
				
			||||||
	std::string second_name;
 | 
							   [[maybe_unused]] std::unique_ptr<PreparedFilter> b)
 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	template<typename F, typename S, typename N>
 | 
					 | 
				
			||||||
	PreparedTwoFilters(F &&_first, S &&_second, N &&_second_name) noexcept
 | 
					 | 
				
			||||||
		:first(std::forward<F>(_first)),
 | 
					 | 
				
			||||||
		 second(std::forward<S>(_second)),
 | 
					 | 
				
			||||||
		 second_name(std::forward<N>(_second_name)) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> Open(AudioFormat &audio_format) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Create a #PreparedTwoFilters instance, but only if both parameters
 | 
					 | 
				
			||||||
 * are not nullptr.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
template<typename F, typename S, typename N>
 | 
					 | 
				
			||||||
static std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
ChainFilters(F &&first, S &&second, N &&second_name) noexcept
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!second)
 | 
						return nullptr;
 | 
				
			||||||
		return std::forward<F>(first);
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!first)
 | 
					 | 
				
			||||||
		return std::forward<S>(second);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return std::make_unique<PreparedTwoFilters>(std::forward<F>(first),
 | 
					inline std::unique_ptr<PreparedFilter>
 | 
				
			||||||
						    std::forward<S>(second),
 | 
					ChainFilters([[maybe_unused]] std::unique_ptr<PreparedFilter> a,
 | 
				
			||||||
						    std::forward<N>(second_name));
 | 
						     [[maybe_unused]] std::unique_ptr<PreparedFilter> b,
 | 
				
			||||||
 | 
						     [[maybe_unused]] const char *name = nullptr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return nullptr;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,71 +0,0 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "VolumeFilterPlugin.hxx"
 | 
					 | 
				
			||||||
#include "filter/Filter.hxx"
 | 
					 | 
				
			||||||
#include "filter/Prepared.hxx"
 | 
					 | 
				
			||||||
#include "pcm/Volume.hxx"
 | 
					 | 
				
			||||||
#include "pcm/AudioFormat.hxx"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class VolumeFilter final : public Filter {
 | 
					 | 
				
			||||||
	PcmVolume pv;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	explicit VolumeFilter(const AudioFormat &audio_format)
 | 
					 | 
				
			||||||
		:Filter(audio_format) {
 | 
					 | 
				
			||||||
		out_audio_format.format = pv.Open(out_audio_format.format,
 | 
					 | 
				
			||||||
						  true);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	[[nodiscard]] unsigned GetVolume() const noexcept {
 | 
					 | 
				
			||||||
		return pv.GetVolume();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void SetVolume(unsigned _volume) noexcept {
 | 
					 | 
				
			||||||
		pv.SetVolume(_volume);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* virtual methods from class Filter */
 | 
					 | 
				
			||||||
	std::span<const std::byte> FilterPCM(std::span<const std::byte> src) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class PreparedVolumeFilter final : public PreparedFilter {
 | 
					 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	/* virtual methods from class Filter */
 | 
					 | 
				
			||||||
	std::unique_ptr<Filter> Open(AudioFormat &af) override;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<Filter>
 | 
					 | 
				
			||||||
PreparedVolumeFilter::Open(AudioFormat &audio_format)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::make_unique<VolumeFilter>(audio_format);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::span<const std::byte>
 | 
					 | 
				
			||||||
VolumeFilter::FilterPCM(std::span<const std::byte> src)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return pv.Apply(src);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					 | 
				
			||||||
volume_filter_prepare() noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return std::make_unique<PreparedVolumeFilter>();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
unsigned
 | 
					 | 
				
			||||||
volume_filter_get(const Filter *_filter) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const auto *filter =
 | 
					 | 
				
			||||||
		(const VolumeFilter *)_filter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return filter->GetVolume();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
volume_filter_set(Filter *_filter, unsigned volume) noexcept
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	auto *filter = (VolumeFilter *)_filter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	filter->SetVolume(volume);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,21 +1,28 @@
 | 
				
			|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
					// SPDX-License-Identifier: GPL-2.0-or-later
 | 
				
			||||||
// Copyright The Music Player Daemon Project
 | 
					// Copyright The Music Player Daemon Project
 | 
				
			||||||
 | 
					// STUB FILE - Filter support removed for mpd-dbcreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef MPD_VOLUME_FILTER_PLUGIN_HXX
 | 
					#ifndef MPD_VOLUME_FILTER_PLUGIN_HXX
 | 
				
			||||||
#define MPD_VOLUME_FILTER_PLUGIN_HXX
 | 
					#define MPD_VOLUME_FILTER_PLUGIN_HXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "filter/Prepared.hxx"
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PreparedFilter;
 | 
					 | 
				
			||||||
class Filter;
 | 
					class Filter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::unique_ptr<PreparedFilter>
 | 
					/**
 | 
				
			||||||
volume_filter_prepare() noexcept;
 | 
					 * Stub implementation - filter support not needed for database creation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					inline void
 | 
				
			||||||
 | 
					volume_filter_set(Filter *, unsigned) noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// No-op stub
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unsigned
 | 
					inline std::unique_ptr<PreparedFilter>
 | 
				
			||||||
volume_filter_get(const Filter *filter) noexcept;
 | 
					volume_filter_prepare() noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
void
 | 
						return nullptr;
 | 
				
			||||||
volume_filter_set(Filter *filter, unsigned volume) noexcept;
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -1,38 +0,0 @@
 | 
				
			|||||||
filter_plugins_sources = []
 | 
					 | 
				
			||||||
filter_plugins_deps = [fmt_dep]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if libavfilter_dep.found()
 | 
					 | 
				
			||||||
  filter_plugins_sources += [
 | 
					 | 
				
			||||||
    'FfmpegFilter.cxx',
 | 
					 | 
				
			||||||
    'FfmpegFilterPlugin.cxx',
 | 
					 | 
				
			||||||
    'HdcdFilterPlugin.cxx',
 | 
					 | 
				
			||||||
  ]
 | 
					 | 
				
			||||||
  filter_plugins_deps += ffmpeg_dep
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
filter_plugins = static_library(
 | 
					 | 
				
			||||||
  'filter_plugins',
 | 
					 | 
				
			||||||
  'NullFilterPlugin.cxx',
 | 
					 | 
				
			||||||
  'TwoFilters.cxx',
 | 
					 | 
				
			||||||
  'AutoConvertFilterPlugin.cxx',
 | 
					 | 
				
			||||||
  'ConvertFilterPlugin.cxx',
 | 
					 | 
				
			||||||
  'RouteFilterPlugin.cxx',
 | 
					 | 
				
			||||||
  'NormalizeFilterPlugin.cxx',
 | 
					 | 
				
			||||||
  'ReplayGainFilterPlugin.cxx',
 | 
					 | 
				
			||||||
  'VolumeFilterPlugin.cxx',
 | 
					 | 
				
			||||||
  filter_plugins_sources,
 | 
					 | 
				
			||||||
  include_directories: inc,
 | 
					 | 
				
			||||||
  dependencies: [
 | 
					 | 
				
			||||||
    filter_plugins_deps,
 | 
					 | 
				
			||||||
    log_dep,
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
filter_plugins_dep = declare_dependency(
 | 
					 | 
				
			||||||
  link_with: filter_plugins,
 | 
					 | 
				
			||||||
  dependencies: [
 | 
					 | 
				
			||||||
    filter_api_dep,
 | 
					 | 
				
			||||||
    pcm_dep,
 | 
					 | 
				
			||||||
    config_dep,
 | 
					 | 
				
			||||||
  ] + filter_plugins_deps,
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
					Loading…
					
					
				
		Reference in New Issue