Merge branch 'add-openmpt-decoder' of git://github.com/GrimReaperFloof/MPD

This commit is contained in:
Max Kellermann 2021-05-28 13:53:09 +02:00
commit 38bfef7d0b
8 changed files with 243 additions and 0 deletions

2
NEWS
View File

@ -7,6 +7,8 @@ ver 0.23 (not yet released)
- proxy: require libmpdclient 2.11 or later - proxy: require libmpdclient 2.11 or later
- proxy: split search into chunks to avoid exceeding the output buffer - proxy: split search into chunks to avoid exceeding the output buffer
- upnp: support libnpupnp instead of libupnp - upnp: support libnpupnp instead of libupnp
* decoder
- openmt: new plugin
* output * output
- pipewire: new plugin - pipewire: new plugin
- snapcast: new plugin - snapcast: new plugin

View File

@ -494,6 +494,34 @@ Module player based on MODPlug.
* - **loop_count** * - **loop_count**
- Number of times to loop the module if it uses backward loops. Default is 0 which prevents looping. -1 loops forever. - Number of times to loop the module if it uses backward loops. Default is 0 which prevents looping. -1 loops forever.
openmpt
-------
Module player based on `libopenmpt <https://lib.openmpt.org>`_.
.. list-table::
:widths: 20 80
:header-rows: 1
* - Setting
- Description
* - **repeat_count**
- Set how many times the module repeats. -1: repeat forever. 0: play once, repeat zero times (the default). n>0: play once and repeat n times after that.
* - **stereo_separation**
- Sets the stereo separation. The supported value range is [0,200]. Defaults to 100.
* - **interpolation_filter 0|1|2|4|8**
- Sets the interpolation filter. 0: internal default. 1: no interpolation (zero order hold). 2: linear interpolation. 4: cubic interpolation. 8: windowed sinc with 8 taps. Defaults to 0.
* - **override_mptm_interp_filter yes|no**
- If `interpolation_filter` has been changed, setting this to yes will force all MPTM modules to use that interpolation filter. If set to no, MPTM modules will play with their own interpolation filter regardless of the value of `interpolation_filter`. Defaults to no.
* - **volume_ramping**
- Sets the amount of volume ramping done by the libopenmpt mixer. The default value is -1, which indicates a recommended default value. The meaningful value range is [-1..10]. A value of 0 completely disables volume ramping. This might cause clicks in sound output. Higher values imply slower/softer volume ramps.
* - **sync_samples yes|no**
- Syncs sample playback when seeking. Defaults to yes.
* - **emulate_amiga yes|no**
- Enables the Amiga resampler for Amiga modules. This emulates the sound characteristics of the Paula chip and overrides the selected interpolation filter. Non-Amiga module formats are not affected by this setting. Defaults to yes.
* - **emulate_amiga_type**
- Configures the filter type to use for the Amiga resampler. Supported values are: "auto": Filter type is chosen by the library and might change. This is the default. "a500": Amiga A500 filter. "a1200": Amiga A1200 filter. "unfiltered": BLEP synthesis without model-specific filters. The LED filter is ignored by this setting. This filter mode is considered to be experimental and might change in the future. Defaults to "auto". Requires libopenmpt 0.5 or higher.
mpcdec mpcdec
------ ------

View File

