mikmod_plugin.c 4.86 KB
Newer Older
1
/* the Music Player Daemon (MPD)
2
 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * This project's homepage is: 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

19
#include "../decoder_api.h"
20

21
#include <glib.h>
22 23
#include <mikmod.h>

24 25 26
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "mikmod"

27 28 29 30
/* this is largely copied from alsaplayer */

#define MIKMOD_FRAME_SIZE	4096

Avuton Olrich's avatar
Avuton Olrich committed
31 32
static BOOL mod_mpd_Init(void)
{
33 34 35
	return VC_Init();
}

Avuton Olrich's avatar
Avuton Olrich committed
36 37
static void mod_mpd_Exit(void)
{
38 39 40
	VC_Exit();
}

Avuton Olrich's avatar
Avuton Olrich committed
41 42
static void mod_mpd_Update(void)
{
43 44
}

Avuton Olrich's avatar
Avuton Olrich committed
45 46
static BOOL mod_mpd_IsThere(void)
{
47 48 49
	return 1;
}

50 51 52 53 54 55 56
static char drv_name[] = "MPD";
static char drv_version[] = "MPD Output Driver v0.1";

#if (LIBMIKMOD_VERSION > 0x030106)
static char drv_alias[] = "mpd";
#endif

Avuton Olrich's avatar
Avuton Olrich committed
57
static MDRIVER drv_mpd = {
58
	NULL,
59 60
	drv_name,
	drv_version,
61 62
	0,
	255,
63
#if (LIBMIKMOD_VERSION > 0x030106)
64
	drv_alias,
65
#if (LIBMIKMOD_VERSION >= 0x030200)
66 67 68 69
	NULL,  /* CmdLineHelp */
#endif
	NULL,  /* CommandLine */
#endif
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
	mod_mpd_IsThere,
	VC_SampleLoad,
	VC_SampleUnload,
	VC_SampleSpace,
	VC_SampleLength,
	mod_mpd_Init,
	mod_mpd_Exit,
	NULL,
	VC_SetNumVoices,
	VC_PlayStart,
	VC_PlayStop,
	mod_mpd_Update,
	NULL,
	VC_VoiceSetVolume,
	VC_VoiceGetVolume,
	VC_VoiceSetFrequency,
	VC_VoiceGetFrequency,
	VC_VoiceSetPanning,
	VC_VoiceGetPanning,
	VC_VoicePlay,
	VC_VoiceStop,
	VC_VoiceStopped,
	VC_VoiceGetPosition,
	VC_VoiceRealVolume
};

96 97
static bool
mod_initMikMod(G_GNUC_UNUSED const struct config_param *param)
Avuton Olrich's avatar
Avuton Olrich committed
98
{
99 100
	static char params[] = "";

101 102
	md_device = 0;
	md_reverb = 0;
103

104 105
	MikMod_RegisterDriver(&drv_mpd);
	MikMod_RegisterAllLoaders();
106 107 108 109

	md_pansep = 64;
	md_mixfreq = 44100;
	md_mode = (DMODE_SOFT_MUSIC | DMODE_INTERP | DMODE_STEREO |
Avuton Olrich's avatar
Avuton Olrich committed
110
		   DMODE_16BITS);
111

112
	if (MikMod_Init(params)) {
113 114
		g_warning("Could not init MikMod: %s\n",
			  MikMod_strerror(MikMod_errno));
115
		return false;
116 117
	}

118
	return true;
119 120
}

Avuton Olrich's avatar
Avuton Olrich committed
121 122
static void mod_finishMikMod(void)
{
123 124 125 126
	MikMod_Exit();
}

typedef struct _mod_Data {
Avuton Olrich's avatar
Avuton Olrich committed
127
	MODULE *moduleHandle;
128
	SBYTE audio_buffer[MIKMOD_FRAME_SIZE];
Avuton Olrich's avatar
Avuton Olrich committed
129
} mod_Data;
130

