Handle streams separately in tree_add_track()
[cmus.git] / mpc.c
blob68df486a363124980de0b0ca6496ce99e06e1026
1 /*
2 * Copyright 2006 Chun-Yu Shei <cshei AT cs.indiana.edu>
4 * Cleaned up by Timo Hirvonen <tihirvon@gmail.com>
5 */
7 #include "ip.h"
8 #include "ape.h"
9 #include "comment.h"
10 #include "file.h"
11 #include "xmalloc.h"
12 #include "read_wrapper.h"
14 #include <mpcdec/mpcdec.h>
15 #include <inttypes.h>
16 #include <errno.h>
18 struct mpc_private {
19 mpc_decoder decoder;
20 mpc_reader reader;
21 mpc_streaminfo info;
23 off_t file_size;
25 int samples_pos;
26 int samples_avail;
28 /* mpcdec/mpcdec.h
30 * the api doc says this is pcm samples per mpc frame
31 * but it's really pcm _frames_ per mpc frame
32 * MPC_FRAME_LENGTH = 36 * 32 (1152)
34 * this is wrong, it should be 2 * MPC_FRAME_LENGTH (2304)
35 * MPC_DECODER_BUFFER_LENGTH = 4 * MPC_FRAME_LENGTH (4608)
37 * use MPC_DECODER_BUFFER_LENGTH just to be sure it works
39 MPC_SAMPLE_FORMAT samples[MPC_DECODER_BUFFER_LENGTH];
42 /* callbacks */
43 static mpc_int32_t read_impl(void *data, void *ptr, mpc_int32_t size)
45 struct input_plugin_data *ip_data = data;
46 int rc;
48 rc = read_wrapper(ip_data, ptr, size);
49 if (rc == -1)
50 return -1;
51 if (rc == 0) {
52 errno = 0;
53 return 0;
55 return rc;
58 static mpc_bool_t seek_impl(void *data, mpc_int32_t offset)
60 struct input_plugin_data *ip_data = data;
61 int rc;
63 rc = lseek(ip_data->fd, offset, SEEK_SET);
64 if (rc == -1)
65 return FALSE;
66 return TRUE;
69 static mpc_int32_t tell_impl(void *data)
71 struct input_plugin_data *ip_data = data;
73 return lseek(ip_data->fd, 0, SEEK_CUR);
76 static mpc_int32_t get_size_impl(void *data)
78 struct input_plugin_data *ip_data = data;
79 struct mpc_private *priv = ip_data->private;
81 return priv->file_size;
84 static mpc_bool_t canseek_impl(void *data)
86 struct input_plugin_data *ip_data = data;
88 return !ip_data->remote;
91 static int mpc_open(struct input_plugin_data *ip_data)
93 struct mpc_private *priv;
95 priv = xnew0(struct mpc_private, 1);
97 priv->file_size = -1;
98 if (!ip_data->remote) {
99 priv->file_size = lseek(ip_data->fd, 0, SEEK_END);
100 lseek(ip_data->fd, 0, SEEK_SET);
103 /* set up an mpc_reader linked to our function implementations */
104 priv->reader.read = read_impl;
105 priv->reader.seek = seek_impl;
106 priv->reader.tell = tell_impl;
107 priv->reader.get_size = get_size_impl;
108 priv->reader.canseek = canseek_impl;
109 priv->reader.data = ip_data;
111 /* must be before mpc_streaminfo_read() */
112 ip_data->private = priv;
114 /* read file's streaminfo data */
115 mpc_streaminfo_init(&priv->info);
116 if (mpc_streaminfo_read(&priv->info, &priv->reader) != ERROR_CODE_OK) {
117 free(priv);
118 return -IP_ERROR_FILE_FORMAT;
121 /* instantiate a decoder with our file reader */
122 mpc_decoder_setup(&priv->decoder, &priv->reader);
123 if (!mpc_decoder_initialize(&priv->decoder, &priv->info)) {
124 free(priv);
125 return -IP_ERROR_FILE_FORMAT;
128 priv->samples_avail = 0;
129 priv->samples_pos = 0;
131 ip_data->sf = sf_rate(priv->info.sample_freq) | sf_channels(priv->info.channels) |
132 sf_bits(16) | sf_signed(1);
133 return 0;
136 static int mpc_close(struct input_plugin_data *ip_data)
138 struct mpc_private *priv = ip_data->private;
140 free(priv);
141 ip_data->private = NULL;
142 return 0;
145 static int scale(struct input_plugin_data *ip_data, char *buffer, int count)
147 struct mpc_private *priv = ip_data->private;
148 const MPC_SAMPLE_FORMAT *samples;
149 const int clip_min = -1 << (16 - 1);
150 const int clip_max = (1 << (16 - 1)) - 1;
151 const int float_scale = 1 << (16 - 1);
152 int i, sample_count;
154 /* number of bytes to 16-bit samples */
155 sample_count = count / 2;
156 if (sample_count > priv->samples_avail)
157 sample_count = priv->samples_avail;
159 /* scale 32-bit samples to 16-bit */
160 samples = priv->samples + priv->samples_pos;
161 for (i = 0; i < sample_count; i++) {
162 int val;
164 val = samples[i] * float_scale;
165 if (val < clip_min) {
166 val = clip_min;
167 } else if (val > clip_max) {
168 val = clip_max;
171 buffer[i * 2 + 0] = val & 0xff;
172 buffer[i * 2 + 1] = val >> 8;
175 priv->samples_pos += sample_count;
176 priv->samples_avail -= sample_count;
177 if (priv->samples_avail == 0)
178 priv->samples_pos = 0;
180 /* number of 16-bit samples to bytes */
181 return sample_count * 2;
184 static int mpc_read(struct input_plugin_data *ip_data, char *buffer, int count)
186 struct mpc_private *priv = ip_data->private;
188 if (priv->samples_avail == 0) {
189 uint32_t status = mpc_decoder_decode(&priv->decoder, priv->samples, NULL, NULL);
191 if (status == (uint32_t)(-1)) {
192 /* right ret val? */
193 return -IP_ERROR_ERRNO;
195 if (status == 0) {
196 /* EOF */
197 return 0;
200 /* status seems to be number of _frames_
201 * the api documentation is wrong
203 priv->samples_avail = status * priv->info.channels;
205 return scale(ip_data, buffer, count);
208 static int mpc_seek(struct input_plugin_data *ip_data, double offset)
210 struct mpc_private *priv = ip_data->private;
212 priv->samples_pos = 0;
213 priv->samples_avail = 0;
215 if (mpc_decoder_seek_seconds(&priv->decoder, offset))
216 return 0;
217 return -1;
220 static const char *gain_to_str(int gain)
222 static char buf[16];
223 int b, a = gain / 100;
225 if (gain < 0) {
226 b = -gain % 100;
227 } else {
228 b = gain % 100;
230 sprintf(buf, "%d.%02d", a, b);
231 return buf;
234 static const char *peak_to_str(unsigned int peak)
236 static char buf[16];
237 sprintf(buf, "%d.%05d", peak / 32767, peak % 32767);
238 return buf;
241 static int mpc_read_comments(struct input_plugin_data *ip_data, struct keyval **comments)
243 struct mpc_private *priv = ip_data->private;
244 GROWING_KEYVALS(c);
245 int count, i;
246 APETAG(ape);
248 count = ape_read_tags(&ape, ip_data->fd, 1);
249 if (count < 0)
250 goto out;
252 for (i = 0; i < count; i++) {
253 char *k, *v;
254 k = ape_get_comment(&ape, &v);
255 if (!k)
256 break;
257 comments_add(&c, k, v);
258 free(k);
261 out:
262 if (priv->info.gain_title && priv->info.peak_title) {
263 comments_add_const(&c, "replaygain_track_gain", gain_to_str(priv->info.gain_title));
264 comments_add_const(&c, "replaygain_track_peak", peak_to_str(priv->info.peak_title));
266 if (priv->info.gain_album && priv->info.peak_album) {
267 comments_add_const(&c, "replaygain_album_gain", gain_to_str(priv->info.gain_album));
268 comments_add_const(&c, "replaygain_album_peak", peak_to_str(priv->info.peak_album));
270 keyvals_terminate(&c);
272 *comments = c.keyvals;
273 ape_free(&ape);
274 return 0;
277 static int mpc_duration(struct input_plugin_data *ip_data)
279 struct mpc_private *priv = ip_data->private;
281 /* priv->info.pcm_samples seems to be number of frames
282 * priv->info.frames is _not_ pcm frames
284 return priv->info.pcm_samples / priv->info.sample_freq;
287 const struct input_plugin_ops ip_ops = {
288 .open = mpc_open,
289 .close = mpc_close,
290 .read = mpc_read,
291 .seek = mpc_seek,
292 .read_comments = mpc_read_comments,
293 .duration = mpc_duration
296 const char *const ip_extensions[] = { "mpc", "mpp", "mp+", NULL };
297 const char *const ip_mime_types[] = { "audio/x-musepack", NULL };