Initial revision 6759
[qball-mpd.git] / src / inputPlugins / mpc_plugin.c
blob885f6cfc933f211994f7f1afe67860211a310e3a
1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "../inputPlugin.h"
21 #ifdef HAVE_MPCDEC
23 #include "../utils.h"
24 #include "../audio.h"
25 #include "../log.h"
26 #include "../pcm_utils.h"
27 #include "../inputStream.h"
28 #include "../outputBuffer.h"
29 #include "../replayGain.h"
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <mpcdec/mpcdec.h>
36 #include <errno.h>
37 #include <math.h>
39 typedef struct _MpcCallbackData {
40 InputStream *inStream;
41 DecoderControl *dc;
42 } MpcCallbackData;
44 static mpc_int32_t mpc_read_cb(void *vdata, void *ptr, mpc_int32_t size)
46 mpc_int32_t ret = 0;
47 MpcCallbackData *data = (MpcCallbackData *) vdata;
49 while (1) {
50 ret = readFromInputStream(data->inStream, ptr, 1, size);
51 if (ret == 0 && !inputStreamAtEOF(data->inStream) &&
52 (data->dc && !data->dc->stop)) {
53 my_usleep(10000);
54 } else
55 break;
58 return ret;
61 static mpc_bool_t mpc_seek_cb(void *vdata, mpc_int32_t offset)
63 MpcCallbackData *data = (MpcCallbackData *) vdata;
65 return seekInputStream(data->inStream, offset, SEEK_SET) < 0 ? 0 : 1;
68 static mpc_int32_t mpc_tell_cb(void *vdata)
70 MpcCallbackData *data = (MpcCallbackData *) vdata;
72 return (long)(data->inStream->offset);
75 static mpc_bool_t mpc_canseek_cb(void *vdata)
77 MpcCallbackData *data = (MpcCallbackData *) vdata;
79 return data->inStream->seekable;
82 static mpc_int32_t mpc_getsize_cb(void *vdata)
84 MpcCallbackData *data = (MpcCallbackData *) vdata;
86 return data->inStream->size;
89 /* this _looks_ performance-critical, don't de-inline -- eric */
90 static inline mpd_sint16 convertSample(MPC_SAMPLE_FORMAT sample)
92 /* only doing 16-bit audio for now */
93 mpd_sint32 val;
95 const int clip_min = -1 << (16 - 1);
96 const int clip_max = (1 << (16 - 1)) - 1;
98 #ifdef MPC_FIXED_POINT
99 const int shift = 16 - MPC_FIXED_POINT_SCALE_SHIFT;
101 if (sample > 0) {
102 sample <<= shift;
103 } else if (shift < 0) {
104 sample >>= -shift;
106 val = sample;
107 #else
108 const int float_scale = 1 << (16 - 1);
110 val = sample * float_scale;
111 #endif
113 if (val < clip_min)
114 val = clip_min;
115 else if (val > clip_max)
116 val = clip_max;
118 return val;
121 static int mpc_decode(OutputBuffer * cb, DecoderControl * dc,
122 InputStream * inStream)
124 mpc_decoder decoder;
125 mpc_reader reader;
126 mpc_streaminfo info;
128 MpcCallbackData data;
130 MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH];
132 int eof = 0;
133 long ret;
134 #define MPC_CHUNK_SIZE 4096
135 char chunk[MPC_CHUNK_SIZE];
136 int chunkpos = 0;
137 long bitRate = 0;
138 mpd_sint16 *s16 = (mpd_sint16 *) chunk;
139 unsigned long samplePos = 0;
140 mpc_uint32_t vbrUpdateAcc;
141 mpc_uint32_t vbrUpdateBits;
142 float time;
143 int i;
144 ReplayGainInfo *replayGainInfo = NULL;
146 data.inStream = inStream;
147 data.dc = dc;
149 reader.read = mpc_read_cb;
150 reader.seek = mpc_seek_cb;
151 reader.tell = mpc_tell_cb;
152 reader.get_size = mpc_getsize_cb;
153 reader.canseek = mpc_canseek_cb;
154 reader.data = &data;
156 mpc_streaminfo_init(&info);
158 if ((ret = mpc_streaminfo_read(&info, &reader)) != ERROR_CODE_OK) {
159 closeInputStream(inStream);
160 if (!dc->stop) {
161 ERROR("Not a valid musepack stream");
162 return -1;
163 } else {
164 dc->state = DECODE_STATE_STOP;
165 dc->stop = 0;
167 return 0;
170 mpc_decoder_setup(&decoder, &reader);
172 if (!mpc_decoder_initialize(&decoder, &info)) {
173 closeInputStream(inStream);
174 if (!dc->stop) {
175 ERROR("Not a valid musepack stream");
176 } else {
177 dc->state = DECODE_STATE_STOP;
178 dc->stop = 0;
182 dc->totalTime = mpc_streaminfo_get_length(&info);
184 dc->audioFormat.bits = 16;
185 dc->audioFormat.channels = info.channels;
186 dc->audioFormat.sampleRate = info.sample_freq;
188 getOutputAudioFormat(&(dc->audioFormat), &(cb->audioFormat));
190 replayGainInfo = newReplayGainInfo();
191 replayGainInfo->albumGain = info.gain_album * 0.01;
192 replayGainInfo->albumPeak = info.peak_album / 32767.0;
193 replayGainInfo->trackGain = info.gain_title * 0.01;
194 replayGainInfo->trackPeak = info.peak_title / 32767.0;
196 dc->state = DECODE_STATE_DECODE;
198 while (!eof) {
199 if (dc->seek) {
200 samplePos = dc->seekWhere * dc->audioFormat.sampleRate;
201 if (mpc_decoder_seek_sample(&decoder, samplePos)) {
202 clearOutputBuffer(cb);
203 s16 = (mpd_sint16 *) chunk;
204 chunkpos = 0;
205 } else
206 dc->seekError = 1;
207 dc->seek = 0;
210 vbrUpdateAcc = 0;
211 vbrUpdateBits = 0;
212 ret = mpc_decoder_decode(&decoder, sample_buffer,
213 &vbrUpdateAcc, &vbrUpdateBits);
215 if (ret <= 0 || dc->stop) {
216 eof = 1;
217 break;
220 samplePos += ret;
222 /* ret is in samples, and we have stereo */
223 ret *= 2;
225 for (i = 0; i < ret; i++) {
226 /* 16 bit audio again */
227 *s16 = convertSample(sample_buffer[i]);
228 chunkpos += 2;
229 s16++;
231 if (chunkpos >= MPC_CHUNK_SIZE) {
232 time = ((float)samplePos) /
233 dc->audioFormat.sampleRate;
235 bitRate = vbrUpdateBits *
236 dc->audioFormat.sampleRate / 1152 / 1000;
238 sendDataToOutputBuffer(cb, inStream, dc,
239 inStream->seekable,
240 chunk, chunkpos,
241 time,
242 bitRate, replayGainInfo);
244 chunkpos = 0;
245 s16 = (mpd_sint16 *) chunk;
246 if (dc->stop) {
247 eof = 1;
248 break;
254 if (!dc->stop && chunkpos > 0) {
255 time = ((float)samplePos) / dc->audioFormat.sampleRate;
257 bitRate =
258 vbrUpdateBits * dc->audioFormat.sampleRate / 1152 / 1000;
260 sendDataToOutputBuffer(cb, NULL, dc, inStream->seekable,
261 chunk, chunkpos, time, bitRate,
262 replayGainInfo);
265 closeInputStream(inStream);
267 flushOutputBuffer(cb);
269 freeReplayGainInfo(replayGainInfo);
271 if (dc->stop) {
272 dc->state = DECODE_STATE_STOP;
273 dc->stop = 0;
274 } else {
275 dc->state = DECODE_STATE_STOP;
278 return 0;
281 static float mpcGetTime(char *file)
283 InputStream inStream;
284 float time = -1;
286 mpc_reader reader;
287 mpc_streaminfo info;
288 MpcCallbackData data;
290 data.inStream = &inStream;
291 data.dc = NULL;
293 reader.read = mpc_read_cb;
294 reader.seek = mpc_seek_cb;
295 reader.tell = mpc_tell_cb;
296 reader.get_size = mpc_getsize_cb;
297 reader.canseek = mpc_canseek_cb;
298 reader.data = &data;
300 mpc_streaminfo_init(&info);
302 if (openInputStream(&inStream, file) < 0) {
303 DEBUG("mpcGetTime: Failed to open file: %s\n", file);
304 return -1;
307 if (mpc_streaminfo_read(&info, &reader) != ERROR_CODE_OK) {
308 closeInputStream(&inStream);
309 return -1;
312 time = mpc_streaminfo_get_length(&info);
314 closeInputStream(&inStream);
316 return time;
319 static MpdTag *mpcTagDup(char *file)
321 MpdTag *ret = NULL;
322 float time = mpcGetTime(file);
324 if (time < 0) {
325 DEBUG("mpcTagDup: Failed to get Songlength of file: %s\n",
326 file);
327 return NULL;
330 ret = apeDup(file);
331 if (!ret)
332 ret = id3Dup(file);
333 if (!ret)
334 ret = newMpdTag();
335 ret->time = time;
337 return ret;
340 static char *mpcSuffixes[] = { "mpc", NULL };
342 InputPlugin mpcPlugin = {
343 "mpc",
344 NULL,
345 NULL,
346 NULL,
347 mpc_decode,
348 NULL,
349 mpcTagDup,
350 INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE,
351 mpcSuffixes,
352 NULL
355 #else
357 InputPlugin mpcPlugin;
359 #endif /* HAVE_MPCDEC */