131
static mod_Data *mod_open(const char *path)
Avuton Olrich's avatar
Avuton Olrich committed
132
{
133
	char *path2;
Avuton Olrich's avatar
Avuton Olrich committed
134 135
	MODULE *moduleHandle;
	mod_Data *data;
136

137 138 139 140 141
	path2 = g_strdup(path);
	moduleHandle = Player_Load(path2, 128, 0);
	g_free(path2);

	if (moduleHandle == NULL)
Avuton Olrich's avatar
Avuton Olrich committed
142
		return NULL;
143

144 145 146
	/* Prevent module from looping forever */
	moduleHandle->loop = 0;

147
	data = g_new(mod_Data, 1);
148 149 150 151 152 153 154
	data->moduleHandle = moduleHandle;

	Player_Start(data->moduleHandle);

	return data;
}

Avuton Olrich's avatar
Avuton Olrich committed
155 156
static void mod_close(mod_Data * data)
{
157 158
	Player_Stop();
	Player_Free(data->moduleHandle);
159
	g_free(data);
160 161
}

162
static void
163
mod_decode(struct decoder *decoder, const char *path)
Avuton Olrich's avatar
Avuton Olrich committed
164 165
{
	mod_Data *data;
166
	struct audio_format audio_format;
Max Kellermann's avatar
Max Kellermann committed
167
	float total_time = 0.0;
168 169
	int ret;
	float secPerByte;
170
	enum decoder_command cmd = DECODE_COMMAND_NONE;
171

Avuton Olrich's avatar
Avuton Olrich committed
172
	if (!(data = mod_open(path))) {
173
		g_warning("failed to open mod: %s\n", path);
174
		return;
175
	}
Avuton Olrich's avatar
Avuton Olrich committed
176

177
	audio_format.bits = 16;
178
	audio_format.sample_rate = 44100;
179
	audio_format.channels = 2;
Avuton Olrich's avatar
Avuton Olrich committed
180 181

	secPerByte =
182
	    1.0 / ((audio_format.bits * audio_format.channels / 8.0) *
183
		   (float)audio_format.sample_rate);
184

185
	decoder_initialized(decoder, &audio_format, false, 0);
186

187
	while (cmd == DECODE_COMMAND_NONE && Player_Active()) {
188
		ret = VC_WriteBytes(data->audio_buffer, MIKMOD_FRAME_SIZE);
Max Kellermann's avatar
Max Kellermann committed
189
		total_time += ret * secPerByte;
190 191 192
		cmd = decoder_data(decoder, NULL,
				   data->audio_buffer, ret,
				   total_time, 0, NULL);
193 194 195 196 197
	}

	mod_close(data);
}

198
static struct tag *modTagDup(const char *file)
Avuton Olrich's avatar
Avuton Olrich committed
199
{
200
	char *path2;
201
	struct tag *ret = NULL;
Avuton Olrich's avatar
Avuton Olrich committed
202 203
	MODULE *moduleHandle;
	char *title;
204

205 206 207 208 209
	path2 = g_strdup(file);
	moduleHandle = Player_Load(path2, 128, 0);
	g_free(path2);

	if (moduleHandle == NULL) {
210
		g_debug("Failed to open file: %s", file);
211
		return NULL;
212

213
	}
214 215
	Player_Free(moduleHandle);

216
	ret = tag_new();
217 218

	ret->time = 0;
219 220

	path2 = g_strdup(file);
221
	title = g_strdup(Player_LoadTitle(path2));
222
	g_free(path2);
Avuton Olrich's avatar
Avuton Olrich committed
223
	if (title)
224
		tag_add_item(ret, TAG_ITEM_TITLE, title);
225 226 227 228

	return ret;
}

229 230
static const char *const modSuffixes[] = {
	"amf",
Avuton Olrich's avatar
Avuton Olrich committed
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	"dsm",
	"far",
	"gdm",
	"imf",
	"it",
	"med",
	"mod",
	"mtm",
	"s3m",
	"stm",
	"stx",
	"ult",
	"uni",
	"xm",
	NULL
};

248 249
const struct decoder_plugin mikmod_decoder_plugin = {
	.name = "mikmod",
250
	.init = mod_initMikMod,
251 252 253 254
	.finish = mod_finishMikMod,
	.file_decode = mod_decode,
	.tag_dup = modTagDup,
	.suffixes = modSuffixes,
255
};