aboutsummaryrefslogtreecommitdiffstats
path: root/src/decoder/plugins/MikmodDecoderPlugin.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/decoder/plugins/MikmodDecoderPlugin.cxx')
-rw-r--r--src/decoder/plugins/MikmodDecoderPlugin.cxx250
1 files changed, 250 insertions, 0 deletions
diff --git a/src/decoder/plugins/MikmodDecoderPlugin.cxx b/src/decoder/plugins/MikmodDecoderPlugin.cxx
new file mode 100644
index 000000000..85633f1fc
--- /dev/null
+++ b/src/decoder/plugins/MikmodDecoderPlugin.cxx
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2003-2014 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 "config.h"
+#include "MikmodDecoderPlugin.hxx"
+#include "../DecoderAPI.hxx"
+#include "tag/TagHandler.hxx"
+#include "system/FatalError.hxx"
+#include "fs/Path.hxx"
+#include "util/Domain.hxx"
+#include "Log.hxx"
+
+#include <mikmod.h>
+
+#include <assert.h>
+
+static constexpr Domain mikmod_domain("mikmod");
+
+/* this is largely copied from alsaplayer */
+
+static constexpr size_t MIKMOD_FRAME_SIZE = 4096;
+
+static BOOL
+mikmod_mpd_init(void)
+{
+ return VC_Init();
+}
+
+static void
+mikmod_mpd_exit(void)
+{
+ VC_Exit();
+}
+
+static void
+mikmod_mpd_update(void)
+{
+}
+
+static BOOL
+mikmod_mpd_is_present(void)
+{
+ return true;
+}
+
+static char drv_name[] = PACKAGE_NAME;
+static char drv_version[] = VERSION;
+
+#if (LIBMIKMOD_VERSION > 0x030106)
+static char drv_alias[] = PACKAGE;
+#endif
+
+static MDRIVER drv_mpd = {
+ nullptr,
+ drv_name,
+ drv_version,
+ 0,
+ 255,
+#if (LIBMIKMOD_VERSION > 0x030106)
+ drv_alias,
+#if (LIBMIKMOD_VERSION >= 0x030200)
+ nullptr, /* CmdLineHelp */
+#endif
+ nullptr, /* CommandLine */
+#endif
+ mikmod_mpd_is_present,
+ VC_SampleLoad,
+ VC_SampleUnload,
+ VC_SampleSpace,
+ VC_SampleLength,
+ mikmod_mpd_init,
+ mikmod_mpd_exit,
+ nullptr,
+ VC_SetNumVoices,
+ VC_PlayStart,
+ VC_PlayStop,
+ mikmod_mpd_update,
+ nullptr,
+ VC_VoiceSetVolume,
+ VC_VoiceGetVolume,
+ VC_VoiceSetFrequency,
+ VC_VoiceGetFrequency,
+ VC_VoiceSetPanning,
+ VC_VoiceGetPanning,
+ VC_VoicePlay,
+ VC_VoiceStop,
+ VC_VoiceStopped,
+ VC_VoiceGetPosition,
+ VC_VoiceRealVolume
+};
+
+static bool mikmod_loop;
+static unsigned mikmod_sample_rate;
+
+static bool
+mikmod_decoder_init(const config_param &param)
+{
+ static char params[] = "";
+
+ mikmod_loop = param.GetBlockValue("loop", false);
+ mikmod_sample_rate = param.GetBlockValue("sample_rate", 44100u);
+ if (!audio_valid_sample_rate(mikmod_sample_rate))
+ FormatFatalError("Invalid sample rate in line %d: %u",
+ param.line, mikmod_sample_rate);
+
+ md_device = 0;
+ md_reverb = 0;
+
+ MikMod_RegisterDriver(&drv_mpd);
+ MikMod_RegisterAllLoaders();
+
+ md_pansep = 64;
+ md_mixfreq = mikmod_sample_rate;
+ md_mode = (DMODE_SOFT_MUSIC | DMODE_INTERP | DMODE_STEREO |
+ DMODE_16BITS);
+
+ if (MikMod_Init(params)) {
+ FormatError(mikmod_domain,
+ "Could not init MikMod: %s",
+ MikMod_strerror(MikMod_errno));
+ return false;
+ }
+
+ return true;
+}
+
+static void
+mikmod_decoder_finish(void)
+{
+ MikMod_Exit();
+}
+
+static void
+mikmod_decoder_file_decode(Decoder &decoder, Path path_fs)
+{
+ /* deconstify the path because libmikmod wants a non-const
+ string pointer */
+ char *const path2 = const_cast<char *>(path_fs.c_str());
+
+ MODULE *handle;
+ int ret;
+ SBYTE buffer[MIKMOD_FRAME_SIZE];
+
+ handle = Player_Load(path2, 128, 0);
+
+ if (handle == nullptr) {
+ FormatError(mikmod_domain,
+ "failed to open mod: %s", path_fs.c_str());
+ return;
+ }
+
+ handle->loop = mikmod_loop;
+
+ const AudioFormat audio_format(mikmod_sample_rate, SampleFormat::S16, 2);
+ assert(audio_format.IsValid());
+
+ decoder_initialized(decoder, audio_format, false,
+ SignedSongTime::Negative());
+
+ Player_Start(handle);
+
+ DecoderCommand cmd = DecoderCommand::NONE;
+ while (cmd == DecoderCommand::NONE && Player_Active()) {
+ ret = VC_WriteBytes(buffer, sizeof(buffer));
+ cmd = decoder_data(decoder, nullptr, buffer, ret, 0);
+ }
+
+ Player_Stop();
+ Player_Free(handle);
+}
+
+static bool
+mikmod_decoder_scan_file(Path path_fs,
+ const struct tag_handler *handler, void *handler_ctx)
+{
+ /* deconstify the path because libmikmod wants a non-const
+ string pointer */
+ char *const path2 = const_cast<char *>(path_fs.c_str());
+
+ MODULE *handle = Player_Load(path2, 128, 0);
+
+ if (handle == nullptr) {
+ FormatDebug(mikmod_domain,
+ "Failed to open file: %s", path_fs.c_str());
+ return false;
+ }
+
+ Player_Free(handle);
+
+ char *title = Player_LoadTitle(path2);
+ if (title != nullptr) {
+ tag_handler_invoke_tag(handler, handler_ctx,
+ TAG_TITLE, title);
+#if (LIBMIKMOD_VERSION >= 0x030200)
+ MikMod_free(title);
+#else
+ free(title);
+#endif
+ }
+
+ return true;
+}
+
+static const char *const mikmod_decoder_suffixes[] = {
+ "amf",
+ "dsm",
+ "far",
+ "gdm",
+ "imf",
+ "it",
+ "med",
+ "mod",
+ "mtm",
+ "s3m",
+ "stm",
+ "stx",
+ "ult",
+ "uni",
+ "xm",
+ nullptr
+};
+
+const struct DecoderPlugin mikmod_decoder_plugin = {
+ "mikmod",
+ mikmod_decoder_init,
+ mikmod_decoder_finish,
+ nullptr,
+ mikmod_decoder_file_decode,
+ mikmod_decoder_scan_file,
+ nullptr,
+ nullptr,
+ mikmod_decoder_suffixes,
+ nullptr,
+};