@ -138,6 +138,7 @@ option('gme', type: 'feature', description: 'Game Music Emulator decoder plugin'
option('mad', type: 'feature', description: 'MP3 decoder using libmad') option('mad', type: 'feature', description: 'MP3 decoder using libmad')
option('mikmod', type: 'feature', description: 'MikMod decoder plugin') option('mikmod', type: 'feature', description: 'MikMod decoder plugin')
option('modplug', type: 'feature', description: 'Modplug decoder plugin') option('modplug', type: 'feature', description: 'Modplug decoder plugin')
option('openmpt', type: 'feature', description: 'OpenMPT decoder plugin')
option('mpcdec', type: 'feature', description: 'Musepack decoder plugin') option('mpcdec', type: 'feature', description: 'Musepack decoder plugin')
option('mpg123', type: 'feature', description: 'MP3 decoder using libmpg123') option('mpg123', type: 'feature', description: 'MP3 decoder using libmpg123')
option('opus', type: 'feature', description: 'Opus decoder plugin') option('opus', type: 'feature', description: 'Opus decoder plugin')

View File

@ -123,6 +123,15 @@ libmodplug = AutotoolsProject(
], ],
) )
libopenmpt = AutotoolsProject(
'https://lib.openmpt.org/files/libopenmpt/src/libopenmpt-0.5.8+release.autotools.tar.gz',
'61de7cc0c011b10472ca16adcc123689',
'lib/libopenmpt.a',
[
'--disable-shared', '--enable-static'
],
)
wildmidi = CmakeProject( wildmidi = CmakeProject(
'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.3', 'https://codeload.github.com/Mindwerks/wildmidi/tar.gz/wildmidi-0.4.3',
'498e5a96455bb4b91b37188ad6dcb070824e92c44f5ed452b90adbaec8eef3c5', '498e5a96455bb4b91b37188ad6dcb070824e92c44f5ed452b90adbaec8eef3c5',

View File

@ -44,6 +44,7 @@
#include "plugins/WildmidiDecoderPlugin.hxx" #include "plugins/WildmidiDecoderPlugin.hxx"
#include "plugins/MikmodDecoderPlugin.hxx" #include "plugins/MikmodDecoderPlugin.hxx"
#include "plugins/ModplugDecoderPlugin.hxx" #include "plugins/ModplugDecoderPlugin.hxx"
#include "plugins/OpenmptDecoderPlugin.hxx"
#include "plugins/MpcdecDecoderPlugin.hxx" #include "plugins/MpcdecDecoderPlugin.hxx"
#include "plugins/FluidsynthDecoderPlugin.hxx" #include "plugins/FluidsynthDecoderPlugin.hxx"
#include "plugins/SidplayDecoderPlugin.hxx" #include "plugins/SidplayDecoderPlugin.hxx"
@ -90,6 +91,9 @@ constexpr const struct DecoderPlugin *decoder_plugins[] = {
#ifdef ENABLE_WAVPACK #ifdef ENABLE_WAVPACK
&wavpack_decoder_plugin, &wavpack_decoder_plugin,
#endif #endif
#ifdef ENABLE_OPENMPT
&openmpt_decoder_plugin,
#endif
#ifdef ENABLE_MODPLUG #ifdef ENABLE_MODPLUG
&modplug_decoder_plugin, &modplug_decoder_plugin,
#endif #endif

View File

@ -0,0 +1,163 @@
/*
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "OpenmptDecoderPlugin.hxx"
#include "decoder/Features.h"
#include "ModCommon.hxx"
#include "../DecoderAPI.hxx"
#include "input/InputStream.hxx"
#include "tag/Handler.hxx"
#include "tag/Type.h"
#include "util/Domain.hxx"
#include "util/RuntimeError.hxx"
#include "util/StringView.hxx"
#include "Log.hxx"
#include <libopenmpt/libopenmpt.hpp>
#include <cassert>
static constexpr Domain openmpt_domain("openmpt");
static constexpr size_t OPENMPT_FRAME_SIZE = 4096;
static constexpr int32_t OPENMPT_SAMPLE_RATE = 48000;
static int openmpt_repeat_count;
static int openmpt_stereo_separation;
static int openmpt_interpolation_filter;
static bool openmpt_override_mptm_interp_filter;
static int openmpt_volume_ramping;
static bool openmpt_sync_samples;
static bool openmpt_emulate_amiga;
#ifdef HAVE_LIBOPENMPT_VERSION_0_5
static std::string_view openmpt_emulate_amiga_type;
#endif
static bool
openmpt_decoder_init(const ConfigBlock &block)
{
openmpt_repeat_count = block.GetBlockValue("repeat_count", 0);
openmpt_stereo_separation = block.GetBlockValue("stereo_separation", 100);
openmpt_interpolation_filter = block.GetBlockValue("interpolation_filter", 0);
openmpt_override_mptm_interp_filter = block.GetBlockValue("override_mptm_interp_filter", false);
openmpt_volume_ramping = block.GetBlockValue("volume_ramping", -1);
openmpt_sync_samples = block.GetBlockValue("sync_samples", true);
openmpt_emulate_amiga = block.GetBlockValue("emulate_amiga", true);
#ifdef HAVE_LIBOPENMPT_VERSION_0_5
openmpt_emulate_amiga_type = block.GetBlockValue("emulate_amiga_type", "auto");
#endif
return true;
}
static void
mod_decode(DecoderClient &client, InputStream &is)
{
int ret;
char audio_buffer[OPENMPT_FRAME_SIZE];
const auto buffer = mod_loadfile(&openmpt_domain, &client, is);
if (buffer.IsNull()) {
LogWarning(openmpt_domain, "could not load stream");
return;
}
openmpt::module mod(buffer.data(), buffer.size());
/* alter settings */
mod.set_repeat_count(openmpt_repeat_count);
mod.set_render_param(mod.RENDER_STEREOSEPARATION_PERCENT, openmpt_stereo_separation);
mod.set_render_param(mod.RENDER_INTERPOLATIONFILTER_LENGTH, openmpt_interpolation_filter);
if (!openmpt_override_mptm_interp_filter && mod.get_metadata("type") == "mptm") {
/* The MPTM format has a setting for which interpolation filter should be used.
* If we want to play the module back the way the composer intended it,
* we have to set the interpolation filter setting in libopenmpt back to 0: internal default. */
mod.set_render_param(mod.RENDER_INTERPOLATIONFILTER_LENGTH, 0);
}
mod.set_render_param(mod.RENDER_VOLUMERAMPING_STRENGTH, openmpt_volume_ramping);
#ifdef HAVE_LIBOPENMPT_VERSION_0_5
mod.ctl_set_boolean("seek.sync_samples", openmpt_sync_samples);
mod.ctl_set_boolean("render.resampler.emulate_amiga", openmpt_emulate_amiga);
mod.ctl_set_text("render.resampler.emulate_amiga_type", openmpt_emulate_amiga_type);
#else
mod.ctl_set("seek.sync_samples", std::to_string((unsigned)openmpt_sync_samples));
mod.ctl_set("render.resampler.emulate_amiga", std::to_string((unsigned)openmpt_emulate_amiga));
#endif
static constexpr AudioFormat audio_format(OPENMPT_SAMPLE_RATE, SampleFormat::FLOAT, 2);
assert(audio_format.IsValid());
client.Ready(audio_format, is.IsSeekable(),
SongTime::FromS(mod.get_duration_seconds()));
DecoderCommand cmd;
do {
ret = mod.read_interleaved_stereo(OPENMPT_SAMPLE_RATE, OPENMPT_FRAME_SIZE / 2 / sizeof(float), (float*)audio_buffer);
if (ret <= 0)
break;
cmd = client.SubmitData(nullptr,
audio_buffer, ret * 2 * sizeof(float),
0);
if (cmd == DecoderCommand::SEEK) {
mod.set_position_seconds(client.GetSeekTime().ToS());
client.CommandFinished();
}
} while (cmd != DecoderCommand::STOP);
}
static bool
openmpt_scan_stream(InputStream &is, TagHandler &handler) noexcept
{
const auto buffer = mod_loadfile(&openmpt_domain, nullptr, is);
if (buffer.IsNull()) {
LogWarning(openmpt_domain, "could not load stream");
return false;
}
openmpt::module mod(buffer.data(), buffer.size());
handler.OnDuration(SongTime::FromS(mod.get_duration_seconds()));
/* Tagging */
handler.OnTag(TAG_TITLE, mod.get_metadata("title").c_str());
handler.OnTag(TAG_ARTIST, mod.get_metadata("artist").c_str());
handler.OnTag(TAG_COMMENT, mod.get_metadata("message").c_str());
handler.OnTag(TAG_DATE, mod.get_metadata("date").c_str());
handler.OnTag(TAG_PERFORMER, mod.get_metadata("tracker").c_str());
return true;
}
static const char *const mod_suffixes[] = {
"mptm", "mod", "s3m", "xm", "it", "669", "amf", "ams",
"c67", "dbm", "digi", "dmf", "dsm", "dtm", "far", "imf",
"ice", "j2b", "m15", "mdl", "med", "mms", "mt2", "mtm",
"nst", "okt", "plm", "psm", "pt36", "ptm", "sfx", "sfx2",
"st26", "stk", "stm", "stp", "ult", "wow", "gdm", "mo3",
"oxm", "umx", "xpk", "ppm", "mmcmp",
nullptr
};
constexpr DecoderPlugin openmpt_decoder_plugin =
DecoderPlugin("openmpt", mod_decode, openmpt_scan_stream)
.WithInit(openmpt_decoder_init)
.WithSuffixes(mod_suffixes);

View File

@ -0,0 +1,25 @@
/*
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_DECODER_OPENMPT_HXX
#define MPD_DECODER_OPENMPT_HXX
extern const struct DecoderPlugin openmpt_decoder_plugin;
#endif

View File

@ -108,6 +108,16 @@ if libmodplug_dep.found()
] ]
endif endif
libopenmpt_dep = dependency('libopenmpt', required: get_option('openmpt'))
decoder_features.set('ENABLE_OPENMPT', libopenmpt_dep.found())
decoder_features.set('HAVE_LIBOPENMPT_VERSION_0_5', libopenmpt_dep.version().version_compare('>= 0.5'))
if libopenmpt_dep.found()
decoder_plugins_sources += [
'OpenmptDecoderPlugin.cxx',
'ModCommon.cxx'
]
endif
libmpcdec_dep = c_compiler.find_library('mpcdec', required: get_option('mpcdec')) libmpcdec_dep = c_compiler.find_library('mpcdec', required: get_option('mpcdec'))
decoder_features.set('ENABLE_MPCDEC', libmpcdec_dep.found()) decoder_features.set('ENABLE_MPCDEC', libmpcdec_dep.found())
if libmpcdec_dep.found() if libmpcdec_dep.found()
@ -188,6 +198,7 @@ decoder_plugins = static_library(
libmad_dep, libmad_dep,
libmikmod_dep, libmikmod_dep,
libmodplug_dep, libmodplug_dep,
libopenmpt_dep,
libmpcdec_dep, libmpcdec_dep,
libmpg123_dep, libmpg123_dep,
libopus_dep, libopus_dep,