Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

libmadplugin.cpp

Go to the documentation of this file.
00001 /**********************************************************************
00002 ** Copyright (C) 2001 Trolltech AS.  All rights reserved.
00003 **
00004 ** This file is part of Qtopia Environment.
00005 **
00006 ** This file may be distributed and/or modified under the terms of the
00007 ** GNU General Public License version 2 as published by the Free Software
00008 ** Foundation and appearing in the file LICENSE.GPL included in the
00009 ** packaging of this file.
00010 **
00011 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00012 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00013 **
00014 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00015 **
00016 ** Contact info@trolltech.com if any conditions of this licensing are
00017 ** not clear to you.
00018 **
00019 **********************************************************************/
00020 // largly modified by Maximilian Reiss <max.reiss@gmx.de>
00021 
00022 #include "libmadplugin.h"
00023 
00024 /* OPIE */
00025 #include <qpe/config.h>
00026 #include <opie2/odebug.h>
00027 
00028 /* QT */
00029 #include <qapplication.h>
00030 #include <qmessagebox.h>
00031 #include <qregexp.h>
00032 
00033 /* STD */
00034 #include <stdio.h>
00035 #include <stdarg.h>
00036 #include <stdlib.h>
00037 #include <sys/types.h>
00038 #include <sys/stat.h>
00039 #include <fcntl.h>
00040 #include <unistd.h>
00041 #include <string.h>
00042 #include <ctype.h>
00043 #include <errno.h>
00044 #include <time.h>
00045 #include <locale.h>
00046 #include <math.h>
00047 #include <assert.h>
00048 
00049 // for network handling
00050 #include <netinet/in.h>
00051 #include <netdb.h>
00052 #include <linux/limits.h>
00053 #include <sys/socket.h>
00054 #include <arpa/inet.h>
00055 #include <unistd.h>
00056 
00057 
00058 //#define HAVE_MMAP
00059 
00060 #if defined(HAVE_MMAP)
00061 #   include <sys/mman.h>
00062 #endif
00063 
00064 
00065 extern "C" {
00066 #include "mad.h"
00067 }
00068 
00069 
00070 #define MPEG_BUFFER_SIZE    65536
00071 //#define MPEG_BUFFER_SIZE    32768 //16384 // 8192
00072 //#define debugMsg(a)     qDebug(a)
00073 #define debugMsg(a)
00074 
00075 
00076 class Input {
00077 public:
00078     char const *path;
00079     int fd;
00080 #if defined(HAVE_MMAP)
00081     void *fdm;
00082 #endif
00083     unsigned long fileLength;
00084    unsigned char *data;
00085     unsigned long length;
00086     int eof;
00087 };
00088 
00089 
00090 class Output {
00091 public:
00092     mad_fixed_t attenuate;
00093     struct filter *filters;
00094     unsigned int channels_in;
00095     unsigned int channels_out;
00096     unsigned int speed_in;
00097     unsigned int speed_out;
00098     const char *path;
00099 };
00100 
00101 
00102 # if defined(HAVE_MMAP)
00103 static void *map_file(int fd, unsigned long *length)
00104 {
00105   void *fdm;
00106 
00107   *length += MAD_BUFFER_GUARD;
00108 
00109   fdm = mmap(0, *length, PROT_READ, MAP_SHARED, fd, 0);
00110   if (fdm == MAP_FAILED)
00111     return 0;
00112 
00113 # if defined(HAVE_MADVISE)
00114   madvise(fdm, *length, MADV_SEQUENTIAL);
00115 # endif
00116 
00117   return fdm;
00118 }
00119 
00120 
00121 static int unmap_file(void *fdm, unsigned long length)
00122 {
00123   if (munmap(fdm, length) == -1)
00124     return -1;
00125 
00126   return 0;
00127 }
00128 # endif
00129 
00130 
00131 static inline QString tr( const char *str ) {
00132     // Apparently this is okay from a plugin as it runs in the process space of the owner of the plugin
00133     return qApp->translate( "OpiePlayer", str, "libmad strings for mp3 file info" );
00134 }
00135 
00136 
00137 class LibMadPluginData {
00138 public:
00139     Input input;
00140     Output output;
00141     int bad_last_frame;
00142     struct mad_stream stream;
00143     struct mad_frame frame;
00144     struct mad_synth synth;
00145     bool flush;
00146 };
00147 
00148 
00149 LibMadPlugin::LibMadPlugin() {
00150     d = new LibMadPluginData;
00151     d->input.fd = 0;
00152 #if defined(HAVE_MMAP)
00153     d->input.fdm = 0;
00154 #endif
00155     d->input.data = 0;
00156     d->flush = TRUE;
00157     info = tr( "No Song Open" );
00158 }
00159 
00160 
00161 LibMadPlugin::~LibMadPlugin() {
00162     close();
00163     delete d;
00164 }
00165 
00166 
00167 bool LibMadPlugin::isFileSupported( const QString& path ) {
00168     debugMsg( "LibMadPlugin::isFileSupported" );
00169 
00170     // Mpeg file extensions
00171     //  "mp2","mp3","m1v","m2v","m2s","mpg","vob","mpeg","ac3"
00172     // Other media extensions
00173     //  "wav","mid","mod","s3m","ogg","avi","mov","sid"
00174 
00175     char *ext = strrchr( path.latin1(), '.' );
00176 
00177     // Test file extension
00178     if ( ext ) {
00179         if ( strncasecmp(ext, ".mp2", 4) == 0 )
00180             return TRUE;
00181         if ( strncasecmp(ext, ".mp3", 4) == 0 )
00182             return TRUE;
00183     }
00184 
00185     // UGLY - just for fast testing
00186     if ( path.left(4) == "http") {
00187         return TRUE;
00188     }
00189 
00190    return FALSE;
00191 }
00192 
00193 
00194 
00195 int LibMadPlugin::is_address_multicast(unsigned long address) {
00196     if ((address & 255) >= 224 && (address & 255) <= 239)
00197         return (1);
00198     return (0);
00199 }
00200 
00201 
00202 int LibMadPlugin::udp_open(char *address, int port) {
00203 
00204     int enable = 1L;
00205     struct sockaddr_in stAddr;
00206     struct sockaddr_in stLclAddr;
00207     struct ip_mreq stMreq;
00208     struct hostent *host;
00209     int sock;
00210 
00211     stAddr.sin_family = AF_INET;
00212     stAddr.sin_port = htons(port);
00213 
00214     if ((host = gethostbyname(address)) == NULL) {
00215         return (0);
00216     }
00217 
00218     stAddr.sin_addr = *((struct in_addr *)host->h_addr_list[0]);
00219 
00220     /* Create a UDP socket */
00221     if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
00222         return (0);
00223     }
00224 
00225     /* Allow multiple instance of the client to share the same address and port */
00226     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&enable, sizeof(unsigned long int)) < 0) {
00227         return (0);
00228     }
00229 
00230     /* If the address is multicast, register to the multicast group */
00231     if (is_address_multicast(stAddr.sin_addr.s_addr)) {
00232         /* Bind the socket to port */
00233         stLclAddr.sin_family = AF_INET;
00234         stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
00235         stLclAddr.sin_port = stAddr.sin_port;
00236         if (bind(sock, (struct sockaddr *)&stLclAddr, sizeof(stLclAddr)) < 0) {
00237             return (0);
00238         }
00239 
00240         /* Register to a multicast address */
00241         stMreq.imr_multiaddr.s_addr = stAddr.sin_addr.s_addr;
00242         stMreq.imr_interface.s_addr = INADDR_ANY;
00243         if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&stMreq, sizeof(stMreq)) < 0) {
00244             return (0);
00245         }
00246     } else {
00247         /* Bind the socket to port */
00248         stLclAddr.sin_family = AF_INET;
00249         stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
00250         stLclAddr.sin_port = htons(0);
00251         if (bind(sock, (struct sockaddr *)&stLclAddr, sizeof(stLclAddr)) < 0) {
00252             return (0);
00253         }
00254     }
00255     return (sock);
00256 }
00257 
00258 int LibMadPlugin::tcp_open(char *address, int port) {
00259     struct sockaddr_in stAddr;
00260     struct hostent *host;
00261     int sock;
00262     struct linger l;
00263 
00264     memset(&stAddr, 0, sizeof(stAddr));
00265     stAddr.sin_family = AF_INET;
00266     stAddr.sin_port = htons(port);
00267 
00268     if ((host = gethostbyname(address)) == NULL) {
00269         return (0);
00270     }
00271 
00272     stAddr.sin_addr = *((struct in_addr *)host->h_addr_list[0]);
00273 
00274     if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
00275         return (0);
00276     }
00277 
00278     l.l_onoff = 1;
00279     l.l_linger = 5;
00280     if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&l, sizeof(l)) < 0) {
00281         return (0);
00282     }
00283 
00284     if (connect(sock, (struct sockaddr *)&stAddr, sizeof(stAddr)) < 0) {
00285         return (0);
00286     }
00287 
00288     return (sock);
00289 }
00290 
00291 
00300 int LibMadPlugin::http_read_line(int tcp_sock, char *buf, int size) {
00301     int offset = 0;
00302 
00303     do {
00304         if (::read(tcp_sock, buf + offset, 1) < 0)
00305             return -1;
00306         if (buf[offset] != '\r')    /* Strip \r from answer */
00307             offset++;
00308     } while (offset < size - 1 && buf[offset - 1] != '\n');
00309 
00310     buf[offset] = 0;
00311     return offset;
00312 }
00313 
00314 int LibMadPlugin::http_open(const QString& path ) {
00315     char *host;
00316     int port;
00317     char *request;
00318     int tcp_sock;
00319     char http_request[PATH_MAX];
00320     char filename[PATH_MAX];
00321     //char c;
00322     char *arg =strdup(path.latin1());
00323 
00324     /* Check for URL syntax */
00325     if (strncmp(arg, "http://", strlen("http://"))) {
00326         return (0);
00327     }
00328 
00329     /* Parse URL */
00330     port = 80;
00331     host = arg + strlen("http://");
00332     if ((request = strchr(host, '/')) == NULL) {
00333         return (0);
00334     }
00335 
00336     *request++ = 0;
00337 
00338     if (strchr(host, ':') != NULL) { /* port is specified */
00339         port = atoi(strchr(host, ':') + 1);
00340         *strchr(host, ':') = 0;
00341     }
00342 
00343     /* Open a TCP socket */
00344     if (!(tcp_sock = tcp_open(host, port))) {
00345         perror("http_open");
00346         return (0);
00347     }
00348 
00349     snprintf(filename, sizeof(filename) - strlen(host) - 75, "%s", request);
00350 
00351     /* Send HTTP GET request */
00352     /* Please don't use a Agent know by shoutcast (Lynx, Mozilla) seems to be reconized and print
00353      * a html page and not the stream */
00354     snprintf(http_request, sizeof(http_request), "GET /%s HTTP/1.0\r\n"
00355     /*  "User-Agent: Mozilla/2.0 (Win95; I)\r\n" */
00356         "Pragma: no-cache\r\n" "Host: %s\r\n" "Accept: */*\r\n" "\r\n", filename, host);
00357 
00358     send(tcp_sock, http_request, strlen(http_request), 0);
00359 
00360     /* Parse server reply */
00361 #if 0
00362     do
00363         read(tcp_sock, &c, sizeof(char));
00364     while (c != ' ');
00365     read(tcp_sock, http_request, 4 * sizeof(char));
00366     http_request[4] = 0;
00367     if (strcmp(http_request, "200 "))     {
00368         fprintf(stderr, "http_open: ");
00369         do {
00370             read(tcp_sock, &c, sizeof(char));
00371             fprintf(stderr, "%c", c);
00372         } while (c != '\r');
00373         fprintf(stderr, "\n");
00374         return (0);
00375     }
00376 #endif
00377 
00378     QString name;
00379     QString genre;
00380     QString bitrate;
00381     QString url;
00382     QString message = tr("Info: ");
00383     do {
00384 
00385         int len;
00386 
00387         len = http_read_line(tcp_sock, http_request, sizeof(http_request));
00388 
00389         if (len == -1) {
00390            //            odebug << "http_open: "+ QString(strerror(errno)) +"\n" << oendl;
00391             return 0;
00392         }
00393 
00394         if (QString(http_request).left(9) == "Location:") {
00395             /* redirect */
00396             ::close(tcp_sock);
00397             http_request[strlen(http_request) - 1] = '\0';
00398             return http_open(&http_request[10]);
00399         }
00400 
00401         if (QString(http_request).left(4) == "ICY ") {
00402             /* This is shoutcast/icecast streaming */
00403             if (strncmp(http_request + 4, "200 ", 4)) {
00404                //                odebug << "http_open: " + QString(http_request) + "\n" << oendl;
00405                 return 0;
00406             }
00407         } else if (QString(http_request).left(4) == "icy-") {
00408             /* we can have: icy-noticeX, icy-name, icy-genre, icy-url, icy-pub, icy-metaint, icy-br */
00409             if ( QString( http_request ).left( 8 ) == "icy-name" ) {
00410                 name = tr("Name: ") + QString(http_request).mid(9, (QString(http_request).length())- 9 );
00411             } else if ( QString( http_request ).left( 9 ) == "icy-genre" ) {
00412                 genre = tr("Genre: ") + QString(http_request).mid(10, (QString(http_request).length())-10 );
00413             } else if ( QString( http_request ).left( 6 ) == "icy-br" ) {
00414                 bitrate = tr("Bitrate: ") + QString(http_request).mid(7, (QString(http_request).length())- 7 );
00415             } else if ( QString( http_request ).left( 7 ) == "icy-url" ) {
00416                 url = tr("URL: ") + QString(http_request).mid(8, (QString(http_request).length())- 8 );
00417             }  else if ( QString( http_request ).left( 10 ) == "icy-notice" ) {
00418                 message += QString(http_request).mid(11, QString(http_request).length()-11 ) ;
00419             }
00420         }
00421     } while (strcmp(http_request, "\n") != 0);
00422 
00423     info = QString(name + genre + url + bitrate + message).replace( QRegExp("\n"), " : " );
00424 
00425     //    odebug << "Stream info: " + info << oendl;
00426 
00427     return (tcp_sock);
00428 }
00429 
00430 
00431 
00432 bool LibMadPlugin::open( const QString& path ) {
00433     debugMsg( "LibMadPlugin::open" );
00434     Config cfg("OpiePlayer");
00435     cfg.setGroup("Options");
00436     bufferSize = cfg.readNumEntry("MPeg_BufferSize",MPEG_BUFFER_SIZE);
00437     //    odebug << "buffer size is " << bufferSize << "" << oendl;
00438     d->bad_last_frame = 0;
00439     d->flush = TRUE;
00440     info = QString( "" );
00441 
00442     //odebug << "Opening " << path << "" << oendl;
00443 
00444     if (path.left( 4 ) == "http" ) {
00445         // in case of any error we get 0 here
00446         if ( !(http_open(path) == 0) ) {
00447             d->input.fd = http_open(path);
00448         } else {
00449             return FALSE;
00450         }
00451     } else {
00452         d->input.path = path.latin1();
00453         d->input.fd = ::open( d->input.path, O_RDONLY );
00454         // thats a better place, since it should only seek for ID3 tags on mp3 files, not streams
00455         printID3Tags();
00456     }
00457     if (d->input.fd == -1) {
00458        //        odebug << "error opening " << d->input.path << "" << oendl;
00459         return FALSE;
00460     }
00461 
00462     struct stat stat;
00463     if (fstat(d->input.fd, &stat) == -1) {
00464        //    odebug << "error calling fstat" << oendl;  return FALSE;
00465     }
00466     if (S_ISREG(stat.st_mode) && stat.st_size > 0)
00467   d->input.fileLength = stat.st_size;
00468     else
00469   d->input.fileLength = 0;
00470 
00471 #if defined(HAVE_MMAP)
00472     if (S_ISREG(stat.st_mode) && stat.st_size > 0) {
00473         d->input.length = stat.st_size;
00474         d->input.fdm = map_file(d->input.fd, &d->input.length);
00475         if (d->input.fdm == 0) {
00476            //            odebug << "error mmapping file" << oendl;  return FALSE;
00477         }
00478         d->input.data = (unsigned char *)d->input.fdm;
00479     }
00480 #endif
00481 
00482     if (d->input.data == 0) {
00483         d->input.data = (unsigned char *)malloc( bufferSize /*MPEG_BUFFER_SIZE*/);
00484         if (d->input.data == 0) {
00485            //            odebug << "error allocating input buffer" << oendl;
00486             return FALSE;
00487         }
00488         d->input.length = 0;
00489     }
00490 
00491     d->input.eof = 0;
00492 
00493     mad_stream_init(&d->stream);
00494     mad_frame_init(&d->frame);
00495     mad_synth_init(&d->synth);
00496 
00497     return TRUE;
00498 }
00499 
00500 
00501 bool LibMadPlugin::close() {
00502     debugMsg( "LibMadPlugin::close" );
00503 
00504     int result = TRUE;
00505 
00506     mad_synth_finish(&d->synth);
00507     mad_frame_finish(&d->frame);
00508     mad_stream_finish(&d->stream);
00509 
00510 #if defined(HAVE_MMAP)
00511     if (d->input.fdm) {
00512         if (unmap_file(d->input.fdm, d->input.length) == -1) {
00513            //            odebug << "error munmapping file" << oendl;
00514             result = FALSE;
00515         }
00516         d->input.fdm  = 0;
00517         d->input.data = 0;
00518     }
00519 #endif
00520 
00521     if (d->input.data) {
00522         free(d->input.data);
00523         d->input.data = 0;
00524     }
00525 
00526     if (::close(d->input.fd) == -1) {
00527        //        odebug << "error closing file " << d->input.path << "" << oendl;
00528         result = FALSE;
00529     }
00530 
00531     d->input.fd = 0;
00532 
00533     return result;
00534 }
00535 
00536 
00537 bool LibMadPlugin::isOpen() {
00538     debugMsg( "LibMadPlugin::isOpen" );
00539     return ( d->input.fd != 0 );
00540 }
00541 
00542 
00543 int LibMadPlugin::audioStreams() {
00544     debugMsg( "LibMadPlugin::audioStreams" );
00545     return 1;
00546 }
00547 
00548 
00549 int LibMadPlugin::audioChannels( int ) {
00550     debugMsg( "LibMadPlugin::audioChannels" );
00551 /*
00552   long t; short t1[5]; audioReadSamples( t1, 2, 1, t, 0 );
00553   odebug << "LibMadPlugin::audioChannels: " << d->frame.header.mode > 0 ? 2 : 1 << "" << oendl;
00554   return d->frame.header.mode > 0 ? 2 : 1;
00555 */
00556     return 2;
00557 }
00558 
00559 
00560 int LibMadPlugin::audioFrequency( int ) {
00561     debugMsg( "LibMadPlugin::audioFrequency" );
00562     long t; short t1[5]; audioReadSamples( t1, 2, 1, t, 0 );
00563     //    odebug << "LibMadPlugin::audioFrequency: " << d->frame.header.samplerate << "" << oendl;
00564     return d->frame.header.samplerate;
00565 }
00566 
00567 
00568 int LibMadPlugin::audioSamples( int ) {
00569    debugMsg( "LibMadPlugin::audioSamples" );
00570 
00571    long t; short t1[5]; audioReadSamples( t1, 2, 1, t, 0 );
00572    mad_header_decode( (struct mad_header *)&d->frame.header, &d->stream );
00573 /*
00574   odebug << "LibMadPlugin::audioSamples: " <<  d->frame.header.duration.seconds << "*" << d->frame.header.samplerate << oendl;
00575   return d->frame.header.duration.seconds * d->frame.header.samplerate;
00576 */
00577    if ( d->frame.header.bitrate == 0 )
00578       return 0;
00579    int samples = (d->input.fileLength / (d->frame.header.bitrate/8)) * d->frame.header.samplerate;
00580 
00581    //   odebug << "LibMadPlugin::audioSamples: " << (int)d->input.fileLength
00582    //          << " * " << (int)d->frame.header.samplerate << " * 8 / " << (int)d->frame.header.bitrate << oendl;
00583    //   odebug << "LibMadPlugin::audioSamples: " << samples << "" << oendl;
00584 
00585    return samples;
00586 
00587 //    return 10000000;
00588 }
00589 
00590 
00591 bool LibMadPlugin::audioSetSample( long, int ) {
00592     debugMsg( "LibMadPlugin::audioSetSample" );
00593 
00594 //     long totalSamples = audioSamples(0);
00595 //     if ( totalSamples <= 1 )
00596 //   return FALSE;
00597 
00598 //     // Seek to requested position
00599 //     odebug << "seek pos: " << (int)((double)pos * d->input.fileLength / totalSamples) << "" << oendl;
00600 //     ::lseek( d->input.fd, (long)((double)pos * d->input.fileLength / totalSamples), SEEK_SET );
00601 //     mad_stream_sync(&d->stream);
00602 
00603 //     mad_stream_init(&d->stream);
00604 //     mad_frame_init(&d->frame);
00605 //     mad_synth_init(&d->synth);
00606 
00607 //     return TRUE;
00608     debugMsg( "LibMadPlugin::audioSetSample" );
00609     return FALSE;
00610 }
00611 
00612 
00613 long LibMadPlugin::audioGetSample( int ) {
00614     debugMsg( "LibMadPlugin::audioGetSample" );
00615     return 0;
00616 }
00617 
00618 /*
00619 bool LibMadPlugin::audioReadSamples( short *, int, long, int ) {
00620 debugMsg( "LibMadPlugin::audioReadSamples" );
00621 return FALSE;
00622 }
00623 
00624 
00625 bool LibMadPlugin::audioReReadSamples( short *, int, long, int ) {
00626 debugMsg( "LibMadPlugin::audioReReadSamples" );
00627     return FALSE;
00628     }
00629 */
00630 
00631 bool LibMadPlugin::read() {
00632     debugMsg( "LibMadPlugin::read" );
00633     int len;
00634 
00635       if (d->input.eof)
00636   return FALSE;
00637 
00638 #if defined(HAVE_MMAP)
00639       if (d->input.fdm) {
00640           unsigned long skip = 0;
00641 
00642           if (d->stream.next_frame) {
00643               struct stat stat;
00644 
00645               if (fstat(d->input.fd, &stat) == -1)
00646                   return FALSE;
00647 
00648               if (stat.st_size + MAD_BUFFER_GUARD <= (signed)d->input.length)
00649                   return FALSE;
00650 
00651               // file size changed; update memory map
00652               skip = d->stream.next_frame - d->input.data;
00653 
00654               if (unmap_file(d->input.fdm, d->input.length) == -1) {
00655                   d->input.fdm  = 0;
00656                   d->input.data = 0;
00657                   return FALSE;
00658               }
00659 
00660               d->input.length = stat.st_size;
00661 
00662               d->input.fdm = map_file(d->input.fd, &d->input.length);
00663               if (d->input.fdm == 0) {
00664                   d->input.data = 0;
00665                   return FALSE;
00666               }
00667 
00668               d->input.data = (unsigned char *)d->input.fdm;
00669           }
00670 
00671           mad_stream_buffer(&d->stream, d->input.data + skip, d->input.length - skip);
00672 
00673       } else
00674 #endif
00675       {
00676           if (d->stream.next_frame) {
00677               memmove(d->input.data, d->stream.next_frame,
00678                       d->input.length = &d->input.data[d->input.length] - d->stream.next_frame);
00679           }
00680 
00681           do {
00682               len = ::read(d->input.fd, d->input.data + d->input.length, bufferSize /* MPEG_BUFFER_SIZE*/ - d->input.length);
00683           }
00684           while (len == -1 && errno == EINTR);
00685 
00686           if (len == -1) {
00687              //              odebug << "error reading audio" << oendl;
00688               return FALSE;
00689           }
00690           else if (len == 0) {
00691               d->input.eof = 1;
00692 
00693               assert(bufferSize /*MPEG_BUFFER_SIZE*/ - d->input.length >= MAD_BUFFER_GUARD);
00694 
00695               while (len < MAD_BUFFER_GUARD)
00696                   d->input.data[d->input.length + len++] = 0;
00697           }
00698 
00699           mad_stream_buffer(&d->stream, d->input.data, d->input.length += len);
00700       }
00701 
00702       return TRUE;
00703 }
00704 
00705 
00706 static mad_fixed_t left_err, right_err;
00707 static const int bits = 16;
00708 static const int shift = MAD_F_FRACBITS + 1 - bits;
00709 
00710 
00711 inline long audio_linear_dither( mad_fixed_t sample, mad_fixed_t& error ) {
00712     sample += error;
00713     mad_fixed_t quantized = (sample >= MAD_F_ONE) ? MAD_F_ONE - 1 : ( (sample < -MAD_F_ONE) ? -MAD_F_ONE : sample );
00714     quantized &= ~((1L << shift) - 1);
00715     error = sample - quantized;
00716     return quantized >> shift;
00717 }
00718 
00719 
00720 inline void audio_pcm( short *data, unsigned int nsamples, mad_fixed_t *left, mad_fixed_t *right ) {
00721     if ( right ) {
00722         while (nsamples--) {
00723             data[0] = audio_linear_dither( *left++,  left_err );
00724             data[1] = audio_linear_dither( *right++, right_err );
00725             data += 2;
00726         }
00727     } else {
00728         while (nsamples--) {
00729             data[0] = data[1] = audio_linear_dither( *left++,  left_err );
00730             data += 2;
00731         }
00732     }
00733 }
00734 
00735 
00736 bool LibMadPlugin::decode( short *output, long samples, long& samplesMade ) {
00737     debugMsg( "LibMadPlugin::decode" );
00738 
00739     static int buffered = 0;
00740     static mad_fixed_t buffer[2][65536 * 2];
00741     int offset = buffered;
00742     samplesMade = 0;
00743 
00744     static int maxBuffered = 8000; // 65536;
00745 
00746     if ( samples > maxBuffered ) {
00747         samples = maxBuffered;
00748     }
00749 
00750     if ( d->flush ) {
00751         buffered = 0;
00752         offset = 0;
00753         d->flush = FALSE;
00754     }
00755 
00756     while ( buffered < maxBuffered ) {
00757 
00758         while (mad_frame_decode(&d->frame, &d->stream) == -1) {
00759             if (!MAD_RECOVERABLE(d->stream.error)) {
00760                 debugMsg( "feed me" );
00761                 return FALSE; // Feed me
00762             }
00763             if ( d->stream.error == MAD_ERROR_BADCRC ) {
00764                 mad_frame_mute(&d->frame);
00765                 //                odebug << "error decoding, bad crc" << oendl;
00766             }
00767         }
00768 
00769         mad_synth_frame(&d->synth, &d->frame);
00770         int decodedSamples = d->synth.pcm.length;
00771         memcpy( &(buffer[0][offset]), d->synth.pcm.samples[0], decodedSamples * sizeof(mad_fixed_t) );
00772         if ( d->synth.pcm.channels == 2 )
00773             memcpy( &(buffer[1][offset]), d->synth.pcm.samples[1], decodedSamples * sizeof(mad_fixed_t) );
00774         offset += decodedSamples;
00775         buffered += decodedSamples;
00776     }
00777 
00778 //qApp->processEvents();
00779     audio_pcm( output, samples, buffer[0], (d->synth.pcm.channels == 2) ? buffer[1] : 0 );
00780 //    audio_pcm( output, samples, buffer[1], buffer[0] );
00781 //    audio_pcm( output, samples, buffer[0], buffer[1] );
00782     samplesMade = samples;
00783     memmove( buffer[0], &(buffer[0][samples]), (buffered - samples) * sizeof(mad_fixed_t) );
00784     if ( d->synth.pcm.channels == 2 ) {
00785         memmove( buffer[1], &(buffer[1][samples]), (buffered - samples) * sizeof(mad_fixed_t) );
00786     }
00787     buffered -= samples;
00788 
00789     return TRUE;
00790 }
00791 
00792 /*bool LibMadPlugin::audioReadStereoSamples( short *output, long samples, long& samplesMade, int ) {
00793 */
00794 bool LibMadPlugin::audioReadSamples( short *output, int /*channels*/, long samples, long& samplesMade, int ) {
00795     debugMsg( "LibMadPlugin::audioReadStereoSamples" );
00796 
00797     static bool needInput = TRUE;
00798 
00799     if ( samples == 0 )
00800         return FALSE;
00801 
00802     do {
00803         if ( needInput )
00804             if ( !read() ) {
00805                 return FALSE;
00806             }
00807 
00808         needInput = FALSE;
00809 
00810         if ( decode( output, samples, samplesMade ) )
00811             return TRUE;
00812         else
00813             needInput = TRUE;
00814     }
00815     while ( ( samplesMade < samples ) && ( !d->input.eof ) );
00816 
00817     return FALSE;
00818 }
00819 
00820 
00821 double LibMadPlugin::getTime() {
00822     debugMsg( "LibMadPlugin::getTime" );
00823     return 0.0;
00824 }
00825 
00826 
00827 void LibMadPlugin::printID3Tags() {
00828    //    odebug << "LibMadPlugin::printID3Tags" << oendl;
00829 
00830     char id3v1[128 + 1];
00831 
00832     if ( ::lseek( d->input.fd, -128, SEEK_END ) == -1 ) {
00833        //        odebug << "error seeking to id3 tags" << oendl;
00834         return;
00835     }
00836 
00837     if ( ::read( d->input.fd, id3v1, 128 ) != 128 ) {
00838        //        odebug << "error reading in id3 tags" << oendl;
00839         return;
00840     }
00841 
00842     if ( ::strncmp( (const char *)id3v1, "TAG", 3 ) != 0 ) {
00843         debugMsg( "sorry, no id3 tags" );
00844     } else {
00845         int len[5] = { 30, 30, 30, 4, 30 };
00846         QString label[5] = { tr( "Title" ), tr( "Artist" ), tr( "Album" ), tr( "Year" ), tr( "Comment" ) };
00847         char *ptr = id3v1 + 3, *ptr2 = ptr + len[0];
00848         //        odebug << "ID3 tags in file:" << oendl;
00849         info = "";
00850         for ( int i = 0; i < 5; ptr += len[i], i++, ptr2 += len[i] ) {
00851             char push = *ptr2;
00852             *ptr2 = '\0';
00853             char *ptr3 = ptr2;
00854             while ( ptr3-1 >= ptr && isspace(ptr3[-1]) ) ptr3--;
00855             char push2 = *ptr3; *ptr3 = '\0';
00856             if ( strcmp( ptr, "" ) ) {
00857                 if( ((QString)ptr).find("  ") == -1) // don't add anything that has blanks
00858                 info += ( i != 0 ? ", " : "" ) + label[i] + ": " + ptr;
00859             }
00860 //             odebug << info.latin1() << oendl;
00861             *ptr3 = push2;
00862             *ptr2 = push;
00863         }
00864         if (id3v1[126] == 0 && id3v1[127] != 0)
00865             info += tr( ", Track: " ) + id3v1[127];
00866     }
00867 
00868     if ( ::lseek(d->input.fd, 0, SEEK_SET) == -1 ) {
00869        //        odebug << "error seeking back to beginning" << oendl;
00870         return;
00871     }
00872 }
00873 

Generated on Sat Nov 5 16:15:37 2005 for OPIE by  doxygen 1.4.2