MikmodDecoderPlugin.cxx 5.03 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 The Music Player Daemon Project
3 4
 * http://www.musicpd.org
 *
5 6 7 8 9 10 11 12 13
 * 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.
14 15 16 17
 *
 * 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.
18 19
 */

20
#include "config.h"
21
#include "MikmodDecoderPlugin.hxx"
22
#include "../DecoderAPI.hxx"
23
#include "tag/TagHandler.hxx"
24
#include "system/FatalError.hxx"
25
#include "fs/Path.hxx"
26 27
#include "util/Domain.hxx"
#include "Log.hxx"
28 29

#include <mikmod.h>
30

31
#include <assert.h>
32

33
static constexpr Domain mikmod_domain("mikmod");
34

35 36
/* this is largely copied from alsaplayer */

37
static constexpr size_t MIKMOD_FRAME_SIZE = 4096;
38

39 40
static BOOL
mikmod_mpd_init(void)
Avuton Olrich's avatar
Avuton Olrich committed
41
{
42 43 44
	return VC_Init();
}

45 46
static void
mikmod_mpd_exit(void)
Avuton Olrich's avatar
Avuton Olrich committed
47
{
48 49 50
	VC_Exit();
}

51 52
static void
mikmod_mpd_update(void)
Avuton Olrich's avatar
Avuton Olrich committed
53
{
54 55
}

56 57
static BOOL
mikmod_mpd_is_present(void)
Avuton Olrich's avatar
Avuton Olrich committed
58
{
59
	return true;
60 61
}

62 63
static char drv_name[] = PACKAGE_NAME;
static char drv_version[] = VERSION;
64 65

#if (LIBMIKMOD_VERSION > 0x030106)
66
static char drv_alias[] = PACKAGE;
67 68
#endif

Avuton Olrich's avatar
Avuton Olrich committed
69
static MDRIVER drv_mpd = {
70
	nullptr,
71 72
	drv_name,
	drv_version,
73 74
	0,
	255,
75
#if (LIBMIKMOD_VERSION > 0x030106)
76
	drv_alias,
77
#if (LIBMIKMOD_VERSION >= 0x030200)
78
	nullptr,  /* CmdLineHelp */
79
#endif
80
	nullptr,  /* CommandLine */
81
#endif
82
	mikmod_mpd_is_present,
83 84 85 86
	VC_SampleLoad,
	VC_SampleUnload,
	VC_SampleSpace,
	VC_SampleLength,
87 88
	mikmod_mpd_init,
	mikmod_mpd_exit,
89
	nullptr,
90 91 92
	VC_SetNumVoices,
	VC_PlayStart,
	VC_PlayStop,
93
	mikmod_mpd_update,
94
	nullptr,
95 96 97 98 99 100 101 102 103 104 105 106 107
	VC_VoiceSetVolume,
	VC_VoiceGetVolume,
	VC_VoiceSetFrequency,
	VC_VoiceGetFrequency,
	VC_VoiceSetPanning,
	VC_VoiceGetPanning,
	VC_VoicePlay,
	VC_VoiceStop,
	VC_VoiceStopped,
	VC_VoiceGetPosition,
	VC_VoiceRealVolume
};

108
static bool mikmod_loop;
109 110
static unsigned mikmod_sample_rate;

111
static bool
112
mikmod_decoder_init(const config_param &param)
Avuton Olrich's avatar
Avuton Olrich committed
113
{
114 115
	static char params[] = "";

116
	mikmod_loop = param.GetBlockValue("loop", false);
117
	mikmod_sample_rate = param.GetBlockValue("sample_rate", 44100u);
118
	if (!audio_valid_sample_rate(mikmod_sample_rate))
119 120
		FormatFatalError("Invalid sample rate in line %d: %u",
				 param.line, mikmod_sample_rate);
121

122 123
	md_device = 0;
	md_reverb = 0;
124

125 126
	MikMod_RegisterDriver(&drv_mpd);
	MikMod_RegisterAllLoaders();
127 128

	md_pansep = 64;
129
	md_mixfreq = mikmod_sample_rate;
130
	md_mode = (DMODE_SOFT_MUSIC | DMODE_INTERP | DMODE_STEREO |
Avuton Olrich's avatar
Avuton Olrich committed
131
		   DMODE_16BITS);
132

133
	if (MikMod_Init(params)) {
134 135 136
		FormatError(mikmod_domain,
			    "Could not init MikMod: %s",
			    MikMod_strerror(MikMod_errno));
137
		return false;
138 139
	}

140
	return true;
141 142
}

