summaryrefslogtreecommitdiff
path: root/core/multimedia/opieplayer/libmad/libmadplugin.cpp
Unidiff
Diffstat (limited to 'core/multimedia/opieplayer/libmad/libmadplugin.cpp') (more/less context) (show whitespace changes)
-rw-r--r--core/multimedia/opieplayer/libmad/libmadplugin.cpp578
1 files changed, 578 insertions, 0 deletions
diff --git a/core/multimedia/opieplayer/libmad/libmadplugin.cpp b/core/multimedia/opieplayer/libmad/libmadplugin.cpp
new file mode 100644
index 0000000..b2b876f
--- a/dev/null
+++ b/core/multimedia/opieplayer/libmad/libmadplugin.cpp
@@ -0,0 +1,578 @@
1/**********************************************************************
2** Copyright (C) 2001 Trolltech AS. All rights reserved.
3**
4** This file is part of Qtopia Environment.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20#include <stdio.h>
21#include <stdarg.h>
22#include <stdlib.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <fcntl.h>
26#include <unistd.h>
27#include <string.h>
28#include <ctype.h>
29#include <errno.h>
30#include <time.h>
31#include <locale.h>
32#include <math.h>
33#include <assert.h>
34#include <qapplication.h>
35
36//#define HAVE_MMAP
37
38#if defined(HAVE_MMAP)
39# include <sys/mman.h>
40#endif
41#include "libmadplugin.h"
42
43
44extern "C" {
45#include "mad.h"
46}
47
48
49#define MPEG_BUFFER_SIZE 65536
50 //#define debugMsg(a) qDebug(a)
51#define debugMsg(a)
52
53
54class Input {
55public:
56 char const *path;
57 int fd;
58#if defined(HAVE_MMAP)
59 void *fdm;
60#endif
61 unsigned char *data;
62 unsigned long length;
63 int eof;
64};
65
66
67class Output {
68public:
69 mad_fixed_t attenuate;
70 struct filter *filters;
71 unsigned int channels_in;
72 unsigned int channels_out;
73 unsigned int speed_in;
74 unsigned int speed_out;
75 const char *path;
76};
77
78
79# if defined(HAVE_MMAP)
80static void *map_file(int fd, unsigned long *length)
81{
82 void *fdm;
83
84 *length += MAD_BUFFER_GUARD;
85
86 fdm = mmap(0, *length, PROT_READ, MAP_SHARED, fd, 0);
87 if (fdm == MAP_FAILED)
88 return 0;
89
90# if defined(HAVE_MADVISE)
91 madvise(fdm, *length, MADV_SEQUENTIAL);
92# endif
93
94 return fdm;
95}
96
97
98static int unmap_file(void *fdm, unsigned long length)
99{
100 if (munmap(fdm, length) == -1)
101 return -1;
102
103 return 0;
104}
105# endif
106
107
108static inline QString tr( const char *str ) {
109 // Apparently this is okay from a plugin as it runs in the process space of the owner of the plugin
110 return qApp->translate( "MediaPlayer", str, "libmad strings for mp3 file info" );
111}
112
113
114class LibMadPluginData {
115public:
116 Input input;
117 Output output;
118 int bad_last_frame;
119 struct mad_stream stream;
120 struct mad_frame frame;
121 struct mad_synth synth;
122 bool flush;
123};
124
125
126LibMadPlugin::LibMadPlugin() {
127 d = new LibMadPluginData;
128 d->input.fd = 0;
129#if defined(HAVE_MMAP)
130 d->input.fdm = 0;
131#endif
132 d->input.data = 0;
133 d->flush = TRUE;
134 info = tr( "No Song Open" );
135}
136
137
138LibMadPlugin::~LibMadPlugin() {
139 close();
140 delete d;
141}
142
143
144bool LibMadPlugin::isFileSupported( const QString& path ) {
145 debugMsg( "LibMadPlugin::isFileSupported" );
146
147 // Mpeg file extensions
148 //"mp2","mp3","m1v","m2v","m2s","mpg","vob","mpeg","ac3"
149 // Other media extensions
150 // "wav","mid","mod","s3m","ogg","avi","mov","sid"
151
152 char *ext = strrchr( path.latin1(), '.' );
153
154 // Test file extension
155 if ( ext ) {
156 if ( strncasecmp(ext, ".mp2", 4) == 0 )
157 return TRUE;
158 if ( strncasecmp(ext, ".mp3", 4) == 0 )
159 return TRUE;
160 }
161
162 return FALSE;
163}
164
165
166bool LibMadPlugin::open( const QString& path ) {
167 debugMsg( "LibMadPlugin::open" );
168
169 d->bad_last_frame = 0;
170 d->flush = TRUE;
171 info = QString( "" );
172
173 //qDebug( "Opening %s", path.latin1() );
174
175 d->input.path = path.latin1();
176 d->input.fd = ::open( d->input.path, O_RDONLY );
177 if (d->input.fd == -1) {
178 qDebug("error opening %s", d->input.path );
179 return FALSE;
180 }
181
182 printID3Tags();
183
184#if defined(HAVE_MMAP)
185 struct stat stat;
186 if (fstat(d->input.fd, &stat) == -1) {
187 qDebug("error calling fstat"); return FALSE;
188 }
189 if (S_ISREG(stat.st_mode) && stat.st_size > 0) {
190 d->input.length = stat.st_size;
191 d->input.fdm = map_file(d->input.fd, &d->input.length);
192 if (d->input.fdm == 0) {
193 qDebug("error mmapping file"); return FALSE;
194 }
195 d->input.data = (unsigned char *)d->input.fdm;
196 }
197#endif
198
199 if (d->input.data == 0) {
200 d->input.data = (unsigned char *)malloc(MPEG_BUFFER_SIZE);
201 if (d->input.data == 0) {
202 qDebug("error allocating input buffer");
203 return FALSE;
204 }
205 d->input.length = 0;
206 }
207
208 d->input.eof = 0;
209
210 mad_stream_init(&d->stream);
211 mad_frame_init(&d->frame);
212 mad_synth_init(&d->synth);
213
214 return TRUE;
215}
216
217
218bool LibMadPlugin::close() {
219 debugMsg( "LibMadPlugin::close" );
220
221 int result = TRUE;
222
223 mad_synth_finish(&d->synth);
224 mad_frame_finish(&d->frame);
225 mad_stream_finish(&d->stream);
226
227#if defined(HAVE_MMAP)
228 if (d->input.fdm) {
229 if (unmap_file(d->input.fdm, d->input.length) == -1) {
230 qDebug("error munmapping file");
231 result = FALSE;
232 }
233 d->input.fdm = 0;
234 d->input.data = 0;
235 }
236#endif
237
238 if (d->input.data) {
239 free(d->input.data);
240 d->input.data = 0;
241 }
242
243 if (::close(d->input.fd) == -1) {
244 qDebug("error closing file %s", d->input.path);
245 result = FALSE;
246 }
247
248 d->input.fd = 0;
249
250 return result;
251}
252
253
254bool LibMadPlugin::isOpen() {
255 debugMsg( "LibMadPlugin::isOpen" );
256 return ( d->input.fd != 0 );
257}
258
259
260int LibMadPlugin::audioStreams() {
261 debugMsg( "LibMadPlugin::audioStreams" );
262 return 1;
263}
264
265
266int LibMadPlugin::audioChannels( int ) {
267 debugMsg( "LibMadPlugin::audioChannels" );
268/*
269 long t; short t1[5]; audioReadSamples( t1, 2, 1, t, 0 );
270 qDebug( "LibMadPlugin::audioChannels: %i", d->frame.header.mode > 0 ? 2 : 1 );
271 return d->frame.header.mode > 0 ? 2 : 1;
272*/
273 return 2;
274}
275
276
277int LibMadPlugin::audioFrequency( int ) {
278 debugMsg( "LibMadPlugin::audioFrequency" );
279 long t; short t1[5]; audioReadSamples( t1, 2, 1, t, 0 );
280 qDebug( "LibMadPlugin::audioFrequency: %i", d->frame.header.samplerate );
281 return d->frame.header.samplerate;
282}
283
284
285int LibMadPlugin::audioSamples( int ) {
286 debugMsg( "LibMadPlugin::audioSamples" );
287/*
288 long t; short t1[5]; audioReadSamples( t1, 2, 1, t, 0 );
289 mad_header_decode( (struct mad_header *)&d->frame.header, &d->stream );
290 qDebug( "LibMadPlugin::audioSamples: %i*%i", d->frame.header.duration.seconds, d->frame.header.samplerate );
291 return d->frame.header.duration.seconds * d->frame.header.samplerate;
292*/
293 return 10000000;
294}
295
296
297bool LibMadPlugin::audioSetSample( long, int ) {
298 debugMsg( "LibMadPlugin::audioSetSample" );
299 return FALSE;
300}
301
302
303long LibMadPlugin::audioGetSample( int ) {
304 debugMsg( "LibMadPlugin::audioGetSample" );
305 return 0;
306}
307
308/*
309bool LibMadPlugin::audioReadSamples( short *, int, long, int ) {
310 debugMsg( "LibMadPlugin::audioReadSamples" );
311 return FALSE;
312}
313
314
315bool LibMadPlugin::audioReReadSamples( short *, int, long, int ) {
316 debugMsg( "LibMadPlugin::audioReReadSamples" );
317 return FALSE;
318}
319*/
320
321bool LibMadPlugin::read() {
322 debugMsg( "LibMadPlugin::read" );
323 int len;
324
325 if (d->input.eof)
326 return FALSE;
327
328#if defined(HAVE_MMAP)
329 if (d->input.fdm) {
330 unsigned long skip = 0;
331
332 if (d->stream.next_frame) {
333 struct stat stat;
334
335 if (fstat(d->input.fd, &stat) == -1)
336 return FALSE;
337
338 if (stat.st_size + MAD_BUFFER_GUARD <= (signed)d->input.length)
339 return FALSE;
340
341 // file size changed; update memory map
342 skip = d->stream.next_frame - d->input.data;
343
344 if (unmap_file(d->input.fdm, d->input.length) == -1) {
345 d->input.fdm = 0;
346 d->input.data = 0;
347 return FALSE;
348 }
349
350 d->input.length = stat.st_size;
351
352 d->input.fdm = map_file(d->input.fd, &d->input.length);
353 if (d->input.fdm == 0) {
354 d->input.data = 0;
355 return FALSE;
356 }
357
358 d->input.data = (unsigned char *)d->input.fdm;
359 }
360
361 mad_stream_buffer(&d->stream, d->input.data + skip, d->input.length - skip);
362
363 } else
364#endif
365 {
366 if (d->stream.next_frame) {
367 memmove(d->input.data, d->stream.next_frame,
368 d->input.length = &d->input.data[d->input.length] - d->stream.next_frame);
369 }
370
371 do {
372 len = ::read(d->input.fd, d->input.data + d->input.length, MPEG_BUFFER_SIZE - d->input.length);
373 }
374 while (len == -1 && errno == EINTR);
375
376 if (len == -1) {
377 qDebug("error reading audio");
378 return FALSE;
379 }
380 else if (len == 0) {
381 d->input.eof = 1;
382
383 assert(MPEG_BUFFER_SIZE - d->input.length >= MAD_BUFFER_GUARD);
384
385 while (len < MAD_BUFFER_GUARD)
386 d->input.data[d->input.length + len++] = 0;
387 }
388
389 mad_stream_buffer(&d->stream, d->input.data, d->input.length += len);
390 }
391
392 return TRUE;
393}
394
395
396static mad_fixed_t left_err, right_err;
397static const int bits = 16;
398static const int shift = MAD_F_FRACBITS + 1 - bits;
399
400
401inline long audio_linear_dither( mad_fixed_t sample, mad_fixed_t& error )
402{
403 sample += error;
404 mad_fixed_t quantized = (sample >= MAD_F_ONE) ? MAD_F_ONE - 1 : ( (sample < -MAD_F_ONE) ? -MAD_F_ONE : sample );
405 quantized &= ~((1L << shift) - 1);
406 error = sample - quantized;
407 return quantized >> shift;
408}
409
410
411inline void audio_pcm( short *data, unsigned int nsamples, mad_fixed_t *left, mad_fixed_t *right )
412{
413 if ( right ) {
414 while (nsamples--) {
415 data[0] = audio_linear_dither( *left++, left_err );
416 data[1] = audio_linear_dither( *right++, right_err );
417 data += 2;
418 }
419 } else {
420 while (nsamples--) {
421 data[0] = data[1] = audio_linear_dither( *left++, left_err );
422 data += 2;
423 }
424 }
425}
426
427
428bool LibMadPlugin::decode( short *output, long samples, long& samplesMade ) {
429 debugMsg( "LibMadPlugin::decode" );
430
431 static int buffered = 0;
432 static mad_fixed_t buffer[2][65536 * 2];
433 int offset = buffered;
434 samplesMade = 0;
435
436 static int maxBuffered = 8000; // 65536;
437
438 if ( samples > maxBuffered )
439 samples = maxBuffered;
440
441 if ( d->flush ) {
442 buffered = 0;
443 offset = 0;
444 d->flush = FALSE;
445 }
446
447 while ( buffered < maxBuffered ) {
448
449 while (mad_frame_decode(&d->frame, &d->stream) == -1) {
450 if (!MAD_RECOVERABLE(d->stream.error)) {
451 debugMsg( "feed me" );
452 return FALSE; // Feed me
453 }
454 if ( d->stream.error == MAD_ERROR_BADCRC ) {
455 mad_frame_mute(&d->frame);
456 qDebug( "error decoding, bad crc" );
457 }
458 }
459
460 mad_synth_frame(&d->synth, &d->frame);
461 int decodedSamples = d->synth.pcm.length;
462 memcpy( &(buffer[0][offset]), d->synth.pcm.samples[0], decodedSamples * sizeof(mad_fixed_t) );
463 if ( d->synth.pcm.channels == 2 )
464 memcpy( &(buffer[1][offset]), d->synth.pcm.samples[1], decodedSamples * sizeof(mad_fixed_t) );
465 offset += decodedSamples;
466 buffered += decodedSamples;
467 }
468
469 audio_pcm( output, samples, buffer[0], (d->synth.pcm.channels == 2) ? buffer[1] : 0 );
470// audio_pcm( output, samples, buffer[1], buffer[0] );
471// audio_pcm( output, samples, buffer[0], buffer[1] );
472 samplesMade = samples;
473 memmove( buffer[0], &(buffer[0][samples]), (buffered - samples) * sizeof(mad_fixed_t) );
474 if ( d->synth.pcm.channels == 2 )
475 memmove( buffer[1], &(buffer[1][samples]), (buffered - samples) * sizeof(mad_fixed_t) );
476 buffered -= samples;
477
478 return TRUE;
479}
480
481/*
482bool LibMadPlugin::audioReadMonoSamples( short *, long, long&, int ) {
483 debugMsg( "LibMadPlugin::audioReadMonoSamples" );
484 return FALSE;
485}
486
487
488bool LibMadPlugin::audioReadStereoSamples( short *output, long samples, long& samplesMade, int ) {
489*/
490bool LibMadPlugin::audioReadSamples( short *output, int /*channels*/, long samples, long& samplesMade, int ) {
491 debugMsg( "LibMadPlugin::audioReadStereoSamples" );
492
493 static bool needInput = TRUE;
494
495 if ( samples == 0 )
496 return TRUE;
497
498 do {
499 if ( needInput )
500 if ( !read() ) {
501 // if ( d->input.eof )
502 // needInput = FALSE;
503 // else
504 return TRUE;
505 }
506
507 needInput = FALSE;
508
509 if ( decode( output, samples, samplesMade ) )
510 return FALSE;
511 else
512 needInput = TRUE;
513 }
514 while ( ( samplesMade < samples ) && ( !d->input.eof ) );
515/*
516 static bool firstTimeThru = TRUE;
517
518 if ( firstTimeThru ) {
519 firstTimeThru = FALSE;
520 decode( output, samples, samplesMade );
521 return FALSE;
522 } else
523*/
524 return TRUE;
525}
526
527
528double LibMadPlugin::getTime() {
529 debugMsg( "LibMadPlugin::getTime" );
530 return 0.0;
531}
532
533
534void LibMadPlugin::printID3Tags() {
535 debugMsg( "LibMadPlugin::printID3Tags" );
536
537 char id3v1[128 + 1];
538
539 if ( ::lseek( d->input.fd, -128, SEEK_END ) == -1 ) {
540 qDebug( "error seeking to id3 tags" );
541 return;
542 }
543
544 if ( ::read( d->input.fd, id3v1, 128 ) != 128 ) {
545 qDebug( "error reading in id3 tags" );
546 return;
547 }
548
549 if ( ::strncmp( (const char *)id3v1, "TAG", 3 ) != 0 ) {
550 debugMsg( "sorry, no id3 tags" );
551 } else {
552 int len[5] = { 30, 30, 30, 4, 30 };
553 QString label[5] = { tr( "Title" ), tr( "Artist" ), tr( "Album" ), tr( "Year" ), tr( "Comment" ) };
554 char *ptr = id3v1 + 3, *ptr2 = ptr + len[0];
555 qDebug( "ID3 tags in file:" );
556 info = "";
557 for ( int i = 0; i < 5; ptr += len[i], i++, ptr2 += len[i] ) {
558 char push = *ptr2;
559 *ptr2 = '\0';
560 char *ptr3 = ptr2;
561 while ( ptr3-1 >= ptr && isspace(ptr3[-1]) ) ptr3--;
562 char push2 = *ptr3; *ptr3 = '\0';
563 if ( strcmp( ptr, "" ) )
564 info += ( i != 0 ? ", " : "" ) + label[i] + ": " + ptr;
565 //qDebug( info.latin1() );
566 *ptr3 = push2;
567 *ptr2 = push;
568 }
569 if (id3v1[126] == 0 && id3v1[127] != 0)
570 info += tr( ", Track: " ) + id3v1[127];
571 }
572
573 if ( ::lseek(d->input.fd, 0, SEEK_SET) == -1 ) {
574 qDebug( "error seeking back to beginning" );
575 return;
576 }
577}
578