1
/*
2
 * Copyright 2008-2011 Various Authors
3
 * Copyright 2005 Timo Hirvonen
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License as
7
 * published by the Free Software Foundation; either version 2 of the
8
 * License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful, but
11
 * WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17
 */
18
19
#include "ip.h"
20
#include "file.h"
21
#include "xmalloc.h"
22
#include "comment.h"
23
#ifdef HAVE_CONFIG
24
#include "config/modplug.h"
25
#endif
26
27
#include <modplug.h>
28
#include <sys/types.h>
29
#include <unistd.h>
30
#include <stdlib.h>
31
#include <errno.h>
32
33
struct mod_private {
34
	ModPlugFile *file;
35
};
36
37
static int mod_open(struct input_plugin_data *ip_data)
38
{
39
	struct mod_private *priv;
40
	char *contents;
41
	off_t size;
42
	ssize_t rc;
43
	ModPlugFile *file;
44
	ModPlug_Settings settings;
45
46
	size = lseek(ip_data->fd, 0, SEEK_END);
47
	if (size == -1)
48
		return -IP_ERROR_ERRNO;
49
	if (lseek(ip_data->fd, 0, SEEK_SET) == -1)
50
		return -IP_ERROR_ERRNO;
51
52
	contents = xnew(char, size);
53
	rc = read_all(ip_data->fd, contents, size);
54
	if (rc == -1) {
55
		int save = errno;
56
57
		free(contents);
58
		errno = save;
59
		return -IP_ERROR_ERRNO;
60
	}
61
	if (rc != size) {
62
		free(contents);
63
		return -IP_ERROR_FILE_FORMAT;
64
	}
65
	errno = 0;
66
	file = ModPlug_Load(contents, size);
67
	if (file == NULL) {
68
		int save = errno;
69
70
		free(contents);
71
		errno = save;
72
		if (errno == 0) {
73
			/* libmodplug never sets errno? */
74
			return -IP_ERROR_FILE_FORMAT;
75
		}
76
		return -IP_ERROR_ERRNO;
77
	}
78
	free(contents);
79
80
	priv = xnew(struct mod_private, 1);
81
	priv->file = file;
82
83
	ModPlug_GetSettings(&settings);
84
	settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING | MODPLUG_ENABLE_NOISE_REDUCTION;
85
/* 	settings.mFlags |= MODPLUG_ENABLE_REVERB; */
86
/* 	settings.mFlags |= MODPLUG_ENABLE_MEGABASS; */
87
/* 	settings.mFlags |= MODPLUG_ENABLE_SURROUND; */
88
	settings.mChannels = 2;
89
	settings.mBits = 16;
90
	settings.mFrequency = 44100;
91
	settings.mResamplingMode = MODPLUG_RESAMPLE_FIR;
92
	ModPlug_SetSettings(&settings);
93
94
	ip_data->private = priv;
95
	ip_data->sf = sf_bits(16) | sf_rate(44100) | sf_channels(2) | sf_signed(1);
96
#ifdef WORDS_BIGENDIAN
97
	ip_data->sf |= sf_bigendian(1);
98
#endif
99
	channel_map_init_stereo(ip_data->channel_map);
100
	return 0;
101
}
102
103
static int mod_close(struct input_plugin_data *ip_data)
104
{
105
	struct mod_private *priv = ip_data->private;
106
107
	ModPlug_Unload(priv->file);
108
	free(priv);
109
	ip_data->private = NULL;
110
	return 0;
111
}
112
113
static int mod_read(struct input_plugin_data *ip_data, char *buffer, int count)
114
{
115
	struct mod_private *priv = ip_data->private;
116
	int rc;
117
	
118
	errno = 0;
119
	rc = ModPlug_Read(priv->file, buffer, count);
120
	if (rc < 0) {
121
		if (errno == 0)
122
			return -IP_ERROR_INTERNAL;
123
		return -IP_ERROR_ERRNO;
124
	}
125
	return rc;
126
}
127
128
static int mod_seek(struct input_plugin_data *ip_data, double offset)
129
{
130
	struct mod_private *priv = ip_data->private;
131
	int ms = (int)(offset * 1000.0 + 0.5);
132
133
	/* void */
134
	ModPlug_Seek(priv->file, ms);
135
	return 0;
136
}
137
138
static int mod_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
139
{
140
	struct mod_private *priv = ip_data->private;
141
	GROWING_KEYVALS(c);
142
	const char *val;
143
144
	val = ModPlug_GetName(priv->file);
145
	if (val && val[0])
146
		comments_add_const(&c, "title", val);
147
148
#if MODPLUG_API_8
149
	val = ModPlug_GetMessage(priv->file);
150
	if (val && val[0])
151
		comments_add_const(&c, "comment", val);
152
#endif
153
154
	keyvals_terminate(&c);
155
	*comments = c.keyvals;
156
	return 0;
157
}
158
159
static int mod_duration(struct input_plugin_data *ip_data)
160
{
161
	struct mod_private *priv = ip_data->private;
162
163
	return (ModPlug_GetLength(priv->file) + 500) / 1000;
164
}
165
166
static long mod_bitrate(struct input_plugin_data *ip_data)
167
{
168
	return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
169
}
170
171
#if MODPLUG_API_8
172
static const char *mod_type_to_string(int type)
173
{
174
	/* from <libmodplug/sndfile.h>, which is C++ */
175
	switch (type) {
176
	case 0x01:	return "mod";
177
	case 0x02:	return "s3m";
178
	case 0x04:	return "xm";
179
	case 0x08:	return "med";
180
	case 0x10:	return "mtm";
181
	case 0x20:	return "it";
182
	case 0x40:	return "699";
183
	case 0x80:	return "ult";
184
	case 0x100:	return "stm";
185
	case 0x200:	return "far";
186
	case 0x800:	return "amf";
187
	case 0x1000:	return "ams";
188
	case 0x2000:	return "dsm";
189
	case 0x4000:	return "mdl";
190
	case 0x8000:	return "okt";
191
	case 0x10000:	return "midi";
192
	case 0x20000:	return "dmf";
193
	case 0x40000:	return "ptm";
194
	case 0x80000:	return "dbm";
195
	case 0x100000:	return "mt2";
196
	case 0x200000:	return "amf0";
197
	case 0x400000:	return "psm";
198
	case 0x80000000:return "umx";
199
	}
200
	return NULL;
201
}
202
#endif
203
204
static char *mod_codec(struct input_plugin_data *ip_data)
205
{
206
#if MODPLUG_API_8
207
	struct mod_private *priv = ip_data->private;
208
	const char *codec;
209
	int type;
210
211
	type = ModPlug_GetModuleType(priv->file);
212
	codec = mod_type_to_string(type);
213
214
	return codec ? xstrdup(codec) : NULL;
215
#else
216
	return NULL;
217
#endif
218
}
219
220
static char *mod_codec_profile(struct input_plugin_data *ip_data)
221
{
222
	return NULL;
223
}
224
225
const struct input_plugin_ops ip_ops = {
226
	.open = mod_open,
227
	.close = mod_close,
228
	.read = mod_read,
229
	.seek = mod_seek,
230
	.read_comments = mod_read_comments,
231
	.duration = mod_duration,
232
	.bitrate = mod_bitrate,
233
	.bitrate_current = mod_bitrate,
234
	.codec = mod_codec,
235
	.codec_profile = mod_codec_profile
236
};
237
238
const int ip_priority = 50;
239
const char * const ip_extensions[] = {
240
	"mod", "s3m", "xm", "it", "669", "amf", "ams", "dbm", "dmf", "dsm",
241
	"far", "mdl", "med", "mtm", "okt", "ptm", "stm", "ult", "umx", "mt2",
242
	"psm", NULL
243
};
244
const char * const ip_mime_types[] = { NULL };
245
const char * const ip_options[] = { NULL };