143 144
static void
mikmod_decoder_finish(void)
Avuton Olrich's avatar
Avuton Olrich committed
145
{
146 147 148
	MikMod_Exit();
}

149
static void
150
mikmod_decoder_file_decode(Decoder &decoder, Path path_fs)
Avuton Olrich's avatar
Avuton Olrich committed
151
{
152 153
	/* deconstify the path because libmikmod wants a non-const
	   string pointer */
154
	char *const path2 = const_cast<char *>(path_fs.c_str());
155

156
	MODULE *handle;
157
	int ret;
158
	SBYTE buffer[MIKMOD_FRAME_SIZE];
159

160
	handle = Player_Load(path2, 128, 0);
161

162
	if (handle == nullptr) {
163
		FormatError(mikmod_domain,
164
			    "failed to open mod: %s", path_fs.c_str());
165
		return;
166
	}
Avuton Olrich's avatar
Avuton Olrich committed
167

168
	handle->loop = mikmod_loop;
169

170 171
	const AudioFormat audio_format(mikmod_sample_rate, SampleFormat::S16, 2);
	assert(audio_format.IsValid());
Avuton Olrich's avatar
Avuton Olrich committed
172

173
	decoder_initialized(decoder, audio_format, false, 0);
174

175
	Player_Start(handle);
176 177 178

	DecoderCommand cmd = DecoderCommand::NONE;
	while (cmd == DecoderCommand::NONE && Player_Active()) {
179
		ret = VC_WriteBytes(buffer, sizeof(buffer));
180
		cmd = decoder_data(decoder, nullptr, buffer, ret, 0);
181 182
	}

183
	Player_Stop();
184
	Player_Free(handle);
185 186
}

187
static bool
188
mikmod_decoder_scan_file(Path path_fs,
189
			 const struct tag_handler *handler, void *handler_ctx)
Avuton Olrich's avatar
Avuton Olrich committed
190
{
191 192
	/* deconstify the path because libmikmod wants a non-const
	   string pointer */
193
	char *const path2 = const_cast<char *>(path_fs.c_str());
194

195
	MODULE *handle = Player_Load(path2, 128, 0);
196

197
	if (handle == nullptr) {
198
		FormatDebug(mikmod_domain,
199
			    "Failed to open file: %s", path_fs.c_str());
200
		return false;
201
	}
202

203
	Player_Free(handle);
204

205
	char *title = Player_LoadTitle(path2);
206
	if (title != nullptr) {
207 208
		tag_handler_invoke_tag(handler, handler_ctx,
				       TAG_TITLE, title);
209 210 211
#if (LIBMIKMOD_VERSION >= 0x030200)
		MikMod_free(title);
#else
212
		free(title);
213
#endif
214
	}
215

216
	return true;
217 218
}

219
static const char *const mikmod_decoder_suffixes[] = {
220
	"amf",
Avuton Olrich's avatar
Avuton Olrich committed
221 222 223 224 225 226 227 228 229 230 231 232 233 234
	"dsm",
	"far",
	"gdm",
	"imf",
	"it",
	"med",
	"mod",
	"mtm",
	"s3m",
	"stm",
	"stx",
	"ult",
	"uni",
	"xm",
235
	nullptr
Avuton Olrich's avatar
Avuton Olrich committed
236 237
};

238
const struct DecoderPlugin mikmod_decoder_plugin = {
239 240 241 242 243 244 245 246 247 248
	"mikmod",
	mikmod_decoder_init,
	mikmod_decoder_finish,
	nullptr,
	mikmod_decoder_file_decode,
	mikmod_decoder_scan_file,
	nullptr,
	nullptr,
	mikmod_decoder_suffixes,
	nullptr,
249
};