lastfm-player-1.1.4_p1910/0000755000175000001440000000000010352604572014141 5ustar daviduserslastfm-player-1.1.4_p1910/src/0000755000175000001440000000000010352604613014724 5ustar daviduserslastfm-player-1.1.4_p1910/src/md5/0000755000175000001440000000000010352604621015410 5ustar daviduserslastfm-player-1.1.4_p1910/src/md5/md5.h0000644000175000001440000000620210352604462016251 0ustar davidusers/* MD5 converted to C++ class by Frank Thilo (thilo@unix-ag.org) for bzflag (http://www.bzflag.org) based on: md5.h and md5.c reference implemantion of RFC 1321 Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ #ifndef BZF_MD5_H #define BZF_MD5_H #include #include // a small class for calculating MD5 hashes of strings or byte arrays // it is not meant to be fast or secure // // usage: 1) feed it blocks of uchars with update() // 2) finalize() // 3) get hexdigest() string // or // MD5(std::string).hexdigest() // // assumes that char is 8 bit and int is 32 bit class MD5 { public: typedef unsigned int size_type; // must be 32bit MD5(); MD5(const std::string& text); void update(const unsigned char *buf, size_type length); void update(const char *buf, size_type length); MD5& finalize(); std::string hexdigest() const; friend std::ostream& operator<<(std::ostream&, MD5 md5); private: void init(); typedef unsigned char uint1; // 8bit typedef unsigned int uint4; // 32bit enum {blocksize = 64}; // VC6 won't eat a const static int here void transform(const uint1 block[blocksize]); static void decode(uint4 output[], const uint1 input[], size_type len); static void encode(uint1 output[], const uint4 input[], size_type len); bool finalized; uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk uint4 count[2]; // 64bit counter for number of bits (lo, hi) uint4 state[4]; // digest so far uint1 digest[16]; // the result // low level logic operations static inline uint4 F(uint4 x, uint4 y, uint4 z); static inline uint4 G(uint4 x, uint4 y, uint4 z); static inline uint4 H(uint4 x, uint4 y, uint4 z); static inline uint4 I(uint4 x, uint4 y, uint4 z); static inline uint4 rotate_left(uint4 x, int n); static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); }; #endif // Local Variables: *** // mode:C++ *** // tab-width: 8 *** // c-basic-offset: 2 *** // indent-tabs-mode: t *** // End: *** // ex: shiftwidth=2 tabstop=8 lastfm-player-1.1.4_p1910/src/md5/md5.cpp0000644000175000001440000002337610352604462016617 0ustar davidusers/* MD5 converted to C++ class by Frank Thilo (thilo@unix-ag.org) for bzflag (http://www.bzflag.org) based on: md5.h and md5.c reference implemantion of RFC 1321 Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights reserved. License to copy and use this software is granted provided that it is identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing this software or this function. License is also granted to make and use derivative works provided that such works are identified as "derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm" in all material mentioning or referencing the derived work. RSA Data Security, Inc. makes no representations concerning either the merchantability of this software or the suitability of this software for any particular purpose. It is provided "as is" without express or implied warranty of any kind. These notices must be retained in any copies of any part of this documentation and/or software. */ #include #include #include #include "md5.h" // Constants for MD5Transform routine. #define S11 7 #define S12 12 #define S13 17 #define S14 22 #define S21 5 #define S22 9 #define S23 14 #define S24 20 #define S31 4 #define S32 11 #define S33 16 #define S34 23 #define S41 6 #define S42 10 #define S43 15 #define S44 21 // F, G, H and I are basic MD5 functions. inline MD5::uint4 MD5::F(uint4 x, uint4 y, uint4 z) { return x&y | ~x&z; } inline MD5::uint4 MD5::G(uint4 x, uint4 y, uint4 z) { return x&z | y&~z; } inline MD5::uint4 MD5::H(uint4 x, uint4 y, uint4 z) { return x^y^z; } inline MD5::uint4 MD5::I(uint4 x, uint4 y, uint4 z) { return y ^ (x | ~z); } // rotate_left rotates x left n bits. inline MD5::uint4 MD5::rotate_left(uint4 x, int n) { return (x << n) | (x >> (32-n)); } // FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. // Rotation is separate from addition to prevent recomputation. inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { a = rotate_left(a+ F(b,c,d) + x + ac, s) + b; } inline void MD5::GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { a = rotate_left(a + G(b,c,d) + x + ac, s) + b; } inline void MD5::HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { a = rotate_left(a + H(b,c,d) + x + ac, s) + b; } inline void MD5::II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { a = rotate_left(a + I(b,c,d) + x + ac, s) + b; } // default ctor, just initailize MD5::MD5() { init(); } // nifty shortcut ctor, compute MD5 for string and finalize it right away MD5::MD5(const std::string &text) { init(); update(text.c_str(), text.length()); finalize(); } void MD5::init() { finalized=false; count[0] = 0; count[1] = 0; // load magic initialization constants. state[0] = 0x67452301; state[1] = 0xefcdab89; state[2] = 0x98badcfe; state[3] = 0x10325476; } // decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4. void MD5::decode(uint4 output[], const uint1 input[], size_type len) { for (unsigned int i = 0, j = 0; j < len; i++, j += 4) output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); } // encodes input (uint4) into output (unsigned char). Assumes len is // a multiple of 4. void MD5::encode(uint1 output[], const uint4 input[], size_type len) { for (size_type i = 0, j = 0; j < len; i++, j += 4) { output[j] = input[i] & 0xff; output[j+1] = (input[i] >> 8) & 0xff; output[j+2] = (input[i] >> 16) & 0xff; output[j+3] = (input[i] >> 24) & 0xff; } } // apply MD5 algo on a block void MD5::transform(const uint1 block[blocksize]) { uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; decode (x, block, blocksize); /* Round 1 */ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; // Zeroize sensitive information. memset(x, 0, sizeof x); } // MD5 block update operation. Continues an MD5 message-digest // operation, processing another message block void MD5::update(const unsigned char input[], size_type length) { // compute number of bytes mod 64 size_type index = count[0] / 8 % blocksize; // Update number of bits if ((count[0] += (length << 3)) < (length << 3)) count[1]++; count[1] += (length >> 29); // number of bytes we need to fill in buffer size_type firstpart = 64 - index; size_type i; // transform as many times as possible. if (length >= firstpart) { // fill buffer first, transform memcpy(&buffer[index], input, firstpart); transform(buffer); // transform chunks of blocksize (64 bytes) for (i = firstpart; i + blocksize <= length; i += blocksize) transform(&input[i]); index = 0; } else i = 0; // buffer remaining input memcpy(&buffer[index], &input[i], length-i); } // for convenience provide a verson with signed char void MD5::update(const char input[], size_type length) { update((const unsigned char*)input, length); } // MD5 finalization. Ends an MD5 message-digest operation, writing the // the message digest and zeroizing the context. MD5& MD5::finalize() { static unsigned char padding[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (!finalized) { // Save number of bits unsigned char bits[8]; encode(bits, count, 8); // pad out to 56 mod 64. size_type index = count[0] / 8 % 64; size_type padLen = (index < 56) ? (56 - index) : (120 - index); update(padding, padLen); // Append length (before padding) update(bits, 8); // Store state in digest encode(digest, state, 16); // Zeroize sensitive information. memset(buffer, 0, sizeof buffer); memset(count, 0, sizeof count); finalized=true; } return *this; } // return hex representation of digest as string std::string MD5::hexdigest() const { if (!finalized) return ""; char buf[33]; for (int i=0; i<16; i++) sprintf(buf+i*2, "%02x", digest[i]); buf[32]=0; return std::string(buf); } std::ostream& operator<<(std::ostream& out, MD5 md5) { return out << md5.hexdigest(); } // Local Variables: *** // mode:C++ *** // tab-width: 8 *** // c-basic-offset: 2 *** // indent-tabs-mode: t *** // End: *** // ex: shiftwidth=2 tabstop=8 lastfm-player-1.1.4_p1910/src/imagefader.h0000644000175000001440000000364610352604467017201 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef IMAGEFADER_H #define IMAGEFADER_H #include #include class QPixmap; class QTimer; class ImageFader : public QObject { Q_OBJECT public: ImageFader( QObject *parent = 0 ); void startFade( const QImage &from, const QImage &to, unsigned int ms ); signals: void fadeProgress( const QPixmap &pm ); private slots: void fade(); private: QImage m_src, m_dst, m_tmp; double m_fadeProgress, m_fadeStep; QTimer *m_fadeTimer; }; #endif // IMAGEFADER_H lastfm-player-1.1.4_p1910/src/coverloader.h0000644000175000001440000000444110352604467017414 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef COVERLOADER_H #define COVERLOADER_H #include class QHttp; class QIODevice; class CoverLoader : public QObject { Q_OBJECT public: CoverLoader( QObject *parent = 0, QString cachePath = QString( "./" ) ); void setHost( const QString &host ); void setProxy( const QString &host, int port, const QString &user = QString(), const QString &password = QString() ); void get( const QString &path, QIODevice *to = 0 ); signals: void requestFinished( bool error ); private slots: void slotRequestFinished( bool error ); private: bool haveCachedCopy() const; QByteArray getCachedCopy() const; void putCachedCopy( const QByteArray &data ); QString pathToCachedCopy() const; QHttp *m_transport; QString m_cachePath; QString m_cacheKey; QString m_host; QIODevice *m_buffer; }; #endif // COVERLOADER_H lastfm-player-1.1.4_p1910/src/playlist.h0000644000175000001440000000370110352604467016746 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include class Playlist { public: void appendTrack( QString track, QString url, QString id ) { m_tracks.append( track ); m_urls.append( url ); m_ids.append( id ); } void clear() { m_tracks.clear(); m_urls.clear(); m_ids.clear(); } const int count() const { return m_tracks.count(); } QPair, QString> trackAt( int id ) const; private: QStringList m_tracks; QStringList m_urls; QStringList m_ids; }; lastfm-player-1.1.4_p1910/src/song.cpp0000644000175000001440000000445010352604467016410 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "song.h" bool Song::sameAs( const Song &song ) { if ( artist() != song.artist() ) return false; if ( album() != song.album() ) return false; if ( track() != song.track() ) return false; return true; } void Song::fetch( const Song &song ) { setArtist( song.artist() ); setAlbum( song.album() ); setTrack( song.track() ); setStation( song.station() ); setCover( song.cover() ); setDuration( song.duration() ); setProgress( song.progress() ); setArtistUrl( song.artistUrl() ); setAlbumUrl( song.albumUrl() ); setTrackUrl( song.trackUrl() ); } void Song::clear() { setArtist( "" ); setAlbum( "" ); setTrack( "" ); setStation( "" ); setCover( "" ); setDuration( 0 ); setProgress( 0 ); setArtistUrl( "" ); setAlbumUrl( "" ); setTrackUrl( "" ); } lastfm-player-1.1.4_p1910/src/linklabel.h0000644000175000001440000000332710352604467017046 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef __LINKLABEL__ #define __LINKLABEL__ 1 #include #include class LinkLabel : public QLabel { Q_OBJECT public: LinkLabel( QWidget *parent = 0 ); void mousePressEvent( QMouseEvent * e ); signals: void clicked(); }; #endif lastfm-player-1.1.4_p1910/src/imagebutton.h0000644000175000001440000000421010352604467017417 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef __IMAGEBUTTON__ #define __IMAGEBUTTON__ 1 #include #include #include class ImageButton : public QLabel { Q_OBJECT public: ImageButton( QWidget *parent = 0 ); void setImages( QString prefix, QString normal, QString down, QString hover, QString disabled ); void setEnabled( bool enabled ); void enterEvent( QEvent * e ); void leaveEvent( QEvent * e ); void mousePressEvent( QMouseEvent * e ); void mouseReleaseEvent( QMouseEvent * e ); private: QPixmap normal; QPixmap down; QPixmap hover; QPixmap disabled; bool state; signals: void clicked(); }; #endif lastfm-player-1.1.4_p1910/src/urldialog.h0000644000175000001440000000342410352604467017071 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include "ui_urldialog.h" class WebserviceConnector; class UrlDialog : public QDialog { Q_OBJECT public: UrlDialog( QWidget *parent = 0, WebserviceConnector *webserviceConnector = 0 ); private: Ui::UrlDialog ui; WebserviceConnector *ws; public slots: void okPressed(); }; lastfm-player-1.1.4_p1910/src/urldialog.ui0000644000175000001440000000722410352604467017261 0ustar davidusers UrlDialog 0 0 393 155 393 155 393 155 Enter Station Address 20 22 95 16 Station Address: 130 20 241 21 20 110 351 33 0 6 Qt::Horizontal 131 31 OK Cancel 20 60 351 31 <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Hint:</span> To change the station, you can also drag lastfm:// links from your browser to the player!</p></body></html> true okButton clicked() UrlDialog accept() 278 253 96 254 cancelButton clicked() UrlDialog reject() 369 253 179 282 lastfm-player-1.1.4_p1910/src/progressframe.cpp0000644000175000001440000000417010352604467020320 0ustar davidusers/************************************************************************* * * * Last.fm Player, Copyright (C) 2005 Chris Muehlhaeuser, Last.fm Ltd. * * All rights reserved. Email: chris@last.fm * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of: * * * * The BSD-style license that is included with this library in * * the file LICENSE. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file * * LICENSE for more details. * * * *************************************************************************/ #include "progressframe.h" #include ProgressFrame::ProgressFrame( QWidget *parent ) : QFrame( parent ) { m_maxValue = 1; m_value = 0; setMinimumHeight( 16 ); setMinimumWidth( 220 ); setFrameStyle( QFrame::Panel | QFrame::Sunken ); setLineWidth( 1 ); } void ProgressFrame::paintEvent ( QPaintEvent * event ) { QPainter painter( this ); drawFrame( &painter ); painter.setPen( QColor( 145, 153, 142, 255 ) ); painter.setBrush( QColor( 145, 153, 142, 255 ) ); int w = ( (float)m_value / (float)m_maxValue ) * ( width() - 8 ); if ( w > width() - 8 ) w = width() - 8; painter.drawRect( 3, 3, w, 8 ); } void ProgressFrame::setMaximum( int value ) { m_maxValue = value; } void ProgressFrame::setValue( int value ) { m_value = value; update(); } int ProgressFrame::value() { return m_value; } lastfm-player-1.1.4_p1910/src/player.ui0000644000175000001440000011535110352604467016574 0ustar davidusers PlayerForm 0 0 442 230 442 230 442 230 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 227 230 216 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 227 230 216 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 227 230 216 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 true Last.fm Player ../data/icon.png 70 260 55 16 TextLabel 20 180 271 47 10 260 55 16 TextLabel 0 0 621 31 7 5 0 0 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 Sans Serif 10 75 false true false false <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:600; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt; font-weight:400; color:#ffffff;">StationLabel</span></p></body></html> 0 10 410 5 24 20 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 logo 10 40 130 130 13 13 0 0 16 16 13 Click to go to this album page true 0 180 621 51 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 210 0 57 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 210 0 57 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 210 0 57 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 QFrame::NoFrame QFrame::Raised 14 0 16 51 ../data/volume_left.png 120 0 21 51 ../data/volume_right.png 30 18 81 16 Qt::Horizontal 153 0 41 51 Love this track ../data/buttons/love_enabled.png Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter|Qt::AlignHCenter 193 0 41 51 Skip this track ../data/buttons/skip_enabled.png Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter|Qt::AlignHCenter 233 0 41 51 Ban this track ../data/buttons/ban_enabled.png Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter|Qt::AlignHCenter 396 0 41 51 Options... ../data/buttons/config_enabled.png 354 0 41 51 true Stop playback ../data/buttons/stop_enabled.png Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter|Qt::AlignHCenter 155 40 221 136 0 6 0 6 0 5 0 0 ../data/artist.png Qt::AlignTop Sans Serif 10 50 false false false false 13 Click to go to this artist page Artist Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter Qt::Vertical 20 3 0 6 0 5 0 0 ../data/album.png Qt::AlignTop Sans Serif 10 50 false false false false 13 Click to go to this album page Album Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter Qt::Vertical 20 3 0 6 0 5 0 0 ../data/song.png Qt::AlignTop Sans Serif 10 50 false false false false 13 Click to go to this track page Track Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter Qt::Vertical QSizePolicy::Fixed 20 4 16777215 15 Qt::Vertical QSizePolicy::Fixed 20 5 391 31 50 149 ../data/curve.png 410 70 21 21 Write a Journal about this Song ../data/buttons/blog_normal.png 411 40 20 21 Tag this Song ../data/buttons/tag_normal.png ProgressFrame QProgressBar
src/progressframe.h
0
LinkLabel QLabel
src/linklabel.h
0
SlidingLabel QLabel
src/slidinglabel.h
0
ImageButton QLabel
src/imagebutton.h
0
lastfm-player-1.1.4_p1910/src/rtaudio/0000755000175000001440000000000010352604630016372 5ustar daviduserslastfm-player-1.1.4_p1910/src/rtaudio/RtAudio.cpp0000644000175000001440000111277210352604467020470 0ustar davidusers/************************************************************************/ /*! \class RtAudio \brief Realtime audio i/o C++ classes. RtAudio provides a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, Jack, and OSS), SGI, Macintosh OS X (CoreAudio), and Windows (DirectSound and ASIO) operating systems. RtAudio WWW site: http://music.mcgill.ca/~gary/rtaudio/ RtAudio: realtime audio i/o C++ classes Copyright (c) 2001-2005 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is requested to send the modifications to the original developer so that they can be incorporated into the canonical version. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /************************************************************************/ // RtAudio: Version 3.0.3 (18 November 2005) #include #include "../settings.h" #ifdef Q_WS_X11 #define __LINUX_ALSA__ #endif #ifdef Q_WS_FREEBSD #define __LINUX_OSS__ #endif #ifdef Q_WS_MAC #define __MACOSX_CORE__ #endif #ifdef WIN32 #define __WINDOWS_DS__ #endif #include "RtAudio.h" #include #include // Static variable definitions. const unsigned int RtApi::MAX_SAMPLE_RATES = 14; const unsigned int RtApi::SAMPLE_RATES[] = { 4000, 5512, 8000, 9600, 11025, 16000, 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000 }; #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A) #define MUTEX_DESTROY(A) DeleteCriticalSection(A); #define MUTEX_LOCK(A) EnterCriticalSection(A) #define MUTEX_UNLOCK(A) LeaveCriticalSection(A) #else // pthread API #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL) #define MUTEX_DESTROY(A) pthread_mutex_destroy(A); #define MUTEX_LOCK(A) pthread_mutex_lock(A) #define MUTEX_UNLOCK(A) pthread_mutex_unlock(A) #endif #define WAVE_FORMAT_48M08 0x00001000 #define WAVE_FORMAT_48S08 0x00002000 #define WAVE_FORMAT_48M16 0x00004000 #define WAVE_FORMAT_48S16 0x00008000 #define WAVE_FORMAT_96M08 0x00010000 #define WAVE_FORMAT_96S08 0x00020000 #define WAVE_FORMAT_96M16 0x00040000 #define WAVE_FORMAT_96S16 0x00080000 // *************************************************** // // // Public common (OS-independent) methods. // // *************************************************** // RtAudio :: RtAudio( RtAudioApi api ) { initialize( api ); } RtAudio :: RtAudio( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int numberOfBuffers, RtAudioApi api ) { initialize( api ); try { rtapi_->openStream( outputDevice, outputChannels, inputDevice, inputChannels, format, sampleRate, bufferSize, numberOfBuffers ); } catch (RtError &exception) { // Deallocate the RtApi instance. delete rtapi_; throw exception; } } RtAudio :: RtAudio( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int *numberOfBuffers, RtAudioApi api ) { initialize( api ); try { rtapi_->openStream( outputDevice, outputChannels, inputDevice, inputChannels, format, sampleRate, bufferSize, numberOfBuffers ); } catch (RtError &exception) { // Deallocate the RtApi instance. delete rtapi_; throw exception; } } RtAudio :: ~RtAudio() { delete rtapi_; } void RtAudio :: openStream( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int numberOfBuffers ) { rtapi_->openStream( outputDevice, outputChannels, inputDevice, inputChannels, format, sampleRate, bufferSize, numberOfBuffers ); } void RtAudio :: openStream( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int *numberOfBuffers ) { rtapi_->openStream( outputDevice, outputChannels, inputDevice, inputChannels, format, sampleRate, bufferSize, *numberOfBuffers ); } void RtAudio::initialize( RtAudioApi api ) { rtapi_ = 0; // First look for a compiled match to a specified API value. If one // of these constructors throws an error, it will be passed up the // inheritance chain. #if defined(__LINUX_JACK__) if ( api == LINUX_JACK ) rtapi_ = new RtApiJack(); #endif #if defined(__LINUX_ALSA__) if ( api == LINUX_ALSA ) rtapi_ = new RtApiAlsa(); #endif #if defined(__LINUX_OSS__) if ( api == LINUX_OSS ) rtapi_ = new RtApiOss(); #endif #if defined(__WINDOWS_ASIO__) if ( api == WINDOWS_ASIO ) rtapi_ = new RtApiAsio(); #endif #if defined(__WINDOWS_DS__) if ( api == WINDOWS_DS ) rtapi_ = new RtApiDs(); #endif #if defined(__IRIX_AL__) if ( api == IRIX_AL ) rtapi_ = new RtApiAl(); #endif #if defined(__MACOSX_CORE__) if ( api == MACOSX_CORE ) rtapi_ = new RtApiCore(); #endif if ( rtapi_ ) return; if ( api > 0 ) { // No compiled support for specified API value. throw RtError( "RtAudio: no compiled support for specified API argument!", RtError::INVALID_PARAMETER ); } // No specified API ... search for "best" option. try { #if defined(__LINUX_JACK__) rtapi_ = new RtApiJack(); #elif defined(__WINDOWS_ASIO__) rtapi_ = new RtApiAsio(); #elif defined(__IRIX_AL__) rtapi_ = new RtApiAl(); #elif defined(__MACOSX_CORE__) rtapi_ = new RtApiCore(); #else ; #endif } catch (RtError &) { #if defined(__RTAUDIO_DEBUG__) fprintf(stderr, "\nRtAudio: no devices found for first api option (JACK, ASIO, Al, or CoreAudio).\n\n"); #endif rtapi_ = 0; } if ( rtapi_ ) return; // Try second API support if ( rtapi_ == 0 ) { try { #if defined(__LINUX_ALSA__) rtapi_ = new RtApiAlsa(); #elif defined(__WINDOWS_DS__) rtapi_ = new RtApiDs(); #else ; #endif } catch (RtError &) { #if defined(__RTAUDIO_DEBUG__) fprintf(stderr, "\nRtAudio: no devices found for second api option (Alsa or DirectSound).\n\n"); #endif rtapi_ = 0; } } if ( rtapi_ ) return; // Try third API support if ( rtapi_ == 0 ) { #if defined(__LINUX_OSS__) try { rtapi_ = new RtApiOss(); } catch (RtError &error) { rtapi_ = 0; } #else ; #endif } if ( rtapi_ == 0 ) { // No devices found. throw RtError( "RtAudio: no devices found for compiled audio APIs!", RtError::NO_DEVICES_FOUND ); } } RtApi :: RtApi() { stream_.mode = UNINITIALIZED; stream_.state = STREAM_STOPPED; stream_.apiHandle = 0; MUTEX_INITIALIZE(&stream_.mutex); } RtApi :: ~RtApi() { MUTEX_DESTROY(&stream_.mutex); } void RtApi :: openStream( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int *numberOfBuffers ) { this->openStream( outputDevice, outputChannels, inputDevice, inputChannels, format, sampleRate, bufferSize, *numberOfBuffers ); *numberOfBuffers = stream_.nBuffers; } void RtApi :: openStream( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int numberOfBuffers ) { if ( stream_.mode != UNINITIALIZED ) { sprintf(message_, "RtApi: only one open stream allowed per class instance."); error(RtError::INVALID_STREAM); } if (outputChannels < 1 && inputChannels < 1) { sprintf(message_,"RtApi: one or both 'channel' parameters must be greater than zero."); error(RtError::INVALID_PARAMETER); } if ( formatBytes(format) == 0 ) { sprintf(message_,"RtApi: 'format' parameter value is undefined."); error(RtError::INVALID_PARAMETER); } if ( outputChannels > 0 ) { if (outputDevice > nDevices_ || outputDevice < 0) { sprintf(message_,"RtApi: 'outputDevice' parameter value (%d) is invalid.", outputDevice); error(RtError::INVALID_PARAMETER); } } if ( inputChannels > 0 ) { if (inputDevice > nDevices_ || inputDevice < 0) { sprintf(message_,"RtApi: 'inputDevice' parameter value (%d) is invalid.", inputDevice); error(RtError::INVALID_PARAMETER); } } std::string errorMessages; clearStreamInfo(); bool result = FAILURE; int device, defaultDevice = 0; StreamMode mode; int channels; if ( outputChannels > 0 ) { mode = OUTPUT; channels = outputChannels; if ( outputDevice == 0 ) { // Try default device first. defaultDevice = getDefaultOutputDevice(); device = defaultDevice; } else device = outputDevice - 1; for ( int i=-1; i= 0 ) { if ( i == defaultDevice ) continue; device = i; } if ( devices_[device].probed == false ) { // If the device wasn't successfully probed before, try it // (again) now. clearDeviceInfo(&devices_[device]); probeDeviceInfo(&devices_[device]); } if ( devices_[device].probed ) result = probeDeviceOpen(device, mode, channels, sampleRate, format, bufferSize, numberOfBuffers); if ( result == SUCCESS ) break; errorMessages.append( " " ); errorMessages.append( message_ ); errorMessages.append( "\n" ); if ( outputDevice > 0 ) break; clearStreamInfo(); } } if ( inputChannels > 0 && ( result == SUCCESS || outputChannels <= 0 ) ) { mode = INPUT; channels = inputChannels; if ( inputDevice == 0 ) { // Try default device first. defaultDevice = getDefaultInputDevice(); device = defaultDevice; } else device = inputDevice - 1; for ( int i=-1; i= 0 ) { if ( i == defaultDevice ) continue; device = i; } if ( devices_[device].probed == false ) { // If the device wasn't successfully probed before, try it // (again) now. clearDeviceInfo(&devices_[device]); probeDeviceInfo(&devices_[device]); } if ( devices_[device].probed ) result = probeDeviceOpen( device, mode, channels, sampleRate, format, bufferSize, numberOfBuffers ); if ( result == SUCCESS ) break; errorMessages.append( " " ); errorMessages.append( message_ ); errorMessages.append( "\n" ); if ( inputDevice > 0 ) break; } } if ( result == SUCCESS ) return; // If we get here, all attempted probes failed. Close any opened // devices and clear the stream structure. if ( stream_.mode != UNINITIALIZED ) closeStream(); clearStreamInfo(); if ( ( outputDevice == 0 && outputChannels > 0 ) || ( inputDevice == 0 && inputChannels > 0 ) ) sprintf(message_,"RtApi: no devices found for given stream parameters: \n%s", errorMessages.c_str()); else sprintf(message_,"RtApi: unable to open specified device(s) with given stream parameters: \n%s", errorMessages.c_str()); error(RtError::INVALID_PARAMETER); return; } int RtApi :: getDeviceCount(void) { return devices_.size(); } RtApi::StreamState RtApi :: getStreamState( void ) const { return stream_.state; } RtAudioDeviceInfo RtApi :: getDeviceInfo( int device ) { if (device > (int) devices_.size() || device < 1) { sprintf(message_, "RtApi: invalid device specifier (%d)!", device); error(RtError::INVALID_DEVICE); } RtAudioDeviceInfo info; int deviceIndex = device - 1; // If the device wasn't successfully probed before, try it now (or again). if (devices_[deviceIndex].probed == false) { clearDeviceInfo(&devices_[deviceIndex]); probeDeviceInfo(&devices_[deviceIndex]); } info.name.append( devices_[deviceIndex].name ); info.probed = devices_[deviceIndex].probed; if ( info.probed == true ) { info.outputChannels = devices_[deviceIndex].maxOutputChannels; info.inputChannels = devices_[deviceIndex].maxInputChannels; info.duplexChannels = devices_[deviceIndex].maxDuplexChannels; for (unsigned int i=0; i #include #include #include #include #include #include #include #include #define DAC_NAME "/dev/dsp" #define MAX_DEVICES 16 #define MAX_CHANNELS 16 extern "C" void *ossCallbackHandler(void * ptr); RtApiOss :: RtApiOss() { this->initialize(); if (nDevices_ <= 0) { sprintf(message_, "RtApiOss: no Linux OSS audio devices found!"); error(RtError::NO_DEVICES_FOUND); } } RtApiOss :: ~RtApiOss() { if ( stream_.mode != UNINITIALIZED ) closeStream(); } void RtApiOss :: initialize(void) { // Count cards and devices nDevices_ = 0; // We check /dev/dsp before probing devices. /dev/dsp is supposed to // be a link to the "default" audio device, of the form /dev/dsp0, // /dev/dsp1, etc... However, I've seen many cases where /dev/dsp was a // real device, so we need to check for that. Also, sometimes the // link is to /dev/dspx and other times just dspx. I'm not sure how // the latter works, but it does. char device_name[16]; struct stat dspstat; int dsplink = -1; int i = 0; if (lstat(DAC_NAME, &dspstat) == 0) { if (S_ISLNK(dspstat.st_mode)) { i = readlink(DAC_NAME, device_name, sizeof(device_name)); if (i > 0) { device_name[i] = '\0'; if (i > 8) { // check for "/dev/dspx" if (!strncmp(DAC_NAME, device_name, 8)) dsplink = atoi(&device_name[8]); } else if (i > 3) { // check for "dspx" if (!strncmp("dsp", device_name, 3)) dsplink = atoi(&device_name[3]); } } else { sprintf(message_, "RtApiOss: cannot read value of symbolic link %s.", DAC_NAME); error(RtError::SYSTEM_ERROR); } } } else { sprintf(message_, "RtApiOss: cannot stat %s.", DAC_NAME); error(RtError::SYSTEM_ERROR); } // The OSS API doesn't provide a routine for determining the number // of devices. Thus, we'll just pursue a brute force method. The // idea is to start with /dev/dsp(0) and continue with higher device // numbers until we reach MAX_DSP_DEVICES. This should tell us how // many devices we have ... it is not a fullproof scheme, but hopefully // it will work most of the time. int fd = 0; RtApiDevice device; for (i=-1; i= 0) close(fd); device.name.erase(); device.name.append( (const char *)device_name, strlen(device_name)+1); devices_.push_back(device); nDevices_++; } } void RtApiOss :: probeDeviceInfo(RtApiDevice *info) { int i, fd, channels, mask; // The OSS API doesn't provide a means for probing the capabilities // of devices. Thus, we'll just pursue a brute force method. // First try for playback fd = open(info->name.c_str(), O_WRONLY | O_NONBLOCK); if (fd == -1) { // Open device failed ... either busy or doesn't exist if (errno == EBUSY || errno == EAGAIN) sprintf(message_, "RtApiOss: OSS playback device (%s) is busy and cannot be probed.", info->name.c_str()); else sprintf(message_, "RtApiOss: OSS playback device (%s) open error.", info->name.c_str()); error(RtError::DEBUG_WARNING); goto capture_probe; } // We have an open device ... see how many channels it can handle for (i=MAX_CHANNELS; i>0; i--) { channels = i; if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1) { // This would normally indicate some sort of hardware error, but under ALSA's // OSS emulation, it sometimes indicates an invalid channel value. Further, // the returned channel value is not changed. So, we'll ignore the possible // hardware error. continue; // try next channel number } // Check to see whether the device supports the requested number of channels if (channels != i ) continue; // try next channel number // If here, we found the largest working channel value break; } info->maxOutputChannels = i; // Now find the minimum number of channels it can handle for (i=1; i<=info->maxOutputChannels; i++) { channels = i; if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) continue; // try next channel number // If here, we found the smallest working channel value break; } info->minOutputChannels = i; close(fd); capture_probe: // Now try for capture fd = open(info->name.c_str(), O_RDONLY | O_NONBLOCK); if (fd == -1) { // Open device for capture failed ... either busy or doesn't exist if (errno == EBUSY || errno == EAGAIN) sprintf(message_, "RtApiOss: OSS capture device (%s) is busy and cannot be probed.", info->name.c_str()); else sprintf(message_, "RtApiOss: OSS capture device (%s) open error.", info->name.c_str()); error(RtError::DEBUG_WARNING); if (info->maxOutputChannels == 0) // didn't open for playback either ... device invalid return; goto probe_parameters; } // We have the device open for capture ... see how many channels it can handle for (i=MAX_CHANNELS; i>0; i--) { channels = i; if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) { continue; // as above } // If here, we found a working channel value break; } info->maxInputChannels = i; // Now find the minimum number of channels it can handle for (i=1; i<=info->maxInputChannels; i++) { channels = i; if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) continue; // try next channel number // If here, we found the smallest working channel value break; } info->minInputChannels = i; close(fd); if (info->maxOutputChannels == 0 && info->maxInputChannels == 0) { sprintf(message_, "RtApiOss: device (%s) reports zero channels for input and output.", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } // If device opens for both playback and capture, we determine the channels. if (info->maxOutputChannels == 0 || info->maxInputChannels == 0) goto probe_parameters; fd = open(info->name.c_str(), O_RDWR | O_NONBLOCK); if (fd == -1) goto probe_parameters; ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); ioctl(fd, SNDCTL_DSP_GETCAPS, &mask); if (mask & DSP_CAP_DUPLEX) { info->hasDuplexSupport = true; // We have the device open for duplex ... see how many channels it can handle for (i=MAX_CHANNELS; i>0; i--) { channels = i; if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) continue; // as above // If here, we found a working channel value break; } info->maxDuplexChannels = i; // Now find the minimum number of channels it can handle for (i=1; i<=info->maxDuplexChannels; i++) { channels = i; if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) continue; // try next channel number // If here, we found the smallest working channel value break; } info->minDuplexChannels = i; } close(fd); probe_parameters: // At this point, we need to figure out the supported data formats // and sample rates. We'll proceed by openning the device in the // direction with the maximum number of channels, or playback if // they are equal. This might limit our sample rate options, but so // be it. if (info->maxOutputChannels >= info->maxInputChannels) { fd = open(info->name.c_str(), O_WRONLY | O_NONBLOCK); channels = info->maxOutputChannels; } else { fd = open(info->name.c_str(), O_RDONLY | O_NONBLOCK); channels = info->maxInputChannels; } if (fd == -1) { // We've got some sort of conflict ... abort sprintf(message_, "RtApiOss: device (%s) won't reopen during probe.", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } // We have an open device ... set to maximum channels. i = channels; if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) { // We've got some sort of conflict ... abort close(fd); sprintf(message_, "RtApiOss: device (%s) won't revert to previous channel setting.", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) { close(fd); sprintf(message_, "RtApiOss: device (%s) can't get supported audio formats.", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } // Probe the supported data formats ... we don't care about endian-ness just yet. int format; info->nativeFormats = 0; #if defined (AFMT_S32_BE) // This format does not seem to be in the 2.4 kernel version of OSS soundcard.h if (mask & AFMT_S32_BE) { format = AFMT_S32_BE; info->nativeFormats |= RTAUDIO_SINT32; } #endif #if defined (AFMT_S32_LE) /* This format is not in the 2.4.4 kernel version of OSS soundcard.h */ if (mask & AFMT_S32_LE) { format = AFMT_S32_LE; info->nativeFormats |= RTAUDIO_SINT32; } #endif if (mask & AFMT_S8) { format = AFMT_S8; info->nativeFormats |= RTAUDIO_SINT8; } if (mask & AFMT_S16_BE) { format = AFMT_S16_BE; info->nativeFormats |= RTAUDIO_SINT16; } if (mask & AFMT_S16_LE) { format = AFMT_S16_LE; info->nativeFormats |= RTAUDIO_SINT16; } // Check that we have at least one supported format if (info->nativeFormats == 0) { close(fd); sprintf(message_, "RtApiOss: device (%s) data format not supported by RtAudio.", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } // Set the format i = format; if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1 || format != i) { close(fd); sprintf(message_, "RtApiOss: device (%s) error setting data format.", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } // Probe the supported sample rates. info->sampleRates.clear(); for (unsigned int k=0; ksampleRates.push_back(speed); } if (info->sampleRates.size() == 0) { close(fd); sprintf(message_, "RtApiOss: no supported sample rates found for device (%s).", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } // That's all ... close the device and return close(fd); info->probed = true; return; } bool RtApiOss :: probeDeviceOpen(int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers) { int buffers, buffer_bytes, device_channels, device_format; int srate, temp, fd; int *handle = (int *) stream_.apiHandle; const char *name = devices_[device].name.c_str(); if (mode == OUTPUT) fd = open(name, O_WRONLY | O_NONBLOCK); else { // mode == INPUT if (stream_.mode == OUTPUT && stream_.device[0] == device) { // We just set the same device for playback ... close and reopen for duplex (OSS only). close(handle[0]); handle[0] = 0; // First check that the number previously set channels is the same. if (stream_.nUserChannels[0] != channels) { sprintf(message_, "RtApiOss: input/output channels must be equal for OSS duplex device (%s).", name); goto error; } fd = open(name, O_RDWR | O_NONBLOCK); } else fd = open(name, O_RDONLY | O_NONBLOCK); } if (fd == -1) { if (errno == EBUSY || errno == EAGAIN) sprintf(message_, "RtApiOss: device (%s) is busy and cannot be opened.", name); else sprintf(message_, "RtApiOss: device (%s) cannot be opened.", name); goto error; } // Now reopen in blocking mode. close(fd); if (mode == OUTPUT) fd = open(name, O_WRONLY | O_SYNC); else { // mode == INPUT if (stream_.mode == OUTPUT && stream_.device[0] == device) fd = open(name, O_RDWR | O_SYNC); else fd = open(name, O_RDONLY | O_SYNC); } if (fd == -1) { sprintf(message_, "RtApiOss: device (%s) cannot be opened.", name); goto error; } // Get the sample format mask int mask; if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) { close(fd); sprintf(message_, "RtApiOss: device (%s) can't get supported audio formats.", name); goto error; } // Determine how to set the device format. stream_.userFormat = format; device_format = -1; stream_.doByteSwap[mode] = false; if (format == RTAUDIO_SINT8) { if (mask & AFMT_S8) { device_format = AFMT_S8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } } else if (format == RTAUDIO_SINT16) { if (mask & AFMT_S16_NE) { device_format = AFMT_S16_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } #if BYTE_ORDER == LITTLE_ENDIAN else if (mask & AFMT_S16_BE) { device_format = AFMT_S16_BE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; stream_.doByteSwap[mode] = true; } #else else if (mask & AFMT_S16_LE) { device_format = AFMT_S16_LE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; stream_.doByteSwap[mode] = true; } #endif } #if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE) else if (format == RTAUDIO_SINT32) { if (mask & AFMT_S32_NE) { device_format = AFMT_S32_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; } #if BYTE_ORDER == LITTLE_ENDIAN else if (mask & AFMT_S32_BE) { device_format = AFMT_S32_BE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; stream_.doByteSwap[mode] = true; } #else else if (mask & AFMT_S32_LE) { device_format = AFMT_S32_LE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; stream_.doByteSwap[mode] = true; } #endif } #endif if (device_format == -1) { // The user requested format is not natively supported by the device. if (mask & AFMT_S16_NE) { device_format = AFMT_S16_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; } #if BYTE_ORDER == LITTLE_ENDIAN else if (mask & AFMT_S16_BE) { device_format = AFMT_S16_BE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; stream_.doByteSwap[mode] = true; } #else else if (mask & AFMT_S16_LE) { device_format = AFMT_S16_LE; stream_.deviceFormat[mode] = RTAUDIO_SINT16; stream_.doByteSwap[mode] = true; } #endif #if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE) else if (mask & AFMT_S32_NE) { device_format = AFMT_S32_NE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; } #if BYTE_ORDER == LITTLE_ENDIAN else if (mask & AFMT_S32_BE) { device_format = AFMT_S32_BE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; stream_.doByteSwap[mode] = true; } #else else if (mask & AFMT_S32_LE) { device_format = AFMT_S32_LE; stream_.deviceFormat[mode] = RTAUDIO_SINT32; stream_.doByteSwap[mode] = true; } #endif #endif else if (mask & AFMT_S8) { device_format = AFMT_S8; stream_.deviceFormat[mode] = RTAUDIO_SINT8; } } if (stream_.deviceFormat[mode] == 0) { // This really shouldn't happen ... close(fd); sprintf(message_, "RtApiOss: device (%s) data format not supported by RtAudio.", name); goto error; } // Determine the number of channels for this device. Note that the // channel value requested by the user might be < min_X_Channels. stream_.nUserChannels[mode] = channels; device_channels = channels; if (mode == OUTPUT) { if (channels < devices_[device].minOutputChannels) device_channels = devices_[device].minOutputChannels; } else { // mode == INPUT if (stream_.mode == OUTPUT && stream_.device[0] == device) { // We're doing duplex setup here. if (channels < devices_[device].minDuplexChannels) device_channels = devices_[device].minDuplexChannels; } else { if (channels < devices_[device].minInputChannels) device_channels = devices_[device].minInputChannels; } } stream_.nDeviceChannels[mode] = device_channels; // Attempt to set the buffer size. According to OSS, the minimum // number of buffers is two. The supposed minimum buffer size is 16 // bytes, so that will be our lower bound. The argument to this // call is in the form 0xMMMMSSSS (hex), where the buffer size (in // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM. // We'll check the actual value used near the end of the setup // procedure. buffer_bytes = *bufferSize * formatBytes(stream_.deviceFormat[mode]) * device_channels; if (buffer_bytes < 16) buffer_bytes = 16; buffers = numberOfBuffers; if (buffers < 2) buffers = 2; temp = ((int) buffers << 16) + (int)(log10((double)buffer_bytes)/log10(2.0)); if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &temp)) { close(fd); sprintf(message_, "RtApiOss: error setting fragment size for device (%s).", name); goto error; } stream_.nBuffers = buffers; // Set the data format. temp = device_format; if (ioctl(fd, SNDCTL_DSP_SETFMT, &device_format) == -1 || device_format != temp) { close(fd); sprintf(message_, "RtApiOss: error setting data format for device (%s).", name); goto error; } // Set the number of channels. temp = device_channels; if (ioctl(fd, SNDCTL_DSP_CHANNELS, &device_channels) == -1 || device_channels != temp) { close(fd); sprintf(message_, "RtApiOss: error setting %d channels on device (%s).", temp, name); goto error; } // Set the sample rate. srate = sampleRate; temp = srate; if (ioctl(fd, SNDCTL_DSP_SPEED, &srate) == -1) { close(fd); sprintf(message_, "RtApiOss: error setting sample rate = %d on device (%s).", temp, name); goto error; } // Verify the sample rate setup worked. if (abs(srate - temp) > 100) { close(fd); sprintf(message_, "RtApiOss: error ... audio device (%s) doesn't support sample rate of %d.", name, temp); goto error; } stream_.sampleRate = sampleRate; if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &buffer_bytes) == -1) { close(fd); sprintf(message_, "RtApiOss: error getting buffer size for device (%s).", name); goto error; } // Save buffer size (in sample frames). *bufferSize = buffer_bytes / (formatBytes(stream_.deviceFormat[mode]) * device_channels); stream_.bufferSize = *bufferSize; if (mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) { // We're doing duplex setup here. stream_.deviceFormat[0] = stream_.deviceFormat[1]; stream_.nDeviceChannels[0] = device_channels; } // Allocate the stream handles if necessary and then save. if ( stream_.apiHandle == 0 ) { handle = (int *) calloc(2, sizeof(int)); stream_.apiHandle = (void *) handle; handle[0] = 0; handle[1] = 0; } else { handle = (int *) stream_.apiHandle; } handle[mode] = fd; // Set flags for buffer conversion stream_.doConvertBuffer[mode] = false; if (stream_.userFormat != stream_.deviceFormat[mode]) stream_.doConvertBuffer[mode] = true; if (stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode]) stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { long buffer_bytes; if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) buffer_bytes = stream_.nUserChannels[0]; else buffer_bytes = stream_.nUserChannels[1]; buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); if (stream_.userBuffer) free(stream_.userBuffer); stream_.userBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.userBuffer == NULL) { close(fd); sprintf(message_, "RtApiOss: error allocating user buffer memory (%s).", name); goto error; } } if ( stream_.doConvertBuffer[mode] ) { long buffer_bytes; bool makeBuffer = true; if ( mode == OUTPUT ) buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); else { // mode == INPUT buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( buffer_bytes < bytes_out ) makeBuffer = false; } } if ( makeBuffer ) { buffer_bytes *= *bufferSize; if (stream_.deviceBuffer) free(stream_.deviceBuffer); stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.deviceBuffer == NULL) { close(fd); sprintf(message_, "RtApiOss: error allocating device buffer memory (%s).", name); goto error; } } } stream_.device[mode] = device; stream_.state = STREAM_STOPPED; if ( stream_.mode == OUTPUT && mode == INPUT ) { stream_.mode = DUPLEX; if (stream_.device[0] == device) handle[0] = fd; } else stream_.mode = mode; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) { if (mode == INPUT) { // convert device to user buffer stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; stream_.convertInfo[mode].outFormat = stream_.userFormat; } else { // convert user to device buffer stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; stream_.convertInfo[mode].inFormat = stream_.userFormat; stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; } if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; else stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; // Set up the interleave/deinterleave offsets. if ( mode == INPUT && stream_.deInterleave[1] ) { for (int k=0; kstopStream(); } void RtApiOss :: setStreamCallback(RtAudioCallback callback, void *userData) { verifyStream(); CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; if ( info->usingCallback ) { sprintf(message_, "RtApiOss: A callback is already set for this stream!"); error(RtError::WARNING); return; } info->callback = (void *) callback; info->userData = userData; info->usingCallback = true; info->object = (void *) this; // Set the thread attributes for joinable and realtime scheduling // priority. The higher priority will only take affect if the // program is run as root or suid. pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_attr_setschedpolicy(&attr, SCHED_OTHER); int err = pthread_create(&(info->thread), &attr, ossCallbackHandler, &stream_.callbackInfo); pthread_attr_destroy(&attr); if (err) { info->usingCallback = false; sprintf(message_, "RtApiOss: error starting callback thread!"); error(RtError::THREAD_ERROR); } } void RtApiOss :: cancelStreamCallback() { verifyStream(); if (stream_.callbackInfo.usingCallback) { if (stream_.state == STREAM_RUNNING) stopStream(); MUTEX_LOCK(&stream_.mutex); stream_.callbackInfo.usingCallback = false; pthread_join(stream_.callbackInfo.thread, NULL); stream_.callbackInfo.thread = 0; stream_.callbackInfo.callback = NULL; stream_.callbackInfo.userData = NULL; MUTEX_UNLOCK(&stream_.mutex); } } extern "C" void *ossCallbackHandler(void *ptr) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiOss *object = (RtApiOss *) info->object; bool *usingCallback = &info->usingCallback; while ( *usingCallback ) { pthread_testcancel(); try { object->tickStream(); } catch (RtError &exception) { fprintf(stderr, "\nRtApiOss: callback thread error (%s) ... closing thread.\n\n", exception.getMessageString()); break; } } return 0; } //******************** End of __LINUX_OSS__ *********************// #endif #if defined(__MACOSX_CORE__) // The OS X CoreAudio API is designed to use a separate callback // procedure for each of its audio devices. A single RtAudio duplex // stream using two different devices is supported here, though it // cannot be guaranteed to always behave correctly because we cannot // synchronize these two callbacks. This same functionality can be // achieved with better synchrony by opening two separate streams for // the devices and using RtAudio blocking calls (i.e. tickStream()). // // A property listener is installed for over/underrun information. // However, no functionality is currently provided to allow property // listeners to trigger user handlers because it is unclear what could // be done if a critical stream parameter (buffer size, sample rate, // device disconnect) notification arrived. The listeners entail // quite a bit of extra code and most likely, a user program wouldn't // be prepared for the result anyway. // A structure to hold various information related to the CoreAudio API // implementation. struct CoreHandle { UInt32 index[2]; bool stopStream; bool xrun; char *deviceBuffer; pthread_cond_t condition; CoreHandle() :stopStream(false), xrun(false), deviceBuffer(0) {} }; RtApiCore :: RtApiCore() { this->initialize(); if (nDevices_ <= 0) { sprintf(message_, "RtApiCore: no Macintosh OS-X Core Audio devices found!"); error(RtError::NO_DEVICES_FOUND); } } RtApiCore :: ~RtApiCore() { // The subclass destructor gets called before the base class // destructor, so close an existing stream before deallocating // apiDeviceId memory. if ( stream_.mode != UNINITIALIZED ) closeStream(); // Free our allocated apiDeviceId memory. AudioDeviceID *id; for ( unsigned int i=0; iapiDeviceId; err = AudioDeviceGetProperty( *id, 0, false, kAudioDevicePropertyDeviceManufacturer, &dataSize, name ); if (err != noErr) { sprintf( message_, "RtApiCore: OS-X error getting device manufacturer." ); error(RtError::DEBUG_WARNING); return; } strncpy(fullname, name, 256); strcat(fullname, ": " ); dataSize = 256; err = AudioDeviceGetProperty( *id, 0, false, kAudioDevicePropertyDeviceName, &dataSize, name ); if (err != noErr) { sprintf( message_, "RtApiCore: OS-X error getting device name." ); error(RtError::DEBUG_WARNING); return; } strncat(fullname, name, 254); info->name.erase(); info->name.append( (const char *)fullname, strlen(fullname)+1); // Get output channel information. unsigned int i, minChannels = 0, maxChannels = 0, nStreams = 0; AudioBufferList *bufferList = nil; err = AudioDeviceGetPropertyInfo( *id, 0, false, kAudioDevicePropertyStreamConfiguration, &dataSize, NULL ); if (err == noErr && dataSize > 0) { bufferList = (AudioBufferList *) malloc( dataSize ); if (bufferList == NULL) { sprintf(message_, "RtApiCore: memory allocation error!"); error(RtError::DEBUG_WARNING); return; } err = AudioDeviceGetProperty( *id, 0, false, kAudioDevicePropertyStreamConfiguration, &dataSize, bufferList ); if (err == noErr) { maxChannels = 0; minChannels = 1000; nStreams = bufferList->mNumberBuffers; for ( i=0; imBuffers[i].mNumberChannels; if ( bufferList->mBuffers[i].mNumberChannels < minChannels ) minChannels = bufferList->mBuffers[i].mNumberChannels; } } } free (bufferList); if (err != noErr || dataSize <= 0) { sprintf( message_, "RtApiCore: OS-X error getting output channels for device (%s).", info->name.c_str() ); error(RtError::DEBUG_WARNING); return; } if ( nStreams ) { if ( maxChannels > 0 ) info->maxOutputChannels = maxChannels; if ( minChannels > 0 ) info->minOutputChannels = minChannels; } // Get input channel information. bufferList = nil; err = AudioDeviceGetPropertyInfo( *id, 0, true, kAudioDevicePropertyStreamConfiguration, &dataSize, NULL ); if (err == noErr && dataSize > 0) { bufferList = (AudioBufferList *) malloc( dataSize ); if (bufferList == NULL) { sprintf(message_, "RtApiCore: memory allocation error!"); error(RtError::DEBUG_WARNING); return; } err = AudioDeviceGetProperty( *id, 0, true, kAudioDevicePropertyStreamConfiguration, &dataSize, bufferList ); if (err == noErr) { maxChannels = 0; minChannels = 1000; nStreams = bufferList->mNumberBuffers; for ( i=0; imBuffers[i].mNumberChannels < minChannels ) minChannels = bufferList->mBuffers[i].mNumberChannels; maxChannels += bufferList->mBuffers[i].mNumberChannels; } } } free (bufferList); if (err != noErr || dataSize <= 0) { sprintf( message_, "RtApiCore: OS-X error getting input channels for device (%s).", info->name.c_str() ); error(RtError::DEBUG_WARNING); return; } if ( nStreams ) { if ( maxChannels > 0 ) info->maxInputChannels = maxChannels; if ( minChannels > 0 ) info->minInputChannels = minChannels; } // If device opens for both playback and capture, we determine the channels. if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) { info->hasDuplexSupport = true; info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ? info->maxInputChannels : info->maxOutputChannels; info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ? info->minInputChannels : info->minOutputChannels; } // Probe the device sample rate and data format parameters. The // core audio query mechanism is performed on a "stream" // description, which can have a variable number of channels and // apply to input or output only. // Create a stream description structure. AudioStreamBasicDescription description; dataSize = sizeof( AudioStreamBasicDescription ); memset(&description, 0, sizeof(AudioStreamBasicDescription)); bool isInput = false; if ( info->maxOutputChannels == 0 ) isInput = true; bool isDuplex = false; if ( info->maxDuplexChannels > 0 ) isDuplex = true; // Determine the supported sample rates. info->sampleRates.clear(); for (unsigned int k=0; ksampleRates.push_back( SAMPLE_RATES[k] ); } if (info->sampleRates.size() == 0) { sprintf( message_, "RtApiCore: No supported sample rates found for OS-X device (%s).", info->name.c_str() ); error(RtError::DEBUG_WARNING); return; } // Determine the supported data formats. info->nativeFormats = 0; description.mFormatID = kAudioFormatLinearPCM; description.mBitsPerChannel = 8; description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_SINT8; else { description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_SINT8; } description.mBitsPerChannel = 16; description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_SINT16; else { description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_SINT16; } description.mBitsPerChannel = 32; description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_SINT32; else { description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_SINT32; } description.mBitsPerChannel = 24; description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsAlignedHigh | kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_SINT24; else { description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_SINT24; } description.mBitsPerChannel = 32; description.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_FLOAT32; else { description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_FLOAT32; } description.mBitsPerChannel = 64; description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_FLOAT64; else { description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian; if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) ) info->nativeFormats |= RTAUDIO_FLOAT64; } // Check that we have at least one supported format. if (info->nativeFormats == 0) { sprintf(message_, "RtApiCore: OS-X device (%s) data format not supported by RtAudio.", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } info->probed = true; } OSStatus callbackHandler( AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* infoPointer ) { CallbackInfo *info = (CallbackInfo *) infoPointer; RtApiCore *object = (RtApiCore *) info->object; try { object->callbackEvent( inDevice, (void *)inInputData, (void *)outOutputData ); } catch (RtError &exception) { fprintf(stderr, "\nRtApiCore: callback handler error (%s)!\n\n", exception.getMessageString()); return kAudioHardwareUnspecifiedError; } return kAudioHardwareNoError; } OSStatus deviceListener( AudioDeviceID inDevice, UInt32 channel, Boolean isInput, AudioDevicePropertyID propertyID, void* handlePointer ) { CoreHandle *handle = (CoreHandle *) handlePointer; if ( propertyID == kAudioDeviceProcessorOverload ) { if ( isInput ) fprintf(stderr, "\nRtApiCore: OS-X audio input overrun detected!\n"); else fprintf(stderr, "\nRtApiCore: OS-X audio output underrun detected!\n"); handle->xrun = true; } return kAudioHardwareNoError; } bool RtApiCore :: probeDeviceOpen( int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers ) { // Setup for stream mode. bool isInput = false; AudioDeviceID id = *((AudioDeviceID *) devices_[device].apiDeviceId); if ( mode == INPUT ) isInput = true; // Search for a stream which contains the desired number of channels. OSStatus err = noErr; UInt32 dataSize; unsigned int deviceChannels, nStreams = 0; UInt32 iChannel = 0, iStream = 0; AudioBufferList *bufferList = nil; err = AudioDeviceGetPropertyInfo( id, 0, isInput, kAudioDevicePropertyStreamConfiguration, &dataSize, NULL ); if (err == noErr && dataSize > 0) { bufferList = (AudioBufferList *) malloc( dataSize ); if (bufferList == NULL) { sprintf(message_, "RtApiCore: memory allocation error in probeDeviceOpen()!"); error(RtError::DEBUG_WARNING); return FAILURE; } err = AudioDeviceGetProperty( id, 0, isInput, kAudioDevicePropertyStreamConfiguration, &dataSize, bufferList ); if (err == noErr) { stream_.deInterleave[mode] = false; nStreams = bufferList->mNumberBuffers; for ( iStream=0; iStreammBuffers[iStream].mNumberChannels >= (unsigned int) channels ) break; iChannel += bufferList->mBuffers[iStream].mNumberChannels; } // If we didn't find a single stream above, see if we can meet // the channel specification in mono mode (i.e. using separate // non-interleaved buffers). This can only work if there are N // consecutive one-channel streams, where N is the number of // desired channels. iChannel = 0; if ( iStream >= nStreams && nStreams >= (unsigned int) channels ) { int counter = 0; for ( iStream=0; iStreammBuffers[iStream].mNumberChannels == 1 ) counter++; else counter = 0; if ( counter == channels ) { iStream -= channels - 1; iChannel -= channels - 1; stream_.deInterleave[mode] = true; break; } iChannel += bufferList->mBuffers[iStream].mNumberChannels; } } } } if (err != noErr || dataSize <= 0) { if ( bufferList ) free( bufferList ); sprintf( message_, "RtApiCore: OS-X error getting channels for device (%s).", devices_[device].name.c_str() ); error(RtError::DEBUG_WARNING); return FAILURE; } if (iStream >= nStreams) { free (bufferList); sprintf( message_, "RtApiCore: unable to find OS-X audio stream on device (%s) for requested channels (%d).", devices_[device].name.c_str(), channels ); error(RtError::DEBUG_WARNING); return FAILURE; } // This is ok even for mono mode ... it gets updated later. deviceChannels = bufferList->mBuffers[iStream].mNumberChannels; free (bufferList); // Determine the buffer size. AudioValueRange bufferRange; dataSize = sizeof(AudioValueRange); err = AudioDeviceGetProperty( id, 0, isInput, kAudioDevicePropertyBufferSizeRange, &dataSize, &bufferRange); if (err != noErr) { sprintf( message_, "RtApiCore: OS-X error getting buffer size range for device (%s).", devices_[device].name.c_str() ); error(RtError::DEBUG_WARNING); return FAILURE; } long bufferBytes = *bufferSize * deviceChannels * formatBytes(RTAUDIO_FLOAT32); if (bufferRange.mMinimum > bufferBytes) bufferBytes = (int) bufferRange.mMinimum; else if (bufferRange.mMaximum < bufferBytes) bufferBytes = (int) bufferRange.mMaximum; // Set the buffer size. For mono mode, I'm assuming we only need to // make this setting for the first channel. UInt32 theSize = (UInt32) bufferBytes; dataSize = sizeof( UInt32); err = AudioDeviceSetProperty(id, NULL, 0, isInput, kAudioDevicePropertyBufferSize, dataSize, &theSize); if (err != noErr) { sprintf( message_, "RtApiCore: OS-X error setting the buffer size for device (%s).", devices_[device].name.c_str() ); error(RtError::DEBUG_WARNING); return FAILURE; } // If attempting to setup a duplex stream, the bufferSize parameter // MUST be the same in both directions! *bufferSize = bufferBytes / ( deviceChannels * formatBytes(RTAUDIO_FLOAT32) ); if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) { sprintf( message_, "RtApiCore: OS-X error setting buffer size for duplex stream on device (%s).", devices_[device].name.c_str() ); error(RtError::DEBUG_WARNING); return FAILURE; } stream_.bufferSize = *bufferSize; stream_.nBuffers = 1; // Set the stream format description. Do for each channel in mono mode. AudioStreamBasicDescription description; dataSize = sizeof( AudioStreamBasicDescription ); if ( stream_.deInterleave[mode] ) nStreams = channels; else nStreams = 1; for ( unsigned int i=0; i 1 && stream_.deInterleave[mode]) stream_.doConvertBuffer[mode] = true; // Allocate our CoreHandle structure for the stream. CoreHandle *handle; if ( stream_.apiHandle == 0 ) { handle = (CoreHandle *) calloc(1, sizeof(CoreHandle)); if ( handle == NULL ) { sprintf(message_, "RtApiCore: OS-X error allocating coreHandle memory (%s).", devices_[device].name.c_str()); goto error; } handle->index[0] = 0; handle->index[1] = 0; if ( pthread_cond_init(&handle->condition, NULL) ) { sprintf(message_, "RtApiCore: error initializing pthread condition variable (%s).", devices_[device].name.c_str()); goto error; } stream_.apiHandle = (void *) handle; } else handle = (CoreHandle *) stream_.apiHandle; handle->index[mode] = iStream; // Allocate necessary internal buffers. if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { long buffer_bytes; if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) buffer_bytes = stream_.nUserChannels[0]; else buffer_bytes = stream_.nUserChannels[1]; buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); if (stream_.userBuffer) free(stream_.userBuffer); stream_.userBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.userBuffer == NULL) { sprintf(message_, "RtApiCore: OS-X error allocating user buffer memory (%s).", devices_[device].name.c_str()); goto error; } } if ( stream_.deInterleave[mode] ) { long buffer_bytes; bool makeBuffer = true; if ( mode == OUTPUT ) buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); else { // mode == INPUT buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( buffer_bytes < bytes_out ) makeBuffer = false; } } if ( makeBuffer ) { buffer_bytes *= *bufferSize; if (stream_.deviceBuffer) free(stream_.deviceBuffer); stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.deviceBuffer == NULL) { sprintf(message_, "RtApiCore: error allocating device buffer memory (%s).", devices_[device].name.c_str()); goto error; } // If not de-interleaving, we point stream_.deviceBuffer to the // OS X supplied device buffer before doing any necessary data // conversions. This presents a problem if we have a duplex // stream using one device which needs de-interleaving and // another device which doesn't. So, save a pointer to our own // device buffer in the CallbackInfo structure. handle->deviceBuffer = stream_.deviceBuffer; } } stream_.sampleRate = sampleRate; stream_.device[mode] = device; stream_.state = STREAM_STOPPED; stream_.callbackInfo.object = (void *) this; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) { if (mode == INPUT) { // convert device to user buffer stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; stream_.convertInfo[mode].outFormat = stream_.userFormat; } else { // convert user to device buffer stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; stream_.convertInfo[mode].inFormat = stream_.userFormat; stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; } if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; else stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; // Set up the interleave/deinterleave offsets. if ( mode == INPUT && stream_.deInterleave[1] ) { for (int k=0; kcondition); free(handle); stream_.apiHandle = 0; } if (stream_.userBuffer) { free(stream_.userBuffer); stream_.userBuffer = 0; } error(RtError::DEBUG_WARNING); return FAILURE; } void RtApiCore :: closeStream() { // We don't want an exception to be thrown here because this // function is called by our class destructor. So, do our own // stream check. if ( stream_.mode == UNINITIALIZED ) { sprintf(message_, "RtApiCore::closeStream(): no open stream to close!"); error(RtError::WARNING); return; } AudioDeviceID id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId ); if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { if (stream_.state == STREAM_RUNNING) AudioDeviceStop( id, callbackHandler ); AudioDeviceRemoveIOProc( id, callbackHandler ); } id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId ); if (stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1]) ) { if (stream_.state == STREAM_RUNNING) AudioDeviceStop( id, callbackHandler ); AudioDeviceRemoveIOProc( id, callbackHandler ); } if (stream_.userBuffer) { free(stream_.userBuffer); stream_.userBuffer = 0; } if ( stream_.deInterleave[0] || stream_.deInterleave[1] ) { free(stream_.deviceBuffer); stream_.deviceBuffer = 0; } CoreHandle *handle = (CoreHandle *) stream_.apiHandle; // Destroy pthread condition variable and free the CoreHandle structure. if ( handle ) { pthread_cond_destroy(&handle->condition); free( handle ); stream_.apiHandle = 0; } stream_.mode = UNINITIALIZED; } void RtApiCore :: startStream() { verifyStream(); if (stream_.state == STREAM_RUNNING) return; MUTEX_LOCK(&stream_.mutex); OSStatus err; AudioDeviceID id; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId ); err = AudioDeviceStart(id, callbackHandler); if (err != noErr) { sprintf(message_, "RtApiCore: OS-X error starting callback procedure on device (%s).", devices_[stream_.device[0]].name.c_str()); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } } if (stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1]) ) { id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId ); err = AudioDeviceStart(id, callbackHandler); if (err != noErr) { sprintf(message_, "RtApiCore: OS-X error starting input callback procedure on device (%s).", devices_[stream_.device[0]].name.c_str()); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } } CoreHandle *handle = (CoreHandle *) stream_.apiHandle; handle->stopStream = false; stream_.state = STREAM_RUNNING; MUTEX_UNLOCK(&stream_.mutex); } void RtApiCore :: stopStream() { verifyStream(); if (stream_.state == STREAM_STOPPED) return; // Change the state before the lock to improve shutdown response // when using a callback. stream_.state = STREAM_STOPPED; MUTEX_LOCK(&stream_.mutex); OSStatus err; AudioDeviceID id; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId ); err = AudioDeviceStop(id, callbackHandler); if (err != noErr) { sprintf(message_, "RtApiCore: OS-X error stopping callback procedure on device (%s).", devices_[stream_.device[0]].name.c_str()); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } } if (stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1]) ) { id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId ); err = AudioDeviceStop(id, callbackHandler); if (err != noErr) { sprintf(message_, "RtApiCore: OS-X error stopping input callback procedure on device (%s).", devices_[stream_.device[0]].name.c_str()); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } } MUTEX_UNLOCK(&stream_.mutex); } void RtApiCore :: abortStream() { stopStream(); } void RtApiCore :: tickStream() { verifyStream(); if (stream_.state == STREAM_STOPPED) return; if (stream_.callbackInfo.usingCallback) { sprintf(message_, "RtApiCore: tickStream() should not be used when a callback function is set!"); error(RtError::WARNING); return; } CoreHandle *handle = (CoreHandle *) stream_.apiHandle; MUTEX_LOCK(&stream_.mutex); pthread_cond_wait(&handle->condition, &stream_.mutex); MUTEX_UNLOCK(&stream_.mutex); } void RtApiCore :: callbackEvent( AudioDeviceID deviceId, void *inData, void *outData ) { verifyStream(); if (stream_.state == STREAM_STOPPED) return; CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; CoreHandle *handle = (CoreHandle *) stream_.apiHandle; AudioBufferList *inBufferList = (AudioBufferList *) inData; AudioBufferList *outBufferList = (AudioBufferList *) outData; if ( info->usingCallback && handle->stopStream ) { // Check if the stream should be stopped (via the previous user // callback return value). We stop the stream here, rather than // after the function call, so that output data can first be // processed. this->stopStream(); return; } MUTEX_LOCK(&stream_.mutex); // Invoke user callback first, to get fresh output data. Don't // invoke the user callback if duplex mode AND the input/output devices // are different AND this function is called for the input device. AudioDeviceID id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId ); if ( info->usingCallback && (stream_.mode != DUPLEX || deviceId == id ) ) { RtAudioCallback callback = (RtAudioCallback) info->callback; handle->stopStream = callback(stream_.userBuffer, stream_.bufferSize, info->userData); if ( handle->xrun == true ) { handle->xrun = false; MUTEX_UNLOCK(&stream_.mutex); return; } } if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == id ) ) { if (stream_.doConvertBuffer[0]) { if ( !stream_.deInterleave[0] ) stream_.deviceBuffer = (char *) outBufferList->mBuffers[handle->index[0]].mData; else stream_.deviceBuffer = handle->deviceBuffer; convertBuffer( stream_.deviceBuffer, stream_.userBuffer, stream_.convertInfo[0] ); if ( stream_.doByteSwap[0] ) byteSwapBuffer(stream_.deviceBuffer, stream_.bufferSize * stream_.nDeviceChannels[0], stream_.deviceFormat[0]); if ( stream_.deInterleave[0] ) { int bufferBytes = outBufferList->mBuffers[handle->index[0]].mDataByteSize; for ( int i=0; imBuffers[handle->index[0]+i].mData, &stream_.deviceBuffer[i*bufferBytes], bufferBytes ); } } } else { if (stream_.doByteSwap[0]) byteSwapBuffer(stream_.userBuffer, stream_.bufferSize * stream_.nUserChannels[0], stream_.userFormat); memcpy(outBufferList->mBuffers[handle->index[0]].mData, stream_.userBuffer, outBufferList->mBuffers[handle->index[0]].mDataByteSize ); } } id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId ); if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == id ) ) { if (stream_.doConvertBuffer[1]) { if ( stream_.deInterleave[1] ) { stream_.deviceBuffer = (char *) handle->deviceBuffer; int bufferBytes = inBufferList->mBuffers[handle->index[1]].mDataByteSize; for ( int i=0; imBuffers[handle->index[1]+i].mData, bufferBytes ); } } else stream_.deviceBuffer = (char *) inBufferList->mBuffers[handle->index[1]].mData; if ( stream_.doByteSwap[1] ) byteSwapBuffer(stream_.deviceBuffer, stream_.bufferSize * stream_.nDeviceChannels[1], stream_.deviceFormat[1]); convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] ); } else { memcpy(stream_.userBuffer, inBufferList->mBuffers[handle->index[1]].mData, inBufferList->mBuffers[handle->index[1]].mDataByteSize ); if (stream_.doByteSwap[1]) byteSwapBuffer(stream_.userBuffer, stream_.bufferSize * stream_.nUserChannels[1], stream_.userFormat); } } if ( !info->usingCallback && (stream_.mode != DUPLEX || deviceId == id ) ) pthread_cond_signal(&handle->condition); MUTEX_UNLOCK(&stream_.mutex); } void RtApiCore :: setStreamCallback(RtAudioCallback callback, void *userData) { verifyStream(); if ( stream_.callbackInfo.usingCallback ) { sprintf(message_, "RtApiCore: A callback is already set for this stream!"); error(RtError::WARNING); return; } stream_.callbackInfo.callback = (void *) callback; stream_.callbackInfo.userData = userData; stream_.callbackInfo.usingCallback = true; } void RtApiCore :: cancelStreamCallback() { verifyStream(); if (stream_.callbackInfo.usingCallback) { if (stream_.state == STREAM_RUNNING) stopStream(); MUTEX_LOCK(&stream_.mutex); stream_.callbackInfo.usingCallback = false; stream_.callbackInfo.userData = NULL; stream_.state = STREAM_STOPPED; stream_.callbackInfo.callback = NULL; MUTEX_UNLOCK(&stream_.mutex); } } //******************** End of __MACOSX_CORE__ *********************// #endif #if defined(__LINUX_JACK__) // JACK is a low-latency audio server, written primarily for the // GNU/Linux operating system. It can connect a number of different // applications to an audio device, as well as allowing them to share // audio between themselves. // // The JACK server must be running before RtApiJack can be instantiated. // RtAudio will report just a single "device", which is the JACK audio // server. The JACK server is typically started in a terminal as follows: // // .jackd -d alsa -d hw:0 // // or through an interface program such as qjackctl. Many of the // parameters normally set for a stream are fixed by the JACK server // and can be specified when the JACK server is started. In // particular, // // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4 // // specifies a sample rate of 44100 Hz, a buffer size of 512 sample // frames, and number of buffers = 4. Once the server is running, it // is not possible to override these values. If the values are not // specified in the command-line, the JACK server uses default values. #include #include // A structure to hold various information related to the Jack API // implementation. struct JackHandle { jack_client_t *client; jack_port_t **ports[2]; bool clientOpen; bool stopStream; pthread_cond_t condition; JackHandle() :client(0), clientOpen(false), stopStream(false) {} }; std::string jackmsg; static void jackerror (const char *desc) { jackmsg.erase(); jackmsg.append( desc, strlen(desc)+1 ); } RtApiJack :: RtApiJack() { this->initialize(); if (nDevices_ <= 0) { sprintf(message_, "RtApiJack: no Linux Jack server found or connection error (jack: %s)!", jackmsg.c_str()); error(RtError::NO_DEVICES_FOUND); } } RtApiJack :: ~RtApiJack() { if ( stream_.mode != UNINITIALIZED ) closeStream(); } void RtApiJack :: initialize(void) { nDevices_ = 0; // Tell the jack server to call jackerror() when it experiences an // error. This function saves the error message for subsequent // reporting via the normal RtAudio error function. jack_set_error_function( jackerror ); // Look for jack server and try to become a client. jack_client_t *client; if ( (client = jack_client_new( "RtApiJack" )) == 0) return; /* RtApiDevice device; // Determine the name of the device. device.name = "Jack Server"; devices_.push_back(device); nDevices_++; */ const char **ports; std::string port, prevPort; unsigned int nChannels = 0; ports = jack_get_ports( client, NULL, NULL, 0 ); if ( ports ) { port = (char *) ports[ nChannels ]; unsigned int colonPos = 0; do { port = (char *) ports[ nChannels ]; if ( (colonPos = port.find(":")) != std::string::npos ) { port = port.substr( 0, colonPos+1 ); if ( port != prevPort ) { RtApiDevice device; device.name = port; devices_.push_back( device ); nDevices_++; prevPort = port; } } } while ( ports[++nChannels] ); free( ports ); } jack_client_close(client); } void RtApiJack :: probeDeviceInfo(RtApiDevice *info) { // Look for jack server and try to become a client. jack_client_t *client; if ( (client = jack_client_new( "RtApiJack_Probe" )) == 0) { sprintf(message_, "RtApiJack: error connecting to Linux Jack server in probeDeviceInfo() (jack: %s)!", jackmsg.c_str()); error(RtError::WARNING); return; } // Get the current jack server sample rate. info->sampleRates.clear(); info->sampleRates.push_back( jack_get_sample_rate(client) ); // Count the available ports as device channels. Jack "input ports" // equal RtAudio output channels. const char **ports; char *port; unsigned int nChannels = 0; ports = jack_get_ports( client, info->name.c_str(), NULL, JackPortIsInput ); if ( ports ) { port = (char *) ports[nChannels]; while ( port ) port = (char *) ports[++nChannels]; free( ports ); info->maxOutputChannels = nChannels; info->minOutputChannels = 1; } // Jack "output ports" equal RtAudio input channels. nChannels = 0; ports = jack_get_ports( client, info->name.c_str(), NULL, JackPortIsOutput ); if ( ports ) { port = (char *) ports[nChannels]; while ( port ) port = (char *) ports[++nChannels]; free( ports ); info->maxInputChannels = nChannels; info->minInputChannels = 1; } if (info->maxOutputChannels == 0 && info->maxInputChannels == 0) { jack_client_close(client); sprintf(message_, "RtApiJack: error determining jack input/output channels!"); error(RtError::DEBUG_WARNING); return; } if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) { info->hasDuplexSupport = true; info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ? info->maxInputChannels : info->maxOutputChannels; info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ? info->minInputChannels : info->minOutputChannels; } // Get the jack data format type. There isn't much documentation // regarding supported data formats in jack. I'm assuming here that // the default type will always be a floating-point type, of length // equal to either 4 or 8 bytes. int sample_size = sizeof( jack_default_audio_sample_t ); if ( sample_size == 4 ) info->nativeFormats = RTAUDIO_FLOAT32; else if ( sample_size == 8 ) info->nativeFormats = RTAUDIO_FLOAT64; // Check that we have a supported format if (info->nativeFormats == 0) { jack_client_close(client); sprintf(message_, "RtApiJack: error determining jack server data format!"); error(RtError::DEBUG_WARNING); return; } jack_client_close(client); info->probed = true; } int jackCallbackHandler(jack_nframes_t nframes, void *infoPointer) { CallbackInfo *info = (CallbackInfo *) infoPointer; RtApiJack *object = (RtApiJack *) info->object; try { object->callbackEvent( (unsigned long) nframes ); } catch (RtError &exception) { fprintf(stderr, "\nRtApiJack: callback handler error (%s)!\n\n", exception.getMessageString()); return 0; } return 0; } void jackShutdown(void *infoPointer) { CallbackInfo *info = (CallbackInfo *) infoPointer; JackHandle *handle = (JackHandle *) info->apiInfo; handle->clientOpen = false; RtApiJack *object = (RtApiJack *) info->object; // Check current stream state. If stopped, then we'll assume this // was called as a result of a call to RtApiJack::stopStream (the // deactivation of a client handle causes this function to be called). // If not, we'll assume the Jack server is shutting down or some // other problem occurred and we should close the stream. if ( object->getStreamState() == RtApi::STREAM_STOPPED ) return; try { object->closeStream(); } catch (RtError &exception) { fprintf(stderr, "\nRtApiJack: jackShutdown error (%s)!\n\n", exception.getMessageString()); return; } fprintf(stderr, "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!!\n\n"); } int jackXrun( void * ) { fprintf(stderr, "\nRtApiJack: audio overrun/underrun reported!\n"); return 0; } bool RtApiJack :: probeDeviceOpen(int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers) { // Compare the jack server channels to the requested number of channels. if ( (mode == OUTPUT && devices_[device].maxOutputChannels < channels ) || (mode == INPUT && devices_[device].maxInputChannels < channels ) ) { sprintf(message_, "RtApiJack: the Jack server does not support requested channels!"); error(RtError::DEBUG_WARNING); return FAILURE; } JackHandle *handle = (JackHandle *) stream_.apiHandle; // Look for jack server and try to become a client (only do once per stream). char label[32]; jack_client_t *client = 0; if ( mode == OUTPUT || (mode == INPUT && stream_.mode != OUTPUT) ) { snprintf(label, 32, "RtApiJack"); if ( (client = jack_client_new( (const char *) label )) == 0) { sprintf(message_, "RtApiJack: cannot connect to Linux Jack server in probeDeviceOpen() (jack: %s)!", jackmsg.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } } else { // The handle must have been created on an earlier pass. client = handle->client; } // First, check the jack server sample rate. int jack_rate; jack_rate = (int) jack_get_sample_rate(client); if ( sampleRate != jack_rate ) { jack_client_close(client); sprintf( message_, "RtApiJack: the requested sample rate (%d) is different than the JACK server rate (%d).", sampleRate, jack_rate ); error(RtError::DEBUG_WARNING); return FAILURE; } stream_.sampleRate = jack_rate; // The jack server seems to support just a single floating-point // data type. Since we already checked it before, just use what we // found then. stream_.deviceFormat[mode] = devices_[device].nativeFormats; stream_.userFormat = format; // Jack always uses non-interleaved buffers. We'll need to // de-interleave if we have more than one channel. stream_.deInterleave[mode] = false; if ( channels > 1 ) stream_.deInterleave[mode] = true; // Jack always provides host byte-ordered data. stream_.doByteSwap[mode] = false; // Get the buffer size. The buffer size and number of buffers // (periods) is set when the jack server is started. stream_.bufferSize = (int) jack_get_buffer_size(client); *bufferSize = stream_.bufferSize; stream_.nDeviceChannels[mode] = channels; stream_.nUserChannels[mode] = channels; stream_.doConvertBuffer[mode] = false; if (stream_.userFormat != stream_.deviceFormat[mode]) stream_.doConvertBuffer[mode] = true; if (stream_.deInterleave[mode]) stream_.doConvertBuffer[mode] = true; // Allocate our JackHandle structure for the stream. if ( handle == 0 ) { handle = (JackHandle *) calloc(1, sizeof(JackHandle)); if ( handle == NULL ) { sprintf(message_, "RtApiJack: error allocating JackHandle memory (%s).", devices_[device].name.c_str()); goto error; } handle->ports[0] = 0; handle->ports[1] = 0; if ( pthread_cond_init(&handle->condition, NULL) ) { sprintf(message_, "RtApiJack: error initializing pthread condition variable!"); goto error; } stream_.apiHandle = (void *) handle; handle->client = client; handle->clientOpen = true; } // Allocate necessary internal buffers. if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { long buffer_bytes; if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) buffer_bytes = stream_.nUserChannels[0]; else buffer_bytes = stream_.nUserChannels[1]; buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); if (stream_.userBuffer) free(stream_.userBuffer); stream_.userBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.userBuffer == NULL) { sprintf(message_, "RtApiJack: error allocating user buffer memory (%s).", devices_[device].name.c_str()); goto error; } } if ( stream_.doConvertBuffer[mode] ) { long buffer_bytes; bool makeBuffer = true; if ( mode == OUTPUT ) buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); else { // mode == INPUT buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( buffer_bytes < bytes_out ) makeBuffer = false; } } if ( makeBuffer ) { buffer_bytes *= *bufferSize; if (stream_.deviceBuffer) free(stream_.deviceBuffer); stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.deviceBuffer == NULL) { sprintf(message_, "RtApiJack: error allocating device buffer memory (%s).", devices_[device].name.c_str()); goto error; } } } // Allocate memory for the Jack ports (channels) identifiers. handle->ports[mode] = (jack_port_t **) malloc (sizeof (jack_port_t *) * channels); if ( handle->ports[mode] == NULL ) { sprintf(message_, "RtApiJack: error allocating port handle memory (%s).", devices_[device].name.c_str()); goto error; } stream_.device[mode] = device; stream_.state = STREAM_STOPPED; stream_.callbackInfo.usingCallback = false; stream_.callbackInfo.object = (void *) this; stream_.callbackInfo.apiInfo = (void *) handle; if ( stream_.mode == OUTPUT && mode == INPUT ) // We had already set up the stream for output. stream_.mode = DUPLEX; else { stream_.mode = mode; jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo ); jack_set_xrun_callback( handle->client, jackXrun, NULL ); jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo ); } // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) { if (mode == INPUT) { // convert device to user buffer stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; stream_.convertInfo[mode].outFormat = stream_.userFormat; } else { // convert user to device buffer stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; stream_.convertInfo[mode].inFormat = stream_.userFormat; stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; } if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; else stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; // Set up the interleave/deinterleave offsets. if ( mode == INPUT && stream_.deInterleave[1] ) { for (int k=0; kcondition); if ( handle->clientOpen == true ) jack_client_close(handle->client); if ( handle->ports[0] ) free(handle->ports[0]); if ( handle->ports[1] ) free(handle->ports[1]); free( handle ); stream_.apiHandle = 0; } if (stream_.userBuffer) { free(stream_.userBuffer); stream_.userBuffer = 0; } error(RtError::DEBUG_WARNING); return FAILURE; } void RtApiJack :: closeStream() { // We don't want an exception to be thrown here because this // function is called by our class destructor. So, do our own // stream check. if ( stream_.mode == UNINITIALIZED ) { sprintf(message_, "RtApiJack::closeStream(): no open stream to close!"); error(RtError::WARNING); return; } JackHandle *handle = (JackHandle *) stream_.apiHandle; if ( handle && handle->clientOpen == true ) { if (stream_.state == STREAM_RUNNING) jack_deactivate(handle->client); jack_client_close(handle->client); } if ( handle ) { if ( handle->ports[0] ) free(handle->ports[0]); if ( handle->ports[1] ) free(handle->ports[1]); pthread_cond_destroy(&handle->condition); free( handle ); stream_.apiHandle = 0; } if (stream_.userBuffer) { free(stream_.userBuffer); stream_.userBuffer = 0; } if (stream_.deviceBuffer) { free(stream_.deviceBuffer); stream_.deviceBuffer = 0; } stream_.mode = UNINITIALIZED; } void RtApiJack :: startStream() { verifyStream(); if (stream_.state == STREAM_RUNNING) return; MUTEX_LOCK(&stream_.mutex); char label[64]; JackHandle *handle = (JackHandle *) stream_.apiHandle; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { for ( int i=0; iports[0][i] = jack_port_register(handle->client, (const char *)label, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); } } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { for ( int i=0; iports[1][i] = jack_port_register(handle->client, (const char *)label, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); } } if (jack_activate(handle->client)) { sprintf(message_, "RtApiJack: unable to activate JACK client!"); error(RtError::SYSTEM_ERROR); } const char **ports; int result; // Get the list of available ports. if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { ports = jack_get_ports(handle->client, devices_[stream_.device[0]].name.c_str(), NULL, JackPortIsInput); if ( ports == NULL) { sprintf(message_, "RtApiJack: error determining available jack input ports!"); error(RtError::SYSTEM_ERROR); } // Now make the port connections. Since RtAudio wasn't designed to // allow the user to select particular channels of a device, we'll // just open the first "nChannels" ports. for ( int i=0; iclient, jack_port_name(handle->ports[0][i]), ports[i] ); if ( result ) { free(ports); sprintf(message_, "RtApiJack: error connecting output ports!"); error(RtError::SYSTEM_ERROR); } } free(ports); } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { ports = jack_get_ports( handle->client, devices_[stream_.device[1]].name.c_str(), NULL, JackPortIsOutput ); if ( ports == NULL) { sprintf(message_, "RtApiJack: error determining available jack output ports!"); error(RtError::SYSTEM_ERROR); } // Now make the port connections. See note above. for ( int i=0; iclient, ports[i], jack_port_name(handle->ports[1][i]) ); if ( result ) { free(ports); sprintf(message_, "RtApiJack: error connecting input ports!"); error(RtError::SYSTEM_ERROR); } } free(ports); } handle->stopStream = false; stream_.state = STREAM_RUNNING; MUTEX_UNLOCK(&stream_.mutex); } void RtApiJack :: stopStream() { verifyStream(); if (stream_.state == STREAM_STOPPED) return; // Change the state before the lock to improve shutdown response // when using a callback. stream_.state = STREAM_STOPPED; MUTEX_LOCK(&stream_.mutex); JackHandle *handle = (JackHandle *) stream_.apiHandle; jack_deactivate(handle->client); MUTEX_UNLOCK(&stream_.mutex); } void RtApiJack :: abortStream() { stopStream(); } void RtApiJack :: tickStream() { verifyStream(); if (stream_.state == STREAM_STOPPED) return; if (stream_.callbackInfo.usingCallback) { sprintf(message_, "RtApiJack: tickStream() should not be used when a callback function is set!"); error(RtError::WARNING); return; } JackHandle *handle = (JackHandle *) stream_.apiHandle; MUTEX_LOCK(&stream_.mutex); pthread_cond_wait(&handle->condition, &stream_.mutex); MUTEX_UNLOCK(&stream_.mutex); } void RtApiJack :: callbackEvent( unsigned long nframes ) { verifyStream(); if (stream_.state == STREAM_STOPPED) return; CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; JackHandle *handle = (JackHandle *) stream_.apiHandle; if ( info->usingCallback && handle->stopStream ) { // Check if the stream should be stopped (via the previous user // callback return value). We stop the stream here, rather than // after the function call, so that output data can first be // processed. this->stopStream(); return; } MUTEX_LOCK(&stream_.mutex); // Invoke user callback first, to get fresh output data. if ( info->usingCallback ) { RtAudioCallback callback = (RtAudioCallback) info->callback; handle->stopStream = callback(stream_.userBuffer, stream_.bufferSize, info->userData); } jack_default_audio_sample_t *jackbuffer; long bufferBytes = nframes * sizeof(jack_default_audio_sample_t); if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if (stream_.doConvertBuffer[0]) { convertBuffer( stream_.deviceBuffer, stream_.userBuffer, stream_.convertInfo[0] ); for ( int i=0; iports[0][i], (jack_nframes_t) nframes); memcpy(jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes ); } } else { // single channel only jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[0][0], (jack_nframes_t) nframes); memcpy(jackbuffer, stream_.userBuffer, bufferBytes ); } } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { if (stream_.doConvertBuffer[1]) { for ( int i=0; iports[1][i], (jack_nframes_t) nframes); memcpy(&stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes ); } convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] ); } else { // single channel only jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[1][0], (jack_nframes_t) nframes); memcpy(stream_.userBuffer, jackbuffer, bufferBytes ); } } if ( !info->usingCallback ) pthread_cond_signal(&handle->condition); MUTEX_UNLOCK(&stream_.mutex); } void RtApiJack :: setStreamCallback(RtAudioCallback callback, void *userData) { verifyStream(); if ( stream_.callbackInfo.usingCallback ) { sprintf(message_, "RtApiJack: A callback is already set for this stream!"); error(RtError::WARNING); return; } stream_.callbackInfo.callback = (void *) callback; stream_.callbackInfo.userData = userData; stream_.callbackInfo.usingCallback = true; } void RtApiJack :: cancelStreamCallback() { verifyStream(); if (stream_.callbackInfo.usingCallback) { if (stream_.state == STREAM_RUNNING) stopStream(); MUTEX_LOCK(&stream_.mutex); stream_.callbackInfo.usingCallback = false; stream_.callbackInfo.userData = NULL; stream_.state = STREAM_STOPPED; stream_.callbackInfo.callback = NULL; MUTEX_UNLOCK(&stream_.mutex); } } #endif #if defined(__LINUX_ALSA__) #include #include #include // A structure to hold various information related to the ALSA API // implementation. struct AlsaHandle { snd_pcm_t *handles[2]; bool synchronized; char *tempBuffer; AlsaHandle() :synchronized(false), tempBuffer(0) {} }; extern "C" void *alsaCallbackHandler(void * ptr); RtApiAlsa :: RtApiAlsa() { this->initialize(); if (nDevices_ <= 0) { sprintf(message_, "RtApiAlsa: no Linux ALSA audio devices found!"); error(RtError::NO_DEVICES_FOUND); } } RtApiAlsa :: ~RtApiAlsa() { if ( stream_.mode != UNINITIALIZED ) closeStream(); } void RtApiAlsa :: initialize(void) { int card, subdevice, result; char name[64]; const char *cardId; snd_ctl_t *handle; snd_ctl_card_info_t *info; snd_ctl_card_info_alloca(&info); RtApiDevice device; // Count cards and devices nDevices_ = 0; card = -1; snd_card_next(&card); while ( card >= 0 ) { sprintf(name, "hw:%d", card); result = snd_ctl_open(&handle, name, 0); if (result < 0) { sprintf(message_, "RtApiAlsa: control open (%i): %s.", card, snd_strerror(result)); error(RtError::DEBUG_WARNING); goto next_card; } result = snd_ctl_card_info(handle, info); if (result < 0) { sprintf(message_, "RtApiAlsa: control hardware info (%i): %s.", card, snd_strerror(result)); error(RtError::DEBUG_WARNING); goto next_card; } cardId = snd_ctl_card_info_get_id(info); subdevice = -1; while (1) { result = snd_ctl_pcm_next_device(handle, &subdevice); if (result < 0) { sprintf(message_, "RtApiAlsa: control next device (%i): %s.", card, snd_strerror(result)); error(RtError::DEBUG_WARNING); break; } if (subdevice < 0) break; sprintf( name, "hw:%d,%d", card, subdevice ); // If a cardId exists and it contains at least one non-numeric // character, use it to identify the device. This avoids a bug // in ALSA such that a numeric string is interpreted as a device // number. for ( unsigned int i=0; iname.c_str(), 64 ); card = strtok(name, ","); err = snd_ctl_open(&chandle, card, SND_CTL_NONBLOCK); if (err < 0) { sprintf(message_, "RtApiAlsa: control open (%s): %s.", card, snd_strerror(err)); error(RtError::DEBUG_WARNING); return; } unsigned int dev = (unsigned int) atoi( strtok(NULL, ",") ); // First try for playback stream = SND_PCM_STREAM_PLAYBACK; snd_pcm_info_set_device(pcminfo, dev); snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_stream(pcminfo, stream); if ((err = snd_ctl_pcm_info(chandle, pcminfo)) < 0) { if (err == -ENOENT) { sprintf(message_, "RtApiAlsa: pcm device (%s) doesn't handle output!", info->name.c_str()); error(RtError::DEBUG_WARNING); } else { sprintf(message_, "RtApiAlsa: snd_ctl_pcm_info error for device (%s) output: %s", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); } goto capture_probe; } err = snd_pcm_open(&handle, info->name.c_str(), stream, open_mode | SND_PCM_NONBLOCK ); if (err < 0) { if ( err == EBUSY ) sprintf(message_, "RtApiAlsa: pcm playback device (%s) is busy: %s.", info->name.c_str(), snd_strerror(err)); else sprintf(message_, "RtApiAlsa: pcm playback open (%s) error: %s.", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); goto capture_probe; } // We have an open device ... allocate the parameter structure. err = snd_pcm_hw_params_any(handle, params); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); goto capture_probe; } // Get output channel information. unsigned int value; err = snd_pcm_hw_params_get_channels_min(params, &value); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware minimum channel probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); goto capture_probe; } info->minOutputChannels = value; err = snd_pcm_hw_params_get_channels_max(params, &value); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware maximum channel probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); goto capture_probe; } info->maxOutputChannels = value; snd_pcm_close(handle); capture_probe: // Now try for capture stream = SND_PCM_STREAM_CAPTURE; snd_pcm_info_set_stream(pcminfo, stream); err = snd_ctl_pcm_info(chandle, pcminfo); snd_ctl_close(chandle); if ( err < 0 ) { if (err == -ENOENT) { sprintf(message_, "RtApiAlsa: pcm device (%s) doesn't handle input!", info->name.c_str()); error(RtError::DEBUG_WARNING); } else { sprintf(message_, "RtApiAlsa: snd_ctl_pcm_info error for device (%s) input: %s", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); } if (info->maxOutputChannels == 0) // didn't open for playback either ... device invalid return; goto probe_parameters; } err = snd_pcm_open(&handle, info->name.c_str(), stream, open_mode | SND_PCM_NONBLOCK); if (err < 0) { if ( err == EBUSY ) sprintf(message_, "RtApiAlsa: pcm capture device (%s) is busy: %s.", info->name.c_str(), snd_strerror(err)); else sprintf(message_, "RtApiAlsa: pcm capture open (%s) error: %s.", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); if (info->maxOutputChannels == 0) // didn't open for playback either ... device invalid return; goto probe_parameters; } // We have an open capture device ... allocate the parameter structure. err = snd_pcm_hw_params_any(handle, params); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); if (info->maxOutputChannels > 0) goto probe_parameters; else return; } // Get input channel information. err = snd_pcm_hw_params_get_channels_min(params, &value); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware minimum in channel probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); if (info->maxOutputChannels > 0) goto probe_parameters; else return; } info->minInputChannels = value; err = snd_pcm_hw_params_get_channels_max(params, &value); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware maximum in channel probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); if (info->maxOutputChannels > 0) goto probe_parameters; else return; } info->maxInputChannels = value; snd_pcm_close(handle); // If device opens for both playback and capture, we determine the channels. if (info->maxOutputChannels == 0 || info->maxInputChannels == 0) goto probe_parameters; info->hasDuplexSupport = true; info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ? info->maxInputChannels : info->maxOutputChannels; info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ? info->minInputChannels : info->minOutputChannels; probe_parameters: // At this point, we just need to figure out the supported data // formats and sample rates. We'll proceed by opening the device in // the direction with the maximum number of channels, or playback if // they are equal. This might limit our sample rate options, but so // be it. if (info->maxOutputChannels >= info->maxInputChannels) stream = SND_PCM_STREAM_PLAYBACK; else stream = SND_PCM_STREAM_CAPTURE; err = snd_pcm_open(&handle, info->name.c_str(), stream, open_mode); if (err < 0) { sprintf(message_, "RtApiAlsa: pcm (%s) won't reopen during probe: %s.", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); return; } // We have an open device ... allocate the parameter structure. err = snd_pcm_hw_params_any(handle, params); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: hardware reopen probe error (%s): %s.", info->name.c_str(), snd_strerror(err)); error(RtError::DEBUG_WARNING); return; } // Test our discrete set of sample rate values. int dir = 0; info->sampleRates.clear(); for (unsigned int i=0; isampleRates.push_back(SAMPLE_RATES[i]); } if (info->sampleRates.size() == 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: no supported sample rates found for device (%s).", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } // Probe the supported data formats ... we don't care about endian-ness just yet snd_pcm_format_t format; info->nativeFormats = 0; format = SND_PCM_FORMAT_S8; if (snd_pcm_hw_params_test_format(handle, params, format) == 0) info->nativeFormats |= RTAUDIO_SINT8; format = SND_PCM_FORMAT_S16; if (snd_pcm_hw_params_test_format(handle, params, format) == 0) info->nativeFormats |= RTAUDIO_SINT16; format = SND_PCM_FORMAT_S24; if (snd_pcm_hw_params_test_format(handle, params, format) == 0) info->nativeFormats |= RTAUDIO_SINT24; format = SND_PCM_FORMAT_S32; if (snd_pcm_hw_params_test_format(handle, params, format) == 0) info->nativeFormats |= RTAUDIO_SINT32; format = SND_PCM_FORMAT_FLOAT; if (snd_pcm_hw_params_test_format(handle, params, format) == 0) info->nativeFormats |= RTAUDIO_FLOAT32; format = SND_PCM_FORMAT_FLOAT64; if (snd_pcm_hw_params_test_format(handle, params, format) == 0) info->nativeFormats |= RTAUDIO_FLOAT64; // Check that we have at least one supported format if (info->nativeFormats == 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: pcm device (%s) data format not supported by RtAudio.", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } // That's all ... close the device and return snd_pcm_close(handle); info->probed = true; return; } bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers ) { #if defined(__RTAUDIO_DEBUG__) snd_output_t *out; snd_output_stdio_attach(&out, stderr, 0); #endif // I'm not using the "plug" interface ... too much inconsistent behavior. const char *name = devices_[device].name.c_str(); snd_pcm_stream_t alsa_stream; if (mode == OUTPUT) alsa_stream = SND_PCM_STREAM_PLAYBACK; else alsa_stream = SND_PCM_STREAM_CAPTURE; int err; snd_pcm_t *handle; int alsa_open_mode = SND_PCM_ASYNC; err = snd_pcm_open(&handle, name, alsa_stream, alsa_open_mode); if (err < 0) { sprintf(message_,"RtApiAlsa: pcm device (%s) won't open: %s.", name, snd_strerror(err)); error(RtError::DEBUG_WARNING); return FAILURE; } // Fill the parameter structure. snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_alloca(&hw_params); err = snd_pcm_hw_params_any(handle, hw_params); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error getting parameter handle (%s): %s.", name, snd_strerror(err)); error(RtError::DEBUG_WARNING); return FAILURE; } #if defined(__RTAUDIO_DEBUG__) fprintf(stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n"); snd_pcm_hw_params_dump(hw_params, out); #endif // Set access ... try interleaved access first, then non-interleaved if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) ) { err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); } else if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED) ) { err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED); stream_.deInterleave[mode] = true; } else { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: device (%s) access not supported by RtAudio.", name); error(RtError::DEBUG_WARNING); return FAILURE; } if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error setting access ( (%s): %s.", name, snd_strerror(err)); error(RtError::DEBUG_WARNING); return FAILURE; } // Determine how to set the device format. stream_.userFormat = format; snd_pcm_format_t device_format = SND_PCM_FORMAT_UNKNOWN; if (format == RTAUDIO_SINT8) device_format = SND_PCM_FORMAT_S8; else if (format == RTAUDIO_SINT16) device_format = SND_PCM_FORMAT_S16; else if (format == RTAUDIO_SINT24) device_format = SND_PCM_FORMAT_S24; else if (format == RTAUDIO_SINT32) device_format = SND_PCM_FORMAT_S32; else if (format == RTAUDIO_FLOAT32) device_format = SND_PCM_FORMAT_FLOAT; else if (format == RTAUDIO_FLOAT64) device_format = SND_PCM_FORMAT_FLOAT64; if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { stream_.deviceFormat[mode] = format; goto set_format; } // The user requested format is not natively supported by the device. device_format = SND_PCM_FORMAT_FLOAT64; if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT64; goto set_format; } device_format = SND_PCM_FORMAT_FLOAT; if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; goto set_format; } device_format = SND_PCM_FORMAT_S32; if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { stream_.deviceFormat[mode] = RTAUDIO_SINT32; goto set_format; } device_format = SND_PCM_FORMAT_S24; if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { stream_.deviceFormat[mode] = RTAUDIO_SINT24; goto set_format; } device_format = SND_PCM_FORMAT_S16; if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { stream_.deviceFormat[mode] = RTAUDIO_SINT16; goto set_format; } device_format = SND_PCM_FORMAT_S8; if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) { stream_.deviceFormat[mode] = RTAUDIO_SINT8; goto set_format; } // If we get here, no supported format was found. sprintf(message_,"RtApiAlsa: pcm device (%s) data format not supported by RtAudio.", name); snd_pcm_close(handle); error(RtError::DEBUG_WARNING); return FAILURE; set_format: err = snd_pcm_hw_params_set_format(handle, hw_params, device_format); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error setting format (%s): %s.", name, snd_strerror(err)); error(RtError::DEBUG_WARNING); return FAILURE; } // Determine whether byte-swaping is necessary. stream_.doByteSwap[mode] = false; if (device_format != SND_PCM_FORMAT_S8) { err = snd_pcm_format_cpu_endian(device_format); if (err == 0) stream_.doByteSwap[mode] = true; else if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error getting format endian-ness (%s): %s.", name, snd_strerror(err)); error(RtError::DEBUG_WARNING); return FAILURE; } } // Set the sample rate. err = snd_pcm_hw_params_set_rate(handle, hw_params, (unsigned int)sampleRate, 0); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error setting sample rate (%d) on device (%s): %s.", sampleRate, name, snd_strerror(err)); error(RtError::DEBUG_WARNING); return FAILURE; } // Determine the number of channels for this device. We support a possible // minimum device channel number > than the value requested by the user. stream_.nUserChannels[mode] = channels; unsigned int value; err = snd_pcm_hw_params_get_channels_max(hw_params, &value); int device_channels = value; if (err < 0 || device_channels < channels) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: channels (%d) not supported by device (%s).", channels, name); error(RtError::DEBUG_WARNING); return FAILURE; } err = snd_pcm_hw_params_get_channels_min(hw_params, &value); if (err < 0 ) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error getting min channels count on device (%s).", name); error(RtError::DEBUG_WARNING); return FAILURE; } device_channels = value; if (device_channels < channels) device_channels = channels; stream_.nDeviceChannels[mode] = device_channels; // Set the device channels. err = snd_pcm_hw_params_set_channels(handle, hw_params, device_channels); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error setting channels (%d) on device (%s): %s.", device_channels, name, snd_strerror(err)); error(RtError::DEBUG_WARNING); return FAILURE; } // Set the buffer number, which in ALSA is referred to as the "period". int dir; unsigned int periods = numberOfBuffers; // Even though the hardware might allow 1 buffer, it won't work reliably. if (periods < 2) periods = 2; err = snd_pcm_hw_params_set_periods_near(handle, hw_params, &periods, &dir); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error setting periods (%s): %s.", name, snd_strerror(err)); error(RtError::DEBUG_WARNING); return FAILURE; } // Set the buffer (or period) size. snd_pcm_uframes_t period_size = *bufferSize; err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, &dir); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error setting period size (%s): %s.", name, snd_strerror(err)); error(RtError::DEBUG_WARNING); return FAILURE; } *bufferSize = period_size; // If attempting to setup a duplex stream, the bufferSize parameter // MUST be the same in both directions! if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) { sprintf( message_, "RtApiAlsa: error setting buffer size for duplex stream on device (%s).", name ); error(RtError::DEBUG_WARNING); return FAILURE; } stream_.bufferSize = *bufferSize; // Install the hardware configuration err = snd_pcm_hw_params(handle, hw_params); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtApiAlsa: error installing hardware configuration (%s): %s.", name, snd_strerror(err)); error(RtError::DEBUG_WARNING); return FAILURE; } #if defined(__RTAUDIO_DEBUG__) fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n"); snd_pcm_hw_params_dump(hw_params, out); #endif // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns. snd_pcm_sw_params_t *sw_params = NULL; snd_pcm_sw_params_alloca( &sw_params ); snd_pcm_sw_params_current( handle, sw_params ); snd_pcm_sw_params_set_start_threshold( handle, sw_params, *bufferSize ); snd_pcm_sw_params_set_stop_threshold( handle, sw_params, 0x7fffffff ); snd_pcm_sw_params_set_silence_threshold( handle, sw_params, 0 ); snd_pcm_sw_params_set_silence_size( handle, sw_params, INT_MAX ); err = snd_pcm_sw_params( handle, sw_params ); if (err < 0) { snd_pcm_close(handle); sprintf(message_, "RtAudio: ALSA error installing software configuration (%s): %s.", name, snd_strerror(err)); error(RtError::DEBUG_WARNING); return FAILURE; } #if defined(__RTAUDIO_DEBUG__) fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n"); snd_pcm_sw_params_dump(sw_params, out); #endif // Allocate the ApiHandle if necessary and then save. AlsaHandle *apiInfo = 0; if ( stream_.apiHandle == 0 ) { apiInfo = (AlsaHandle *) new AlsaHandle; stream_.apiHandle = (void *) apiInfo; apiInfo->handles[0] = 0; apiInfo->handles[1] = 0; } else { apiInfo = (AlsaHandle *) stream_.apiHandle; } apiInfo->handles[mode] = handle; // Set flags for buffer conversion stream_.doConvertBuffer[mode] = false; if (stream_.userFormat != stream_.deviceFormat[mode]) stream_.doConvertBuffer[mode] = true; if (stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode]) stream_.doConvertBuffer[mode] = true; if (stream_.nUserChannels[mode] > 1 && stream_.deInterleave[mode]) stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { long buffer_bytes; if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) buffer_bytes = stream_.nUserChannels[0]; else buffer_bytes = stream_.nUserChannels[1]; buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); if (stream_.userBuffer) free(stream_.userBuffer); if (apiInfo->tempBuffer) free(apiInfo->tempBuffer); stream_.userBuffer = (char *) calloc(buffer_bytes, 1); apiInfo->tempBuffer = (char *) calloc(buffer_bytes, 1); if ( stream_.userBuffer == NULL || apiInfo->tempBuffer == NULL ) { sprintf(message_, "RtApiAlsa: error allocating user buffer memory (%s).", devices_[device].name.c_str()); goto error; } } if ( stream_.doConvertBuffer[mode] ) { long buffer_bytes; bool makeBuffer = true; if ( mode == OUTPUT ) buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); else { // mode == INPUT buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( buffer_bytes < bytes_out ) makeBuffer = false; } } if ( makeBuffer ) { buffer_bytes *= *bufferSize; if (stream_.deviceBuffer) free(stream_.deviceBuffer); stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.deviceBuffer == NULL) { sprintf(message_, "RtApiAlsa: error allocating device buffer memory (%s).", devices_[device].name.c_str()); goto error; } } } stream_.device[mode] = device; stream_.state = STREAM_STOPPED; if ( stream_.mode == OUTPUT && mode == INPUT ) { // We had already set up an output stream. stream_.mode = DUPLEX; // Link the streams if possible. apiInfo->synchronized = false; if (snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0) apiInfo->synchronized = true; else { sprintf(message_, "RtApiAlsa: unable to synchronize input and output streams (%s).", devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); } } else stream_.mode = mode; stream_.nBuffers = periods; stream_.sampleRate = sampleRate; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) { if (mode == INPUT) { // convert device to user buffer stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; stream_.convertInfo[mode].outFormat = stream_.userFormat; } else { // convert user to device buffer stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; stream_.convertInfo[mode].inFormat = stream_.userFormat; stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; } if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; else stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; // Set up the interleave/deinterleave offsets. if ( mode == INPUT && stream_.deInterleave[1] ) { for (int k=0; khandles[0]) snd_pcm_close(apiInfo->handles[0]); if (apiInfo->handles[1]) snd_pcm_close(apiInfo->handles[1]); if ( apiInfo->tempBuffer ) free(apiInfo->tempBuffer); delete apiInfo; stream_.apiHandle = 0; } if (stream_.userBuffer) { free(stream_.userBuffer); stream_.userBuffer = 0; } error(RtError::DEBUG_WARNING); return FAILURE; } void RtApiAlsa :: closeStream() { // We don't want an exception to be thrown here because this // function is called by our class destructor. So, do our own // stream check. if ( stream_.mode == UNINITIALIZED ) { sprintf(message_, "RtApiAlsa::closeStream(): no open stream to close!"); error(RtError::WARNING); return; } AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; if (stream_.state == STREAM_RUNNING) { if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) snd_pcm_drop(apiInfo->handles[0]); if (stream_.mode == INPUT || stream_.mode == DUPLEX) snd_pcm_drop(apiInfo->handles[1]); stream_.state = STREAM_STOPPED; } if (stream_.callbackInfo.usingCallback) { stream_.callbackInfo.usingCallback = false; pthread_join(stream_.callbackInfo.thread, NULL); } if (apiInfo) { if (apiInfo->handles[0]) snd_pcm_close(apiInfo->handles[0]); if (apiInfo->handles[1]) snd_pcm_close(apiInfo->handles[1]); free(apiInfo->tempBuffer); delete apiInfo; stream_.apiHandle = 0; } if (stream_.userBuffer) { free(stream_.userBuffer); stream_.userBuffer = 0; } if (stream_.deviceBuffer) { free(stream_.deviceBuffer); stream_.deviceBuffer = 0; } stream_.mode = UNINITIALIZED; } // Pump a bunch of zeros into the output buffer. This is needed only when we // are doing duplex operations. bool RtApiAlsa :: primeOutputBuffer() { int err; char *buffer; int channels; snd_pcm_t **handle; RtAudioFormat format; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; handle = (snd_pcm_t **) apiInfo->handles; if (stream_.mode == DUPLEX) { // Setup parameters and do buffer conversion if necessary. if ( stream_.doConvertBuffer[0] ) { convertBuffer( stream_.deviceBuffer, apiInfo->tempBuffer, stream_.convertInfo[0] ); channels = stream_.nDeviceChannels[0]; format = stream_.deviceFormat[0]; } else { channels = stream_.nUserChannels[0]; format = stream_.userFormat; } buffer = new char[stream_.bufferSize * formatBytes(format) * channels]; bzero(buffer, stream_.bufferSize * formatBytes(format) * channels); for (int i=0; ihandles; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { state = snd_pcm_state(handle[0]); if (state != SND_PCM_STATE_PREPARED) { err = snd_pcm_prepare(handle[0]); if (err < 0) { sprintf(message_, "RtApiAlsa: error preparing pcm device (%s): %s.", devices_[stream_.device[0]].name.c_str(), snd_strerror(err)); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } // Reprime output buffer if needed if ( (stream_.mode == DUPLEX) && ( !primeOutputBuffer() ) ) { MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } } } if ( (stream_.mode == INPUT || stream_.mode == DUPLEX) && !apiInfo->synchronized ) { state = snd_pcm_state(handle[1]); if (state != SND_PCM_STATE_PREPARED) { err = snd_pcm_prepare(handle[1]); if (err < 0) { sprintf(message_, "RtApiAlsa: error preparing pcm device (%s): %s.", devices_[stream_.device[1]].name.c_str(), snd_strerror(err)); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } } } if ( (stream_.mode == DUPLEX) && ( !primeOutputBuffer() ) ) { MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } stream_.state = STREAM_RUNNING; MUTEX_UNLOCK(&stream_.mutex); } void RtApiAlsa :: stopStream() { verifyStream(); if (stream_.state == STREAM_STOPPED) return; // Change the state before the lock to improve shutdown response // when using a callback. stream_.state = STREAM_STOPPED; MUTEX_LOCK(&stream_.mutex); int err; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { err = snd_pcm_drain(handle[0]); if (err < 0) { sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.", devices_[stream_.device[0]].name.c_str(), snd_strerror(err)); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } } if ( (stream_.mode == INPUT || stream_.mode == DUPLEX) && !apiInfo->synchronized ) { err = snd_pcm_drain(handle[1]); if (err < 0) { sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.", devices_[stream_.device[1]].name.c_str(), snd_strerror(err)); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } } MUTEX_UNLOCK(&stream_.mutex); } void RtApiAlsa :: abortStream() { verifyStream(); if (stream_.state == STREAM_STOPPED) return; // Change the state before the lock to improve shutdown response // when using a callback. stream_.state = STREAM_STOPPED; MUTEX_LOCK(&stream_.mutex); int err; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { err = snd_pcm_drop(handle[0]); if (err < 0) { sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.", devices_[stream_.device[0]].name.c_str(), snd_strerror(err)); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } } if ( (stream_.mode == INPUT || stream_.mode == DUPLEX) && !apiInfo->synchronized ) { err = snd_pcm_drop(handle[1]); if (err < 0) { sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.", devices_[stream_.device[1]].name.c_str(), snd_strerror(err)); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } } MUTEX_UNLOCK(&stream_.mutex); } int RtApiAlsa :: streamWillBlock() { verifyStream(); if (stream_.state == STREAM_STOPPED) return 0; MUTEX_LOCK(&stream_.mutex); int err = 0, frames = 0; AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle; snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { err = snd_pcm_avail_update(handle[0]); if (err < 0) { sprintf(message_, "RtApiAlsa: error getting available frames for device (%s): %s.", devices_[stream_.device[0]].name.c_str(), snd_strerror(err)); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } } frames = err; if (stream_.mode == INPUT || stream_.mode == DUPLEX) { err = snd_pcm_avail_update(handle[1]); if (err < 0) { sprintf(message_, "RtApiAlsa: error getting available frames for device (%s): %s.", devices_[stream_.device[1]].name.c_str(), snd_strerror(err)); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } if (frames > err) frames = err; } frames = stream_.bufferSize - frames; if (frames < 0) frames = 0; MUTEX_UNLOCK(&stream_.mutex); return frames; } void RtApiAlsa :: tickStream() { verifyStream(); int stopStream = 0; if (stream_.state == STREAM_STOPPED) { if (stream_.callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds return; } else if (stream_.callbackInfo.usingCallback) { RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; stopStream = callback(stream_.userBuffer, stream_.bufferSize, stream_.callbackInfo.userData); } MUTEX_LOCK(&stream_.mutex); // The state might change while waiting on a mutex. if (stream_.state == STREAM_STOPPED) goto unlock; int err; char *buffer; int channels; AlsaHandle *apiInfo; snd_pcm_t **handle; RtAudioFormat format; apiInfo = (AlsaHandle *) stream_.apiHandle; handle = (snd_pcm_t **) apiInfo->handles; if ( stream_.mode == DUPLEX ) { // In duplex mode, we need to make the snd_pcm_read call before // the snd_pcm_write call in order to avoid under/over runs. So, // copy the userData to our temporary buffer. int bufferBytes; bufferBytes = stream_.bufferSize * stream_.nUserChannels[0] * formatBytes(stream_.userFormat); memcpy( apiInfo->tempBuffer, stream_.userBuffer, bufferBytes ); } if (stream_.mode == INPUT || stream_.mode == DUPLEX) { // Setup parameters. if (stream_.doConvertBuffer[1]) { buffer = stream_.deviceBuffer; channels = stream_.nDeviceChannels[1]; format = stream_.deviceFormat[1]; } else { buffer = stream_.userBuffer; channels = stream_.nUserChannels[1]; format = stream_.userFormat; } // Read samples from device in interleaved/non-interleaved format. if (stream_.deInterleave[1]) { void *bufs[channels]; size_t offset = stream_.bufferSize * formatBytes(format); for (int i=0; itempBuffer, stream_.convertInfo[0] ); else convertBuffer( buffer, stream_.userBuffer, stream_.convertInfo[0] ); channels = stream_.nDeviceChannels[0]; format = stream_.deviceFormat[0]; } else { if ( stream_.mode == DUPLEX ) buffer = apiInfo->tempBuffer; else buffer = stream_.userBuffer; channels = stream_.nUserChannels[0]; format = stream_.userFormat; } // Do byte swapping if necessary. if (stream_.doByteSwap[0]) byteSwapBuffer(buffer, stream_.bufferSize * channels, format); // Write samples to device in interleaved/non-interleaved format. if (stream_.deInterleave[0]) { void *bufs[channels]; size_t offset = stream_.bufferSize * formatBytes(format); for (int i=0; istopStream(); } void RtApiAlsa :: setStreamCallback(RtAudioCallback callback, void *userData) { verifyStream(); CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; if ( info->usingCallback ) { sprintf(message_, "RtApiAlsa: A callback is already set for this stream!"); error(RtError::WARNING); return; } info->callback = (void *) callback; info->userData = userData; info->usingCallback = true; info->object = (void *) this; // Set the thread attributes for joinable and realtime scheduling // priority. The higher priority will only take affect if the // program is run as root or suid. pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_attr_setschedpolicy(&attr, SCHED_OTHER); int err = pthread_create(&info->thread, &attr, alsaCallbackHandler, &stream_.callbackInfo); pthread_attr_destroy(&attr); if (err) { info->usingCallback = false; sprintf(message_, "RtApiAlsa: error starting callback thread!"); error(RtError::THREAD_ERROR); } } void RtApiAlsa :: cancelStreamCallback() { verifyStream(); if (stream_.callbackInfo.usingCallback) { if (stream_.state == STREAM_RUNNING) stopStream(); MUTEX_LOCK(&stream_.mutex); stream_.callbackInfo.usingCallback = false; pthread_join(stream_.callbackInfo.thread, NULL); stream_.callbackInfo.thread = 0; stream_.callbackInfo.callback = NULL; stream_.callbackInfo.userData = NULL; MUTEX_UNLOCK(&stream_.mutex); } } extern "C" void *alsaCallbackHandler(void *ptr) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiAlsa *object = (RtApiAlsa *) info->object; bool *usingCallback = &info->usingCallback; while ( *usingCallback ) { try { object->tickStream(); } catch (RtError &exception) { fprintf(stderr, "\nRtApiAlsa: callback thread error (%s) ... closing thread.\n\n", exception.getMessageString()); break; } } pthread_exit(NULL); } //******************** End of __LINUX_ALSA__ *********************// #endif #if defined(__WINDOWS_ASIO__) // ASIO API on Windows // The ASIO API is designed around a callback scheme, so this // implementation is similar to that used for OS-X CoreAudio and Linux // Jack. The primary constraint with ASIO is that it only allows // access to a single driver at a time. Thus, it is not possible to // have more than one simultaneous RtAudio stream. // // This implementation also requires a number of external ASIO files // and a few global variables. The ASIO callback scheme does not // allow for the passing of user data, so we must create a global // pointer to our callbackInfo structure. // // On unix systems, we make use of a pthread condition variable. // Since there is no equivalent in Windows, I hacked something based // on information found in // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html. #include "asio/asiosys.h" #include "asio/asio.h" #include "asio/iasiothiscallresolver.h" #include "asio/asiodrivers.h" #include AsioDrivers drivers; ASIOCallbacks asioCallbacks; ASIODriverInfo driverInfo; CallbackInfo *asioCallbackInfo; struct AsioHandle { bool stopStream; ASIOBufferInfo *bufferInfos; HANDLE condition; AsioHandle() :stopStream(false), bufferInfos(0) {} }; static const char* GetAsioErrorString( ASIOError result ) { struct Messages { ASIOError value; const char*message; }; static Messages m[] = { { ASE_NotPresent, "Hardware input or output is not present or available." }, { ASE_HWMalfunction, "Hardware is malfunctioning." }, { ASE_InvalidParameter, "Invalid input parameter." }, { ASE_InvalidMode, "Invalid mode." }, { ASE_SPNotAdvancing, "Sample position not advancing." }, { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." }, { ASE_NoMemory, "Not enough memory to complete the request." } }; for (unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i) if (m[i].value == result) return m[i].message; return "Unknown error."; } RtApiAsio :: RtApiAsio() { this->coInitialized = false; this->initialize(); if (nDevices_ <= 0) { sprintf(message_, "RtApiAsio: no Windows ASIO audio drivers found!"); error(RtError::NO_DEVICES_FOUND); } } RtApiAsio :: ~RtApiAsio() { if ( stream_.mode != UNINITIALIZED ) closeStream(); if ( coInitialized ) CoUninitialize(); } void RtApiAsio :: initialize(void) { // ASIO cannot run on a multi-threaded appartment. You can call CoInitialize beforehand, but it must be // for appartment threading (in which case, CoInitilialize will return S_FALSE here). coInitialized = false; HRESULT hr = CoInitialize(NULL); if ( FAILED(hr) ) { sprintf(message_,"RtApiAsio: ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)"); } coInitialized = true; nDevices_ = drivers.asioGetNumDev(); if (nDevices_ <= 0) return; // Create device structures and write device driver names to each. RtApiDevice device; char name[128]; for (int i=0; iname.c_str() ) ) { sprintf(message_, "RtApiAsio: error loading driver (%s).", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } ASIOError result = ASIOInit( &driverInfo ); if ( result != ASE_OK ) { sprintf(message_, "RtApiAsio: error (%s) initializing driver (%s).", GetAsioErrorString(result), info->name.c_str()); error(RtError::DEBUG_WARNING); return; } // Determine the device channel information. long inputChannels, outputChannels; result = ASIOGetChannels( &inputChannels, &outputChannels ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); sprintf(message_, "RtApiAsio: error (%s) getting input/output channel count (%s).", GetAsioErrorString(result), info->name.c_str()); error(RtError::DEBUG_WARNING); return; } info->maxOutputChannels = outputChannels; if ( outputChannels > 0 ) info->minOutputChannels = 1; info->maxInputChannels = inputChannels; if ( inputChannels > 0 ) info->minInputChannels = 1; // If device opens for both playback and capture, we determine the channels. if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) { info->hasDuplexSupport = true; info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ? info->maxInputChannels : info->maxOutputChannels; info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ? info->minInputChannels : info->minOutputChannels; } // Determine the supported sample rates. info->sampleRates.clear(); for (unsigned int i=0; isampleRates.push_back( SAMPLE_RATES[i] ); } if (info->sampleRates.size() == 0) { drivers.removeCurrentDriver(); sprintf( message_, "RtApiAsio: No supported sample rates found for driver (%s).", info->name.c_str() ); error(RtError::DEBUG_WARNING); return; } // Determine supported data types ... just check first channel and assume rest are the same. ASIOChannelInfo channelInfo; channelInfo.channel = 0; channelInfo.isInput = true; if ( info->maxInputChannels <= 0 ) channelInfo.isInput = false; result = ASIOGetChannelInfo( &channelInfo ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); sprintf(message_, "RtApiAsio: error (%s) getting driver (%s) channel information.", GetAsioErrorString(result), info->name.c_str()); error(RtError::DEBUG_WARNING); return; } if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) info->nativeFormats |= RTAUDIO_SINT16; else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) info->nativeFormats |= RTAUDIO_SINT32; else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) info->nativeFormats |= RTAUDIO_FLOAT32; else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) info->nativeFormats |= RTAUDIO_FLOAT64; // Check that we have at least one supported format. if (info->nativeFormats == 0) { drivers.removeCurrentDriver(); sprintf(message_, "RtApiAsio: driver (%s) data format not supported by RtAudio.", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } info->probed = true; drivers.removeCurrentDriver(); } void bufferSwitch(long index, ASIOBool processNow) { RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object; try { object->callbackEvent( index ); } catch (RtError &exception) { fprintf(stderr, "\nRtApiAsio: callback handler error (%s)!\n\n", exception.getMessageString()); return; } return; } void sampleRateChanged(ASIOSampleRate sRate) { // The ASIO documentation says that this usually only happens during // external sync. Audio processing is not stopped by the driver, // actual sample rate might not have even changed, maybe only the // sample rate status of an AES/EBU or S/PDIF digital input at the // audio device. RtAudio *object = (RtAudio *) asioCallbackInfo->object; try { object->stopStream(); } catch (RtError &exception) { fprintf(stderr, "\nRtApiAsio: sampleRateChanged() error (%s)!\n\n", exception.getMessageString()); return; } fprintf(stderr, "\nRtApiAsio: driver reports sample rate changed to %d ... stream stopped!!!", (int) sRate); } long asioMessages(long selector, long value, void* message, double* opt) { long ret = 0; switch(selector) { case kAsioSelectorSupported: if(value == kAsioResetRequest || value == kAsioEngineVersion || value == kAsioResyncRequest || value == kAsioLatenciesChanged // The following three were added for ASIO 2.0, you don't // necessarily have to support them. || value == kAsioSupportsTimeInfo || value == kAsioSupportsTimeCode || value == kAsioSupportsInputMonitor) ret = 1L; break; case kAsioResetRequest: // Defer the task and perform the reset of the driver during the // next "safe" situation. You cannot reset the driver right now, // as this code is called from the driver. Reset the driver is // done by completely destruct is. I.e. ASIOStop(), // ASIODisposeBuffers(), Destruction Afterwards you initialize the // driver again. fprintf(stderr, "\nRtApiAsio: driver reset requested!!!"); ret = 1L; break; case kAsioResyncRequest: // This informs the application that the driver encountered some // non-fatal data loss. It is used for synchronization purposes // of different media. Added mainly to work around the Win16Mutex // problems in Windows 95/98 with the Windows Multimedia system, // which could lose data because the Mutex was held too long by // another thread. However a driver can issue it in other // situations, too. fprintf(stderr, "\nRtApiAsio: driver resync requested!!!"); ret = 1L; break; case kAsioLatenciesChanged: // This will inform the host application that the drivers were // latencies changed. Beware, it this does not mean that the // buffer sizes have changed! You might need to update internal // delay data. fprintf(stderr, "\nRtApiAsio: driver latency may have changed!!!"); ret = 1L; break; case kAsioEngineVersion: // Return the supported ASIO version of the host application. If // a host application does not implement this selector, ASIO 1.0 // is assumed by the driver. ret = 2L; break; case kAsioSupportsTimeInfo: // Informs the driver whether the // asioCallbacks.bufferSwitchTimeInfo() callback is supported. // For compatibility with ASIO 1.0 drivers the host application // should always support the "old" bufferSwitch method, too. ret = 0; break; case kAsioSupportsTimeCode: // Informs the driver wether application is interested in time // code info. If an application does not need to know about time // code, the driver has less work to do. ret = 0; break; } return ret; } bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers) { // For ASIO, a duplex stream MUST use the same driver. if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) { sprintf(message_, "RtApiAsio: duplex stream must use the same device for input and output."); error(RtError::WARNING); return FAILURE; } // Only load the driver once for duplex stream. ASIOError result; if ( mode != INPUT || stream_.mode != OUTPUT ) { if ( !drivers.loadDriver( (char *)devices_[device].name.c_str() ) ) { sprintf(message_, "RtApiAsio: error loading driver (%s).", devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } result = ASIOInit( &driverInfo ); if ( result != ASE_OK ) { sprintf(message_, "RtApiAsio: error (%s) initializing driver (%s).", GetAsioErrorString(result), devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } } // Check the device channel count. long inputChannels, outputChannels; result = ASIOGetChannels( &inputChannels, &outputChannels ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); sprintf(message_, "RtApiAsio: error (%s) getting input/output channel count (%s).", GetAsioErrorString(result), devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } if ( ( mode == OUTPUT && channels > outputChannels) || ( mode == INPUT && channels > inputChannels) ) { drivers.removeCurrentDriver(); sprintf(message_, "RtApiAsio: driver (%s) does not support requested channel count (%d).", devices_[device].name.c_str(), channels); error(RtError::DEBUG_WARNING); return FAILURE; } stream_.nDeviceChannels[mode] = channels; stream_.nUserChannels[mode] = channels; // Verify the sample rate is supported. result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); sprintf(message_, "RtApiAsio: driver (%s) does not support requested sample rate (%d).", devices_[device].name.c_str(), sampleRate); error(RtError::DEBUG_WARNING); return FAILURE; } // Set the sample rate. result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); sprintf(message_, "RtApiAsio: driver (%s) error setting sample rate (%d).", devices_[device].name.c_str(), sampleRate); error(RtError::DEBUG_WARNING); return FAILURE; } // Determine the driver data type. ASIOChannelInfo channelInfo; channelInfo.channel = 0; if ( mode == OUTPUT ) channelInfo.isInput = false; else channelInfo.isInput = true; result = ASIOGetChannelInfo( &channelInfo ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); sprintf(message_, "RtApiAsio: driver (%s) error getting data format.", devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } // Assuming WINDOWS host is always little-endian. stream_.doByteSwap[mode] = false; stream_.userFormat = format; stream_.deviceFormat[mode] = 0; if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) { stream_.deviceFormat[mode] = RTAUDIO_SINT16; if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true; } else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) { stream_.deviceFormat[mode] = RTAUDIO_SINT32; if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true; } else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true; } else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) { stream_.deviceFormat[mode] = RTAUDIO_FLOAT64; if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true; } if ( stream_.deviceFormat[mode] == 0 ) { drivers.removeCurrentDriver(); sprintf(message_, "RtApiAsio: driver (%s) data format not supported by RtAudio.", devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } // Set the buffer size. For a duplex stream, this will end up // setting the buffer size based on the input constraints, which // should be ok. long minSize, maxSize, preferSize, granularity; result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity ); if ( result != ASE_OK ) { drivers.removeCurrentDriver(); sprintf(message_, "RtApiAsio: error (%s) on driver (%s) error getting buffer size.", GetAsioErrorString(result), devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } if ( *bufferSize < minSize ) *bufferSize = minSize; else if ( *bufferSize > maxSize ) *bufferSize = maxSize; else if ( granularity == -1 ) { // Make sure bufferSize is a power of two. double power = log10( (double) *bufferSize ) / log10( 2.0 ); *bufferSize = (int) pow( 2.0, floor(power+0.5) ); if ( *bufferSize < minSize ) *bufferSize = minSize; else if ( *bufferSize > maxSize ) *bufferSize = maxSize; else *bufferSize = preferSize; } else if (granularity != 0) { // to an even multiple of granularity, rounding up. *bufferSize = (*bufferSize + granularity-1)/granularity*granularity; } if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) std::cerr << "Possible input/output buffersize discrepancy!" << std::endl; stream_.bufferSize = *bufferSize; stream_.nBuffers = 2; // ASIO always uses deinterleaved channels. stream_.deInterleave[mode] = true; // Allocate, if necessary, our AsioHandle structure for the stream. AsioHandle *handle = (AsioHandle *) stream_.apiHandle; if ( handle == 0 ) { handle = (AsioHandle *) calloc(1, sizeof(AsioHandle)); if ( handle == NULL ) { drivers.removeCurrentDriver(); sprintf(message_, "RtApiAsio: error allocating AsioHandle memory (%s).", devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } handle->bufferInfos = 0; // Create a manual-reset event. handle->condition = CreateEvent( NULL, // no security TRUE, // manual-reset FALSE, // non-signaled initially NULL ); // unnamed stream_.apiHandle = (void *) handle; } // Create the ASIO internal buffers. Since RtAudio sets up input // and output separately, we'll have to dispose of previously // created output buffers for a duplex stream. if ( mode == INPUT && stream_.mode == OUTPUT ) { ASIODisposeBuffers(); if ( handle->bufferInfos ) free( handle->bufferInfos ); } // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure. int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) ); if (handle->bufferInfos == NULL) { sprintf(message_, "RtApiAsio: error allocating bufferInfo memory (%s).", devices_[device].name.c_str()); goto error; } ASIOBufferInfo *infos; infos = handle->bufferInfos; for ( i=0; iisInput = ASIOFalse; infos->channelNum = i; infos->buffers[0] = infos->buffers[1] = 0; } for ( i=0; iisInput = ASIOTrue; infos->channelNum = i; infos->buffers[0] = infos->buffers[1] = 0; } // Set up the ASIO callback structure and create the ASIO data buffers. asioCallbacks.bufferSwitch = &bufferSwitch; asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.asioMessage = &asioMessages; asioCallbacks.bufferSwitchTimeInfo = NULL; result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks); if ( result != ASE_OK ) { sprintf(message_, "RtApiAsio: eror (%s) on driver (%s) error creating buffers.", GetAsioErrorString(result), devices_[device].name.c_str()); goto error; } // Set flags for buffer conversion. stream_.doConvertBuffer[mode] = false; if (stream_.userFormat != stream_.deviceFormat[mode]) stream_.doConvertBuffer[mode] = true; if (stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode]) stream_.doConvertBuffer[mode] = true; if (stream_.nUserChannels[mode] > 1 && stream_.deInterleave[mode]) stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { long buffer_bytes; if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) buffer_bytes = stream_.nUserChannels[0]; else buffer_bytes = stream_.nUserChannels[1]; buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); if (stream_.userBuffer) free(stream_.userBuffer); stream_.userBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.userBuffer == NULL) { sprintf(message_, "RtApiAsio: error (%s) allocating user buffer memory (%s).", GetAsioErrorString(result), devices_[device].name.c_str()); goto error; } } if ( stream_.doConvertBuffer[mode] ) { long buffer_bytes; bool makeBuffer = true; if ( mode == OUTPUT ) buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); else { // mode == INPUT buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( buffer_bytes < bytes_out ) makeBuffer = false; } } if ( makeBuffer ) { buffer_bytes *= *bufferSize; if (stream_.deviceBuffer) free(stream_.deviceBuffer); stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.deviceBuffer == NULL) { sprintf(message_, "RtApiAsio: error (%s) allocating device buffer memory (%s).", GetAsioErrorString(result), devices_[device].name.c_str()); goto error; } } } stream_.device[mode] = device; stream_.state = STREAM_STOPPED; if ( stream_.mode == OUTPUT && mode == INPUT ) // We had already set up an output stream. stream_.mode = DUPLEX; else stream_.mode = mode; stream_.sampleRate = sampleRate; asioCallbackInfo = &stream_.callbackInfo; stream_.callbackInfo.object = (void *) this; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) { if (mode == INPUT) { // convert device to user buffer stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; stream_.convertInfo[mode].outFormat = stream_.userFormat; } else { // convert user to device buffer stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; stream_.convertInfo[mode].inFormat = stream_.userFormat; stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; } if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; else stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; // Set up the interleave/deinterleave offsets. if ( mode == INPUT && stream_.deInterleave[1] ) { for (int k=0; kcondition ); if ( handle->bufferInfos ) free( handle->bufferInfos ); free( handle ); stream_.apiHandle = 0; } if (stream_.userBuffer) { free(stream_.userBuffer); stream_.userBuffer = 0; } error(RtError::DEBUG_WARNING); return FAILURE; } void RtApiAsio :: closeStream() { // We don't want an exception to be thrown here because this // function is called by our class destructor. So, do our own // streamId check. if ( stream_.mode == UNINITIALIZED ) { sprintf(message_, "RtApiAsio::closeStream(): no open stream to close!"); error(RtError::WARNING); return; } if (stream_.state == STREAM_RUNNING) ASIOStop(); ASIODisposeBuffers(); drivers.removeCurrentDriver(); AsioHandle *handle = (AsioHandle *) stream_.apiHandle; if ( handle ) { CloseHandle( handle->condition ); if ( handle->bufferInfos ) free( handle->bufferInfos ); free( handle ); stream_.apiHandle = 0; } if (stream_.userBuffer) { free(stream_.userBuffer); stream_.userBuffer = 0; } if (stream_.deviceBuffer) { free(stream_.deviceBuffer); stream_.deviceBuffer = 0; } stream_.mode = UNINITIALIZED; } void RtApiAsio :: setStreamCallback(RtAudioCallback callback, void *userData) { verifyStream(); if ( stream_.callbackInfo.usingCallback ) { sprintf(message_, "RtApiAsio: A callback is already set for this stream!"); error(RtError::WARNING); return; } stream_.callbackInfo.callback = (void *) callback; stream_.callbackInfo.userData = userData; stream_.callbackInfo.usingCallback = true; } void RtApiAsio :: cancelStreamCallback() { verifyStream(); if (stream_.callbackInfo.usingCallback) { if (stream_.state == STREAM_RUNNING) stopStream(); MUTEX_LOCK(&stream_.mutex); stream_.callbackInfo.usingCallback = false; stream_.callbackInfo.userData = NULL; stream_.state = STREAM_STOPPED; stream_.callbackInfo.callback = NULL; MUTEX_UNLOCK(&stream_.mutex); } } void RtApiAsio :: startStream() { verifyStream(); if (stream_.state == STREAM_RUNNING) return; MUTEX_LOCK(&stream_.mutex); ASIOError result = ASIOStart(); if ( result != ASE_OK ) { sprintf(message_, "RtApiAsio: error starting device (%s).", devices_[stream_.device[0]].name.c_str()); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } AsioHandle *handle = (AsioHandle *) stream_.apiHandle; handle->stopStream = false; stream_.state = STREAM_RUNNING; MUTEX_UNLOCK(&stream_.mutex); } void RtApiAsio :: stopStream() { verifyStream(); if (stream_.state == STREAM_STOPPED) return; // Change the state before the lock to improve shutdown response // when using a callback. stream_.state = STREAM_STOPPED; MUTEX_LOCK(&stream_.mutex); ASIOError result = ASIOStop(); if ( result != ASE_OK ) { sprintf(message_, "RtApiAsio: error stopping device (%s).", devices_[stream_.device[0]].name.c_str()); MUTEX_UNLOCK(&stream_.mutex); error(RtError::DRIVER_ERROR); } MUTEX_UNLOCK(&stream_.mutex); } void RtApiAsio :: abortStream() { stopStream(); } void RtApiAsio :: tickStream() { verifyStream(); if (stream_.state == STREAM_STOPPED) return; if (stream_.callbackInfo.usingCallback) { sprintf(message_, "RtApiAsio: tickStream() should not be used when a callback function is set!"); error(RtError::WARNING); return; } AsioHandle *handle = (AsioHandle *) stream_.apiHandle; MUTEX_LOCK(&stream_.mutex); // Release the stream_mutex here and wait for the event // to become signaled by the callback process. MUTEX_UNLOCK(&stream_.mutex); WaitForMultipleObjects(1, &handle->condition, FALSE, INFINITE); ResetEvent( handle->condition ); } void RtApiAsio :: callbackEvent(long bufferIndex) { verifyStream(); if (stream_.state == STREAM_STOPPED) return; CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; AsioHandle *handle = (AsioHandle *) stream_.apiHandle; if ( info->usingCallback && handle->stopStream ) { // Check if the stream should be stopped (via the previous user // callback return value). We stop the stream here, rather than // after the function call, so that output data can first be // processed. this->stopStream(); return; } MUTEX_LOCK(&stream_.mutex); // Invoke user callback first, to get fresh output data. if ( info->usingCallback ) { RtAudioCallback callback = (RtAudioCallback) info->callback; if ( callback(stream_.userBuffer, stream_.bufferSize, info->userData) ) handle->stopStream = true; } int bufferBytes, j; int nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[0]); if (stream_.doConvertBuffer[0]) { convertBuffer( stream_.deviceBuffer, stream_.userBuffer, stream_.convertInfo[0] ); if ( stream_.doByteSwap[0] ) byteSwapBuffer(stream_.deviceBuffer, stream_.bufferSize * stream_.nDeviceChannels[0], stream_.deviceFormat[0]); // Always de-interleave ASIO output data. j = 0; for ( int i=0; ibufferInfos[i].isInput != ASIOTrue ) memcpy(handle->bufferInfos[i].buffers[bufferIndex], &stream_.deviceBuffer[j++*bufferBytes], bufferBytes ); } } else { // single channel only if (stream_.doByteSwap[0]) byteSwapBuffer(stream_.userBuffer, stream_.bufferSize * stream_.nUserChannels[0], stream_.userFormat); for ( int i=0; ibufferInfos[i].isInput != ASIOTrue ) { memcpy(handle->bufferInfos[i].buffers[bufferIndex], stream_.userBuffer, bufferBytes ); break; } } } } if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) { bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]); if (stream_.doConvertBuffer[1]) { // Always interleave ASIO input data. j = 0; for ( int i=0; ibufferInfos[i].isInput == ASIOTrue ) memcpy(&stream_.deviceBuffer[j++*bufferBytes], handle->bufferInfos[i].buffers[bufferIndex], bufferBytes ); } if ( stream_.doByteSwap[1] ) byteSwapBuffer(stream_.deviceBuffer, stream_.bufferSize * stream_.nDeviceChannels[1], stream_.deviceFormat[1]); convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] ); } else { // single channel only for ( int i=0; ibufferInfos[i].isInput == ASIOTrue ) { memcpy(stream_.userBuffer, handle->bufferInfos[i].buffers[bufferIndex], bufferBytes ); break; } } if (stream_.doByteSwap[1]) byteSwapBuffer(stream_.userBuffer, stream_.bufferSize * stream_.nUserChannels[1], stream_.userFormat); } } if ( !info->usingCallback ) SetEvent( handle->condition ); // The following call was suggested by Malte Clasen. While the API // documentation indicates it should not be required, some device // drivers apparently do not function correctly without it. ASIOOutputReady(); MUTEX_UNLOCK(&stream_.mutex); } //******************** End of __WINDOWS_ASIO__ *********************// #endif #if defined(__WINDOWS_DS__) // Windows DirectSound API #include #include #define MINIMUM_DEVICE_BUFFER_SIZE 32768 #ifdef _MSC_VER // if Microsoft Visual C++ #pragma comment(lib,"winmm.lib") // then, auto-link winmm.lib. Otherwise, it has to be added manually. #endif static inline DWORD dsPointerDifference(DWORD laterPointer,DWORD earlierPointer,DWORD bufferSize) { if (laterPointer > earlierPointer) return laterPointer-earlierPointer; else return laterPointer-earlierPointer+bufferSize; } static inline DWORD dsPointerBetween(DWORD pointer, DWORD laterPointer,DWORD earlierPointer, DWORD bufferSize) { if (pointer > bufferSize) pointer -= bufferSize; if (laterPointer < earlierPointer) laterPointer += bufferSize; if (pointer < earlierPointer) pointer += bufferSize; return pointer >= earlierPointer && pointer < laterPointer; } #undef GENERATE_DEBUG_LOG // Define this to generate a debug timing log file in c:/rtaudiolog.txt" #ifdef GENERATE_DEBUG_LOG #include "mmsystem.h" #include "fstream" struct TTickRecord { DWORD currentReadPointer, safeReadPointer; DWORD currentWritePointer, safeWritePointer; DWORD readTime, writeTime; DWORD nextWritePointer, nextReadPointer; }; int currentDebugLogEntry = 0; std::vector debugLog(2000); #endif // A structure to hold various information related to the DirectSound // API implementation. struct DsHandle { void *object; void *buffer; UINT bufferPointer; DWORD dsBufferSize; DWORD dsPointerLeadTime; // the number of bytes ahead of the safe pointer to lead by. }; RtApiDs::RtDsStatistics RtApiDs::statistics; // Provides a backdoor hook to monitor for DirectSound read overruns and write underruns. RtApiDs::RtDsStatistics RtApiDs::getDsStatistics() { RtDsStatistics s = statistics; // update the calculated fields. if (s.inputFrameSize != 0) s.latency += s.readDeviceSafeLeadBytes*1.0/s.inputFrameSize / s.sampleRate; if (s.outputFrameSize != 0) s.latency += (s.writeDeviceSafeLeadBytes+ s.writeDeviceBufferLeadBytes)*1.0/s.outputFrameSize / s.sampleRate; return s; } // Declarations for utility functions, callbacks, and structures // specific to the DirectSound implementation. static bool CALLBACK deviceCountCallback(LPGUID lpguid, LPCTSTR description, LPCTSTR module, LPVOID lpContext); static bool CALLBACK deviceInfoCallback(LPGUID lpguid, LPCTSTR description, LPCTSTR module, LPVOID lpContext); static bool CALLBACK defaultDeviceCallback(LPGUID lpguid, LPCTSTR description, LPCTSTR module, LPVOID lpContext); static bool CALLBACK deviceIdCallback(LPGUID lpguid, LPCTSTR description, LPCTSTR module, LPVOID lpContext); static char* getErrorString(int code); extern "C" unsigned __stdcall callbackHandler(void *ptr); struct enum_info { std::string name; LPGUID id; bool isInput; bool isValid; }; RtApiDs :: RtApiDs() { // Dsound will run both-threaded. If CoInitialize fails, then just // accept whatever the mainline chose for a threading model. coInitialized = false; HRESULT hr = CoInitialize(NULL); if ( !FAILED(hr) ) coInitialized = true; this->initialize(); if (nDevices_ <= 0) { sprintf(message_, "RtApiDs: no Windows DirectSound audio devices found!"); error(RtError::NO_DEVICES_FOUND); } } RtApiDs :: ~RtApiDs() { if (coInitialized) CoUninitialize(); // balanced call. if ( stream_.mode != UNINITIALIZED ) closeStream(); } int RtApiDs :: getDefaultInputDevice(void) { enum_info info; // Enumerate through devices to find the default output. HRESULT result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)defaultDeviceCallback, &info); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Error performing default input device enumeration: %s.", getErrorString(result)); error(RtError::WARNING); return 0; } for ( int i=0; i info(count); for (i=0; iname; dsinfo.isValid = false; // Enumerate through input devices to find the id (if it exists). HRESULT result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Error performing input device id enumeration: %s.", getErrorString(result)); error(RtError::DEBUG_WARNING); return; } // Do capture probe first. if ( dsinfo.isValid == false ) goto playback_probe; LPDIRECTSOUNDCAPTURE input; result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Could not create capture object (%s): %s.", info->name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); goto playback_probe; } DSCCAPS in_caps; in_caps.dwSize = sizeof(in_caps); result = input->GetCaps( &in_caps ); if ( FAILED(result) ) { input->Release(); sprintf(message_, "RtApiDs: Could not get capture capabilities (%s): %s.", info->name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); goto playback_probe; } // Get input channel information. info->minInputChannels = 1; info->maxInputChannels = in_caps.dwChannels; // Get sample rate and format information. info->sampleRates.clear(); if( in_caps.dwChannels == 2 ) { if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->nativeFormats |= RTAUDIO_SINT16; if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->nativeFormats |= RTAUDIO_SINT16; if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->nativeFormats |= RTAUDIO_SINT16; if( in_caps.dwFormats & WAVE_FORMAT_48S16 ) info->nativeFormats |= RTAUDIO_SINT16; if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->nativeFormats |= RTAUDIO_SINT8; if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->nativeFormats |= RTAUDIO_SINT8; if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->nativeFormats |= RTAUDIO_SINT8; if ( info->nativeFormats & RTAUDIO_SINT16 ) { if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->sampleRates.push_back( 11025 ); if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->sampleRates.push_back( 22050 ); if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->sampleRates.push_back( 44100 ); if( in_caps.dwFormats & WAVE_FORMAT_48S16 ) info->sampleRates.push_back( 48000 ); } else if ( info->nativeFormats & RTAUDIO_SINT8 ) { if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->sampleRates.push_back( 11025 ); if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->sampleRates.push_back( 22050 ); if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->sampleRates.push_back( 44100 ); } } else if ( in_caps.dwChannels == 1 ) { if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->nativeFormats |= RTAUDIO_SINT16; if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->nativeFormats |= RTAUDIO_SINT16; if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->nativeFormats |= RTAUDIO_SINT16; if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->nativeFormats |= RTAUDIO_SINT8; if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->nativeFormats |= RTAUDIO_SINT8; if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->nativeFormats |= RTAUDIO_SINT8; if ( info->nativeFormats & RTAUDIO_SINT16 ) { if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->sampleRates.push_back( 11025 ); if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->sampleRates.push_back( 22050 ); if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->sampleRates.push_back( 44100 ); } else if ( info->nativeFormats & RTAUDIO_SINT8 ) { if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->sampleRates.push_back( 11025 ); if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->sampleRates.push_back( 22050 ); if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->sampleRates.push_back( 44100 ); } } else info->minInputChannels = 0; // technically, this would be an error input->Release(); playback_probe: dsinfo.isValid = false; // Enumerate through output devices to find the id (if it exists). result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Error performing output device id enumeration: %s.", getErrorString(result)); error(RtError::DEBUG_WARNING); return; } // Now do playback probe. if ( dsinfo.isValid == false ) goto check_parameters; LPDIRECTSOUND output; DSCAPS out_caps; result = DirectSoundCreate( dsinfo.id, &output, NULL ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Could not create playback object (%s): %s.", info->name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); goto check_parameters; } out_caps.dwSize = sizeof(out_caps); result = output->GetCaps( &out_caps ); if ( FAILED(result) ) { output->Release(); sprintf(message_, "RtApiDs: Could not get playback capabilities (%s): %s.", info->name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); goto check_parameters; } // Get output channel information. info->minOutputChannels = 1; info->maxOutputChannels = ( out_caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1; // Get sample rate information. Use capture device rate information // if it exists. if ( info->sampleRates.size() == 0 ) { info->sampleRates.push_back( (int) out_caps.dwMinSecondarySampleRate ); if ( out_caps.dwMaxSecondarySampleRate > out_caps.dwMinSecondarySampleRate ) info->sampleRates.push_back( (int) out_caps.dwMaxSecondarySampleRate ); } else { // Check input rates against output rate range. If there's an // inconsistency (such as a duplex-capable device which reports a // single output rate of 48000 Hz), we'll go with the output // rate(s) since the DirectSoundCapture API is stupid and broken. // Note that the probed sample rate values are NOT used when // opening the device. Thanks to Tue Andersen for reporting this. if ( info->sampleRates.back() < (int) out_caps.dwMinSecondarySampleRate ) { info->sampleRates.clear(); info->sampleRates.push_back( (int) out_caps.dwMinSecondarySampleRate ); if ( out_caps.dwMaxSecondarySampleRate > out_caps.dwMinSecondarySampleRate ) info->sampleRates.push_back( (int) out_caps.dwMaxSecondarySampleRate ); } else { for ( int i=info->sampleRates.size()-1; i>=0; i-- ) { if ( (unsigned int) info->sampleRates[i] > out_caps.dwMaxSecondarySampleRate ) info->sampleRates.erase( info->sampleRates.begin() + i ); } while ( info->sampleRates.size() > 0 && ((unsigned int) info->sampleRates[0] < out_caps.dwMinSecondarySampleRate) ) { info->sampleRates.erase( info->sampleRates.begin() ); } } } // Get format information. if ( out_caps.dwFlags & DSCAPS_PRIMARY16BIT ) info->nativeFormats |= RTAUDIO_SINT16; if ( out_caps.dwFlags & DSCAPS_PRIMARY8BIT ) info->nativeFormats |= RTAUDIO_SINT8; output->Release(); check_parameters: if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 ) { sprintf(message_, "RtApiDs: no reported input or output channels for device (%s).", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } if ( info->sampleRates.size() == 0 || info->nativeFormats == 0 ) { sprintf(message_, "RtApiDs: no reported sample rates or data formats for device (%s).", info->name.c_str()); error(RtError::DEBUG_WARNING); return; } // Determine duplex status. if (info->maxInputChannels < info->maxOutputChannels) info->maxDuplexChannels = info->maxInputChannels; else info->maxDuplexChannels = info->maxOutputChannels; if (info->minInputChannels < info->minOutputChannels) info->minDuplexChannels = info->minInputChannels; else info->minDuplexChannels = info->minOutputChannels; if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true; else info->hasDuplexSupport = false; info->probed = true; return; } bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers) { HRESULT result; HWND hWnd = GetForegroundWindow(); // According to a note in PortAudio, using GetDesktopWindow() // instead of GetForegroundWindow() is supposed to avoid problems // that occur when the application's window is not the foreground // window. Also, if the application window closes before the // DirectSound buffer, DirectSound can crash. However, for console // applications, no sound was produced when using GetDesktopWindow(). long buffer_size; LPVOID audioPtr; DWORD dataLen; int nBuffers; // Check the numberOfBuffers parameter and limit the lowest value to // two. This is a judgement call and a value of two is probably too // low for capture, but it should work for playback. if (numberOfBuffers < 2) nBuffers = 2; else nBuffers = numberOfBuffers; // Define the wave format structure (16-bit PCM, srate, channels) WAVEFORMATEX waveFormat; ZeroMemory(&waveFormat, sizeof(WAVEFORMATEX)); waveFormat.wFormatTag = WAVE_FORMAT_PCM; waveFormat.nChannels = channels; waveFormat.nSamplesPerSec = (unsigned long) sampleRate; // Determine the data format. if ( devices_[device].nativeFormats ) { // 8-bit and/or 16-bit support if ( format == RTAUDIO_SINT8 ) { if ( devices_[device].nativeFormats & RTAUDIO_SINT8 ) waveFormat.wBitsPerSample = 8; else waveFormat.wBitsPerSample = 16; } else { if ( devices_[device].nativeFormats & RTAUDIO_SINT16 ) waveFormat.wBitsPerSample = 16; else waveFormat.wBitsPerSample = 8; } } else { sprintf(message_, "RtApiDs: no reported data formats for device (%s).", devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; // Determine the device buffer size. By default, 32k, but we will // grow it to make allowances for very large software buffer sizes. DWORD dsBufferSize = 0; DWORD dsPointerLeadTime = 0; buffer_size = MINIMUM_DEVICE_BUFFER_SIZE; // sound cards will always *knock wood* support this enum_info dsinfo; void *ohandle = 0, *bhandle = 0; // strncpy( dsinfo.name, devices_[device].name.c_str(), 64 ); dsinfo.name = devices_[device].name; dsinfo.isValid = false; if ( mode == OUTPUT ) { dsPointerLeadTime = numberOfBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels; // If the user wants an even bigger buffer, increase the device buffer size accordingly. while ( dsPointerLeadTime * 2U > (DWORD)buffer_size ) buffer_size *= 2; if ( devices_[device].maxOutputChannels < channels ) { sprintf(message_, "RtApiDs: requested channels (%d) > than supported (%d) by device (%s).", channels, devices_[device].maxOutputChannels, devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } // Enumerate through output devices to find the id (if it exists). result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Error performing output device id enumeration: %s.", getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } if ( dsinfo.isValid == false ) { sprintf(message_, "RtApiDs: output device (%s) id not found!", devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } LPGUID id = dsinfo.id; LPDIRECTSOUND object; LPDIRECTSOUNDBUFFER buffer; DSBUFFERDESC bufferDescription; result = DirectSoundCreate( id, &object, NULL ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Could not create playback object (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } // Set cooperative level to DSSCL_EXCLUSIVE result = object->SetCooperativeLevel(hWnd, DSSCL_PRIORITY); if ( FAILED(result) ) { object->Release(); sprintf(message_, "RtApiDs: Unable to set cooperative level (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } // Even though we will write to the secondary buffer, we need to // access the primary buffer to set the correct output format // (since the default is 8-bit, 22 kHz!). Setup the DS primary // buffer description. ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC)); bufferDescription.dwSize = sizeof(DSBUFFERDESC); bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER; // Obtain the primary buffer result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL); if ( FAILED(result) ) { object->Release(); sprintf(message_, "RtApiDs: Unable to access primary buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } // Set the primary DS buffer sound format. result = buffer->SetFormat(&waveFormat); if ( FAILED(result) ) { object->Release(); sprintf(message_, "RtApiDs: Unable to set primary buffer format (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } // Setup the secondary DS buffer description. dsBufferSize = (DWORD)buffer_size; ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC)); bufferDescription.dwSize = sizeof(DSBUFFERDESC); bufferDescription.dwFlags = ( DSBCAPS_GLOBALFOCUS | DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCHARDWARE ); // Force hardware mixing bufferDescription.dwBufferBytes = buffer_size; bufferDescription.lpwfxFormat = &waveFormat; // Try to create the secondary DS buffer. If that doesn't work, // try to use software mixing. Otherwise, there's a problem. result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL); if ( FAILED(result) ) { bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_LOCSOFTWARE ); // Force software mixing result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL); if ( FAILED(result) ) { object->Release(); sprintf(message_, "RtApiDs: Unable to create secondary DS buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } } // Get the buffer size ... might be different from what we specified. DSBCAPS dsbcaps; dsbcaps.dwSize = sizeof(DSBCAPS); buffer->GetCaps(&dsbcaps); buffer_size = dsbcaps.dwBufferBytes; // Lock the DS buffer result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0); if ( FAILED(result) ) { object->Release(); buffer->Release(); sprintf(message_, "RtApiDs: Unable to lock buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } // Zero the DS buffer ZeroMemory(audioPtr, dataLen); // Unlock the DS buffer result = buffer->Unlock(audioPtr, dataLen, NULL, 0); if ( FAILED(result) ) { object->Release(); buffer->Release(); sprintf(message_, "RtApiDs: Unable to unlock buffer(%s): %s.", devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } ohandle = (void *) object; bhandle = (void *) buffer; stream_.nDeviceChannels[0] = channels; } if ( mode == INPUT ) { if ( devices_[device].maxInputChannels < channels ) { sprintf(message_, "RtAudioDS: device (%s) does not support %d channels.", devices_[device].name.c_str(), channels); error(RtError::DEBUG_WARNING); return FAILURE; } // Enumerate through input devices to find the id (if it exists). result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Error performing input device id enumeration: %s.", getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } if ( dsinfo.isValid == false ) { sprintf(message_, "RtAudioDS: input device (%s) id not found!", devices_[device].name.c_str()); error(RtError::DEBUG_WARNING); return FAILURE; } LPGUID id = dsinfo.id; LPDIRECTSOUNDCAPTURE object; LPDIRECTSOUNDCAPTUREBUFFER buffer; DSCBUFFERDESC bufferDescription; result = DirectSoundCaptureCreate( id, &object, NULL ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Could not create capture object (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } // Setup the secondary DS buffer description. dsBufferSize = buffer_size; ZeroMemory(&bufferDescription, sizeof(DSCBUFFERDESC)); bufferDescription.dwSize = sizeof(DSCBUFFERDESC); bufferDescription.dwFlags = 0; bufferDescription.dwReserved = 0; bufferDescription.dwBufferBytes = buffer_size; bufferDescription.lpwfxFormat = &waveFormat; // Create the capture buffer. result = object->CreateCaptureBuffer(&bufferDescription, &buffer, NULL); if ( FAILED(result) ) { object->Release(); sprintf(message_, "RtApiDs: Unable to create capture buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } // Lock the capture buffer result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0); if ( FAILED(result) ) { object->Release(); buffer->Release(); sprintf(message_, "RtApiDs: Unable to lock capture buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } // Zero the buffer ZeroMemory(audioPtr, dataLen); // Unlock the buffer result = buffer->Unlock(audioPtr, dataLen, NULL, 0); if ( FAILED(result) ) { object->Release(); buffer->Release(); sprintf(message_, "RtApiDs: Unable to unlock capture buffer (%s): %s.", devices_[device].name.c_str(), getErrorString(result)); error(RtError::DEBUG_WARNING); return FAILURE; } ohandle = (void *) object; bhandle = (void *) buffer; stream_.nDeviceChannels[1] = channels; } stream_.userFormat = format; if ( waveFormat.wBitsPerSample == 8 ) stream_.deviceFormat[mode] = RTAUDIO_SINT8; else stream_.deviceFormat[mode] = RTAUDIO_SINT16; stream_.nUserChannels[mode] = channels; stream_.bufferSize = *bufferSize; // Set flags for buffer conversion stream_.doConvertBuffer[mode] = false; if (stream_.userFormat != stream_.deviceFormat[mode]) stream_.doConvertBuffer[mode] = true; if (stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode]) stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { long buffer_bytes; if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) buffer_bytes = stream_.nUserChannels[0]; else buffer_bytes = stream_.nUserChannels[1]; buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); if (stream_.userBuffer) free(stream_.userBuffer); stream_.userBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.userBuffer == NULL) { sprintf(message_, "RtApiDs: error allocating user buffer memory (%s).", devices_[device].name.c_str()); goto error; } } if ( stream_.doConvertBuffer[mode] ) { long buffer_bytes; bool makeBuffer = true; if ( mode == OUTPUT ) buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); else { // mode == INPUT buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( buffer_bytes < bytes_out ) makeBuffer = false; } } if ( makeBuffer ) { buffer_bytes *= *bufferSize; if (stream_.deviceBuffer) free(stream_.deviceBuffer); stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.deviceBuffer == NULL) { sprintf(message_, "RtApiDs: error allocating device buffer memory (%s).", devices_[device].name.c_str()); goto error; } } } // Allocate our DsHandle structures for the stream. DsHandle *handles; if ( stream_.apiHandle == 0 ) { handles = (DsHandle *) calloc(2, sizeof(DsHandle)); if ( handles == NULL ) { sprintf(message_, "RtApiDs: Error allocating DsHandle memory (%s).", devices_[device].name.c_str()); goto error; } handles[0].object = 0; handles[1].object = 0; stream_.apiHandle = (void *) handles; } else handles = (DsHandle *) stream_.apiHandle; handles[mode].object = ohandle; handles[mode].buffer = bhandle; handles[mode].dsBufferSize = dsBufferSize; handles[mode].dsPointerLeadTime = dsPointerLeadTime; stream_.device[mode] = device; stream_.state = STREAM_STOPPED; if ( stream_.mode == OUTPUT && mode == INPUT ) // We had already set up an output stream. stream_.mode = DUPLEX; else stream_.mode = mode; stream_.nBuffers = nBuffers; stream_.sampleRate = sampleRate; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) { if (mode == INPUT) { // convert device to user buffer stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; stream_.convertInfo[mode].outFormat = stream_.userFormat; } else { // convert user to device buffer stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; stream_.convertInfo[mode].inFormat = stream_.userFormat; stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; } if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; else stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; // Set up the interleave/deinterleave offsets. if ( mode == INPUT && stream_.deInterleave[1] ) { for (int k=0; kRelease(); object->Release(); } if (handles[1].object) { LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handles[1].object; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; if (buffer) buffer->Release(); object->Release(); } free(handles); stream_.apiHandle = 0; } if (stream_.userBuffer) { free(stream_.userBuffer); stream_.userBuffer = 0; } error(RtError::DEBUG_WARNING); return FAILURE; } void RtApiDs :: setStreamCallback(RtAudioCallback callback, void *userData) { verifyStream(); CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; if ( info->usingCallback ) { sprintf(message_, "RtApiDs: A callback is already set for this stream!"); error(RtError::WARNING); return; } info->callback = (void *) callback; info->userData = userData; info->usingCallback = true; info->object = (void *) this; unsigned thread_id; info->thread = _beginthreadex(NULL, 0, &callbackHandler, &stream_.callbackInfo, 0, &thread_id); if (info->thread == 0) { info->usingCallback = false; sprintf(message_, "RtApiDs: error starting callback thread!"); error(RtError::THREAD_ERROR); } HANDLE cp; cp = GetCurrentProcess(); SetPriorityClass( cp, HIGH_PRIORITY_CLASS ); SetThreadPriority( (HANDLE)info->thread, THREAD_PRIORITY_HIGHEST ); // When spawning multiple threads in quick succession, it appears to be // necessary to wait a bit for each to initialize ... another windoism! Sleep(1); } void RtApiDs :: cancelStreamCallback() { verifyStream(); if (stream_.callbackInfo.usingCallback) { if (stream_.state == STREAM_RUNNING) stopStream(); MUTEX_LOCK(&stream_.mutex); stream_.callbackInfo.usingCallback = false; WaitForSingleObject( (HANDLE)stream_.callbackInfo.thread, INFINITE ); CloseHandle( (HANDLE)stream_.callbackInfo.thread ); stream_.callbackInfo.thread = 0; stream_.callbackInfo.callback = NULL; stream_.callbackInfo.userData = NULL; MUTEX_UNLOCK(&stream_.mutex); } } void RtApiDs :: closeStream() { // We don't want an exception to be thrown here because this // function is called by our class destructor. So, do our own // streamId check. if ( stream_.mode == UNINITIALIZED ) { sprintf(message_, "RtApiDs::closeStream(): no open stream to close!"); error(RtError::WARNING); return; } if (stream_.callbackInfo.usingCallback) { stream_.callbackInfo.usingCallback = false; WaitForSingleObject( (HANDLE)stream_.callbackInfo.thread, INFINITE ); CloseHandle( (HANDLE)stream_.callbackInfo.thread ); } DsHandle *handles = (DsHandle *) stream_.apiHandle; if (handles) { if (handles[0].object) { LPDIRECTSOUND object = (LPDIRECTSOUND) handles[0].object; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; if (buffer) { buffer->Stop(); buffer->Release(); } object->Release(); } if (handles[1].object) { LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handles[1].object; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; if (buffer) { buffer->Stop(); buffer->Release(); } object->Release(); } free(handles); stream_.apiHandle = 0; } if (stream_.userBuffer) { free(stream_.userBuffer); stream_.userBuffer = 0; } if (stream_.deviceBuffer) { free(stream_.deviceBuffer); stream_.deviceBuffer = 0; } stream_.mode = UNINITIALIZED; } void RtApiDs :: startStream() { verifyStream(); if (stream_.state == STREAM_RUNNING) return; // Increase scheduler frequency on lesser windows (a side-effect of // increasing timer accuracy). On greater windows (Win2K or later), // this is already in effect. MUTEX_LOCK(&stream_.mutex); DsHandle *handles = (DsHandle *) stream_.apiHandle; timeBeginPeriod(1); memset(&statistics,0,sizeof(statistics)); statistics.sampleRate = stream_.sampleRate; statistics.writeDeviceBufferLeadBytes = handles[0].dsPointerLeadTime ; buffersRolling = false; duplexPrerollBytes = 0; if (stream_.mode == DUPLEX) { // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize. duplexPrerollBytes = (int)(0.5*stream_.sampleRate*formatBytes( stream_.deviceFormat[1])*stream_.nDeviceChannels[1]); } #ifdef GENERATE_DEBUG_LOG currentDebugLogEntry = 0; #endif HRESULT result; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { statistics.outputFrameSize = formatBytes( stream_.deviceFormat[0]) *stream_.nDeviceChannels[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; result = buffer->Play( 0, 0, DSBPLAY_LOOPING ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to start buffer (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } } if (stream_.mode == INPUT || stream_.mode == DUPLEX) { statistics.inputFrameSize = formatBytes( stream_.deviceFormat[1]) *stream_.nDeviceChannels[1]; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; result = buffer->Start(DSCBSTART_LOOPING ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to start capture buffer (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } } stream_.state = STREAM_RUNNING; MUTEX_UNLOCK(&stream_.mutex); } void RtApiDs :: stopStream() { verifyStream(); if (stream_.state == STREAM_STOPPED) return; // Change the state before the lock to improve shutdown response // when using a callback. stream_.state = STREAM_STOPPED; MUTEX_LOCK(&stream_.mutex); timeEndPeriod(1); // revert to normal scheduler frequency on lesser windows. #ifdef GENERATE_DEBUG_LOG // Write the timing log to a .TSV file for analysis in Excel. unlink("c:/rtaudiolog.txt"); std::ofstream os("c:/rtaudiolog.txt"); os << "writeTime\treadDelay\tnextWritePointer\tnextReadPointer\tcurrentWritePointer\tsafeWritePointer\tcurrentReadPointer\tsafeReadPointer" << std::endl; for (int i = 0; i < currentDebugLogEntry ; ++i) { TTickRecord &r = debugLog[i]; os << r.writeTime-debugLog[0].writeTime << "\t" << (r.readTime-r.writeTime) << "\t" << r.nextWritePointer % BUFFER_SIZE << "\t" << r.nextReadPointer % BUFFER_SIZE << "\t" << r.currentWritePointer % BUFFER_SIZE << "\t" << r.safeWritePointer % BUFFER_SIZE << "\t" << r.currentReadPointer % BUFFER_SIZE << "\t" << r.safeReadPointer % BUFFER_SIZE << std::endl; } #endif // There is no specific DirectSound API call to "drain" a buffer // before stopping. We can hack this for playback by writing // buffers of zeroes over the entire buffer. For capture, the // concept is less clear so we'll repeat what we do in the // abortStream() case. HRESULT result; DWORD dsBufferSize; LPVOID buffer1 = NULL; LPVOID buffer2 = NULL; DWORD bufferSize1 = 0; DWORD bufferSize2 = 0; DsHandle *handles = (DsHandle *) stream_.apiHandle; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { DWORD currentPos, safePos; long buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; DWORD nextWritePos = handles[0].bufferPointer; dsBufferSize = handles[0].dsBufferSize; DWORD dsBytesWritten = 0; // Write zeroes for at least dsBufferSize bytes. while ( dsBytesWritten < dsBufferSize ) { // Find out where the read and "safe write" pointers are. result = dsBuffer->GetCurrentPosition( ¤tPos, &safePos ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // Chase nextWritePosition. if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset DWORD endWrite = nextWritePos + buffer_bytes; // Check whether the entire write region is behind the play pointer. while ( currentPos < endWrite ) { double millis = (endWrite - currentPos) * 900.0; millis /= ( formatBytes(stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] *stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; Sleep( (DWORD) millis ); // Wake up, find out where we are now result = dsBuffer->GetCurrentPosition( ¤tPos, &safePos ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } if ( currentPos < (DWORD)nextWritePos ) currentPos += dsBufferSize; // unwrap offset } // Lock free space in the buffer result = dsBuffer->Lock( nextWritePos, buffer_bytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to lock buffer during playback (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // Zero the free space ZeroMemory( buffer1, bufferSize1 ); if (buffer2 != NULL) ZeroMemory( buffer2, bufferSize2 ); // Update our buffer offset and unlock sound buffer dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to unlock buffer during playback (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize; handles[0].bufferPointer = nextWritePos; dsBytesWritten += buffer_bytes; } // OK, now stop the buffer. result = dsBuffer->Stop(); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to stop buffer (%s): %s", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // If we play again, start at the beginning of the buffer. handles[0].bufferPointer = 0; } if (stream_.mode == INPUT || stream_.mode == DUPLEX) { LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; buffer1 = NULL; bufferSize1 = 0; result = buffer->Stop(); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to stop capture buffer (%s): %s", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } dsBufferSize = handles[1].dsBufferSize; // Lock the buffer and clear it so that if we start to play again, // we won't have old data playing. result = buffer->Lock(0, dsBufferSize, &buffer1, &bufferSize1, NULL, NULL, 0); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to lock capture buffer (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // Zero the DS buffer ZeroMemory(buffer1, bufferSize1); // Unlock the DS buffer result = buffer->Unlock(buffer1, bufferSize1, NULL, 0); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to unlock capture buffer (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // If we start recording again, we must begin at beginning of buffer. handles[1].bufferPointer = 0; } MUTEX_UNLOCK(&stream_.mutex); } void RtApiDs :: abortStream() { verifyStream(); if (stream_.state == STREAM_STOPPED) return; // Change the state before the lock to improve shutdown response // when using a callback. stream_.state = STREAM_STOPPED; MUTEX_LOCK(&stream_.mutex); timeEndPeriod(1); // revert to normal scheduler frequency on lesser windows. HRESULT result; long dsBufferSize; LPVOID audioPtr; DWORD dataLen; DsHandle *handles = (DsHandle *) stream_.apiHandle; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; result = buffer->Stop(); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to stop buffer (%s): %s", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } dsBufferSize = handles[0].dsBufferSize; // Lock the buffer and clear it so that if we start to play again, // we won't have old data playing. result = buffer->Lock(0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to lock buffer (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // Zero the DS buffer ZeroMemory(audioPtr, dataLen); // Unlock the DS buffer result = buffer->Unlock(audioPtr, dataLen, NULL, 0); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to unlock buffer (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // If we start playing again, we must begin at beginning of buffer. handles[0].bufferPointer = 0; } if (stream_.mode == INPUT || stream_.mode == DUPLEX) { LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; audioPtr = NULL; dataLen = 0; result = buffer->Stop(); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to stop capture buffer (%s): %s", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } dsBufferSize = handles[1].dsBufferSize; // Lock the buffer and clear it so that if we start to play again, // we won't have old data playing. result = buffer->Lock(0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to lock capture buffer (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // Zero the DS buffer ZeroMemory(audioPtr, dataLen); // Unlock the DS buffer result = buffer->Unlock(audioPtr, dataLen, NULL, 0); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to unlock capture buffer (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // If we start recording again, we must begin at beginning of buffer. handles[1].bufferPointer = 0; } MUTEX_UNLOCK(&stream_.mutex); } int RtApiDs :: streamWillBlock() { verifyStream(); if (stream_.state == STREAM_STOPPED) return 0; MUTEX_LOCK(&stream_.mutex); int channels; int frames = 0; HRESULT result; DWORD currentPos, safePos; channels = 1; DsHandle *handles = (DsHandle *) stream_.apiHandle; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; UINT nextWritePos = handles[0].bufferPointer; channels = stream_.nDeviceChannels[0]; DWORD dsBufferSize = handles[0].dsBufferSize; // Find out where the read and "safe write" pointers are. result = dsBuffer->GetCurrentPosition(¤tPos, &safePos); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } DWORD leadPos = safePos + handles[0].dsPointerLeadTime; if (leadPos > dsBufferSize) { leadPos -= dsBufferSize; } if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset frames = (leadPos - nextWritePos); frames /= channels * formatBytes(stream_.deviceFormat[0]); } if (stream_.mode == INPUT ) { // note that we don't block on DUPLEX input anymore. We run lockstep with the write pointer instead. LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; UINT nextReadPos = handles[1].bufferPointer; channels = stream_.nDeviceChannels[1]; DWORD dsBufferSize = handles[1].dsBufferSize; // Find out where the write and "safe read" pointers are. result = dsBuffer->GetCurrentPosition(¤tPos, &safePos); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } if ( safePos < (DWORD)nextReadPos ) safePos += dsBufferSize; // unwrap offset frames = (int)(safePos - nextReadPos); frames /= channels * formatBytes(stream_.deviceFormat[1]); } frames = stream_.bufferSize - frames; if (frames < 0) frames = 0; MUTEX_UNLOCK(&stream_.mutex); return frames; } void RtApiDs :: tickStream() { verifyStream(); int stopStream = 0; if (stream_.state == STREAM_STOPPED) { if (stream_.callbackInfo.usingCallback) Sleep(50); // sleep 50 milliseconds return; } else if (stream_.callbackInfo.usingCallback) { RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; stopStream = callback(stream_.userBuffer, stream_.bufferSize, stream_.callbackInfo.userData); } MUTEX_LOCK(&stream_.mutex); // The state might change while waiting on a mutex. if (stream_.state == STREAM_STOPPED) { MUTEX_UNLOCK(&stream_.mutex); return; } HRESULT result; DWORD currentWritePos, safeWritePos; DWORD currentReadPos, safeReadPos; DWORD leadPos; UINT nextWritePos; #ifdef GENERATE_DEBUG_LOG DWORD writeTime, readTime; #endif LPVOID buffer1 = NULL; LPVOID buffer2 = NULL; DWORD bufferSize1 = 0; DWORD bufferSize2 = 0; char *buffer; long buffer_bytes; DsHandle *handles = (DsHandle *) stream_.apiHandle; if (stream_.mode == DUPLEX && !buffersRolling) { assert(handles[0].dsBufferSize == handles[1].dsBufferSize); // It takes a while for the devices to get rolling. As a result, // there's no guarantee that the capture and write device pointers // will move in lockstep. Wait here for both devices to start // rolling, and then set our buffer pointers accordingly. // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600 // bytes later than the write buffer. // Stub: a serious risk of having a pre-emptive scheduling round // take place between the two GetCurrentPosition calls... but I'm // really not sure how to solve the problem. Temporarily boost to // Realtime priority, maybe; but I'm not sure what priority the // directsound service threads run at. We *should* be roughly // within a ms or so of correct. LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; DWORD initialWritePos, initialSafeWritePos; DWORD initialReadPos, initialSafeReadPos;; result = dsWriteBuffer->GetCurrentPosition(&initialWritePos, &initialSafeWritePos); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } result = dsCaptureBuffer->GetCurrentPosition(&initialReadPos, &initialSafeReadPos); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } while (true) { result = dsWriteBuffer->GetCurrentPosition(¤tWritePos, &safeWritePos); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } result = dsCaptureBuffer->GetCurrentPosition(¤tReadPos, &safeReadPos); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } if (safeWritePos != initialSafeWritePos && safeReadPos != initialSafeReadPos) { break; } Sleep(1); } assert( handles[0].dsBufferSize == handles[1].dsBufferSize ); buffersRolling = true; handles[0].bufferPointer = (safeWritePos + handles[0].dsPointerLeadTime); handles[1].bufferPointer = safeReadPos; } if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer; // Setup parameters and do buffer conversion if necessary. if (stream_.doConvertBuffer[0]) { buffer = stream_.deviceBuffer; convertBuffer( buffer, stream_.userBuffer, stream_.convertInfo[0] ); buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[0]; buffer_bytes *= formatBytes(stream_.deviceFormat[0]); } else { buffer = stream_.userBuffer; buffer_bytes = stream_.bufferSize * stream_.nUserChannels[0]; buffer_bytes *= formatBytes(stream_.userFormat); } // No byte swapping necessary in DirectSound implementation. // Ahhh ... windoze. 16-bit data is signed but 8-bit data is // unsigned. So, we need to convert our signed 8-bit data here to // unsigned. if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 ) for ( int i=0; iGetCurrentPosition(¤tWritePos, &safeWritePos); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } leadPos = safeWritePos + handles[0].dsPointerLeadTime; if ( leadPos > dsBufferSize ) leadPos -= dsBufferSize; if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset endWrite = nextWritePos + buffer_bytes; // Check whether the entire write region is behind the play pointer. if ( leadPos >= endWrite ) break; // If we are here, then we must wait until the play pointer gets // beyond the write region. The approach here is to use the // Sleep() function to suspend operation until safePos catches // up. Calculate number of milliseconds to wait as: // time = distance * (milliseconds/second) * fudgefactor / // ((bytes/sample) * (samples/second)) // A "fudgefactor" less than 1 is used because it was found // that sleeping too long was MUCH worse than sleeping for // several shorter periods. double millis = (endWrite - leadPos) * 900.0; millis /= ( formatBytes(stream_.deviceFormat[0]) *stream_.nDeviceChannels[0]* stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; if ( millis > 50.0 ) { static int nOverruns = 0; ++nOverruns; } Sleep( (DWORD) millis ); } #ifdef GENERATE_DEBUG_LOG writeTime = timeGetTime(); #endif if (statistics.writeDeviceSafeLeadBytes < dsPointerDifference(safeWritePos,currentWritePos,handles[0].dsBufferSize)) { statistics.writeDeviceSafeLeadBytes = dsPointerDifference(safeWritePos,currentWritePos,handles[0].dsBufferSize); } if ( dsPointerBetween( nextWritePos, safeWritePos, currentWritePos, dsBufferSize ) || dsPointerBetween( endWrite, safeWritePos, currentWritePos, dsBufferSize ) ) { // We've strayed into the forbidden zone ... resync the read pointer. ++statistics.numberOfWriteUnderruns; nextWritePos = safeWritePos + handles[0].dsPointerLeadTime-buffer_bytes+dsBufferSize; while (nextWritePos >= dsBufferSize) nextWritePos-= dsBufferSize; handles[0].bufferPointer = nextWritePos; endWrite = nextWritePos + buffer_bytes; } // Lock free space in the buffer result = dsBuffer->Lock( nextWritePos, buffer_bytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0 ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to lock buffer during playback (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } // Copy our buffer into the DS buffer CopyMemory(buffer1, buffer, bufferSize1); if (buffer2 != NULL) CopyMemory(buffer2, buffer+bufferSize1, bufferSize2); // Update our buffer offset and unlock sound buffer dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to unlock buffer during playback (%s): %s.", devices_[stream_.device[0]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize; handles[0].bufferPointer = nextWritePos; } if (stream_.mode == INPUT || stream_.mode == DUPLEX) { // Setup parameters. if (stream_.doConvertBuffer[1]) { buffer = stream_.deviceBuffer; buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[1]; buffer_bytes *= formatBytes(stream_.deviceFormat[1]); } else { buffer = stream_.userBuffer; buffer_bytes = stream_.bufferSize * stream_.nUserChannels[1]; buffer_bytes *= formatBytes(stream_.userFormat); } LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer; long nextReadPos = handles[1].bufferPointer; DWORD dsBufferSize = handles[1].dsBufferSize; // Find out where the write and "safe read" pointers are. result = dsBuffer->GetCurrentPosition(¤tReadPos, &safeReadPos); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset DWORD endRead = nextReadPos + buffer_bytes; // Handling depends on whether we are INPUT or DUPLEX. // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode, // then a wait here will drag the write pointers into the forbidden zone. // // In DUPLEX mode, rather than wait, we will back off the read pointer until // it's in a safe position. This causes dropouts, but it seems to be the only // practical way to sync up the read and write pointers reliably, given the // the very complex relationship between phase and increment of the read and write // pointers. // // In order to minimize audible dropouts in DUPLEX mode, we will // provide a pre-roll period of 0.5 seconds in which we return // zeros from the read buffer while the pointers sync up. if (stream_.mode == DUPLEX) { if (safeReadPos < endRead) { if (duplexPrerollBytes <= 0) { // pre-roll time over. Be more agressive. int adjustment = endRead-safeReadPos; ++statistics.numberOfReadOverruns; // Two cases: // large adjustments: we've probably run out of CPU cycles, so just resync exactly, // and perform fine adjustments later. // small adjustments: back off by twice as much. if (adjustment >= 2*buffer_bytes) { nextReadPos = safeReadPos-2*buffer_bytes; } else { nextReadPos = safeReadPos-buffer_bytes-adjustment; } statistics.readDeviceSafeLeadBytes = currentReadPos-nextReadPos; if (statistics.readDeviceSafeLeadBytes < 0) statistics.readDeviceSafeLeadBytes += dsBufferSize; if (nextReadPos < 0) nextReadPos += dsBufferSize; } else { // in pre=roll time. Just do it. nextReadPos = safeReadPos-buffer_bytes; while (nextReadPos < 0) nextReadPos += dsBufferSize; } endRead = nextReadPos + buffer_bytes; } } else { while ( safeReadPos < endRead ) { // See comments for playback. double millis = (endRead - safeReadPos) * 900.0; millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate); if ( millis < 1.0 ) millis = 1.0; Sleep( (DWORD) millis ); // Wake up, find out where we are now result = dsBuffer->GetCurrentPosition( ¤tReadPos, &safeReadPos ); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset } } #ifdef GENERATE_DEBUG_LOG readTime = timeGetTime(); #endif if (statistics.readDeviceSafeLeadBytes < dsPointerDifference(currentReadPos,nextReadPos ,dsBufferSize)) { statistics.readDeviceSafeLeadBytes = dsPointerDifference(currentReadPos,nextReadPos ,dsBufferSize); } // Lock free space in the buffer result = dsBuffer->Lock (nextReadPos, buffer_bytes, &buffer1, &bufferSize1, &buffer2, &bufferSize2, 0); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to lock buffer during capture (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } if (duplexPrerollBytes <= 0) { // Copy our buffer into the DS buffer CopyMemory(buffer, buffer1, bufferSize1); if (buffer2 != NULL) CopyMemory(buffer+bufferSize1, buffer2, bufferSize2); } else { memset(buffer,0,bufferSize1); if (buffer2 != NULL) memset(buffer+bufferSize1,0,bufferSize2); duplexPrerollBytes -= bufferSize1 + bufferSize2; } // Update our buffer offset and unlock sound buffer nextReadPos = (nextReadPos + bufferSize1 + bufferSize2) % dsBufferSize; dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2); if ( FAILED(result) ) { sprintf(message_, "RtApiDs: Unable to unlock buffer during capture (%s): %s.", devices_[stream_.device[1]].name.c_str(), getErrorString(result)); error(RtError::DRIVER_ERROR); } handles[1].bufferPointer = nextReadPos; // No byte swapping necessary in DirectSound implementation. // If necessary, convert 8-bit data from unsigned to signed. if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 ) for ( int j=0; jstopStream(); } // Definitions for utility functions and callbacks // specific to the DirectSound implementation. extern "C" unsigned __stdcall callbackHandler(void *ptr) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiDs *object = (RtApiDs *) info->object; bool *usingCallback = &info->usingCallback; while ( *usingCallback ) { try { object->tickStream(); } catch (RtError &exception) { fprintf(stderr, "\nRtApiDs: callback thread error (%s) ... closing thread.\n\n", exception.getMessageString()); break; } } _endthreadex( 0 ); return 0; } static bool CALLBACK deviceCountCallback(LPGUID lpguid, LPCTSTR description, LPCTSTR module, LPVOID lpContext) { int *pointer = ((int *) lpContext); (*pointer)++; return true; } #include "tchar.h" std::string convertTChar( LPCTSTR name ) { std::string s; #if defined( UNICODE ) || defined( _UNICODE ) // Yes, this conversion doesn't make sense for two-byte characters // but RtAudio is currently written to return an std::string of // one-byte chars for the device name. for ( unsigned int i=0; iname.empty() ) info++; info->name = convertTChar( description ); info->id = lpguid; HRESULT hr; info->isValid = false; if (info->isInput == true) { DSCCAPS caps; LPDIRECTSOUNDCAPTURE object; hr = DirectSoundCaptureCreate( lpguid, &object, NULL ); if( hr != DS_OK ) return true; caps.dwSize = sizeof(caps); hr = object->GetCaps( &caps ); if( hr == DS_OK ) { if (caps.dwChannels > 0 && caps.dwFormats > 0) info->isValid = true; } object->Release(); } else { DSCAPS caps; LPDIRECTSOUND object; hr = DirectSoundCreate( lpguid, &object, NULL ); if( hr != DS_OK ) return true; caps.dwSize = sizeof(caps); hr = object->GetCaps( &caps ); if( hr == DS_OK ) { if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO ) info->isValid = true; } object->Release(); } return true; } static bool CALLBACK defaultDeviceCallback(LPGUID lpguid, LPCTSTR description, LPCTSTR module, LPVOID lpContext) { enum_info *info = ((enum_info *) lpContext); if ( lpguid == NULL ) { info->name = convertTChar( description ); return false; } return true; } static bool CALLBACK deviceIdCallback(LPGUID lpguid, LPCTSTR description, LPCTSTR module, LPVOID lpContext) { enum_info *info = ((enum_info *) lpContext); std::string s = convertTChar( description ); if ( info->name == s ) { info->id = lpguid; info->isValid = true; return false; } return true; } static char* getErrorString(int code) { switch (code) { case DSERR_ALLOCATED: return "Already allocated."; case DSERR_CONTROLUNAVAIL: return "Control unavailable."; case DSERR_INVALIDPARAM: return "Invalid parameter."; case DSERR_INVALIDCALL: return "Invalid call."; case DSERR_GENERIC: return "Generic error."; case DSERR_PRIOLEVELNEEDED: return "Priority level needed"; case DSERR_OUTOFMEMORY: return "Out of memory"; case DSERR_BADFORMAT: return "The sample rate or the channel format is not supported."; case DSERR_UNSUPPORTED: return "Not supported."; case DSERR_NODRIVER: return "No driver."; case DSERR_ALREADYINITIALIZED: return "Already initialized."; case DSERR_NOAGGREGATION: return "No aggregation."; case DSERR_BUFFERLOST: return "Buffer lost."; case DSERR_OTHERAPPHASPRIO: return "Another application already has priority."; case DSERR_UNINITIALIZED: return "Uninitialized."; default: return "DirectSound unknown error"; } } //******************** End of __WINDOWS_DS__ *********************// #endif #if defined(__IRIX_AL__) // SGI's AL API for IRIX #include #include #include extern "C" void *callbackHandler(void * ptr); RtApiAl :: RtApiAl() { this->initialize(); if (nDevices_ <= 0) { sprintf(message_, "RtApiAl: no Irix AL audio devices found!"); error(RtError::NO_DEVICES_FOUND); } } RtApiAl :: ~RtApiAl() { // The subclass destructor gets called before the base class // destructor, so close any existing streams before deallocating // apiDeviceId memory. if ( stream_.mode != UNINITIALIZED ) closeStream(); // Free our allocated apiDeviceId memory. long *id; for ( unsigned int i=0; iapiDeviceId; resource = id[0]; if (resource > 0) { // Probe output device parameters. result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0); if (result < 0) { sprintf(message_, "RtApiAl: error getting device (%s) channels: %s.", info->name.c_str(), alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); } else { info->maxOutputChannels = value.i; info->minOutputChannels = 1; } result = alGetParamInfo(resource, AL_RATE, &pinfo); if (result < 0) { sprintf(message_, "RtApiAl: error getting device (%s) rates: %s.", info->name.c_str(), alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); } else { info->sampleRates.clear(); for (unsigned int k=0; k= pinfo.min.i && SAMPLE_RATES[k] <= pinfo.max.i ) info->sampleRates.push_back( SAMPLE_RATES[k] ); } } // The AL library supports all our formats, except 24-bit and 32-bit ints. info->nativeFormats = (RtAudioFormat) 51; } // Now get input resource ID if it exists. resource = id[1]; if (resource > 0) { // Probe input device parameters. result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0); if (result < 0) { sprintf(message_, "RtApiAl: error getting device (%s) channels: %s.", info->name.c_str(), alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); } else { info->maxInputChannels = value.i; info->minInputChannels = 1; } result = alGetParamInfo(resource, AL_RATE, &pinfo); if (result < 0) { sprintf(message_, "RtApiAl: error getting device (%s) rates: %s.", info->name.c_str(), alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); } else { // In the case of the default device, these values will // overwrite the rates determined for the output device. Since // the input device is most likely to be more limited than the // output device, this is ok. info->sampleRates.clear(); for (unsigned int k=0; k= pinfo.min.i && SAMPLE_RATES[k] <= pinfo.max.i ) info->sampleRates.push_back( SAMPLE_RATES[k] ); } } // The AL library supports all our formats, except 24-bit and 32-bit ints. info->nativeFormats = (RtAudioFormat) 51; } if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 ) return; if ( info->sampleRates.size() == 0 ) return; // Determine duplex status. if (info->maxInputChannels < info->maxOutputChannels) info->maxDuplexChannels = info->maxInputChannels; else info->maxDuplexChannels = info->maxOutputChannels; if (info->minInputChannels < info->minOutputChannels) info->minDuplexChannels = info->minInputChannels; else info->minDuplexChannels = info->minOutputChannels; if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true; else info->hasDuplexSupport = false; info->probed = true; return; } bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers) { int result, nBuffers; long resource; ALconfig al_config; ALport port; ALpv pvs[2]; long *id = (long *) devices_[device].apiDeviceId; // Get a new ALconfig structure. al_config = alNewConfig(); if ( !al_config ) { sprintf(message_,"RtApiAl: can't get AL config: %s.", alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); return FAILURE; } // Set the channels. result = alSetChannels(al_config, channels); if ( result < 0 ) { alFreeConfig(al_config); sprintf(message_,"RtApiAl: can't set %d channels in AL config: %s.", channels, alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); return FAILURE; } // Attempt to set the queue size. The al API doesn't provide a // means for querying the minimum/maximum buffer size of a device, // so if the specified size doesn't work, take whatever the // al_config structure returns. if ( numberOfBuffers < 1 ) nBuffers = 1; else nBuffers = numberOfBuffers; long buffer_size = *bufferSize * nBuffers; result = alSetQueueSize(al_config, buffer_size); // in sample frames if ( result < 0 ) { // Get the buffer size specified by the al_config and try that. buffer_size = alGetQueueSize(al_config); result = alSetQueueSize(al_config, buffer_size); if ( result < 0 ) { alFreeConfig(al_config); sprintf(message_,"RtApiAl: can't set buffer size (%ld) in AL config: %s.", buffer_size, alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); return FAILURE; } *bufferSize = buffer_size / nBuffers; } // Set the data format. stream_.userFormat = format; stream_.deviceFormat[mode] = format; if (format == RTAUDIO_SINT8) { result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP); result = alSetWidth(al_config, AL_SAMPLE_8); } else if (format == RTAUDIO_SINT16) { result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP); result = alSetWidth(al_config, AL_SAMPLE_16); } else if (format == RTAUDIO_SINT24) { // Our 24-bit format assumes the upper 3 bytes of a 4 byte word. // The AL library uses the lower 3 bytes, so we'll need to do our // own conversion. result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT); stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; } else if (format == RTAUDIO_SINT32) { // The AL library doesn't seem to support the 32-bit integer // format, so we'll need to do our own conversion. result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT); stream_.deviceFormat[mode] = RTAUDIO_FLOAT32; } else if (format == RTAUDIO_FLOAT32) result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT); else if (format == RTAUDIO_FLOAT64) result = alSetSampFmt(al_config, AL_SAMPFMT_DOUBLE); if ( result == -1 ) { alFreeConfig(al_config); sprintf(message_,"RtApiAl: error setting sample format in AL config: %s.", alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); return FAILURE; } if (mode == OUTPUT) { // Set our device. if (device == 0) resource = AL_DEFAULT_OUTPUT; else resource = id[0]; result = alSetDevice(al_config, resource); if ( result == -1 ) { alFreeConfig(al_config); sprintf(message_,"RtApiAl: error setting device (%s) in AL config: %s.", devices_[device].name.c_str(), alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); return FAILURE; } // Open the port. port = alOpenPort("RtApiAl Output Port", "w", al_config); if( !port ) { alFreeConfig(al_config); sprintf(message_,"RtApiAl: error opening output port: %s.", alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); return FAILURE; } // Set the sample rate pvs[0].param = AL_MASTER_CLOCK; pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE; pvs[1].param = AL_RATE; pvs[1].value.ll = alDoubleToFixed((double)sampleRate); result = alSetParams(resource, pvs, 2); if ( result < 0 ) { alClosePort(port); alFreeConfig(al_config); sprintf(message_,"RtApiAl: error setting sample rate (%d) for device (%s): %s.", sampleRate, devices_[device].name.c_str(), alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); return FAILURE; } } else { // mode == INPUT // Set our device. if (device == 0) resource = AL_DEFAULT_INPUT; else resource = id[1]; result = alSetDevice(al_config, resource); if ( result == -1 ) { alFreeConfig(al_config); sprintf(message_,"RtApiAl: error setting device (%s) in AL config: %s.", devices_[device].name.c_str(), alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); return FAILURE; } // Open the port. port = alOpenPort("RtApiAl Input Port", "r", al_config); if( !port ) { alFreeConfig(al_config); sprintf(message_,"RtApiAl: error opening input port: %s.", alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); return FAILURE; } // Set the sample rate pvs[0].param = AL_MASTER_CLOCK; pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE; pvs[1].param = AL_RATE; pvs[1].value.ll = alDoubleToFixed((double)sampleRate); result = alSetParams(resource, pvs, 2); if ( result < 0 ) { alClosePort(port); alFreeConfig(al_config); sprintf(message_,"RtApiAl: error setting sample rate (%d) for device (%s): %s.", sampleRate, devices_[device].name.c_str(), alGetErrorString(oserror())); error(RtError::DEBUG_WARNING); return FAILURE; } } alFreeConfig(al_config); stream_.nUserChannels[mode] = channels; stream_.nDeviceChannels[mode] = channels; // Save stream handle. ALport *handle = (ALport *) stream_.apiHandle; if ( handle == 0 ) { handle = (ALport *) calloc(2, sizeof(ALport)); if ( handle == NULL ) { sprintf(message_, "RtApiAl: Irix Al error allocating handle memory (%s).", devices_[device].name.c_str()); goto error; } stream_.apiHandle = (void *) handle; handle[0] = 0; handle[1] = 0; } handle[mode] = port; // Set flags for buffer conversion stream_.doConvertBuffer[mode] = false; if (stream_.userFormat != stream_.deviceFormat[mode]) stream_.doConvertBuffer[mode] = true; // Allocate necessary internal buffers if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) { long buffer_bytes; if (stream_.nUserChannels[0] >= stream_.nUserChannels[1]) buffer_bytes = stream_.nUserChannels[0]; else buffer_bytes = stream_.nUserChannels[1]; buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat); if (stream_.userBuffer) free(stream_.userBuffer); stream_.userBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.userBuffer == NULL) { sprintf(message_, "RtApiAl: error allocating user buffer memory (%s).", devices_[device].name.c_str()); goto error; } } if ( stream_.doConvertBuffer[mode] ) { long buffer_bytes; bool makeBuffer = true; if ( mode == OUTPUT ) buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); else { // mode == INPUT buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]); if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]); if ( buffer_bytes < bytes_out ) makeBuffer = false; } } if ( makeBuffer ) { buffer_bytes *= *bufferSize; if (stream_.deviceBuffer) free(stream_.deviceBuffer); stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1); if (stream_.deviceBuffer == NULL) { sprintf(message_, "RtApiAl: error allocating device buffer memory (%s).", devices_[device].name.c_str()); goto error; } } } stream_.device[mode] = device; stream_.state = STREAM_STOPPED; if ( stream_.mode == OUTPUT && mode == INPUT ) // We had already set up an output stream. stream_.mode = DUPLEX; else stream_.mode = mode; stream_.nBuffers = nBuffers; stream_.bufferSize = *bufferSize; stream_.sampleRate = sampleRate; // Setup the buffer conversion information structure. if ( stream_.doConvertBuffer[mode] ) { if (mode == INPUT) { // convert device to user buffer stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1]; stream_.convertInfo[mode].outJump = stream_.nUserChannels[1]; stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1]; stream_.convertInfo[mode].outFormat = stream_.userFormat; } else { // convert user to device buffer stream_.convertInfo[mode].inJump = stream_.nUserChannels[0]; stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0]; stream_.convertInfo[mode].inFormat = stream_.userFormat; stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0]; } if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump ) stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump; else stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump; // Set up the interleave/deinterleave offsets. if ( mode == INPUT && stream_.deInterleave[1] ) { for (int k=0; k err) frames = err; } frames = stream_.bufferSize - frames; if (frames < 0) frames = 0; MUTEX_UNLOCK(&stream_.mutex); return frames; } void RtApiAl :: tickStream() { verifyStream(); int stopStream = 0; if (stream_.state == STREAM_STOPPED) { if (stream_.callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds return; } else if (stream_.callbackInfo.usingCallback) { RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; stopStream = callback(stream_.userBuffer, stream_.bufferSize, stream_.callbackInfo.userData); } MUTEX_LOCK(&stream_.mutex); // The state might change while waiting on a mutex. if (stream_.state == STREAM_STOPPED) goto unlock; char *buffer; int channels; RtAudioFormat format; ALport *handle = (ALport *) stream_.apiHandle; if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) { // Setup parameters and do buffer conversion if necessary. if (stream_.doConvertBuffer[0]) { buffer = stream_.deviceBuffer; convertBuffer( buffer, stream_.userBuffer, stream_.convertInfo[0] ); channels = stream_.nDeviceChannels[0]; format = stream_.deviceFormat[0]; } else { buffer = stream_.userBuffer; channels = stream_.nUserChannels[0]; format = stream_.userFormat; } // Do byte swapping if necessary. if (stream_.doByteSwap[0]) byteSwapBuffer(buffer, stream_.bufferSize * channels, format); // Write interleaved samples to device. alWriteFrames(handle[0], buffer, stream_.bufferSize); } if (stream_.mode == INPUT || stream_.mode == DUPLEX) { // Setup parameters. if (stream_.doConvertBuffer[1]) { buffer = stream_.deviceBuffer; channels = stream_.nDeviceChannels[1]; format = stream_.deviceFormat[1]; } else { buffer = stream_.userBuffer; channels = stream_.nUserChannels[1]; format = stream_.userFormat; } // Read interleaved samples from device. alReadFrames(handle[1], buffer, stream_.bufferSize); // Do byte swapping if necessary. if (stream_.doByteSwap[1]) byteSwapBuffer(buffer, stream_.bufferSize * channels, format); // Do buffer conversion if necessary. if (stream_.doConvertBuffer[1]) convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] ); } unlock: MUTEX_UNLOCK(&stream_.mutex); if (stream_.callbackInfo.usingCallback && stopStream) this->stopStream(); } void RtApiAl :: setStreamCallback(RtAudioCallback callback, void *userData) { verifyStream(); CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo; if ( info->usingCallback ) { sprintf(message_, "RtApiAl: A callback is already set for this stream!"); error(RtError::WARNING); return; } info->callback = (void *) callback; info->userData = userData; info->usingCallback = true; info->object = (void *) this; // Set the thread attributes for joinable and realtime scheduling // priority. The higher priority will only take affect if the // program is run as root or suid. pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_attr_setschedpolicy(&attr, SCHED_OTHER); int err = pthread_create(&info->thread, &attr, callbackHandler, &stream_.callbackInfo); pthread_attr_destroy(&attr); if (err) { info->usingCallback = false; sprintf(message_, "RtApiAl: error starting callback thread!"); error(RtError::THREAD_ERROR); } } void RtApiAl :: cancelStreamCallback() { verifyStream(); if (stream_.callbackInfo.usingCallback) { if (stream_.state == STREAM_RUNNING) stopStream(); MUTEX_LOCK(&stream_.mutex); stream_.callbackInfo.usingCallback = false; pthread_join(stream_.callbackInfo.thread, NULL); stream_.callbackInfo.thread = 0; stream_.callbackInfo.callback = NULL; stream_.callbackInfo.userData = NULL; MUTEX_UNLOCK(&stream_.mutex); } } extern "C" void *callbackHandler(void *ptr) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiAl *object = (RtApiAl *) info->object; bool *usingCallback = &info->usingCallback; while ( *usingCallback ) { try { object->tickStream(); } catch (RtError &exception) { fprintf(stderr, "\nRtApiAl: callback thread error (%s) ... closing thread.\n\n", exception.getMessageString()); break; } } return 0; } //******************** End of __IRIX_AL__ *********************// #endif // *************************************************** // // // Protected common (OS-independent) RtAudio methods. // // *************************************************** // // This method can be modified to control the behavior of error // message reporting and throwing. void RtApi :: error(RtError::Type type) { if (type == RtError::WARNING) { fprintf(stderr, "\n%s\n\n", message_); } else if (type == RtError::DEBUG_WARNING) { #if defined(__RTAUDIO_DEBUG__) fprintf(stderr, "\n%s\n\n", message_); #endif } else { #if defined(__RTAUDIO_DEBUG__) fprintf(stderr, "\n%s\n\n", message_); #endif throw RtError(std::string(message_), type); } } void RtApi :: verifyStream() { if ( stream_.mode == UNINITIALIZED ) { sprintf(message_, "RtAudio: stream is not open!"); error(RtError::INVALID_STREAM); } } void RtApi :: clearDeviceInfo(RtApiDevice *info) { // Don't clear the name or DEVICE_ID fields here ... they are // typically set prior to a call of this function. info->probed = false; info->maxOutputChannels = 0; info->maxInputChannels = 0; info->maxDuplexChannels = 0; info->minOutputChannels = 0; info->minInputChannels = 0; info->minDuplexChannels = 0; info->hasDuplexSupport = false; info->sampleRates.clear(); info->nativeFormats = 0; } void RtApi :: clearStreamInfo() { stream_.mode = UNINITIALIZED; stream_.state = STREAM_STOPPED; stream_.sampleRate = 0; stream_.bufferSize = 0; stream_.nBuffers = 0; stream_.userFormat = 0; for ( int i=0; i<2; i++ ) { stream_.device[i] = 0; stream_.doConvertBuffer[i] = false; stream_.deInterleave[i] = false; stream_.doByteSwap[i] = false; stream_.nUserChannels[i] = 0; stream_.nDeviceChannels[i] = 0; stream_.deviceFormat[i] = 0; } } int RtApi :: formatBytes(RtAudioFormat format) { if (format == RTAUDIO_SINT16) return 2; else if (format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32) return 4; else if (format == RTAUDIO_FLOAT64) return 8; else if (format == RTAUDIO_SINT8) return 1; sprintf(message_,"RtApi: undefined format in formatBytes()."); error(RtError::WARNING); return 0; } void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info ) { // This function does format conversion, input/output channel compensation, and // data interleaving/deinterleaving. 24-bit integers are assumed to occupy // the upper three bytes of a 32-bit integer. // Clear our device buffer when in/out duplex device channels are different if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX && stream_.nDeviceChannels[0] != stream_.nDeviceChannels[1] ) memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) ); int j; if (info.outFormat == RTAUDIO_FLOAT64) { Float64 scale; Float64 *out = (Float64 *)outBuffer; if (info.inFormat == RTAUDIO_SINT8) { signed char *in = (signed char *)inBuffer; scale = 1.0 / 128.0; for (int i=0; i> 16) & 0x0000ffff); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_SINT32) { Int32 *in = (Int32 *)inBuffer; for (int i=0; i> 16) & 0x0000ffff); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_FLOAT32) { Float32 *in = (Float32 *)inBuffer; for (int i=0; i> 8) & 0x00ff); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_SINT24) { Int32 *in = (Int32 *)inBuffer; for (int i=0; i> 24) & 0x000000ff); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_SINT32) { Int32 *in = (Int32 *)inBuffer; for (int i=0; i> 24) & 0x000000ff); } in += info.inJump; out += info.outJump; } } else if (info.inFormat == RTAUDIO_FLOAT32) { Float32 *in = (Float32 *)inBuffer; for (int i=0; i #ifdef Q_WS_X11 #define __LINUX_OSS__ #endif #ifdef Q_WS_FREEBSD #define __LINUX_OSS__ #endif #ifdef Q_WS_MAC #define __MACOSX_CORE__ #endif #ifdef WIN32 #define __WINDOWS_DS__ #endif #ifndef __RTAUDIO_H #define __RTAUDIO_H #include "RtError.h" #include #include // Operating system dependent thread functionality. #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) #include #include typedef unsigned long ThreadHandle; typedef CRITICAL_SECTION StreamMutex; #else // Various unix flavors with pthread support. #include typedef pthread_t ThreadHandle; typedef pthread_mutex_t StreamMutex; #endif // This global structure type is used to pass callback information // between the private RtAudio stream structure and global callback // handling functions. struct CallbackInfo { void *object; // Used as a "this" pointer. ThreadHandle thread; bool usingCallback; void *callback; void *userData; void *apiInfo; // void pointer for API specific callback information // Default constructor. CallbackInfo() :object(0), usingCallback(false), callback(0), userData(0), apiInfo(0) {} }; // Support for signed integers and floats. Audio data fed to/from // the tickStream() routine is assumed to ALWAYS be in host // byte order. The internal routines will automatically take care of // any necessary byte-swapping between the host format and the // soundcard. Thus, endian-ness is not a concern in the following // format definitions. typedef unsigned long RtAudioFormat; static const RtAudioFormat RTAUDIO_SINT8 = 0x1; /*!< 8-bit signed integer. */ static const RtAudioFormat RTAUDIO_SINT16 = 0x2; /*!< 16-bit signed integer. */ static const RtAudioFormat RTAUDIO_SINT24 = 0x4; /*!< Upper 3 bytes of 32-bit signed integer. */ static const RtAudioFormat RTAUDIO_SINT32 = 0x8; /*!< 32-bit signed integer. */ static const RtAudioFormat RTAUDIO_FLOAT32 = 0x10; /*!< Normalized between plus/minus 1.0. */ static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; /*!< Normalized between plus/minus 1.0. */ typedef int (*RtAudioCallback)(char *buffer, int bufferSize, void *userData); //! The public device information structure for returning queried values. struct RtAudioDeviceInfo { std::string name; /*!< Character string device identifier. */ bool probed; /*!< true if the device capabilities were successfully probed. */ int outputChannels; /*!< Maximum output channels supported by device. */ int inputChannels; /*!< Maximum input channels supported by device. */ int duplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */ bool isDefault; /*!< true if this is the default output or input device. */ std::vector sampleRates; /*!< Supported sample rates (queried from list of standard rates). */ RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ // Default constructor. RtAudioDeviceInfo() :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0), isDefault(false), nativeFormats(0) {} }; // **************************************************************** // // // RtApi class declaration. // // Note that RtApi is an abstract base class and cannot be // explicitly instantiated. The class RtAudio will create an // instance of an RtApi subclass (RtApiOss, RtApiAlsa, // RtApiJack, RtApiCore, RtApiAl, RtApiDs, or RtApiAsio). // // **************************************************************** // class RtApi { public: enum StreamState { STREAM_STOPPED, STREAM_RUNNING }; RtApi(); virtual ~RtApi(); void openStream( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int numberOfBuffers ); void openStream( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int *numberOfBuffers ); virtual void setStreamCallback( RtAudioCallback callback, void *userData ) = 0; virtual void cancelStreamCallback() = 0; int getDeviceCount(void); RtAudioDeviceInfo getDeviceInfo( int device ); char * const getStreamBuffer(); RtApi::StreamState getStreamState() const; virtual void tickStream() = 0; virtual void closeStream(); virtual void startStream() = 0; virtual void stopStream() = 0; virtual void abortStream() = 0; protected: static const unsigned int MAX_SAMPLE_RATES; static const unsigned int SAMPLE_RATES[]; enum { FAILURE, SUCCESS }; enum StreamMode { OUTPUT, INPUT, DUPLEX, UNINITIALIZED = -75 }; // A protected structure used for buffer conversion. struct ConvertInfo { int channels; int inJump, outJump; RtAudioFormat inFormat, outFormat; std::vector inOffset; std::vector outOffset; }; // A protected structure for audio streams. struct RtApiStream { int device[2]; // Playback and record, respectively. void *apiHandle; // void pointer for API specific stream handle information StreamMode mode; // OUTPUT, INPUT, or DUPLEX. StreamState state; // STOPPED or RUNNING char *userBuffer; char *deviceBuffer; bool doConvertBuffer[2]; // Playback and record, respectively. bool deInterleave[2]; // Playback and record, respectively. bool doByteSwap[2]; // Playback and record, respectively. int sampleRate; int bufferSize; int nBuffers; int nUserChannels[2]; // Playback and record, respectively. int nDeviceChannels[2]; // Playback and record channels, respectively. RtAudioFormat userFormat; RtAudioFormat deviceFormat[2]; // Playback and record, respectively. StreamMutex mutex; CallbackInfo callbackInfo; ConvertInfo convertInfo[2]; RtApiStream() :apiHandle(0), userBuffer(0), deviceBuffer(0) {} }; // A protected device structure for audio devices. struct RtApiDevice { std::string name; /*!< Character string device identifier. */ bool probed; /*!< true if the device capabilities were successfully probed. */ void *apiDeviceId; // void pointer for API specific device information int maxOutputChannels; /*!< Maximum output channels supported by device. */ int maxInputChannels; /*!< Maximum input channels supported by device. */ int maxDuplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */ int minOutputChannels; /*!< Minimum output channels supported by device. */ int minInputChannels; /*!< Minimum input channels supported by device. */ int minDuplexChannels; /*!< Minimum simultaneous input/output channels supported by device. */ bool hasDuplexSupport; /*!< true if device supports duplex mode. */ bool isDefault; /*!< true if this is the default output or input device. */ std::vector sampleRates; /*!< Supported sample rates. */ RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ // Default constructor. RtApiDevice() :probed(false), apiDeviceId(0), maxOutputChannels(0), maxInputChannels(0), maxDuplexChannels(0), minOutputChannels(0), minInputChannels(0), minDuplexChannels(0), isDefault(false), nativeFormats(0) {} }; typedef signed short Int16; typedef signed int Int32; typedef float Float32; typedef double Float64; char message_[1024]; int nDevices_; std::vector devices_; RtApiStream stream_; /*! Protected, api-specific method to count and identify the system audio devices. This function MUST be implemented by all subclasses. */ virtual void initialize(void) = 0; /*! Protected, api-specific method which attempts to fill an RtAudioDevice structure for a given device. This function MUST be implemented by all subclasses. If an error is encountered during the probe, a "warning" message is reported and the value of "probed" remains false (no exception is thrown). A successful probe is indicated by probed = true. */ virtual void probeDeviceInfo( RtApiDevice *info ); /*! Protected, api-specific method which attempts to open a device with the given parameters. This function MUST be implemented by all subclasses. If an error is encountered during the probe, a "warning" message is reported and FAILURE is returned (no exception is thrown). A successful probe is indicated by a return value of SUCCESS. */ virtual bool probeDeviceOpen( int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers ); /*! Protected method which returns the index in the devices array to the default input device. */ virtual int getDefaultInputDevice(void); /*! Protected method which returns the index in the devices array to the default output device. */ virtual int getDefaultOutputDevice(void); //! Protected common method to clear an RtApiDevice structure. void clearDeviceInfo( RtApiDevice *info ); //! Protected common method to clear an RtApiStream structure. void clearStreamInfo(); //! Protected common error method to allow global control over error handling. void error( RtError::Type type ); /*! Protected common method used to check whether a stream is open. If not, an "invalid identifier" exception is thrown. */ void verifyStream(); /*! Protected method used to perform format, channel number, and/or interleaving conversions between the user and device buffers. */ void convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info ); //! Protected common method used to perform byte-swapping on buffers. void byteSwapBuffer( char *buffer, int samples, RtAudioFormat format ); //! Protected common method which returns the number of bytes for a given format. int formatBytes( RtAudioFormat format ); }; // **************************************************************** // // // RtAudio class declaration. // // RtAudio is a "controller" used to select an available audio i/o // interface. It presents a common API for the user to call but all // functionality is implemented by the class RtAudioApi and its // subclasses. RtAudio creates an instance of an RtAudioApi subclass // based on the user's API choice. If no choice is made, RtAudio // attempts to make a "logical" API selection. // // **************************************************************** // class RtAudio { public: //! Audio API specifier arguments. enum RtAudioApi { UNSPECIFIED, /*!< Search for a working compiled API. */ LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ LINUX_OSS, /*!< The Linux Open Sound System API. */ LINUX_JACK, /*!< The Linux Jack Low-Latency Audio Server API. */ MACOSX_CORE, /*!< Macintosh OS-X Core Audio API. */ IRIX_AL, /*!< The Irix Audio Library API. */ WINDOWS_ASIO, /*!< The Steinberg Audio Stream I/O API. */ WINDOWS_DS /*!< The Microsoft Direct Sound API. */ }; //! The default class constructor. /*! Probes the system to make sure at least one audio input/output device is available and determines the api-specific identifier for each device found. An RtError error can be thrown if no devices are found or if a memory allocation error occurs. If no API argument is specified and multiple API support has been compiled, the default order of use is JACK, ALSA, OSS (Linux systems) and ASIO, DS (Windows systems). */ RtAudio( RtAudioApi api=UNSPECIFIED ); //! A constructor which can be used to open a stream during instantiation. /*! The specified output and/or input device identifiers correspond to those enumerated via the getDeviceInfo() method. If device = 0, the default or first available devices meeting the given parameters is selected. If an output or input channel value is zero, the corresponding device value is ignored. When a stream is successfully opened, its identifier is returned via the "streamId" pointer. An RtError can be thrown if no devices are found for the given parameters, if a memory allocation error occurs, or if a driver error occurs. \sa openStream() */ RtAudio( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int numberOfBuffers, RtAudioApi api=UNSPECIFIED ); //! An overloaded constructor which opens a stream and also returns \c numberOfBuffers parameter via pointer argument. /*! See the previous constructor call for details. This overloaded version differs only in that it takes a pointer argument for the \c numberOfBuffers parameter and returns the value used by the audio device (which may be different from that requested). Note that the \c numberofBuffers parameter is not used with the Linux Jack, Macintosh CoreAudio, and Windows ASIO APIs. */ RtAudio( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int *numberOfBuffers, RtAudioApi api=UNSPECIFIED ); //! The destructor. /*! Stops and closes an open stream and devices and deallocates buffer and structure memory. */ ~RtAudio(); //! A public method for opening a stream with the specified parameters. /*! An RtError is thrown if a stream cannot be opened. \param outputDevice: If equal to 0, the default or first device found meeting the given parameters is opened. Otherwise, the device number should correspond to one of those enumerated via the getDeviceInfo() method. \param outputChannels: The desired number of output channels. If equal to zero, the outputDevice identifier is ignored. \param inputDevice: If equal to 0, the default or first device found meeting the given parameters is opened. Otherwise, the device number should correspond to one of those enumerated via the getDeviceInfo() method. \param inputChannels: The desired number of input channels. If equal to zero, the inputDevice identifier is ignored. \param format: An RtAudioFormat specifying the desired sample data format. \param sampleRate: The desired sample rate (sample frames per second). \param *bufferSize: A pointer value indicating the desired internal buffer size in sample frames. The actual value used by the device is returned via the same pointer. A value of zero can be specified, in which case the lowest allowable value is determined. \param numberOfBuffers: A value which can be used to help control device latency. More buffers typically result in more robust performance, though at a cost of greater latency. A value of zero can be specified, in which case the lowest allowable value is used. */ void openStream( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int numberOfBuffers ); //! A public method for opening a stream and also returning \c numberOfBuffers parameter via pointer argument. /*! See the previous function call for details. This overloaded version differs only in that it takes a pointer argument for the \c numberOfBuffers parameter and returns the value used by the audio device (which may be different from that requested). Note that the \c numberofBuffers parameter is not used with the Linux Jack, Macintosh CoreAudio, and Windows ASIO APIs. */ void openStream( int outputDevice, int outputChannels, int inputDevice, int inputChannels, RtAudioFormat format, int sampleRate, int *bufferSize, int *numberOfBuffers ); //! A public method which sets a user-defined callback function for a given stream. /*! This method assigns a callback function to a previously opened stream for non-blocking stream functionality. A separate process is initiated, though the user function is called only when the stream is "running" (between calls to the startStream() and stopStream() methods, respectively). The callback process remains active for the duration of the stream and is automatically shutdown when the stream is closed (via the closeStream() method or by object destruction). The callback process can also be shutdown and the user function de-referenced through an explicit call to the cancelStreamCallback() method. Note that the stream can use only blocking or callback functionality at a particular time, though it is possible to alternate modes on the same stream through the use of the setStreamCallback() and cancelStreamCallback() methods (the blocking tickStream() method can be used before a callback is set and/or after a callback is cancelled). An RtError will be thrown if called when no stream is open or a thread errors occurs. */ void setStreamCallback(RtAudioCallback callback, void *userData) { rtapi_->setStreamCallback( callback, userData ); }; //! A public method which cancels a callback process and function for the stream. /*! This method shuts down a callback process and de-references the user function for the stream. Callback functionality can subsequently be restarted on the stream via the setStreamCallback() method. An RtError will be thrown if called when no stream is open. */ void cancelStreamCallback() { rtapi_->cancelStreamCallback(); }; //! A public method which returns the number of audio devices found. int getDeviceCount(void) { return rtapi_->getDeviceCount(); }; //! Return an RtAudioDeviceInfo structure for a specified device number. /*! Any device integer between 1 and getDeviceCount() is valid. If a device is busy or otherwise unavailable, the structure member "probed" will have a value of "false" and all other members are undefined. If the specified device is the current default input or output device, the "isDefault" member will have a value of "true". An RtError will be thrown for an invalid device argument. */ RtAudioDeviceInfo getDeviceInfo(int device) { return rtapi_->getDeviceInfo( device ); }; //! A public method which returns a pointer to the buffer for an open stream. /*! The user should fill and/or read the buffer data in interleaved format and then call the tickStream() method. An RtError will be thrown if called when no stream is open. */ char * const getStreamBuffer() { return rtapi_->getStreamBuffer(); }; //! Public method used to trigger processing of input/output data for a stream. /*! This method blocks until all buffer data is read/written. An RtError will be thrown if a driver error occurs or if called when no stream is open. */ void tickStream() { rtapi_->tickStream(); }; //! Public method which closes a stream and frees any associated buffers. /*! If a stream is not open, this method issues a warning and returns (an RtError is not thrown). */ void closeStream() { rtapi_->closeStream(); }; //! Public method which starts a stream. /*! An RtError will be thrown if a driver error occurs or if called when no stream is open. */ void startStream() { rtapi_->startStream(); }; //! Stop a stream, allowing any samples remaining in the queue to be played out and/or read in. /*! An RtError will be thrown if a driver error occurs or if called when no stream is open. */ void stopStream() { rtapi_->stopStream(); }; //! Stop a stream, discarding any samples remaining in the input/output queue. /*! An RtError will be thrown if a driver error occurs or if called when no stream is open. */ void abortStream() { rtapi_->abortStream(); }; protected: void initialize( RtAudioApi api ); RtApi *rtapi_; }; // RtApi Subclass prototypes. #if defined(__LINUX_ALSA__) class RtApiAlsa: public RtApi { public: RtApiAlsa(); ~RtApiAlsa(); void tickStream(); void closeStream(); void startStream(); void stopStream(); void abortStream(); int streamWillBlock(); void setStreamCallback( RtAudioCallback callback, void *userData ); void cancelStreamCallback(); private: void initialize(void); bool primeOutputBuffer(); void probeDeviceInfo( RtApiDevice *info ); bool probeDeviceOpen( int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers ); }; #endif #if defined(__LINUX_JACK__) class RtApiJack: public RtApi { public: RtApiJack(); ~RtApiJack(); void tickStream(); void closeStream(); void startStream(); void stopStream(); void abortStream(); void setStreamCallback( RtAudioCallback callback, void *userData ); void cancelStreamCallback(); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesireable results! void callbackEvent( unsigned long nframes ); private: void initialize(void); void probeDeviceInfo( RtApiDevice *info ); bool probeDeviceOpen( int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers ); }; #endif #if defined(__LINUX_OSS__) class RtApiOss: public RtApi { public: RtApiOss(); ~RtApiOss(); void tickStream(); void closeStream(); void startStream(); void stopStream(); void abortStream(); int streamWillBlock(); void setStreamCallback( RtAudioCallback callback, void *userData ); void cancelStreamCallback(); private: void initialize(void); void probeDeviceInfo( RtApiDevice *info ); bool probeDeviceOpen( int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers ); }; #endif #if defined(__MACOSX_CORE__) #include class RtApiCore: public RtApi { public: RtApiCore(); ~RtApiCore(); int getDefaultOutputDevice(void); int getDefaultInputDevice(void); void tickStream(); void closeStream(); void startStream(); void stopStream(); void abortStream(); void setStreamCallback( RtAudioCallback callback, void *userData ); void cancelStreamCallback(); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesireable results! void callbackEvent( AudioDeviceID deviceId, void *inData, void *outData ); private: void initialize(void); void probeDeviceInfo( RtApiDevice *info ); bool probeDeviceOpen( int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers ); }; #endif #if defined(__WINDOWS_DS__) class RtApiDs: public RtApi { public: RtApiDs(); ~RtApiDs(); int getDefaultOutputDevice(void); int getDefaultInputDevice(void); void tickStream(); void closeStream(); void startStream(); void stopStream(); void abortStream(); int streamWillBlock(); void setStreamCallback( RtAudioCallback callback, void *userData ); void cancelStreamCallback(); public: // \brief Internal structure that provide debug information on the state of a running DSound device. struct RtDsStatistics { // \brief Sample Rate. long sampleRate; // \brief The size of one sample * number of channels on the input device. int inputFrameSize; // \brief The size of one sample * number of channels on the output device. int outputFrameSize; /* \brief The number of times the read pointer had to be adjusted to avoid reading from an unsafe buffer position. * * This field is only used when running in DUPLEX mode. INPUT mode devices just wait until the data is * available. */ int numberOfReadOverruns; // \brief The number of times the write pointer had to be adjusted to avoid writing in an unsafe buffer position. int numberOfWriteUnderruns; // \brief Number of bytes by attribute to buffer configuration by which writing must lead the current write pointer. int writeDeviceBufferLeadBytes; // \brief Number of bytes by attributable to the device driver by which writing must lead the current write pointer on this output device. unsigned long writeDeviceSafeLeadBytes; // \brief Number of bytes by which reading must trail the current read pointer on this input device. unsigned long readDeviceSafeLeadBytes; /* \brief Estimated latency in seconds. * * For INPUT mode devices, based the latency of the device's safe read pointer, plus one buffer's * worth of additional latency. * * For OUTPUT mode devices, the latency of the device's safe write pointer, plus N buffers of * additional buffer latency. * * For DUPLEX devices, the sum of latencies for both input and output devices. DUPLEX devices * also back off the read pointers an additional amount in order to maintain synchronization * between out-of-phase read and write pointers. This time is also included. * * Note that most software packages report latency between the safe write pointer * and the software lead pointer, excluding the hardware device's safe write pointer * latency. Figures of 1 or 2ms of latency on Windows audio devices are invariably of this type. * The reality is that hardware devices often have latencies of 30ms or more (often much * higher for duplex operation). */ double latency; }; // \brief Report on the current state of a running DSound device. static RtDsStatistics getDsStatistics(); private: void initialize(void); void probeDeviceInfo( RtApiDevice *info ); bool probeDeviceOpen( int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers ); bool coInitialized; bool buffersRolling; long duplexPrerollBytes; static RtDsStatistics statistics; }; #endif #if defined(__WINDOWS_ASIO__) class RtApiAsio: public RtApi { public: RtApiAsio(); ~RtApiAsio(); void tickStream(); void closeStream(); void startStream(); void stopStream(); void abortStream(); void setStreamCallback( RtAudioCallback callback, void *userData ); void cancelStreamCallback(); // This function is intended for internal use only. It must be // public because it is called by the internal callback handler, // which is not a member of RtAudio. External use of this function // will most likely produce highly undesireable results! void callbackEvent( long bufferIndex ); private: void initialize(void); void probeDeviceInfo( RtApiDevice *info ); bool probeDeviceOpen( int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers ); bool coInitialized; }; #endif #if defined(__IRIX_AL__) class RtApiAl: public RtApi { public: RtApiAl(); ~RtApiAl(); int getDefaultOutputDevice(void); int getDefaultInputDevice(void); void tickStream(); void closeStream(); void startStream(); void stopStream(); void abortStream(); int streamWillBlock(); void setStreamCallback( RtAudioCallback callback, void *userData ); void cancelStreamCallback(); private: void initialize(void); void probeDeviceInfo( RtApiDevice *info ); bool probeDeviceOpen( int device, StreamMode mode, int channels, int sampleRate, RtAudioFormat format, int *bufferSize, int numberOfBuffers ); }; #endif // Define the following flag to have extra information spewed to stderr. //#define __RTAUDIO_DEBUG__ #endif lastfm-player-1.1.4_p1910/src/rtaudio/RtError.h0000644000175000001440000000374610352604467020164 0ustar davidusers/************************************************************************/ /*! \class RtError \brief Exception handling class for RtAudio & RtMidi. The RtError class is quite simple but it does allow errors to be "caught" by RtError::Type. See the RtAudio and RtMidi documentation to know which methods can throw an RtError. */ /************************************************************************/ #ifndef RTERROR_H #define RTERROR_H #include #include class RtError { public: //! Defined RtError types. enum Type { WARNING, /*!< A non-critical error. */ DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ UNSPECIFIED, /*!< The default, unspecified error type. */ NO_DEVICES_FOUND, /*!< No devices found on system. */ INVALID_DEVICE, /*!< An invalid device ID was specified. */ INVALID_STREAM, /*!< An invalid stream ID was specified. */ MEMORY_ERROR, /*!< An error occured during memory allocation. */ INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ DRIVER_ERROR, /*!< A system driver error occured. */ SYSTEM_ERROR, /*!< A system error occured. */ THREAD_ERROR /*!< A thread error occured. */ }; protected: std::string message_; Type type_; public: //! The constructor. RtError(const std::string& message, Type type = RtError::UNSPECIFIED) : message_(message), type_(type) {} //! The destructor. virtual ~RtError(void) {}; //! Prints thrown error message to stderr. virtual void printMessage(void) { std::cerr << '\n' << message_ << "\n\n"; } //! Returns the thrown error message type. virtual const Type& getType(void) { return type_; } //! Returns the thrown error message string. virtual const std::string& getMessage(void) { return message_; } //! Returns the thrown error message as a C string. virtual const char *getMessageString(void) { return message_.c_str(); } }; #endif lastfm-player-1.1.4_p1910/src/progressframe.h0000644000175000001440000000322510352604467017765 0ustar davidusers/************************************************************************* * * * Last.fm Player, Copyright (C) 2005 Chris Muehlhaeuser, Last.fm Ltd. * * All rights reserved. Email: chris@last.fm * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of: * * * * The BSD-style license that is included with this library in * * the file LICENSE. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file * * LICENSE for more details. * * * *************************************************************************/ #ifndef __PROGRESSFRAME__ #define __PROGRESSFRAME__ 1 #include #include class ProgressFrame : public QFrame { Q_OBJECT public: ProgressFrame( QWidget *parent = 0 ); int m_maxValue; int m_value; void paintEvent ( QPaintEvent * event ); void setMaximum( int value ); void setValue( int value ); int value(); }; #endif lastfm-player-1.1.4_p1910/src/tagdialog.h0000644000175000001440000000445110352604467017043 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include "ui_tagdialog.h" class Song; class WebserviceConnector; class TagDialog : public QDialog { Q_OBJECT public: TagDialog( QWidget *parent, Song &song, WebserviceConnector *webserviceConnector ); int execSmall(); private: Ui::TagDialog ui; bool collapsed; WebserviceConnector *ws; Song *m_song; private slots: void okPressed(); void toggleCollapse(); void tagTypeChanged( int type ); void tagsForArtistResult( const QStringList &result ); void tagsForUserResult( const QStringList &result ); void tagsForUserArtistResult( const QStringList &result ); void tagsForUserTrackResult( const QStringList &result ); void tagsForUserAlbumResult( const QStringList &result ); void addTag( QListWidgetItem *item ); }; lastfm-player-1.1.4_p1910/src/slidinglabel.cpp0000644000175000001440000000643010352604467020073 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "slidinglabel.h" #include #include #include static const double Acceleration = 0.10; SlidingLabel::SlidingLabel( QWidget *parent ) : QLabel( parent ), m_offset( 0 ), m_offsetStep( 0 ) { m_switchTimer = new QTimer; connect( m_switchTimer, SIGNAL( timeout() ), this, SLOT( slotAnimationFrame() ) ); } QSize SlidingLabel::sizeHint() const { return QSize( fontMetrics().width( m_text ), height() ); } void SlidingLabel::setText( const QString &text ) { if ( m_text == text ) { return; } m_newText = text; if ( m_text.isEmpty() ) { m_text = text; initScrollingIn(); } else { m_offsetStep = -1.0; } m_switchTimer->start( 1000 / 25 ); } void SlidingLabel::mousePressEvent( QMouseEvent * ) { emit clicked(); } void SlidingLabel::paintEvent( QPaintEvent * ) { QPainter p( this ); p.initFrom( this ); p.eraseRect( rect() ); const QFontMetrics fm( fontMetrics() ); p.drawText( m_offset, height() - fm.ascent() - fm.descent() + 2, m_text ); } void SlidingLabel::slotAnimationFrame() { if ( m_text != m_newText ) { m_offset += qRound( m_offsetStep ); m_offsetStep *= 1 + Acceleration; update( textRect() ); if ( m_offset <= -fontMetrics().width( m_text ) ) { m_text = m_newText; if ( m_text.isEmpty() ) { m_switchTimer->stop(); } else { initScrollingIn(); } } } else { m_offset += qRound( m_offsetStep ); m_offsetStep *= 1 - Acceleration; if ( m_offset >= 0 ) { m_offset = 0; m_switchTimer->stop(); } update( textRect() ); } } QRect SlidingLabel::textRect() const { return QRect( 0, 0, fontMetrics().width( m_text ), height() ); } void SlidingLabel::initScrollingIn() { m_offset = 0; m_offsetStep = 1.0; while ( m_offset > -fontMetrics().width( m_text ) ) { m_offset -= qRound( m_offsetStep ); m_offsetStep *= 1 + Acceleration; } } lastfm-player-1.1.4_p1910/src/main.cpp0000644000175000001440000000424710352604467016372 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include "player.h" #include "settings.h" int main( int argc, char *argv[] ) { QApplication app( argc, argv ); #ifdef Q_WS_X11 QApplication::setStyle( "plastique" ); #endif Settings *settings = new Settings( 0 ); QString cli; for ( int i = 1; i < argc; i ++ ) { if ( QString( argv[i] ).contains( "--debug" ) ) settings->setDebug( true ); if ( QString( argv[i] ).contains( "lastfm://" ) ) cli = argv[i]; } Player *window = new Player( 0, cli, argv[0] ); window->setWindowTitle( "Last.fm Player" ); window->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); window->show(); return app.exec(); delete settings; } lastfm-player-1.1.4_p1910/src/playback.cpp0000644000175000001440000003373210352604467017235 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include "rtaudio/RtAudio.h" #include "playback.h" #include "settings.h" #include "webserviceconnector.h" #define BUFFER 16384 #define PLAYBACKBUFFER 4096 RtAudio *audio; char *buffer; QByteArray *out = new QByteArray(); QHttp *http; QByteArray *waveBuffer; QMutex mutex; struct mpstr_tag mpeg; int volume = 1; bool playbackStopped = false; Playback *Playback::s_instance = 0; int audioCallback( char *buffer, int bufferSize, void *data ) { int bufs = bufferSize * 4; if ( out->size() > bufs && !playbackStopped ) { mutex.lock(); memcpy( buffer, out->data(), bufs ); out->remove( 0, bufs ); mutex.unlock(); } else { memset( buffer, 0, bufs ); } return 0; } void WaveThread::run() { char outBuf[8192]; int size; while ( !playbackStopped ) { mutex.lock(); out->reserve( PLAYBACKBUFFER * 10 ); if ( waveBuffer->size() >= PLAYBACKBUFFER && out->size() < 32768 ) { QByteArray *in = new QByteArray( waveBuffer->left( PLAYBACKBUFFER ) ); waveBuffer->remove( 0, PLAYBACKBUFFER ); mutex.unlock(); int result = decodeMP3( &mpeg, (unsigned char*)in->data(), PLAYBACKBUFFER, outBuf, sizeof( outBuf ), &size ); while ( result == MP3_OK ) { float scale = (float)volume / 100; mutex.lock(); // Append it the binary-safe way! for ( int i = 0; i < ( size / 2 ); i++ ) { union PCMDATA { short i; unsigned char b[2]; } pcmData; pcmData.b[0] = outBuf[i * 2]; pcmData.b[1] = outBuf[i * 2 + 1]; float pcmValue = (float)pcmData.i * scale; pcmData.i = (short)pcmValue; out->append( pcmData.b[0] ); out->append( pcmData.b[1] ); } mutex.unlock(); result = decodeMP3( &mpeg, NULL, 0, outBuf, sizeof( outBuf ), &size ); } delete in; } else { mutex.unlock(); this->msleep( 10 ); } } playbackStopped = false; exit(); } Playback::Playback() { audio = 0; s_instance = this; playing = false; InitMP3( &mpeg ); waveBuffer = new QByteArray(); waveBuffer->reserve( BUFFER * 2 ); int externalSystem = -1; #ifdef WIN32 externalSystem = 1; #endif #ifdef Q_WS_X11 externalSystem = 2; #endif if ( Settings::instance()->soundSystem() == externalSystem ) Settings::instance()->setActAsProxy( true ); else Settings::instance()->setActAsProxy( false ); http = new QHttp( this ); connect( http, SIGNAL( readyRead( QHttpResponseHeader ) ), this, SLOT( dataAvailable( QHttpResponseHeader ) ) ); connect( http, SIGNAL( responseHeaderReceived( QHttpResponseHeader ) ), this, SLOT( responseHeaderReceived( QHttpResponseHeader ) ) ); connect( http, SIGNAL( stateChanged( int ) ), this, SLOT( stateChanged( int ) ) ); if ( Settings::instance()->actAsProxy() ) { proxyServerSocket = new QTcpServer( this ); connect( proxyServerSocket, SIGNAL( newConnection() ), this, SLOT( proxyClientConnect() ) ); proxyServerSocket->listen( QHostAddress( "127.0.0.1" ), 32214 ); } waveThread = new WaveThread(); } Playback::~Playback() { stopPlayback(); qDebug() << "Deinit playback"; if ( Settings::instance()->actAsProxy() ) { proxyServerSocket->close(); delete proxyServerSocket; proxyServerSocket = 0; } delete http; delete waveBuffer; } bool Playback::startPlayback( QUrl url ) { if ( !initSound() ) return false; mutex.lock(); if ( waveThread->isRunning() ) { playbackStopped = true; waveThread->wait( 100 ); } waveBuffer->truncate( 0 ); mutex.unlock(); if ( !Settings::instance()->actAsProxy() ) audio->startStream(); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->setHost( url.host(), url.port() > 0 ? url.port() : 80 ); if ( !url.encodedQuery().isEmpty() ) http->get( url.path() + "?" + QString( url.encodedQuery() ) ); else http->get( url.path() ); return true; } void Playback::stopPlayback() { if ( !isPlaying() ) return; playing = false; qDebug( "Stopping playback!" ); mutex.lock(); http->abort(); if ( waveThread->isRunning() ) { playbackStopped = true; waveThread->wait( 100 ); } waveBuffer->truncate( 0 ); out->truncate( 0 ); mutex.unlock(); qDebug() << "Deinit sockets"; if ( Settings::instance()->actAsProxy() ) { if ( proxySocketList.count() > 0 ) { for ( int i = 0; i < proxySocketList.count(); i++ ) { if ( proxySocketList.at( i )->state() == QAbstractSocket::ConnectedState ) { proxySocketList.at( i )->flush(); proxySocketList.at( i )->close(); } delete proxySocketList.at( i ); } } proxySocketList.clear(); qDebug() << "Proxy clients left:" << proxySocketList.count(); } else { if ( audio != 0 ) { audio->stopStream(); audio->closeStream(); delete audio; audio = 0; } } } bool Playback::isPlaying() { return playing; } void Playback::setVolume( int percentage ) { volume = percentage + 1; } void Playback::setDirectSkip() { m_directSkip = true; } bool Playback::initSound() { if ( Settings::instance()->actAsProxy() ) return true; char data[2]; int channels = 2; int sampleRate = 44100; int nBuffers = 8; int bufferSize = 512; try { RtAudio::RtAudioApi api = RtAudio::UNSPECIFIED; RtAudioFormat format = RTAUDIO_SINT16; #ifdef Q_WS_X11 switch ( Settings::instance()->soundSystem() ) { case 0: qDebug( "Using ALSA!" ); api = RtAudio::LINUX_ALSA; break; case 1: qDebug( "Using OSS!" ); api = RtAudio::LINUX_OSS; break; } #endif audio = new RtAudio(); RtAudioDeviceInfo info = audio->getDeviceInfo( Settings::instance()->soundCard() + 1 ); if ( info.nativeFormats & RTAUDIO_SINT32 ) { qDebug() << "Supports 32 bit"; format = RTAUDIO_SINT32; } if ( info.nativeFormats & RTAUDIO_SINT24 ) { qDebug() << "Supports 24 bit"; format = RTAUDIO_SINT24; } if ( info.nativeFormats & RTAUDIO_SINT16 ) { qDebug() << "Supports 16 bit"; format = RTAUDIO_SINT16; } delete audio; audio = new RtAudio( Settings::instance()->soundCard(), channels, 0, 0, format, sampleRate, &bufferSize, nBuffers, api ); } catch ( RtError &error ) { error.printMessage(); delete audio; audio = 0; return false; } audio->setStreamCallback( &audioCallback, (void *)data ); return true; } void Playback::dataAvailable( const QHttpResponseHeader &resp ) { if ( !m_directSkip && ( waveBuffer->size() >= BUFFER * 2 ) ) return; memset( inBuf, 0, sizeof( inBuf ) ); int len = http->read( inBuf, sizeof( inBuf ) ); if ( resp.statusCode() == 401 || strstr( inBuf, "HTTP/1.0 401" ) != NULL ) { showErrorCode( 401 ); return; } if ( resp.statusCode() == 503 || strstr( inBuf, "HTTP/1.0 503" ) != NULL ) { showErrorCode( 503 ); return; } if ( resp.statusCode() == 666 || strstr( inBuf, "HTTP/1.0 666" ) != NULL ) { showErrorCode( 666 ); return; } if ( resp.statusCode() == 667 || strstr( inBuf, "HTTP/1.0 667" ) != NULL ) { showErrorCode( 667 ); return; } mutex.lock(); // Append it the binary-safe way! for ( int i = 0; i < len; i++ ) waveBuffer->append( inBuf[ i ] ); if ( waveBuffer->indexOf( "SYNC" ) >= 0 ) { qDebug( "New song detected!" ); WebserviceConnector::instance()->metaData(); if ( m_directSkip ) { m_directSkip = false; if ( waveBuffer->size() > BUFFER ) waveBuffer->remove( BUFFER, waveBuffer->size() ); } waveBuffer->remove( waveBuffer->indexOf( "SYNC" ), 4 ); } if ( Settings::instance()->actAsProxy() ) { if ( proxySocketList.count() > 0 ) for ( int i = 0; i < proxySocketList.count(); i++ ) { if ( proxySocketList.at( i )->state() == QAbstractSocket::ConnectedState ) proxySocketList.at( i )->write( waveBuffer->data(), waveBuffer->size() ); } waveBuffer->truncate( 0 ); } if ( ( ( waveBuffer->size() + len ) < PLAYBACKBUFFER ) && waveThread->isRunning() ) { qDebug( "Buffer empty!" ); playbackStopped = true; } if ( !waveThread->isRunning() && waveBuffer->size() >= BUFFER ) waveThread->start( QThread::TimeCriticalPriority ); mutex.unlock(); } void Playback::stateChanged( int state ) { if ( !playing && state == QHttp::Reading ) { playing = true; emit playbackStarting(); } if ( state == QHttp::Unconnected ) { stopPlayback(); emit playbackFinished(); } } void Playback::responseHeaderReceived( const QHttpResponseHeader &resp ) { if ( resp.statusCode() == 503 || resp.statusCode() == 401 || resp.statusCode() == 666 ) showErrorCode( resp.statusCode() ); } void Playback::showErrorCode( int error ) { qDebug( "Streamer disconnected us!" ); switch( error ) { case 401: QMessageBox::information( 0, "Attention", "Your session is invalid! Please login, again!\n" ); break; case 503: QMessageBox::information( 0, "Attention", "No more streaming slots available. Try again in a few minutes!\n" ); break; case 666: QMessageBox::information( 0, "Attention", "Streamer is shutting down for maintenance! Try again in a few minutes!\n" ); break; case 667: QMessageBox::information( 0, "Error", "There is not enough content left to play this station.\n" ); break; } } void Playback::proxyClientConnect() { qDebug() << "New proxy client connected!"; QTcpSocket *socket = proxyServerSocket->nextPendingConnection(); connect( socket, SIGNAL( disconnected() ), this, SLOT( proxyClientDisconnect() ) ); // welcome the new client QByteArray token( "HTTP/1.1 200 OK\n\n" ); socket->write( token, token.length() ); proxySocketList.append( socket ); if ( proxySocketList.count() == 1 ) emit proxyClientConnected(); qDebug() << "Proxy clients atm:" << proxySocketList.count(); } void Playback::proxyClientDisconnect() { qDebug() << "Proxy client disconnected!"; #ifdef WIN32 Sleep( 500 ); #endif #ifndef WIN32 usleep( 500 ); #endif QApplication::processEvents(); for ( int i = 0; i < proxySocketList.count(); i++ ) { if ( proxySocketList.at( i )->state() == QAbstractSocket::ConnectedState ) { return; } } stopPlayback(); emit proxyClientDisconnected(); } lastfm-player-1.1.4_p1910/src/webserviceconnector.h0000644000175000001440000001221510352604467021156 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include class QHttp; class QTimer; class QUrl; class Playlist; class Song; class SettingsForm; class QHttpResponseHeader; class WebserviceConnector : public QObject { Q_OBJECT static WebserviceConnector *s_instance; public: WebserviceConnector(); static WebserviceConnector *instance() { return s_instance; } void handshake( QString username, QString password ); bool isConnected(); bool isSubscriber(); void searchArtist( QString artist ); void searchTag( QString tag ); void tagsForArtist( QString artist ); void tagsForUser( QString artist ); void tagsForUserArtist( QString user, QString artist ); void tagsForUserTrack( QString user, QString artist, QString track ); void tagsForUserAlbum( QString user, QString artist, QString album ); void setTag( Song *song, int mode, QString tag ); void skip(); void love(); void ban(); void recordToProfile( bool enabled ); void discoveryMode( bool enabled ); void changeStation( QString url ); void playlistRemove( QString id ); void playlistClear(); private: bool m_connected; bool m_subscriber; QString baseHost; QString basePath; QHash httpStack; QTimer *m_refreshTimer; QString parameter( QString keyName, QString data ); QStringList parameterArray( QString keyName, QString data ); QStringList parameterKeys( QString keyName, QString data ); void stackAppend( QHttp *http ); QHttp* stackGet( int id ); void stackRemove( int id, bool keepAnimation = false ); void errorCode( int id, QString errorMessage = QString() ); public slots: void metaData( bool force = true ); signals: void handshakeResult( bool connected, bool loginDone, const QUrl& streamUrl ); void metaDataResult( const Song& song, bool discovery = false ); void playlistResult( const Playlist& playlist ); void searchArtistResult( const QString& artist, bool streamable, const QStringList& result ); void searchTagResult( const QStringList& result ); void tagsForArtistResult( const QStringList& result ); void tagsForUserResult( const QStringList& result ); void tagsForUserArtistResult( const QStringList& result ); void tagsForUserTrackResult( const QStringList& result ); void tagsForUserAlbumResult( const QStringList& result ); void skipResult(); void loveResult(); void banResult(); void recordToProfileResult(); void changeStationPrepared(); void changeStationResult( bool error = false ); void actionStarted(); void actionFinished(); private slots: void handshakeHeaderReceived( const QHttpResponseHeader &resp ); void handshakeFinished( int id, bool error ); void metaDataFinished( int id, bool error ); void searchArtistFinished( int id, bool error ); void searchTagFinished( int id, bool error ); void tagsForArtistFinished( int id, bool error ); void tagsForUserFinished( int id, bool error ); void tagsForUserArtistFinished( int id, bool error ); void tagsForUserTrackFinished( int id, bool error ); void tagsForUserAlbumFinished( int id, bool error ); void setTagFinished( int id, bool error ); void skipFinished( int id, bool error ); void loveFinished( int id, bool error ); void banFinished( int id, bool error ); void changeStationFinished( int id, bool error ); void defaultFinished( int id, bool error ); }; lastfm-player-1.1.4_p1910/src/tagdialog.cpp0000644000175000001440000001502510352604467017375 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifdef WIN32 #include #endif #include #include #include "settings.h" #include "song.h" #include "tagdialog.h" #include "webserviceconnector.h" TagDialog::TagDialog( QWidget *parent, Song &song, WebserviceConnector *webserviceConnector ) : QDialog( parent, Qt::Dialog ) , ws( webserviceConnector ) { ui.setupUi( this ); m_song = new Song(); m_song->fetch( song ); connect( ws, SIGNAL( tagsForArtistResult( QStringList ) ), this, SLOT( tagsForArtistResult( QStringList ) ) ); connect( ws, SIGNAL( tagsForUserResult( QStringList ) ), this, SLOT( tagsForUserResult( QStringList ) ) ); connect( ws, SIGNAL( tagsForUserArtistResult( QStringList ) ), this, SLOT( tagsForUserArtistResult( QStringList ) ) ); connect( ws, SIGNAL( tagsForUserTrackResult( QStringList ) ), this, SLOT( tagsForUserTrackResult( QStringList ) ) ); connect( ws, SIGNAL( tagsForUserAlbumResult( QStringList ) ), this, SLOT( tagsForUserAlbumResult( QStringList ) ) ); connect( ui.tagTypeBox, SIGNAL( activated( int ) ), this, SLOT( tagTypeChanged( int ) ) ); connect( ui.okButton, SIGNAL( clicked() ), this, SLOT( okPressed() ) ); connect( ui.collapseButton, SIGNAL( clicked() ), this, SLOT( toggleCollapse() ) ); connect( ui.personalTagsList, SIGNAL( itemDoubleClicked( QListWidgetItem* ) ), this, SLOT( addTag( QListWidgetItem* ) ) ); connect( ui.publicTagsList, SIGNAL( itemDoubleClicked( QListWidgetItem* ) ), this, SLOT( addTag( QListWidgetItem* ) ) ); ws->tagsForArtist( m_song->artist() ); ws->tagsForUser( Settings::instance()->username() ); tagTypeChanged( 0 ); } int TagDialog::execSmall() { setModal( true ); show(); QApplication::processEvents(); delete layout(); collapsed = false; toggleCollapse(); while ( isVisible() ) { #ifdef WIN32 Sleep( 10 ); #endif #ifndef WIN32 usleep( 10 ); #endif QApplication::processEvents(); } done( result() ); return result(); } void TagDialog::okPressed() { ws->setTag( m_song, ui.tagTypeBox->currentIndex(), ui.tagEdit->text() ); } void TagDialog::toggleCollapse() { if ( collapsed ) { collapsed = false; QIcon image( Settings::instance()->dataPath() + "data/collapse.png" ); ui.collapseButton->setIcon( image ); ui.collapseLabel->setText( "Hide favourites" ); resize( 393, 373 ); setMinimumSize( 393, 373 ); setMaximumSize( 393, 373 ); setSizeGripEnabled( false ); } else { collapsed = true; QIcon image( Settings::instance()->dataPath() + "data/expand.png" ); ui.collapseButton->setIcon( image ); ui.collapseLabel->setText( "Show favourites" ); resize( 393, ui.extraLabel1->y() - 4 ); setMinimumSize( 393, ui.extraLabel1->y() - 4 ); setMaximumSize( 393, ui.extraLabel1->y() - 4 ); } } void TagDialog::tagTypeChanged( int type ) { ui.tagTypeBox->setEnabled( false ); // ui.tagEdit->setEnabled( false ); ui.tagEdit->setText( "Loading..." ); switch ( type ) { case 0: ws->tagsForUserArtist( Settings::instance()->username(), m_song->artist() ); break; case 1: ws->tagsForUserAlbum( Settings::instance()->username(), m_song->artist(), m_song->album() ); break; case 2: ws->tagsForUserTrack( Settings::instance()->username(), m_song->artist(), m_song->track() ); break; } } void TagDialog::tagsForArtistResult( const QStringList &result ) { for ( int i = 0; i < result.count(); i++ ) { ui.publicTagsList->addItem( result[i] ); } } void TagDialog::tagsForUserResult( const QStringList &result ) { for ( int i = 0; i < result.count(); i++ ) { ui.personalTagsList->addItem( result[i] ); } } void TagDialog::tagsForUserArtistResult( const QStringList &result ) { QString tags = ""; for ( int i = 0; i < result.count(); i++ ) { tags += ( i > 0 ? ", " : "" ) + result[i]; } ui.tagTypeBox->setEnabled( true ); ui.tagEdit->setEnabled( true ); ui.tagEdit->setText( tags ); ui.tagEdit->setFocus(); } void TagDialog::tagsForUserTrackResult( const QStringList &result ) { QString tags = ""; for ( int i = 0; i < result.count(); i++ ) { tags += ( i > 0 ? ", " : "" ) + result[i]; } ui.tagTypeBox->setEnabled( true ); ui.tagEdit->setEnabled( true ); ui.tagEdit->setText( tags ); ui.tagEdit->setFocus(); } void TagDialog::tagsForUserAlbumResult( const QStringList &result ) { QString tags = ""; for ( int i = 0; i < result.count(); i++ ) { tags += ( i > 0 ? ", " : "" ) + result[i]; } ui.tagTypeBox->setEnabled( true ); ui.tagEdit->setEnabled( true ); ui.tagEdit->setText( tags ); ui.tagEdit->setFocus(); } void TagDialog::addTag( QListWidgetItem *item ) { if ( !ui.tagEdit->text().trimmed().isEmpty() ) ui.tagEdit->setText( ui.tagEdit->text() + ", " + item->text() ); else ui.tagEdit->setText( item->text() ); } lastfm-player-1.1.4_p1910/src/about.ui0000644000175000001440000001447610352604467016420 0ustar davidusers AboutDialog 0 0 383 228 0 0 0 0 383 228 383 228 About Last.fm Player 10 180 351 33 0 6 Qt::Horizontal 131 31 OK Qt::Horizontal 40 20 20 20 341 135 0 6 ../data/icon.png Qt::Horizontal QSizePolicy::Fixed 24 20 0 6 Qt::Vertical QSizePolicy::Fixed 20 4 <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt; font-weight:600;">Last.fm Player 1.1.4</span></p></body></html> Qt::Vertical QSizePolicy::Fixed 20 8 <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Copyright 2005 by Last.fm</p></body></html> <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">http://www.last.fm/</p></body></html> Qt::Vertical 20 40 okButton clicked() AboutDialog accept() 278 253 96 254 lastfm-player-1.1.4_p1910/src/linklabel.cpp0000644000175000001440000000314310352604467017375 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "linklabel.h" LinkLabel::LinkLabel( QWidget *parent ) : QLabel( parent ) { } void LinkLabel::mousePressEvent( QMouseEvent *e ) { emit clicked(); } lastfm-player-1.1.4_p1910/src/tagdialog.ui0000644000175000001440000002336010352604467017231 0ustar davidusers TagDialog 0 0 392 373 0 0 0 0 Tagging 8 6 0 6 Tag this Artist Album Song as Qt::Horizontal QSizePolicy::Fixed 4 4 Qt::Horizontal QSizePolicy::Fixed 4 4 Qt::Vertical QSizePolicy::Fixed 20 4 0 6 0 0 0 0 0 0 16777215 16777215 ../data/expand.png 108 0 Show favourites Qt::AlignTop 0 4 Qt::Horizontal 16 34 0 3 Cancel Save Qt::Vertical QSizePolicy::Fixed 20 6 0 6 Qt::Horizontal QSizePolicy::Fixed 6 4 Your favourite tags: Qt::Horizontal QSizePolicy::Fixed 6 4 Qt::Horizontal QSizePolicy::Fixed 4 4 Popular tags for this item: Qt::Horizontal QSizePolicy::Fixed 4 4 Qt::Vertical QSizePolicy::Fixed 6 8 tagTypeBox tagEdit collapseButton okButton cancelButton personalTagsList publicTagsList okButton clicked() TagDialog accept() 279 115 96 254 cancelButton clicked() TagDialog reject() 369 253 179 282 lastfm-player-1.1.4_p1910/src/settings.h0000644000175000001440000000732610352604467016754 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include "ui_settings.h" class BrowserThread : public QThread { public: void run(); }; class Settings : public QDialog { Q_OBJECT static Settings *s_instance; public: Settings( QWidget *parent = 0 ); static Settings *instance() { return s_instance; } QString dataPath(); void setDataPath( QString path ); void setInitial( bool initial ); bool debug(); void setDebug( bool debug ); bool actAsProxy(); void setActAsProxy( bool proxy ); int volume(); void setVolume( int volume ); int positionX(); void setPositionX( int x ); int positionY(); void setPositionY( int y ); int soundSystem(); void setSoundSystem( int system ); int soundCard(); void setSoundCard( int card ); QString username(); void setUsername( QString username ); QString password(); void setPassword( QString password ); QString browser(); void setBrowser( QString browser ); int bufferSize(); void setBufferSize( int value ); QString proxyUsername(); void setProxyUsername( QString username ); QString proxyPassword(); void setProxyPassword( QString password ); QString proxyHost(); void setProxyHost( QString host ); int proxyPort(); void setProxyPort( int port ); bool proxyUsage(); void setProxyUsage( bool enabled ); int stationCounter( QString url ); QString stationName( QString url ); void incrementStationCounter( QString url, QString stationName = QString::null ); void deleteStationCounter( QString url ); void resetStationCounter( QString url ); void clearStationCounters(); QStringList stationItems(); void startBrowser( QString url ); public slots: void okPressed(); void signUp(); signals: void configChanged(); private: Ui::SettingsForm ui; QSettings *config; QString m_dataPath; bool m_debug; bool m_actAsProxy; bool m_initial; QString originalUsername; QString originalPassword; QString originalProxy; }; lastfm-player-1.1.4_p1910/src/slidinglabel.h0000644000175000001440000000406310352604467017540 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef SLIDINGLABEL_H #define SLIDINGLABEL_H #include class QTimer; class SlidingLabel : public QLabel { Q_OBJECT public: SlidingLabel( QWidget *parent ); QSize sizeHint() const; public slots: void setText( const QString &text ); signals: void clicked(); protected: virtual void mousePressEvent( QMouseEvent *e ); virtual void paintEvent( QPaintEvent *e ); private slots: void slotAnimationFrame(); private: QRect textRect() const; void initScrollingIn(); QString m_text; QString m_newText; int m_offset; double m_offsetStep; QTimer *m_switchTimer; }; #endif // SLIDINGLABEL_H lastfm-player-1.1.4_p1910/src/playermini.ui0000644000175000001440000011174510352604467017454 0ustar davidusers PlayerMiniForm 0 0 310 150 310 150 310 150 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 227 230 216 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 227 230 216 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 227 230 216 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 true Last.fm Player ../data/icon.png 70 260 55 16 TextLabel 20 180 271 47 10 260 55 16 TextLabel 470 210 50 149 ../data/curve.png 400 270 20 21 Tag this Song ../data/buttons/tag_normal.png 330 300 21 21 Write a Journal about this Song ../data/buttons/blog_normal.png 70 290 21 21 0 5 0 0 ../data/artist.png Qt::AlignTop 150 270 21 21 0 5 0 0 ../data/album.png Qt::AlignTop 210 280 21 21 0 5 0 0 ../data/song.png Qt::AlignTop 20 240 621 31 7 5 0 0 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 Sans Serif 10 75 false true false false <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:600; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11pt; font-weight:400; color:#ffffff;">StationLabel</span></p></body></html> 0 10 0 100 621 51 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 210 0 57 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 210 0 57 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 210 0 57 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 QFrame::NoFrame QFrame::Raised 14 0 16 51 ../data/volume_left.png 120 0 21 51 ../data/volume_right.png 30 18 81 16 Qt::Horizontal 193 0 41 51 Skip this track ../data/buttons/skip_enabled.png Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 396 0 41 51 Options... ../data/buttons/config_enabled.png 354 0 41 51 true Stop playback ../data/buttons/stop_enabled.png Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 153 0 41 51 Love this track ../data/buttons/love_enabled.png Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 233 0 41 51 Ban this track ../data/buttons/ban_enabled.png Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 90 250 190 16 Sans Serif 10 50 false false false false 13 Click to go to this track page Track Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 110 230 190 16 Sans Serif 10 50 false false false false 13 Click to go to this album page Album Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 120 210 33 16 0 0 0 0 Sans Serif 10 50 false false false false 13 Click to go to this artist page Artist Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 2 2 61 61 13 13 0 0 16 16 13 Click to go to this album page true 280 0 24 20 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 128 128 128 221 223 228 255 255 255 255 255 255 110 111 114 147 149 152 128 128 128 255 255 255 128 128 128 255 255 255 145 153 142 0 0 0 86 117 148 255 255 255 0 0 238 82 24 139 238 239 241 logo 70 40 181 15 16777215 15 70 10 181 16 TextLabel ProgressFrame QProgressBar
src/progressframe.h
0
LinkLabel QLabel
src/linklabel.h
0
SlidingLabel QLabel
src/slidinglabel.h
0
ImageButton QLabel
src/imagebutton.h
0
lastfm-player-1.1.4_p1910/src/mpglib/0000755000175000001440000000000010352604625016201 5ustar daviduserslastfm-player-1.1.4_p1910/src/mpglib/interface.c0000644000175000001440000003065410352604465020317 0ustar davidusers/* $Id: interface.c,v 1.46 2004/03/10 00:50:54 robert Exp $ */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include "common.h" #include "interface.h" #include "tabinit.h" #include "layer3.h" #include "VbrTag.h" #include "decode_i386.h" #ifdef USE_LAYER_1 #include "layer1.h" #endif #ifdef USE_LAYER_2 #include "layer2.h" #endif #ifdef WITH_DMALLOC #include #endif BOOL InitMP3( PMPSTR mp) { memset(mp,0,sizeof(MPSTR)); mp->framesize = 0; mp->num_frames = 0; mp->enc_delay = -1; mp->enc_padding = -1; mp->vbr_header=0; mp->header_parsed=0; mp->side_parsed=0; mp->data_parsed=0; mp->free_format=0; mp->old_free_format=0; mp->ssize = 0; mp->dsize=0; mp->fsizeold = -1; mp->bsize = 0; mp->head = mp->tail = NULL; mp->fr.single = -1; mp->bsnum = 0; wordpointer = mp->bsspace[mp->bsnum] + 512; mp->synth_bo = 1; mp->sync_bitstream = 1; make_decode_tables(32767); init_layer3(SBLIMIT); #ifdef USE_LAYER_2 init_layer2(); #endif return !0; } void ExitMP3( PMPSTR mp) { struct buf *b,*bn; b = mp->tail; while(b) { free(b->pnt); bn = b->next; free(b); b = bn; } } static struct buf *addbuf( PMPSTR mp, unsigned char *buf,int size) { struct buf *nbuf; nbuf = (struct buf*) malloc( sizeof(struct buf) ); if(!nbuf) { fprintf(stderr,"Out of memory!\n"); return NULL; } nbuf->pnt = (unsigned char*) malloc((size_t)size); if(!nbuf->pnt) { free(nbuf); return NULL; } nbuf->size = size; memcpy(nbuf->pnt,buf,(size_t)size); nbuf->next = NULL; nbuf->prev = mp->head; nbuf->pos = 0; if(!mp->tail) { mp->tail = nbuf; } else { mp->head->next = nbuf; } mp->head = nbuf; mp->bsize += size; return nbuf; } void remove_buf(PMPSTR mp) { struct buf *buf = mp->tail; mp->tail = buf->next; if(mp->tail) mp->tail->prev = NULL; else { mp->tail = mp->head = NULL; } free(buf->pnt); free(buf); } static int read_buf_byte(PMPSTR mp) { unsigned int b; int pos; pos = mp->tail->pos; while(pos >= mp->tail->size) { remove_buf(mp); if(!mp->tail) { fprintf(stderr,"Fatal error! tried to read past mp buffer\n"); exit(1); } pos = mp->tail->pos; } b = mp->tail->pnt[pos]; mp->bsize--; mp->tail->pos++; return b; } static void read_head(PMPSTR mp) { unsigned long head; head = read_buf_byte(mp); head <<= 8; head |= read_buf_byte(mp); head <<= 8; head |= read_buf_byte(mp); head <<= 8; head |= read_buf_byte(mp); mp->header = head; } void copy_mp(PMPSTR mp,int size,unsigned char *ptr) { int len = 0; while(len < size && mp->tail) { int nlen; int blen = mp->tail->size - mp->tail->pos; if( (size - len) <= blen) { nlen = size-len; } else { nlen = blen; } memcpy(ptr+len,mp->tail->pnt+mp->tail->pos,(size_t)nlen); len += nlen; mp->tail->pos += nlen; mp->bsize -= nlen; if(mp->tail->pos == mp->tail->size) { remove_buf(mp); } } } /* number of bytes needed by GetVbrTag to parse header */ #define XING_HEADER_SIZE 194 /* traverse mp data structure without changing it */ /* (just like sync_buffer) */ /* pull out Xing bytes */ /* call vbr header check code from LAME */ /* if we find a header, parse it and also compute the VBR header size */ /* if no header, do nothing. */ /* */ /* bytes = number of bytes before MPEG header. skip this many bytes */ /* before starting to read */ /* return value: number of bytes in VBR header, including syncword */ int check_vbr_header(PMPSTR mp,int bytes) { return 0; } int sync_buffer(PMPSTR mp,int free_match) { /* traverse mp structure without modifing pointers, looking * for a frame valid header. * if free_format, valid header must also have the same * samplerate. * return number of bytes in mp, before the header * return -1 if header is not found */ unsigned int b[4]={0,0,0,0}; int i,h,pos; struct buf *buf=mp->tail; if (!buf) return -1; pos = buf->pos; for (i=0; ibsize; i++) { /* get 4 bytes */ b[0]=b[1]; b[1]=b[2]; b[2]=b[3]; while(pos >= buf->size) { buf = buf->next; pos = buf->pos; if(!buf) { return -1; /* not enough data to read 4 bytes */ } } b[3] = buf->pnt[pos]; ++pos; if (i>=3) { struct frame *fr = &mp->fr; unsigned long head; head = b[0]; head <<= 8; head |= b[1]; head <<= 8; head |= b[2]; head <<= 8; head |= b[3]; h = head_check(head,fr->lay); if (h && free_match) { /* just to be even more thorough, match the sample rate */ int mode,stereo,sampling_frequency,mpeg25,lsf; if( head & (1<<20) ) { lsf = (head & (1<<19)) ? 0x0 : 0x1; mpeg25 = 0; } else { lsf = 1; mpeg25 = 1; } mode = ((head>>6)&0x3); stereo = (mode == MPG_MD_MONO) ? 1 : 2; if(mpeg25) sampling_frequency = 6 + ((head>>10)&0x3); else sampling_frequency = ((head>>10)&0x3) + (lsf*3); h = ((stereo==fr->stereo) && (lsf==fr->lsf) && (mpeg25==fr->mpeg25) && (sampling_frequency == fr->sampling_frequency)); } if (h) { return i-3; } } } return -1; } int decodeMP3_clipchoice( PMPSTR mp,unsigned char *in,int isize,char *out, int osize,int *done, int (*synth_1to1_mono_ptr)(PMPSTR,real *,unsigned char *,int *), int (*synth_1to1_ptr)(PMPSTR,real *,int,unsigned char *, int *) ) { int i,iret,bits,bytes; if (in && isize && addbuf(mp,in,isize) == NULL) return MP3_ERR; /* First decode header */ if(!mp->header_parsed) { if (mp->fsizeold==-1 || mp->sync_bitstream) { int vbrbytes; mp->sync_bitstream=0; /* This is the very first call. sync with anything */ /* bytes= number of bytes before header */ bytes=sync_buffer(mp,0); /* now look for Xing VBR header */ if (mp->bsize >= bytes+XING_HEADER_SIZE ) { /* vbrbytes = number of bytes in entire vbr header */ vbrbytes=check_vbr_header(mp,bytes); } else { /* not enough data to look for Xing header */ return MP3_NEED_MORE; } if (mp->vbr_header) { /* do we have enough data to parse entire Xing header? */ if (bytes+vbrbytes > mp->bsize) return MP3_NEED_MORE; /* read in Xing header. Buffer data in case it * is used by a non zero main_data_begin for the next * frame, but otherwise dont decode Xing header */ /*fprintf(stderr,"found xing header, skipping %i bytes\n",vbrbytes+bytes);*/ for (i=0; i0) { /* there were some extra bytes in front of header. * bitstream problem, but we are now resynced * should try to buffer previous data in case new * frame has nonzero main_data_begin, but we need * to make sure we do not overflow buffer */ int size; fprintf(stderr,"bitstream problem: resyncing...\n"); mp->old_free_format=0; mp->sync_bitstream=1; /* skip some bytes, buffer the rest */ size = (int) (wordpointer - (mp->bsspace[mp->bsnum]+512)); if (size > MAXFRAMESIZE) { /* wordpointer buffer is trashed. probably cant recover, but try anyway */ fprintf(stderr,"mpglib: wordpointer trashed. size=%i (%i) bytes=%i \n", size,MAXFRAMESIZE,bytes); size=0; wordpointer = mp->bsspace[mp->bsnum]+512; } /* buffer contains 'size' data right now we want to add 'bytes' worth of data, but do not exceed MAXFRAMESIZE, so we through away 'i' bytes */ i = (size+bytes)-MAXFRAMESIZE; for (; i>0; --i) { --bytes; read_buf_byte(mp); } copy_mp(mp,bytes,wordpointer); mp->fsizeold += bytes; } read_head(mp); decode_header(&mp->fr,mp->header); mp->header_parsed=1; mp->framesize = mp->fr.framesize; mp->free_format = (mp->framesize==0); if(mp->fr.lsf) mp->ssize = (mp->fr.stereo == 1) ? 9 : 17; else mp->ssize = (mp->fr.stereo == 1) ? 17 : 32; if (mp->fr.error_protection) mp->ssize += 2; mp->bsnum = 1-mp->bsnum; /* toggle buffer */ wordpointer = mp->bsspace[mp->bsnum] + 512; bitindex = 0; /* for very first header, never parse rest of data */ if (mp->fsizeold==-1) return MP3_NEED_MORE; } /* now decode side information */ if (!mp->side_parsed) { /* Layer 3 only */ if (mp->fr.lay==3) { if (mp->bsize < mp->ssize) return MP3_NEED_MORE; copy_mp(mp,mp->ssize,wordpointer); if(mp->fr.error_protection) getbits(16); bits=do_layer3_sideinfo(&mp->fr); /* bits = actual number of bits needed to parse this frame */ /* can be negative, if all bits needed are in the reservoir */ if (bits<0) bits=0; /* read just as many bytes as necessary before decoding */ mp->dsize = (bits+7)/8; /* this will force mpglib to read entire frame before decoding */ /* mp->dsize= mp->framesize - mp->ssize;*/ } else { /* Layers 1 and 2 */ /* check if there is enough input data */ if(mp->fr.framesize > mp->bsize) return MP3_NEED_MORE; /* takes care that the right amount of data is copied into wordpointer */ mp->dsize=mp->fr.framesize; mp->ssize=0; } mp->side_parsed=1; } /* now decode main data */ iret=MP3_NEED_MORE; if (!mp->data_parsed ) { if(mp->dsize > mp->bsize) { return MP3_NEED_MORE; } copy_mp(mp,mp->dsize,wordpointer); *done = 0; /*do_layer3(&mp->fr,(unsigned char *) out,done); */ switch (mp->fr.lay) { #ifdef USE_LAYER_1 case 1: if(mp->fr.error_protection) getbits(16); do_layer1(mp,(unsigned char *) out,done); break; #endif #ifdef USE_LAYER_2 case 2: if(mp->fr.error_protection) getbits(16); do_layer2(mp,(unsigned char *) out,done); break; #endif case 3: do_layer3(mp,(unsigned char *) out,done, synth_1to1_mono_ptr, synth_1to1_ptr); break; default: fprintf(stderr,"invalid layer %d\n",mp->fr.lay); } wordpointer = mp->bsspace[mp->bsnum] + 512 + mp->ssize + mp->dsize; mp->data_parsed=1; iret=MP3_OK; } /* remaining bits are ancillary data, or reservoir for next frame * If free format, scan stream looking for next frame to determine * mp->framesize */ if (mp->free_format) { if (mp->old_free_format) { /* free format. bitrate must not vary */ mp->framesize=mp->fsizeold_nopadding + (mp->fr.padding); }else{ bytes=sync_buffer(mp,1); if (bytes<0) return iret; mp->framesize = bytes + mp->ssize+mp->dsize; mp->fsizeold_nopadding= mp->framesize - mp->fr.padding; /* fprintf(stderr,"freeformat bitstream: estimated bitrate=%ikbs \n", 8*(4+mp->framesize)*freqs[mp->fr.sampling_frequency]/ (1000*576*(2-mp->fr.lsf))); */ } } /* buffer the ancillary data and reservoir for next frame */ bytes = mp->framesize-(mp->ssize+mp->dsize); if (bytes > mp->bsize) { return iret; } if (bytes>0) { int size; copy_mp(mp,bytes,wordpointer); wordpointer += bytes; size = (int) (wordpointer - (mp->bsspace[mp->bsnum]+512)); if (size > MAXFRAMESIZE) { fprintf(stderr,"fatal error. MAXFRAMESIZE not large enough.\n"); } } /* the above frame is completey parsed. start looking for next frame */ mp->fsizeold = mp->framesize; mp->old_free_format = mp->free_format; mp->framesize =0; mp->header_parsed=0; mp->side_parsed=0; mp->data_parsed=0; return iret; } int decodeMP3( PMPSTR mp,unsigned char *in,int isize,char *out, int osize,int *done) { if(osize < 4608) { fprintf(stderr,"To less out space\n"); return MP3_ERR; } /* passing pointers to the functions which clip the samples */ return decodeMP3_clipchoice(mp, in, isize, out, osize, done, synth_1to1_mono, synth_1to1); } int decodeMP3_unclipped( PMPSTR mp,unsigned char *in,int isize,char *out, int osize,int *done) { /* we forbid input with more than 1152 samples per channel for output in unclipped mode */ if(osize < 1152 * 2 * sizeof(real) ) { fprintf(stderr,"To less out space\n"); return MP3_ERR; } /* passing pointers to the functions which don't clip the samples */ return decodeMP3_clipchoice(mp, in, isize, out, osize, done, synth_1to1_mono_unclipped, synth_1to1_unclipped); } lastfm-player-1.1.4_p1910/src/mpglib/interface.h0000644000175000001440000000301210352604465020310 0ustar davidusers/* ** Copyright (C) 2000 Albert L. Faber ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef INTERFACE_H_INCLUDED #define INTERFACE_H_INCLUDED #ifdef __cplusplus extern "C" { #endif #include "common.h" BOOL InitMP3(PMPSTR mp); int decodeMP3(PMPSTR mp,unsigned char *inmemory,int inmemsize,char *outmemory,int outmemsize,int *done); void ExitMP3(PMPSTR mp); /* added decodeMP3_unclipped to support returning raw floating-point values of samples. The representation of the floating-point numbers is defined in mpg123.h as #define real. It is 64-bit double by default. No more than 1152 samples per channel are allowed. */ int decodeMP3_unclipped(PMPSTR mp,unsigned char *inmemory,int inmemsize,char *outmemory,int outmemsize,int *done); /* added remove_buf to support mpglib seeking */ void remove_buf(PMPSTR mp); #ifdef __cplusplus } #endif #endif lastfm-player-1.1.4_p1910/src/mpglib/dct64_i386.c0000644000175000001440000002273610352604465020056 0ustar davidusers/* * Discrete Cosine Tansform (DCT) for subband synthesis * optimized for machines with no auto-increment. * The performance is highly compiler dependend. Maybe * the dct64.c version for 'normal' processor may be faster * even for Intel processors. */ /* $Id: dct64_i386.c,v 1.9 2001/01/05 15:20:34 aleidinger Exp $ */ #ifdef HAVE_CONFIG_H #include #endif #include "dct64_i386.h" #include "tabinit.h" #ifdef WITH_DMALLOC #include #endif static void dct64_1(real *out0,real *out1,real *b1,real *b2,real *samples) { { register real *costab = pnts[0]; b1[0x00] = samples[0x00] + samples[0x1F]; b1[0x1F] = (samples[0x00] - samples[0x1F]) * costab[0x0]; b1[0x01] = samples[0x01] + samples[0x1E]; b1[0x1E] = (samples[0x01] - samples[0x1E]) * costab[0x1]; b1[0x02] = samples[0x02] + samples[0x1D]; b1[0x1D] = (samples[0x02] - samples[0x1D]) * costab[0x2]; b1[0x03] = samples[0x03] + samples[0x1C]; b1[0x1C] = (samples[0x03] - samples[0x1C]) * costab[0x3]; b1[0x04] = samples[0x04] + samples[0x1B]; b1[0x1B] = (samples[0x04] - samples[0x1B]) * costab[0x4]; b1[0x05] = samples[0x05] + samples[0x1A]; b1[0x1A] = (samples[0x05] - samples[0x1A]) * costab[0x5]; b1[0x06] = samples[0x06] + samples[0x19]; b1[0x19] = (samples[0x06] - samples[0x19]) * costab[0x6]; b1[0x07] = samples[0x07] + samples[0x18]; b1[0x18] = (samples[0x07] - samples[0x18]) * costab[0x7]; b1[0x08] = samples[0x08] + samples[0x17]; b1[0x17] = (samples[0x08] - samples[0x17]) * costab[0x8]; b1[0x09] = samples[0x09] + samples[0x16]; b1[0x16] = (samples[0x09] - samples[0x16]) * costab[0x9]; b1[0x0A] = samples[0x0A] + samples[0x15]; b1[0x15] = (samples[0x0A] - samples[0x15]) * costab[0xA]; b1[0x0B] = samples[0x0B] + samples[0x14]; b1[0x14] = (samples[0x0B] - samples[0x14]) * costab[0xB]; b1[0x0C] = samples[0x0C] + samples[0x13]; b1[0x13] = (samples[0x0C] - samples[0x13]) * costab[0xC]; b1[0x0D] = samples[0x0D] + samples[0x12]; b1[0x12] = (samples[0x0D] - samples[0x12]) * costab[0xD]; b1[0x0E] = samples[0x0E] + samples[0x11]; b1[0x11] = (samples[0x0E] - samples[0x11]) * costab[0xE]; b1[0x0F] = samples[0x0F] + samples[0x10]; b1[0x10] = (samples[0x0F] - samples[0x10]) * costab[0xF]; } { register real *costab = pnts[1]; b2[0x00] = b1[0x00] + b1[0x0F]; b2[0x0F] = (b1[0x00] - b1[0x0F]) * costab[0]; b2[0x01] = b1[0x01] + b1[0x0E]; b2[0x0E] = (b1[0x01] - b1[0x0E]) * costab[1]; b2[0x02] = b1[0x02] + b1[0x0D]; b2[0x0D] = (b1[0x02] - b1[0x0D]) * costab[2]; b2[0x03] = b1[0x03] + b1[0x0C]; b2[0x0C] = (b1[0x03] - b1[0x0C]) * costab[3]; b2[0x04] = b1[0x04] + b1[0x0B]; b2[0x0B] = (b1[0x04] - b1[0x0B]) * costab[4]; b2[0x05] = b1[0x05] + b1[0x0A]; b2[0x0A] = (b1[0x05] - b1[0x0A]) * costab[5]; b2[0x06] = b1[0x06] + b1[0x09]; b2[0x09] = (b1[0x06] - b1[0x09]) * costab[6]; b2[0x07] = b1[0x07] + b1[0x08]; b2[0x08] = (b1[0x07] - b1[0x08]) * costab[7]; b2[0x10] = b1[0x10] + b1[0x1F]; b2[0x1F] = (b1[0x1F] - b1[0x10]) * costab[0]; b2[0x11] = b1[0x11] + b1[0x1E]; b2[0x1E] = (b1[0x1E] - b1[0x11]) * costab[1]; b2[0x12] = b1[0x12] + b1[0x1D]; b2[0x1D] = (b1[0x1D] - b1[0x12]) * costab[2]; b2[0x13] = b1[0x13] + b1[0x1C]; b2[0x1C] = (b1[0x1C] - b1[0x13]) * costab[3]; b2[0x14] = b1[0x14] + b1[0x1B]; b2[0x1B] = (b1[0x1B] - b1[0x14]) * costab[4]; b2[0x15] = b1[0x15] + b1[0x1A]; b2[0x1A] = (b1[0x1A] - b1[0x15]) * costab[5]; b2[0x16] = b1[0x16] + b1[0x19]; b2[0x19] = (b1[0x19] - b1[0x16]) * costab[6]; b2[0x17] = b1[0x17] + b1[0x18]; b2[0x18] = (b1[0x18] - b1[0x17]) * costab[7]; } { register real *costab = pnts[2]; b1[0x00] = b2[0x00] + b2[0x07]; b1[0x07] = (b2[0x00] - b2[0x07]) * costab[0]; b1[0x01] = b2[0x01] + b2[0x06]; b1[0x06] = (b2[0x01] - b2[0x06]) * costab[1]; b1[0x02] = b2[0x02] + b2[0x05]; b1[0x05] = (b2[0x02] - b2[0x05]) * costab[2]; b1[0x03] = b2[0x03] + b2[0x04]; b1[0x04] = (b2[0x03] - b2[0x04]) * costab[3]; b1[0x08] = b2[0x08] + b2[0x0F]; b1[0x0F] = (b2[0x0F] - b2[0x08]) * costab[0]; b1[0x09] = b2[0x09] + b2[0x0E]; b1[0x0E] = (b2[0x0E] - b2[0x09]) * costab[1]; b1[0x0A] = b2[0x0A] + b2[0x0D]; b1[0x0D] = (b2[0x0D] - b2[0x0A]) * costab[2]; b1[0x0B] = b2[0x0B] + b2[0x0C]; b1[0x0C] = (b2[0x0C] - b2[0x0B]) * costab[3]; b1[0x10] = b2[0x10] + b2[0x17]; b1[0x17] = (b2[0x10] - b2[0x17]) * costab[0]; b1[0x11] = b2[0x11] + b2[0x16]; b1[0x16] = (b2[0x11] - b2[0x16]) * costab[1]; b1[0x12] = b2[0x12] + b2[0x15]; b1[0x15] = (b2[0x12] - b2[0x15]) * costab[2]; b1[0x13] = b2[0x13] + b2[0x14]; b1[0x14] = (b2[0x13] - b2[0x14]) * costab[3]; b1[0x18] = b2[0x18] + b2[0x1F]; b1[0x1F] = (b2[0x1F] - b2[0x18]) * costab[0]; b1[0x19] = b2[0x19] + b2[0x1E]; b1[0x1E] = (b2[0x1E] - b2[0x19]) * costab[1]; b1[0x1A] = b2[0x1A] + b2[0x1D]; b1[0x1D] = (b2[0x1D] - b2[0x1A]) * costab[2]; b1[0x1B] = b2[0x1B] + b2[0x1C]; b1[0x1C] = (b2[0x1C] - b2[0x1B]) * costab[3]; } { register real const cos0 = pnts[3][0]; register real const cos1 = pnts[3][1]; b2[0x00] = b1[0x00] + b1[0x03]; b2[0x03] = (b1[0x00] - b1[0x03]) * cos0; b2[0x01] = b1[0x01] + b1[0x02]; b2[0x02] = (b1[0x01] - b1[0x02]) * cos1; b2[0x04] = b1[0x04] + b1[0x07]; b2[0x07] = (b1[0x07] - b1[0x04]) * cos0; b2[0x05] = b1[0x05] + b1[0x06]; b2[0x06] = (b1[0x06] - b1[0x05]) * cos1; b2[0x08] = b1[0x08] + b1[0x0B]; b2[0x0B] = (b1[0x08] - b1[0x0B]) * cos0; b2[0x09] = b1[0x09] + b1[0x0A]; b2[0x0A] = (b1[0x09] - b1[0x0A]) * cos1; b2[0x0C] = b1[0x0C] + b1[0x0F]; b2[0x0F] = (b1[0x0F] - b1[0x0C]) * cos0; b2[0x0D] = b1[0x0D] + b1[0x0E]; b2[0x0E] = (b1[0x0E] - b1[0x0D]) * cos1; b2[0x10] = b1[0x10] + b1[0x13]; b2[0x13] = (b1[0x10] - b1[0x13]) * cos0; b2[0x11] = b1[0x11] + b1[0x12]; b2[0x12] = (b1[0x11] - b1[0x12]) * cos1; b2[0x14] = b1[0x14] + b1[0x17]; b2[0x17] = (b1[0x17] - b1[0x14]) * cos0; b2[0x15] = b1[0x15] + b1[0x16]; b2[0x16] = (b1[0x16] - b1[0x15]) * cos1; b2[0x18] = b1[0x18] + b1[0x1B]; b2[0x1B] = (b1[0x18] - b1[0x1B]) * cos0; b2[0x19] = b1[0x19] + b1[0x1A]; b2[0x1A] = (b1[0x19] - b1[0x1A]) * cos1; b2[0x1C] = b1[0x1C] + b1[0x1F]; b2[0x1F] = (b1[0x1F] - b1[0x1C]) * cos0; b2[0x1D] = b1[0x1D] + b1[0x1E]; b2[0x1E] = (b1[0x1E] - b1[0x1D]) * cos1; } { register real const cos0 = pnts[4][0]; b1[0x00] = b2[0x00] + b2[0x01]; b1[0x01] = (b2[0x00] - b2[0x01]) * cos0; b1[0x02] = b2[0x02] + b2[0x03]; b1[0x03] = (b2[0x03] - b2[0x02]) * cos0; b1[0x02] += b1[0x03]; b1[0x04] = b2[0x04] + b2[0x05]; b1[0x05] = (b2[0x04] - b2[0x05]) * cos0; b1[0x06] = b2[0x06] + b2[0x07]; b1[0x07] = (b2[0x07] - b2[0x06]) * cos0; b1[0x06] += b1[0x07]; b1[0x04] += b1[0x06]; b1[0x06] += b1[0x05]; b1[0x05] += b1[0x07]; b1[0x08] = b2[0x08] + b2[0x09]; b1[0x09] = (b2[0x08] - b2[0x09]) * cos0; b1[0x0A] = b2[0x0A] + b2[0x0B]; b1[0x0B] = (b2[0x0B] - b2[0x0A]) * cos0; b1[0x0A] += b1[0x0B]; b1[0x0C] = b2[0x0C] + b2[0x0D]; b1[0x0D] = (b2[0x0C] - b2[0x0D]) * cos0; b1[0x0E] = b2[0x0E] + b2[0x0F]; b1[0x0F] = (b2[0x0F] - b2[0x0E]) * cos0; b1[0x0E] += b1[0x0F]; b1[0x0C] += b1[0x0E]; b1[0x0E] += b1[0x0D]; b1[0x0D] += b1[0x0F]; b1[0x10] = b2[0x10] + b2[0x11]; b1[0x11] = (b2[0x10] - b2[0x11]) * cos0; b1[0x12] = b2[0x12] + b2[0x13]; b1[0x13] = (b2[0x13] - b2[0x12]) * cos0; b1[0x12] += b1[0x13]; b1[0x14] = b2[0x14] + b2[0x15]; b1[0x15] = (b2[0x14] - b2[0x15]) * cos0; b1[0x16] = b2[0x16] + b2[0x17]; b1[0x17] = (b2[0x17] - b2[0x16]) * cos0; b1[0x16] += b1[0x17]; b1[0x14] += b1[0x16]; b1[0x16] += b1[0x15]; b1[0x15] += b1[0x17]; b1[0x18] = b2[0x18] + b2[0x19]; b1[0x19] = (b2[0x18] - b2[0x19]) * cos0; b1[0x1A] = b2[0x1A] + b2[0x1B]; b1[0x1B] = (b2[0x1B] - b2[0x1A]) * cos0; b1[0x1A] += b1[0x1B]; b1[0x1C] = b2[0x1C] + b2[0x1D]; b1[0x1D] = (b2[0x1C] - b2[0x1D]) * cos0; b1[0x1E] = b2[0x1E] + b2[0x1F]; b1[0x1F] = (b2[0x1F] - b2[0x1E]) * cos0; b1[0x1E] += b1[0x1F]; b1[0x1C] += b1[0x1E]; b1[0x1E] += b1[0x1D]; b1[0x1D] += b1[0x1F]; } out0[0x10*16] = b1[0x00]; out0[0x10*12] = b1[0x04]; out0[0x10* 8] = b1[0x02]; out0[0x10* 4] = b1[0x06]; out0[0x10* 0] = b1[0x01]; out1[0x10* 0] = b1[0x01]; out1[0x10* 4] = b1[0x05]; out1[0x10* 8] = b1[0x03]; out1[0x10*12] = b1[0x07]; b1[0x08] += b1[0x0C]; out0[0x10*14] = b1[0x08]; b1[0x0C] += b1[0x0a]; out0[0x10*10] = b1[0x0C]; b1[0x0A] += b1[0x0E]; out0[0x10* 6] = b1[0x0A]; b1[0x0E] += b1[0x09]; out0[0x10* 2] = b1[0x0E]; b1[0x09] += b1[0x0D]; out1[0x10* 2] = b1[0x09]; b1[0x0D] += b1[0x0B]; out1[0x10* 6] = b1[0x0D]; b1[0x0B] += b1[0x0F]; out1[0x10*10] = b1[0x0B]; out1[0x10*14] = b1[0x0F]; b1[0x18] += b1[0x1C]; out0[0x10*15] = b1[0x10] + b1[0x18]; out0[0x10*13] = b1[0x18] + b1[0x14]; b1[0x1C] += b1[0x1a]; out0[0x10*11] = b1[0x14] + b1[0x1C]; out0[0x10* 9] = b1[0x1C] + b1[0x12]; b1[0x1A] += b1[0x1E]; out0[0x10* 7] = b1[0x12] + b1[0x1A]; out0[0x10* 5] = b1[0x1A] + b1[0x16]; b1[0x1E] += b1[0x19]; out0[0x10* 3] = b1[0x16] + b1[0x1E]; out0[0x10* 1] = b1[0x1E] + b1[0x11]; b1[0x19] += b1[0x1D]; out1[0x10* 1] = b1[0x11] + b1[0x19]; out1[0x10* 3] = b1[0x19] + b1[0x15]; b1[0x1D] += b1[0x1B]; out1[0x10* 5] = b1[0x15] + b1[0x1D]; out1[0x10* 7] = b1[0x1D] + b1[0x13]; b1[0x1B] += b1[0x1F]; out1[0x10* 9] = b1[0x13] + b1[0x1B]; out1[0x10*11] = b1[0x1B] + b1[0x17]; out1[0x10*13] = b1[0x17] + b1[0x1F]; out1[0x10*15] = b1[0x1F]; } /* * the call via dct64 is a trick to force GCC to use * (new) registers for the b1,b2 pointer to the bufs[xx] field */ void dct64( real *a,real *b,real *c) { real bufs[0x40]; dct64_1(a,b,bufs,bufs+0x20,c); } lastfm-player-1.1.4_p1910/src/mpglib/dct64_i386.h0000644000175000001440000000161710352604465020056 0ustar davidusers/* ** Copyright (C) 2000 Albert L. Faber ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef MPGLIB_DCT64_I386_H_INCLUDED #define MPGLIB_DCT64_I386_H_INCLUDED #include "common.h" void dct64( real *a,real *b,real *c); #endif lastfm-player-1.1.4_p1910/src/mpglib/decode_i386.c0000644000175000001440000002165010352604465020347 0ustar davidusers/* * Mpeg Layer-1,2,3 audio decoder * ------------------------------ * copyright (c) 1995,1996,1997 by Michael Hipp, All rights reserved. * modified by Aleksander Korzynski (Olcios) '2003 * See also 'README' * * slighlty optimized for machines without autoincrement/decrement. * The performance is highly compiler dependend. Maybe * the decode.c version for 'normal' processor may be faster * even for Intel processors. */ /* $Id: decode_i386.c,v 1.16 2003/02/19 19:46:14 olcios Exp $ */ #ifdef HAVE_CONFIG_H #include #endif # include # include #if defined(__riscos__) && defined(FPA10) #include "ymath.h" #else #include #endif #include "decode_i386.h" #include "dct64_i386.h" #include "tabinit.h" #ifdef WITH_DMALLOC #include #endif /* old WRITE_SAMPLE_CLIPPED */ #define WRITE_SAMPLE_CLIPPED(samples,sum,clip) \ if( (sum) > 32767.0) { *(samples) = 0x7fff; (clip)++; } \ else if( (sum) < -32768.0) { *(samples) = -0x8000; (clip)++; } \ else { *(samples) = ((sum)>0 ? (sum)+0.5 : (sum)-0.5) ; } #define WRITE_SAMPLE_UNCLIPPED(samples,sum,clip) \ *samples = sum; /* versions: clipped (when TYPE == short) and unclipped (when TYPE == real) of synth_1to1_mono* functions */ #define SYNTH_1TO1_MONO_CLIPCHOICE(TYPE,SYNTH_1TO1) \ TYPE samples_tmp[64]; \ TYPE *tmp1 = samples_tmp; \ int i,ret; \ int pnt1 = 0; \ \ ret = SYNTH_1TO1 (mp,bandPtr,0,(unsigned char *) samples_tmp,&pnt1); \ out += *pnt; \ \ for(i=0;i<32;i++) { \ *( (TYPE *) out) = *tmp1; \ out += sizeof(TYPE); \ tmp1 += 2; \ } \ *pnt += 32*sizeof(TYPE); \ \ return ret; int synth_1to1_mono(PMPSTR mp, real *bandPtr,unsigned char *out,int *pnt) { SYNTH_1TO1_MONO_CLIPCHOICE(short,synth_1to1) } int synth_1to1_mono_unclipped(PMPSTR mp, real *bandPtr, unsigned char *out,int *pnt) { SYNTH_1TO1_MONO_CLIPCHOICE(real,synth_1to1_unclipped) } /* versions: clipped (when TYPE == short) and unclipped (when TYPE == real) of synth_1to1* functions */ #define SYNTH_1TO1_CLIPCHOICE(TYPE,WRITE_SAMPLE) \ static const int step = 2; \ int bo; \ TYPE *samples = (TYPE *) (out + *pnt); \ \ real *b0,(*buf)[0x110]; \ int clip = 0; \ int bo1; \ \ bo = mp->synth_bo; \ \ if(!channel) { \ bo--; \ bo &= 0xf; \ buf = mp->synth_buffs[0]; \ } \ else { \ samples++; \ buf = mp->synth_buffs[1]; \ } \ \ if(bo & 0x1) { \ b0 = buf[0]; \ bo1 = bo; \ dct64(buf[1]+((bo+1)&0xf),buf[0]+bo,bandPtr); \ } \ else { \ b0 = buf[1]; \ bo1 = bo+1; \ dct64(buf[0]+bo,buf[1]+bo+1,bandPtr); \ } \ \ mp->synth_bo = bo; \ \ { \ register int j; \ real *window = decwin + 16 - bo1; \ \ for (j=16;j;j--,b0+=0x10,window+=0x20,samples+=step) \ { \ real sum; \ sum = window[0x0] * b0[0x0]; \ sum -= window[0x1] * b0[0x1]; \ sum += window[0x2] * b0[0x2]; \ sum -= window[0x3] * b0[0x3]; \ sum += window[0x4] * b0[0x4]; \ sum -= window[0x5] * b0[0x5]; \ sum += window[0x6] * b0[0x6]; \ sum -= window[0x7] * b0[0x7]; \ sum += window[0x8] * b0[0x8]; \ sum -= window[0x9] * b0[0x9]; \ sum += window[0xA] * b0[0xA]; \ sum -= window[0xB] * b0[0xB]; \ sum += window[0xC] * b0[0xC]; \ sum -= window[0xD] * b0[0xD]; \ sum += window[0xE] * b0[0xE]; \ sum -= window[0xF] * b0[0xF]; \ \ WRITE_SAMPLE (samples,sum,clip); \ } \ \ { \ real sum; \ sum = window[0x0] * b0[0x0]; \ sum += window[0x2] * b0[0x2]; \ sum += window[0x4] * b0[0x4]; \ sum += window[0x6] * b0[0x6]; \ sum += window[0x8] * b0[0x8]; \ sum += window[0xA] * b0[0xA]; \ sum += window[0xC] * b0[0xC]; \ sum += window[0xE] * b0[0xE]; \ WRITE_SAMPLE (samples,sum,clip); \ b0-=0x10,window-=0x20,samples+=step; \ } \ window += bo1<<1; \ \ for (j=15;j;j--,b0-=0x10,window-=0x20,samples+=step) \ { \ real sum; \ sum = -window[-0x1] * b0[0x0]; \ sum -= window[-0x2] * b0[0x1]; \ sum -= window[-0x3] * b0[0x2]; \ sum -= window[-0x4] * b0[0x3]; \ sum -= window[-0x5] * b0[0x4]; \ sum -= window[-0x6] * b0[0x5]; \ sum -= window[-0x7] * b0[0x6]; \ sum -= window[-0x8] * b0[0x7]; \ sum -= window[-0x9] * b0[0x8]; \ sum -= window[-0xA] * b0[0x9]; \ sum -= window[-0xB] * b0[0xA]; \ sum -= window[-0xC] * b0[0xB]; \ sum -= window[-0xD] * b0[0xC]; \ sum -= window[-0xE] * b0[0xD]; \ sum -= window[-0xF] * b0[0xE]; \ sum -= window[-0x0] * b0[0xF]; \ \ WRITE_SAMPLE (samples,sum,clip); \ } \ } \ *pnt += 64*sizeof(TYPE); \ \ return clip; int synth_1to1(PMPSTR mp, real *bandPtr,int channel,unsigned char *out, int *pnt) { SYNTH_1TO1_CLIPCHOICE(short,WRITE_SAMPLE_CLIPPED) } int synth_1to1_unclipped(PMPSTR mp, real *bandPtr,int channel, unsigned char *out, int *pnt) { SYNTH_1TO1_CLIPCHOICE(real,WRITE_SAMPLE_UNCLIPPED) } lastfm-player-1.1.4_p1910/src/mpglib/decode_i386.h0000644000175000001440000000225310352604465020352 0ustar davidusers/* ** Copyright (C) 2000 Albert L. Faber ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef DECODE_I386_H_INCLUDED #define DECODE_I386_H_INCLUDED #include "common.h" int synth_1to1_mono(PMPSTR mp, real *bandPtr,unsigned char *out,int *pnt); int synth_1to1(PMPSTR mp, real *bandPtr,int channel,unsigned char *out,int *pnt); int synth_1to1_mono_unclipped(PMPSTR mp, real *bandPtr,unsigned char *out,int *pnt); int synth_1to1_unclipped(PMPSTR mp, real *bandPtr,int channel,unsigned char *out,int *pnt); #endif lastfm-player-1.1.4_p1910/src/mpglib/encoder.h0000644000175000001440000000743210352604465020001 0ustar davidusers/* * encoder.h include file * * Copyright (c) 2000 Mark Taylor * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef LAME_ENCODER_H #define LAME_ENCODER_H /*********************************************************************** * * encoder and decoder delays * ***********************************************************************/ /* * layer III enc->dec delay: 1056 (1057?) (observed) * layer II enc->dec delay: 480 (481?) (observed) * * polyphase 256-16 (dec or enc) = 240 * mdct 256+32 (9*32) (dec or enc) = 288 * total: 512+16 * * My guess is that delay of polyphase filterbank is actualy 240.5 * (there are technical reasons for this, see postings in mp3encoder). * So total Encode+Decode delay = ENCDELAY + 528 + 1 */ /* * ENCDELAY The encoder delay. * * Minimum allowed is MDCTDELAY (see below) * * The first 96 samples will be attenuated, so using a value less than 96 * will result in corrupt data for the first 96-ENCDELAY samples. * * suggested: 576 * set to 1160 to sync with FhG. */ #define ENCDELAY 576 /* * make sure there is at least one complete frame after the * last frame containing real data * * Using a value of 288 would be sufficient for a * a very sophisticated decoder that can decode granule-by-granule instead * of frame by frame. But lets not assume this, and assume the decoder * will not decode frame N unless it also has data for frame N+1 * */ /*#define POSTDELAY 288*/ #define POSTDELAY 1152 /* * delay of the MDCT used in mdct.c * original ISO routines had a delay of 528! * Takehiro's routines: */ #define MDCTDELAY 48 #define FFTOFFSET (224+MDCTDELAY) /* * Most decoders, including the one we use, have a delay of 528 samples. */ #define DECDELAY 528 /* number of subbands */ #define SBLIMIT 32 /* parition bands bands */ #define CBANDS 64 /* number of critical bands/scale factor bands where masking is computed*/ #define SBPSY_l 21 #define SBPSY_s 12 /* total number of scalefactor bands encoded */ #define SBMAX_l 22 #define SBMAX_s 13 #define PSFB21 6 #define PSFB12 6 /* FFT sizes */ #define BLKSIZE 1024 #define HBLKSIZE (BLKSIZE/2 + 1) #define BLKSIZE_s 256 #define HBLKSIZE_s (BLKSIZE_s/2 + 1) /* #define switch_pe 1800 */ #define NORM_TYPE 0 #define START_TYPE 1 #define SHORT_TYPE 2 #define STOP_TYPE 3 /* * Mode Extention: * When we are in stereo mode, there are 4 possible methods to store these * two channels. The stereo modes -m? are using a subset of them. * * -ms: MPG_MD_LR_LR * -mj: MPG_MD_LR_LR and MPG_MD_MS_LR * -mf: MPG_MD_MS_LR * -mi: all */ #define MPG_MD_LR_LR 0 #define MPG_MD_LR_I 1 #define MPG_MD_MS_LR 2 #define MPG_MD_MS_I 3 #include "machine.h" #include "lame.h" int lame_encode_mp3_frame ( lame_global_flags* const gfp, sample_t* inbuf_l, sample_t* inbuf_r, unsigned char* mp3buf, int mp3buf_size ); #endif /* LAME_ENCODER_H */ lastfm-player-1.1.4_p1910/src/mpglib/mpg123.h0000644000175000001440000000434610352604465017374 0ustar davidusers#ifndef MPG123_H_INCLUDED #define MPG123_H_INCLUDED #include # include #include #if defined(__riscos__) && defined(FPA10) #include "ymath.h" #else #include #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef M_SQRT2 #define M_SQRT2 1.41421356237309504880 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #ifdef REAL_IS_FLOAT # define real float #elif defined(REAL_IS_LONG_DOUBLE) # define real long double #else # define real double #endif #define SBLIMIT 32 #define SSLIMIT 18 #define MPG_MD_STEREO 0 #define MPG_MD_JOINT_STEREO 1 #define MPG_MD_DUAL_CHANNEL 2 #define MPG_MD_MONO 3 #define MAXFRAMESIZE 1792 /* AF: ADDED FOR LAYER1/LAYER2 */ #define SCALE_BLOCK 12 /* Pre Shift fo 16 to 8 bit converter table */ #define AUSHIFT (3) struct frame { int stereo; int jsbound; int single; int lsf; int mpeg25; int header_change; int lay; int error_protection; int bitrate_index; int sampling_frequency; int padding; int extension; int mode; int mode_ext; int copyright; int original; int emphasis; int framesize; /* computed framesize */ /* AF: ADDED FOR LAYER1/LAYER2 */ #if defined(USE_LAYER_2) || defined(USE_LAYER_1) int II_sblimit; struct al_table2 *alloc; int down_sample_sblimit; int down_sample; #endif }; struct gr_info_s { int scfsi; unsigned part2_3_length; unsigned big_values; unsigned scalefac_compress; unsigned block_type; unsigned mixed_block_flag; unsigned table_select[3]; unsigned subblock_gain[3]; unsigned maxband[3]; unsigned maxbandl; unsigned maxb; unsigned region1start; unsigned region2start; unsigned preflag; unsigned scalefac_scale; unsigned count1table_select; real *full_gain[3]; real *pow2gain; }; struct III_sideinfo { unsigned main_data_begin; unsigned private_bits; struct { struct gr_info_s gr[2]; } ch[2]; }; #endif lastfm-player-1.1.4_p1910/src/mpglib/common.c0000644000175000001440000001712010352604465017640 0ustar davidusers/* $Id: common.c,v 1.30 2004/01/10 10:27:28 takehiro Exp $ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #ifdef HAVE_FCNTL_H #include #endif #ifdef macintosh #include #include #else #include #include #endif #include "common.h" #ifdef WITH_DMALLOC #include #endif /* In C++ the array first must be prototyped, why ? */ extern const int tabsel_123 [2] [3] [16]; const int tabsel_123 [2] [3] [16] = { { {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,}, {0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,}, {0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,} }, { {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,}, {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,}, {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,} } }; const long freqs[9] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000 }; int bitindex; unsigned char *wordpointer; unsigned char *pcm_sample; int pcm_point = 0; #if defined( USE_LAYER_1 ) || defined ( USE_LAYER_2 ) real muls[27][64]; #endif #if 0 static void get_II_stuff(struct frame *fr) { static const int translate [3] [2] [16] = /* char ? */ { { { 0,2,2,2,2,2,2,0,0,0,1,1,1,1,1,0 } , { 0,2,2,0,0,0,1,1,1,1,1,1,1,1,1,0 } } , { { 0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0 } , { 0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0 } } , { { 0,3,3,3,3,3,3,0,0,0,1,1,1,1,1,0 } , { 0,3,3,0,0,0,1,1,1,1,1,1,1,1,1,0 } } }; int table,sblim; static const struct al_table2 *tables[5] = { alloc_0, alloc_1, alloc_2, alloc_3 , alloc_4 }; static int sblims[5] = { 27 , 30 , 8, 12 , 30 }; if(fr->lsf) table = 4; else table = translate[fr->sampling_frequency][2-fr->stereo][fr->bitrate_index]; sblim = sblims[table]; fr->alloc = tables[table]; fr->II_sblimit = sblim; } #endif #define HDRCMPMASK 0xfffffd00 int head_check(unsigned long head,int check_layer) { /* look for a valid header. if check_layer > 0, then require that nLayer = check_layer. */ /* bits 13-14 = layer 3 */ int nLayer=4-((head>>17)&3); if( (head & 0xffe00000) != 0xffe00000) { /* syncword */ return FALSE; } #ifndef USE_LAYER_1 if (nLayer == 1) return FALSE; #endif #ifndef USE_LAYER_2 if (nLayer == 2) return FALSE; #endif if (nLayer == 4) return FALSE; if (check_layer > 0 && nLayer != check_layer) return FALSE; if( ((head>>12)&0xf) == 0xf) { /* bits 16,17,18,19 = 1111 invalid bitrate */ return FALSE; } if( ((head>>10)&0x3) == 0x3 ) { /* bits 20,21 = 11 invalid sampling freq */ return FALSE; } if ((head&0x3) == 0x2 ) /* invalid emphasis */ return FALSE; return TRUE; } /* * the code a header and write the information * into the frame structure */ int decode_header(struct frame *fr,unsigned long newhead) { if( newhead & (1<<20) ) { fr->lsf = (newhead & (1<<19)) ? 0x0 : 0x1; fr->mpeg25 = 0; } else { fr->lsf = 1; fr->mpeg25 = 1; } fr->lay = 4-((newhead>>17)&3); if( ((newhead>>10)&0x3) == 0x3) { fprintf(stderr,"Stream error\n"); exit(1); } if(fr->mpeg25) { fr->sampling_frequency = 6 + ((newhead>>10)&0x3); } else fr->sampling_frequency = ((newhead>>10)&0x3) + (fr->lsf*3); fr->error_protection = ((newhead>>16)&0x1)^0x1; if(fr->mpeg25) /* allow Bitrate change for 2.5 ... */ fr->bitrate_index = ((newhead>>12)&0xf); fr->bitrate_index = ((newhead>>12)&0xf); fr->padding = ((newhead>>9)&0x1); fr->extension = ((newhead>>8)&0x1); fr->mode = ((newhead>>6)&0x3); fr->mode_ext = ((newhead>>4)&0x3); fr->copyright = ((newhead>>3)&0x1); fr->original = ((newhead>>2)&0x1); fr->emphasis = newhead & 0x3; fr->stereo = (fr->mode == MPG_MD_MONO) ? 1 : 2; switch(fr->lay) { #ifdef USE_LAYER_1 case 1: fr->framesize = (long) tabsel_123[fr->lsf][0][fr->bitrate_index] * 12000; fr->framesize /= freqs[fr->sampling_frequency]; fr->framesize = ((fr->framesize+fr->padding)<<2)-4; fr->down_sample=0; fr->down_sample_sblimit = SBLIMIT>>(fr->down_sample); break; #endif #ifdef USE_LAYER_2 case 2: fr->framesize = (long) tabsel_123[fr->lsf][1][fr->bitrate_index] * 144000; fr->framesize /= freqs[fr->sampling_frequency]; fr->framesize += fr->padding - 4; fr->down_sample=0; fr->down_sample_sblimit = SBLIMIT>>(fr->down_sample); break; #endif case 3: #if 0 fr->do_layer = do_layer3; if(fr->lsf) ssize = (fr->stereo == 1) ? 9 : 17; else ssize = (fr->stereo == 1) ? 17 : 32; #endif #if 0 if(fr->error_protection) ssize += 2; #endif if (fr->bitrate_index==0) fr->framesize=0; else{ fr->framesize = (long) tabsel_123[fr->lsf][2][fr->bitrate_index] * 144000; fr->framesize /= freqs[fr->sampling_frequency]<<(fr->lsf); fr->framesize = fr->framesize + fr->padding - 4; } break; default: fprintf(stderr,"Sorry, layer %d not supported\n",fr->lay); return (0); } /* print_header(fr); */ return 1; } #if 1 void print_header(struct frame *fr) { static const char *modes[4] = { "Stereo", "Joint-Stereo", "Dual-Channel", "Single-Channel" }; static const char *layers[4] = { "Unknown" , "I", "II", "III" }; fprintf(stderr,"MPEG %s, Layer: %s, Freq: %ld, mode: %s, modext: %d, BPF : %d\n", fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"), layers[fr->lay],freqs[fr->sampling_frequency], modes[fr->mode],fr->mode_ext,fr->framesize+4); fprintf(stderr,"Channels: %d, copyright: %s, original: %s, CRC: %s, emphasis: %d.\n", fr->stereo,fr->copyright?"Yes":"No", fr->original?"Yes":"No",fr->error_protection?"Yes":"No", fr->emphasis); fprintf(stderr,"Bitrate: %d Kbits/s, Extension value: %d\n", tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index],fr->extension); } void print_header_compact(struct frame *fr) { static const char *modes[4] = { "stereo", "joint-stereo", "dual-channel", "mono" }; static const char *layers[4] = { "Unknown" , "I", "II", "III" }; fprintf(stderr,"MPEG %s layer %s, %d kbit/s, %ld Hz %s\n", fr->mpeg25 ? "2.5" : (fr->lsf ? "2.0" : "1.0"), layers[fr->lay], tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index], freqs[fr->sampling_frequency], modes[fr->mode]); } #endif unsigned int getbits(int number_of_bits) { unsigned long rval; if (number_of_bits <= 0 || !wordpointer) return 0; { rval = wordpointer[0]; rval <<= 8; rval |= wordpointer[1]; rval <<= 8; rval |= wordpointer[2]; rval <<= bitindex; rval &= 0xffffff; bitindex += number_of_bits; rval >>= (24-number_of_bits); wordpointer += (bitindex>>3); bitindex &= 7; } return rval; } unsigned int getbits_fast(int number_of_bits) { unsigned long rval; { rval = wordpointer[0]; rval <<= 8; rval |= wordpointer[1]; rval <<= bitindex; rval &= 0xffff; bitindex += number_of_bits; rval >>= (16-number_of_bits); wordpointer += (bitindex>>3); bitindex &= 7; } return rval; } int set_pointer( PMPSTR mp, long backstep) { unsigned char *bsbufold; if(mp->fsizeold < 0 && backstep > 0) { fprintf(stderr,"Can't step back %ld!\n",backstep); return MP3_ERR; } bsbufold = mp->bsspace[1-mp->bsnum] + 512; wordpointer -= backstep; if (backstep) memcpy(wordpointer,bsbufold+mp->fsizeold-backstep,(size_t)backstep); bitindex = 0; return MP3_OK; } lastfm-player-1.1.4_p1910/src/mpglib/common.h0000644000175000001440000000260710352604465017651 0ustar davidusers/* ** Copyright (C) 2000 Albert L. Faber ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef COMMON_H_INCLUDED #define COMMON_H_INCLUDED #include "mpg123.h" #include "mpglib.h" extern const int tabsel_123[2][3][16]; extern const long freqs[9]; extern unsigned char *wordpointer; extern int bitindex; #if defined( USE_LAYER_1 ) || defined ( USE_LAYER_2 ) extern real muls[27][64]; #endif int head_check(unsigned long head,int check_layer); int decode_header(struct frame *fr,unsigned long newhead); void print_header(struct frame *fr); void print_header_compact(struct frame *fr); unsigned int getbits(int number_of_bits); unsigned int getbits_fast(int number_of_bits); int set_pointer( PMPSTR mp, long backstep); #endif lastfm-player-1.1.4_p1910/src/mpglib/mpglib.h0000644000175000001440000000265310352604465017634 0ustar davidusers#include "lame-analysis.h" //extern plotting_data *mpg123_pinfo; struct buf { unsigned char *pnt; long size; long pos; struct buf *next; struct buf *prev; }; struct framebuf { struct buf *buf; long pos; struct frame *next; struct frame *prev; }; typedef struct mpstr_tag { struct buf *head,*tail; int vbr_header; /* 1 if valid Xing vbr header detected */ int num_frames; /* set if vbr header present */ int enc_delay; /* set if vbr header present */ int enc_padding; /* set if vbr header present */ int header_parsed; int side_parsed; int data_parsed; int free_format; /* 1 = free format frame */ int old_free_format; /* 1 = last frame was free format */ int bsize; int framesize; int ssize; int dsize; int fsizeold; int fsizeold_nopadding; struct frame fr; unsigned char bsspace[2][MAXFRAMESIZE+512]; /* MAXFRAMESIZE */ real hybrid_block[2][2][SBLIMIT*SSLIMIT]; int hybrid_blc[2]; unsigned long header; int bsnum; real synth_buffs[2][2][0x110]; int synth_bo; int sync_bitstream; } MPSTR, *PMPSTR; #if ( defined(_MSC_VER) || defined(__BORLANDC__) ) typedef int BOOL; /* windef.h contains the same definition */ #else #define BOOL int #endif #define MP3_ERR -1 #define MP3_OK 0 #define MP3_NEED_MORE 1 lastfm-player-1.1.4_p1910/src/mpglib/lame.h0000644000175000001440000011645710352604465017310 0ustar davidusers/* * Interface to MP3 LAME encoding engine * * Copyright (c) 1999 Mark Taylor * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Id: lame.h,v 1.153 2004/03/23 01:57:16 olcios Exp $ */ #ifndef LAME_LAME_H #define LAME_LAME_H #include #include #if defined(__cplusplus) extern "C" { #endif #if defined(WIN32) #undef CDECL #define CDECL _cdecl #else #define CDECL #endif typedef enum vbr_mode_e { vbr_off=0, vbr_mt, /* obsolete, same as vbr_mtrh */ vbr_rh, vbr_abr, vbr_mtrh, vbr_max_indicator, /* Don't use this! It's used for sanity checks. */ vbr_default=vbr_rh /* change this to change the default VBR mode of LAME */ } vbr_mode; /* MPEG modes */ typedef enum MPEG_mode_e { STEREO = 0, JOINT_STEREO, DUAL_CHANNEL, /* LAME doesn't supports this! */ MONO, NOT_SET, MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ } MPEG_mode; /* Padding types */ typedef enum Padding_type_e { PAD_NO = 0, PAD_ALL, PAD_ADJUST, PAD_MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ } Padding_type; /*presets*/ typedef enum preset_mode_e { /*values from 8 to 320 should be reserved for abr bitrates*/ /*for abr I'd suggest to directly use the targeted bitrate as a value*/ ABR_8 = 8, ABR_320 = 320, V9 = 410, /*Vx to match Lame and VBR_xx to match FhG*/ VBR_10 = 410, V8 = 420, VBR_20 = 420, V7 = 430, VBR_30 = 430, V6 = 440, VBR_40 = 440, V5 = 450, VBR_50 = 450, V4 = 460, VBR_60 = 460, V3 = 470, VBR_70 = 470, V2 = 480, VBR_80 = 480, V1 = 490, VBR_90 = 490, V0 = 500, VBR_100 = 500, /*still there for compatibility*/ R3MIX = 1000, STANDARD = 1001, EXTREME = 1002, INSANE = 1003, STANDARD_FAST = 1004, EXTREME_FAST = 1005, MEDIUM = 1006, MEDIUM_FAST = 1007 } preset_mode; /*asm optimizations*/ typedef enum asm_optimizations_e { MMX = 1, AMD_3DNOW = 2, SSE = 3 } asm_optimizations; /* psychoacoustic model */ typedef enum Psy_model_e { PSY_GPSYCHO = 1, PSY_NSPSYTUNE = 2 } Psy_model; struct lame_global_struct; typedef struct lame_global_struct lame_global_flags; typedef lame_global_flags *lame_t; /*********************************************************************** * * The LAME API * These functions should be called, in this order, for each * MP3 file to be encoded. See the file "API" for more documentation * ***********************************************************************/ /* * REQUIRED: * initialize the encoder. sets default for all encoder parameters, * returns NULL if some malloc()'s failed * otherwise returns pointer to structure needed for all future * API calls. */ lame_global_flags * CDECL lame_init(void); /* obsolete version */ int CDECL lame_init_old(lame_global_flags *); /* * OPTIONAL: * set as needed to override defaults */ /******************************************************************** * input stream description ***********************************************************************/ /* number of samples. default = 2^32-1 */ int CDECL lame_set_num_samples(lame_global_flags *, unsigned long); unsigned long CDECL lame_get_num_samples(const lame_global_flags *); /* input sample rate in Hz. default = 44100hz */ int CDECL lame_set_in_samplerate(lame_global_flags *, int); int CDECL lame_get_in_samplerate(const lame_global_flags *); /* number of channels in input stream. default=2 */ int CDECL lame_set_num_channels(lame_global_flags *, int); int CDECL lame_get_num_channels(const lame_global_flags *); /* scale the input by this amount before encoding. default=0 (disabled) (not used by decoding routines) */ int CDECL lame_set_scale(lame_global_flags *, float); float CDECL lame_get_scale(const lame_global_flags *); /* scale the channel 0 (left) input by this amount before encoding. default=0 (disabled) (not used by decoding routines) */ int CDECL lame_set_scale_left(lame_global_flags *, float); float CDECL lame_get_scale_left(const lame_global_flags *); /* scale the channel 1 (right) input by this amount before encoding. default=0 (disabled) (not used by decoding routines) */ int CDECL lame_set_scale_right(lame_global_flags *, float); float CDECL lame_get_scale_right(const lame_global_flags *); /* output sample rate in Hz. default = 0, which means LAME picks best value based on the amount of compression. MPEG only allows: MPEG1 32, 44.1, 48khz MPEG2 16, 22.05, 24 MPEG2.5 8, 11.025, 12 (not used by decoding routines) */ int CDECL lame_set_out_samplerate(lame_global_flags *, int); int CDECL lame_get_out_samplerate(const lame_global_flags *); /******************************************************************** * general control parameters ***********************************************************************/ /* 1=cause LAME to collect data for an MP3 frame analyzer. default=0 */ int CDECL lame_set_analysis(lame_global_flags *, int); int CDECL lame_get_analysis(const lame_global_flags *); /* 1 = write a Xing VBR header frame. default = 1 this variable must have been added by a Hungarian notation Windows programmer :-) */ int CDECL lame_set_bWriteVbrTag(lame_global_flags *, int); int CDECL lame_get_bWriteVbrTag(const lame_global_flags *); /* 1=decode only. use lame/mpglib to convert mp3/ogg to wav. default=0 */ int CDECL lame_set_decode_only(lame_global_flags *, int); int CDECL lame_get_decode_only(const lame_global_flags *); /* 1=encode a Vorbis .ogg file. default=0 */ /* DEPRECATED */ int CDECL lame_set_ogg(lame_global_flags *, int); int CDECL lame_get_ogg(const lame_global_flags *); /* internal algorithm selection. True quality is determined by the bitrate but this variable will effect quality by selecting expensive or cheap algorithms. quality=0..9. 0=best (very slow). 9=worst. recommended: 2 near-best quality, not too slow 5 good quality, fast 7 ok quality, really fast */ int CDECL lame_set_quality(lame_global_flags *, int); int CDECL lame_get_quality(const lame_global_flags *); /* mode = 0,1,2,3 = stereo, jstereo, dual channel (not supported), mono default: lame picks based on compression ration and input channels */ int CDECL lame_set_mode(lame_global_flags *, MPEG_mode); MPEG_mode CDECL lame_get_mode(const lame_global_flags *); /* mode_automs. Use a M/S mode with a switching threshold based on compression ratio DEPRECATED */ int CDECL lame_set_mode_automs(lame_global_flags *, int); int CDECL lame_get_mode_automs(const lame_global_flags *); /* force_ms. Force M/S for all frames. For testing only. default = 0 (disabled) */ int CDECL lame_set_force_ms(lame_global_flags *, int); int CDECL lame_get_force_ms(const lame_global_flags *); /* use free_format? default = 0 (disabled) */ int CDECL lame_set_free_format(lame_global_flags *, int); int CDECL lame_get_free_format(const lame_global_flags *); /* perform ReplayGain analysis? default = 0 (disabled) */ int CDECL lame_set_findReplayGain(lame_global_flags *, int); int CDECL lame_get_findReplayGain(const lame_global_flags *); /* decode on the fly. Search for the peak sample. If the ReplayGain * analysis is enabled then perform the analysis on the decoded data * stream. default = 0 (disabled) * NOTE: if this option is set the build-in decoder should not be used */ int CDECL lame_set_decode_on_the_fly(lame_global_flags *, int); int CDECL lame_get_decode_on_the_fly(const lame_global_flags *); /* DEPRECATED: now does the same as lame_set_findReplayGain() default = 0 (disabled) */ int CDECL lame_set_ReplayGain_input(lame_global_flags *, int); int CDECL lame_get_ReplayGain_input(const lame_global_flags *); /* DEPRECATED: now does the same as lame_set_decode_on_the_fly() && lame_set_findReplayGain() default = 0 (disabled) */ int CDECL lame_set_ReplayGain_decode(lame_global_flags *, int); int CDECL lame_get_ReplayGain_decode(const lame_global_flags *); /* DEPRECATED: now does the same as lame_set_decode_on_the_fly() default = 0 (disabled) */ int CDECL lame_set_findPeakSample(lame_global_flags *, int); int CDECL lame_get_findPeakSample(const lame_global_flags *); /* * OPTIONAL: * Set printf like error/debug/message reporting functions. * The second argument has to be a pointer to a function which looks like * void my_debugf(const char *format, va_list ap) * { * (void) vfprintf(stdout, format, ap); * } * If you use NULL as the value of the pointer in the set function, the * lame buildin function will be used (prints to stderr). * To quiet any output you have to replace the body of the example function * with just "return;" and use it in the set function. */ int CDECL lame_set_errorf(lame_global_flags *, void (*func)(const char *, va_list)); int CDECL lame_set_debugf(lame_global_flags *, void (*func)(const char *, va_list)); int CDECL lame_set_msgf (lame_global_flags *, void (*func)(const char *, va_list)); /* set one of brate compression ratio. default is compression ratio of 11. */ int CDECL lame_set_brate(lame_global_flags *, int); int CDECL lame_get_brate(const lame_global_flags *); int CDECL lame_set_compression_ratio(lame_global_flags *, float); float CDECL lame_get_compression_ratio(const lame_global_flags *); int CDECL lame_set_preset( lame_global_flags* gfp, int ); int CDECL lame_set_asm_optimizations( lame_global_flags* gfp, int, int ); /******************************************************************** * frame params ***********************************************************************/ /* mark as copyright. default=0 */ int CDECL lame_set_copyright(lame_global_flags *, int); int CDECL lame_get_copyright(const lame_global_flags *); /* mark as original. default=1 */ int CDECL lame_set_original(lame_global_flags *, int); int CDECL lame_get_original(const lame_global_flags *); /* error_protection. Use 2 bytes from each frame for CRC checksum. default=0 */ int CDECL lame_set_error_protection(lame_global_flags *, int); int CDECL lame_get_error_protection(const lame_global_flags *); /* padding_type. 0=pad no frames 1=pad all frames 2=adjust padding(default) */ int CDECL lame_set_padding_type(lame_global_flags *, Padding_type); Padding_type CDECL lame_get_padding_type(const lame_global_flags *); /* MP3 'private extension' bit Meaningless. default=0 */ int CDECL lame_set_extension(lame_global_flags *, int); int CDECL lame_get_extension(const lame_global_flags *); /* enforce strict ISO compliance. default=0 */ int CDECL lame_set_strict_ISO(lame_global_flags *, int); int CDECL lame_get_strict_ISO(const lame_global_flags *); /******************************************************************** * quantization/noise shaping ***********************************************************************/ /* disable the bit reservoir. For testing only. default=0 */ int CDECL lame_set_disable_reservoir(lame_global_flags *, int); int CDECL lame_get_disable_reservoir(const lame_global_flags *); /* select a different "best quantization" function. default=0 */ int CDECL lame_set_quant_comp(lame_global_flags *, int); int CDECL lame_get_quant_comp(const lame_global_flags *); int CDECL lame_set_quant_comp_short(lame_global_flags *, int); int CDECL lame_get_quant_comp_short(const lame_global_flags *); int CDECL lame_set_experimentalX(lame_global_flags *, int); /* compatibility*/ int CDECL lame_get_experimentalX(const lame_global_flags *); /* another experimental option. for testing only */ int CDECL lame_set_experimentalY(lame_global_flags *, int); int CDECL lame_get_experimentalY(const lame_global_flags *); /* another experimental option. for testing only */ int CDECL lame_set_experimentalZ(lame_global_flags *, int); int CDECL lame_get_experimentalZ(const lame_global_flags *); /* Naoki's psycho acoustic model. default=0 */ int CDECL lame_set_exp_nspsytune(lame_global_flags *, int); int CDECL lame_get_exp_nspsytune(const lame_global_flags *); void CDECL lame_set_msfix(lame_global_flags *, double); float CDECL lame_get_msfix(const lame_global_flags *); int lame_set_exp_nspsytune2_int( lame_global_flags*, int, int); float lame_set_exp_nspsytune2_real( lame_global_flags*, int, float); void * lame_set_exp_nspsytune2_pointer( lame_global_flags*, int, void *); /******************************************************************** * VBR control ***********************************************************************/ /* Types of VBR. default = vbr_off = CBR */ int CDECL lame_set_VBR(lame_global_flags *, vbr_mode); vbr_mode CDECL lame_get_VBR(const lame_global_flags *); /* VBR quality level. 0=highest 9=lowest */ int CDECL lame_set_VBR_q(lame_global_flags *, int); int CDECL lame_get_VBR_q(const lame_global_flags *); /* Ignored except for VBR=vbr_abr (ABR mode) */ int CDECL lame_set_VBR_mean_bitrate_kbps(lame_global_flags *, int); int CDECL lame_get_VBR_mean_bitrate_kbps(const lame_global_flags *); int CDECL lame_set_VBR_min_bitrate_kbps(lame_global_flags *, int); int CDECL lame_get_VBR_min_bitrate_kbps(const lame_global_flags *); int CDECL lame_set_VBR_max_bitrate_kbps(lame_global_flags *, int); int CDECL lame_get_VBR_max_bitrate_kbps(const lame_global_flags *); /* 1=strictly enforce VBR_min_bitrate. Normally it will be violated for analog silence */ int CDECL lame_set_VBR_hard_min(lame_global_flags *, int); int CDECL lame_get_VBR_hard_min(const lame_global_flags *); /* for preset */ int CDECL lame_set_preset_expopts(lame_global_flags *, int); /******************************************************************** * Filtering control ***********************************************************************/ /* freq in Hz to apply lowpass. Default = 0 = lame chooses. -1 = disabled */ int CDECL lame_set_lowpassfreq(lame_global_flags *, int); int CDECL lame_get_lowpassfreq(const lame_global_flags *); /* width of transition band, in Hz. Default = one polyphase filter band */ int CDECL lame_set_lowpasswidth(lame_global_flags *, int); int CDECL lame_get_lowpasswidth(const lame_global_flags *); /* freq in Hz to apply highpass. Default = 0 = lame chooses. -1 = disabled */ int CDECL lame_set_highpassfreq(lame_global_flags *, int); int CDECL lame_get_highpassfreq(const lame_global_flags *); /* width of transition band, in Hz. Default = one polyphase filter band */ int CDECL lame_set_highpasswidth(lame_global_flags *, int); int CDECL lame_get_highpasswidth(const lame_global_flags *); /******************************************************************** * psycho acoustics and other arguments which you should not change * unless you know what you are doing ***********************************************************************/ /* only use ATH for masking */ int CDECL lame_set_ATHonly(lame_global_flags *, int); int CDECL lame_get_ATHonly(const lame_global_flags *); /* only use ATH for short blocks */ int CDECL lame_set_ATHshort(lame_global_flags *, int); int CDECL lame_get_ATHshort(const lame_global_flags *); /* disable ATH */ int CDECL lame_set_noATH(lame_global_flags *, int); int CDECL lame_get_noATH(const lame_global_flags *); /* select ATH formula */ int CDECL lame_set_ATHtype(lame_global_flags *, int); int CDECL lame_get_ATHtype(const lame_global_flags *); /* lower ATH by this many db */ int CDECL lame_set_ATHlower(lame_global_flags *, float); float CDECL lame_get_ATHlower(const lame_global_flags *); /* select ATH adaptive adjustment type */ int CDECL lame_set_athaa_type( lame_global_flags *, int); int CDECL lame_get_athaa_type( const lame_global_flags *); /* select the loudness approximation used by the ATH adaptive auto-leveling */ int CDECL lame_set_athaa_loudapprox( lame_global_flags *, int); int CDECL lame_get_athaa_loudapprox( const lame_global_flags *); /* adjust (in dB) the point below which adaptive ATH level adjustment occurs */ int CDECL lame_set_athaa_sensitivity( lame_global_flags *, float); float CDECL lame_get_athaa_sensitivity( const lame_global_flags* ); /* predictability limit (ISO tonality formula) */ int CDECL lame_set_cwlimit(lame_global_flags *, int); int CDECL lame_get_cwlimit(const lame_global_flags *); /* allow blocktypes to differ between channels? default: 0 for jstereo, 1 for stereo */ int CDECL lame_set_allow_diff_short(lame_global_flags *, int); int CDECL lame_get_allow_diff_short(const lame_global_flags *); /* use temporal masking effect (default = 1) */ int CDECL lame_set_useTemporal(lame_global_flags *, int); int CDECL lame_get_useTemporal(const lame_global_flags *); /* use temporal masking effect (default = 1) */ int CDECL lame_set_interChRatio(lame_global_flags *, float); float CDECL lame_get_interChRatio(const lame_global_flags *); /* disable short blocks */ int CDECL lame_set_no_short_blocks(lame_global_flags *, int); int CDECL lame_get_no_short_blocks(const lame_global_flags *); /* force short blocks */ int CDECL lame_set_force_short_blocks(lame_global_flags *, int); int CDECL lame_get_force_short_blocks(const lame_global_flags *); /* Input PCM is emphased PCM (for instance from one of the rarely emphased CDs), it is STRONGLY not recommended to use this, because psycho does not take it into account, and last but not least many decoders ignore these bits */ int CDECL lame_set_emphasis(lame_global_flags *, int); int CDECL lame_get_emphasis(const lame_global_flags *); /************************************************************************/ /* internal variables, cannot be set... */ /* provided because they may be of use to calling application */ /************************************************************************/ /* version 0=MPEG-2 1=MPEG-1 (2=MPEG-2.5) */ int CDECL lame_get_version(const lame_global_flags *); /* encoder delay */ int CDECL lame_get_encoder_delay(const lame_global_flags *); /* padding appended to the input to make sure decoder can fully decode all input. Note that this value can only be calculated during the call to lame_encoder_flush(). Before lame_encoder_flush() has been called, the value of encoder_padding = 0. */ int CDECL lame_get_encoder_padding(const lame_global_flags *); /* size of MPEG frame */ int CDECL lame_get_framesize(const lame_global_flags *); /* number of PCM samples buffered, but not yet encoded to mp3 data. */ int CDECL lame_get_mf_samples_to_encode( const lame_global_flags* gfp ); /* size (bytes) of mp3 data buffered, but not yet encoded. this is the number of bytes which would be output by a call to lame_encode_flush_nogap. NOTE: lame_encode_flush() will return more bytes than this because it will encode the reamining buffered PCM samples before flushing the mp3 buffers. */ int CDECL lame_get_size_mp3buffer( const lame_global_flags* gfp ); /* number of frames encoded so far */ int CDECL lame_get_frameNum(const lame_global_flags *); /* lame's estimate of the total number of frames to be encoded only valid if calling program set num_samples */ int CDECL lame_get_totalframes(const lame_global_flags *); /* RadioGain value. Multiplied by 10 and rounded to the nearest. */ int CDECL lame_get_RadioGain(const lame_global_flags *); /* AudiophileGain value. Multipled by 10 and rounded to the nearest. */ int CDECL lame_get_AudiophileGain(const lame_global_flags *); /* the peak sample */ float CDECL lame_get_PeakSample(const lame_global_flags *); /* is decoding on the fly performed */ int CDECL lame_get_decode_on_the_fly(const lame_global_flags *); /* is ReplayGain analysis performed */ int CDECL lame_get_findReplayGain(const lame_global_flags *); /* Gain change required for preventing clipping. The value is correct only if peak sample searching was enabled. If negative then the waveform already does not clip. The value is multiplied by 10 and rounded up. */ int CDECL lame_get_noclipGainChange(const lame_global_flags *); /* user-specified scale factor required for preventing clipping. Value is correct only if peak sample searching was enabled and no user-specified scaling was performed. If negative then either the waveform already does not clip or the value cannot be determined */ float CDECL lame_get_noclipScale(const lame_global_flags *); /* * REQUIRED: * sets more internal configuration based on data provided above. * returns -1 if something failed. */ int CDECL lame_init_params(lame_global_flags * const ); /* * OPTIONAL: * get the version number, in a string. of the form: * "3.63 (beta)" or just "3.63". */ const char* CDECL get_lame_version ( void ); const char* CDECL get_lame_short_version ( void ); const char* CDECL get_lame_very_short_version ( void ); const char* CDECL get_psy_version ( void ); const char* CDECL get_lame_url ( void ); /* * OPTIONAL: * get the version numbers in numerical form. */ typedef struct { /* generic LAME version */ int major; int minor; int alpha; /* 0 if not an alpha version */ int beta; /* 0 if not a beta version */ /* version of the psy model */ int psy_major; int psy_minor; int psy_alpha; /* 0 if not an alpha version */ int psy_beta; /* 0 if not a beta version */ /* compile time features */ const char *features; /* Don't make assumptions about the contents! */ } lame_version_t; void CDECL get_lame_version_numerical ( lame_version_t *const ); /* * OPTIONAL: * print internal lame configuration to message handler */ void CDECL lame_print_config(const lame_global_flags* gfp); void CDECL lame_print_internals( const lame_global_flags *gfp); /* * input pcm data, output (maybe) mp3 frames. * This routine handles all buffering, resampling and filtering for you. * * return code number of bytes output in mp3buf. Can be 0 * -1: mp3buf was too small * -2: malloc() problem * -3: lame_init_params() not called * -4: psycho acoustic problems * * The required mp3buf_size can be computed from num_samples, * samplerate and encoding rate, but here is a worst case estimate: * * mp3buf_size in bytes = 1.25*num_samples + 7200 * * I think a tighter bound could be: (mt, March 2000) * MPEG1: * num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512 * MPEG2: * num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256 * * but test first if you use that! * * set mp3buf_size = 0 and LAME will not check if mp3buf_size is * large enough. * * NOTE: * if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels * will be averaged into the L channel before encoding only the L channel * This will overwrite the data in buffer_l[] and buffer_r[]. * */ int CDECL lame_encode_buffer ( lame_global_flags* gfp, /* global context handle */ const short int buffer_l [], /* PCM data for left channel */ const short int buffer_r [], /* PCM data for right channel */ const int nsamples, /* number of samples per channel */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ const int mp3buf_size ); /* number of valid octets in this stream */ /* * as above, but input has L & R channel data interleaved. * NOTE: * num_samples = number of samples in the L (or R) * channel, not the total number of samples in pcm[] */ int CDECL lame_encode_buffer_interleaved( lame_global_flags* gfp, /* global context handlei */ short int pcm[], /* PCM data for left and right channel, interleaved */ int num_samples, /* number of samples per channel, _not_ number of samples in pcm[] */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ int mp3buf_size ); /* number of valid octets in this stream */ /* as lame_encode_buffer, but for 'float's. * !! NOTE: !! data must still be scaled to be in the same range as * short int, +/- 32768 */ int CDECL lame_encode_buffer_float( lame_global_flags* gfp, /* global context handle */ const float buffer_l [], /* PCM data for left channel */ const float buffer_r [], /* PCM data for right channel */ const int nsamples, /* number of samples per channel */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ const int mp3buf_size ); /* number of valid octets in this stream */ /* as lame_encode_buffer, but for long's * !! NOTE: !! data must still be scaled to be in the same range as * short int, +/- 32768 * * This scaling was a mistake (doesn't allow one to exploit full * precision of type 'long'. Use lame_encode_buffer_long2() instead. * */ int CDECL lame_encode_buffer_long( lame_global_flags* gfp, /* global context handle */ const long buffer_l [], /* PCM data for left channel */ const long buffer_r [], /* PCM data for right channel */ const int nsamples, /* number of samples per channel */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ const int mp3buf_size ); /* number of valid octets in this stream */ /* Same as lame_encode_buffer_long(), but with correct scaling. * !! NOTE: !! data must still be scaled to be in the same range as * type 'long'. Data should be in the range: +/- 2^(8*size(long)-1) * */ int CDECL lame_encode_buffer_long2( lame_global_flags* gfp, /* global context handle */ const long buffer_l [], /* PCM data for left channel */ const long buffer_r [], /* PCM data for right channel */ const int nsamples, /* number of samples per channel */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ const int mp3buf_size ); /* number of valid octets in this stream */ /* as lame_encode_buffer, but for int's * !! NOTE: !! input should be scaled to the maximum range of 'int' * If int is 4 bytes, then the values should range from * +/- 2147483648. * * This routine does not (and cannot, without loosing precision) use * the same scaling as the rest of the lame_encode_buffer() routines. * */ int CDECL lame_encode_buffer_int( lame_global_flags* gfp, /* global context handle */ const int buffer_l [], /* PCM data for left channel */ const int buffer_r [], /* PCM data for right channel */ const int nsamples, /* number of samples per channel */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ const int mp3buf_size ); /* number of valid octets in this stream */ /* * REQUIRED: * lame_encode_flush will flush the intenal PCM buffers, padding with * 0's to make sure the final frame is complete, and then flush * the internal MP3 buffers, and thus may return a * final few mp3 frames. 'mp3buf' should be at least 7200 bytes long * to hold all possible emitted data. * * will also write id3v1 tags (if any) into the bitstream * * return code = number of bytes output to mp3buf. Can be 0 */ int CDECL lame_encode_flush( lame_global_flags * gfp, /* global context handle */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ int size); /* number of valid octets in this stream */ /* * OPTIONAL: * lame_encode_flush_nogap will flush the internal mp3 buffers and pad * the last frame with ancillary data so it is a complete mp3 frame. * * 'mp3buf' should be at least 7200 bytes long * to hold all possible emitted data. * * After a call to this routine, the outputed mp3 data is complete, but * you may continue to encode new PCM samples and write future mp3 data * to a different file. The two mp3 files will play back with no gaps * if they are concatenated together. * * This routine will NOT write id3v1 tags into the bitstream. * * return code = number of bytes output to mp3buf. Can be 0 */ int CDECL lame_encode_flush_nogap( lame_global_flags * gfp, /* global context handle */ unsigned char* mp3buf, /* pointer to encoded MP3 stream */ int size); /* number of valid octets in this stream */ /* * OPTIONAL: * Normally, this is called by lame_init_params(). It writes id3v2 and * Xing headers into the front of the bitstream, and sets frame counters * and bitrate histogram data to 0. You can also call this after * lame_encode_flush_nogap(). */ int CDECL lame_init_bitstream( lame_global_flags * gfp); /* global context handle */ /* * OPTIONAL: some simple statistics * a bitrate histogram to visualize the distribution of used frame sizes * a stereo mode histogram to visualize the distribution of used stereo * modes, useful in joint-stereo mode only * 0: LR left-right encoded * 1: LR-I left-right and intensity encoded (currently not supported) * 2: MS mid-side encoded * 3: MS-I mid-side and intensity encoded (currently not supported) * * attention: don't call them after lame_encode_finish * suggested: lame_encode_flush -> lame_*_hist -> lame_close */ void CDECL lame_bitrate_hist( const lame_global_flags *const gfp, int bitrate_count[14] ); void CDECL lame_bitrate_kbps( const lame_global_flags *const gfp, int bitrate_kbps [14] ); void CDECL lame_stereo_mode_hist( const lame_global_flags *const gfp, int stereo_mode_count[4] ); void CDECL lame_bitrate_stereo_mode_hist ( const lame_global_flags * const gfp, int bitrate_stmode_count [14] [4] ); void CDECL lame_block_type_hist ( const lame_global_flags * const gfp, int btype_count[6] ); void CDECL lame_bitrate_block_type_hist ( const lame_global_flags * const gfp, int bitrate_btype_count[14][6] ); /* * OPTIONAL: * lame_mp3_tags_fid will append a Xing VBR tag to the mp3 file with file * pointer fid. These calls perform forward and backwards seeks, so make * sure fid is a real file. Make sure lame_encode_flush has been called, * and all mp3 data has been written to the file before calling this * function. * NOTE: * if VBR tags are turned off by the user, or turned off by LAME because * the output is not a regular file, this call does nothing */ void CDECL lame_mp3_tags_fid(lame_global_flags *,FILE* fid); /* * REQUIRED: * final call to free all remaining buffers */ int CDECL lame_close (lame_global_flags *); /* * OBSOLETE: * lame_encode_finish combines lame_encode_flush() and lame_close() in * one call. However, once this call is made, the statistics routines * will no longer work because the data will have been cleared, and * lame_mp3_tags_fid() cannot be called to add data to the VBR header */ int CDECL lame_encode_finish( lame_global_flags* gfp, unsigned char* mp3buf, int size ); /********************************************************************* * * decoding * * a simple interface to mpglib, part of mpg123, is also included if * libmp3lame is compiled with HAVE_MPGLIB * *********************************************************************/ typedef struct { int header_parsed; /* 1 if header was parsed and following data was computed */ int stereo; /* number of channels */ int samplerate; /* sample rate */ int bitrate; /* bitrate */ int mode; /* mp3 frame type */ int mode_ext; /* mp3 frame type */ int framesize; /* number of samples per mp3 frame */ /* this data is only computed if mpglib detects a Xing VBR header */ unsigned long nsamp; /* number of samples in mp3 file. */ int totalframes; /* total number of frames in mp3 file */ /* this data is not currently computed by the mpglib routines */ int framenum; /* frames decoded counter */ } mp3data_struct; /* required call to initialize decoder * NOTE: the decoder should not be used when encoding is performed * with decoding on the fly */ int CDECL lame_decode_init(void); /********************************************************************* * input 1 mp3 frame, output (maybe) pcm data. * * nout = lame_decode(mp3buf,len,pcm_l,pcm_r); * * input: * len : number of bytes of mp3 data in mp3buf * mp3buf[len] : mp3 data to be decoded * * output: * nout: -1 : decoding error * 0 : need more data before we can complete the decode * >0 : returned 'nout' samples worth of data in pcm_l,pcm_r * pcm_l[nout] : left channel data * pcm_r[nout] : right channel data * *********************************************************************/ int CDECL lame_decode( unsigned char * mp3buf, int len, short pcm_l[], short pcm_r[] ); /* same as lame_decode, and also returns mp3 header data */ int CDECL lame_decode_headers( unsigned char* mp3buf, int len, short pcm_l[], short pcm_r[], mp3data_struct* mp3data ); /* same as lame_decode, but returns at most one frame */ int CDECL lame_decode1( unsigned char* mp3buf, int len, short pcm_l[], short pcm_r[] ); /* same as lame_decode1, but returns at most one frame and mp3 header data */ int CDECL lame_decode1_headers( unsigned char* mp3buf, int len, short pcm_l[], short pcm_r[], mp3data_struct* mp3data ); /* same as lame_decode1_headers, but also returns enc_delay and enc_padding from VBR Info tag, (-1 if no info tag was found) */ int CDECL lame_decode1_headersB( unsigned char* mp3buf, int len, short pcm_l[], short pcm_r[], mp3data_struct* mp3data, int *enc_delay, int *enc_padding ); /* cleanup call to exit decoder */ int CDECL lame_decode_exit(void); /********************************************************************* * * id3tag stuff * *********************************************************************/ /* * id3tag.h -- Interface to write ID3 version 1 and 2 tags. * * Copyright (C) 2000 Don Melton. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ /* utility to obtain alphabetically sorted list of genre names with numbers */ extern void id3tag_genre_list( void (*handler)(int, const char *, void *), void* cookie); extern void id3tag_init (lame_global_flags *gfp); /* force addition of version 2 tag */ extern void id3tag_add_v2 (lame_global_flags *gfp); /* add only a version 1 tag */ extern void id3tag_v1_only (lame_global_flags *gfp); /* add only a version 2 tag */ extern void id3tag_v2_only (lame_global_flags *gfp); /* pad version 1 tag with spaces instead of nulls */ extern void id3tag_space_v1 (lame_global_flags *gfp); /* pad version 2 tag with extra 128 bytes */ extern void id3tag_pad_v2 (lame_global_flags *gfp); extern void id3tag_set_title( lame_global_flags* gfp, const char* title ); extern void id3tag_set_artist( lame_global_flags* gfp, const char* artist ); extern void id3tag_set_album( lame_global_flags* gfp, const char* album ); extern void id3tag_set_year( lame_global_flags* gfp, const char* year ); extern void id3tag_set_comment( lame_global_flags* gfp, const char* comment ); extern void id3tag_set_track( lame_global_flags* gfp, const char* track ); /* return non-zero result if genre name or number is invalid */ extern int id3tag_set_genre( lame_global_flags* gfp, const char* genre ); /*********************************************************************** * * list of valid bitrates [kbps] & sample frequencies [Hz]. * first index: 0: MPEG-2 values (sample frequencies 16...24 kHz) * 1: MPEG-1 values (sample frequencies 32...48 kHz) * 2: MPEG-2.5 values (sample frequencies 8...12 kHz) ***********************************************************************/ extern const int bitrate_table [3] [16]; extern const int samplerate_table [3] [ 4]; /* maximum size of mp3buffer needed if you encode at most 1152 samples for each call to lame_encode_buffer. see lame_encode_buffer() below (LAME_MAXMP3BUFFER is now obsolete) */ #define LAME_MAXMP3BUFFER 16384 typedef enum { LAME_OKAY = 0, LAME_NOERROR = 0, LAME_GENERICERROR = -1, LAME_NOMEM = -10, LAME_BADBITRATE = -11, LAME_BADSAMPFREQ = -12, LAME_INTERNALERROR = -13, FRONTEND_READERROR = -80, FRONTEND_WRITEERROR = -81, FRONTEND_FILETOOLARGE = -82 } lame_errorcodes_t; #if defined(__cplusplus) } #endif #endif /* LAME_LAME_H */ lastfm-player-1.1.4_p1910/src/mpglib/main.c0000644000175000001440000000141410352604465017273 0ustar davidusers#include "stdlib.h" #include "unistd.h" #include "mpg123.h" #include "mpglib.h" char buf[16384]; struct mpstr mp; int main(int argc,char **argv) { int size; char out[8192]; char swapped; int len,ret; int i; FILE *fp; InitMP3(&mp); fp = fopen( "lala.wav", "w" ); printf( "starting\n" ); while(1) { len = read(0,buf,16384); if(len <= 0) break; ret = decodeMP3(&mp,buf,len,out,8192,&size); printf( "samplerate: %d - channels: %d\n", freqs[mp.fr.sampling_frequency], mp.fr.stereo ); while(ret == MP3_OK) { printf( "written: %d\n", size ); for ( i = 0; i < ( size / 2 ); i++ ) { fputc( out[i*2 + 1], fp ); fputc( out[i*2], fp ); } ret = decodeMP3(&mp,NULL,0,out,8192,&size); } } fclose( fp ); return 0; } lastfm-player-1.1.4_p1910/src/mpglib/l2tables.h0000644000175000001440000002022510352604465020065 0ustar davidusers/* * Layer 2 Alloc tables .. * most other tables are calculated on program start (which is (of course) * not ISO-conform) .. * Layer-3 huffman table is in huffman.h */ const struct al_table2 alloc_0[] = { {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {2,0},{5,3},{7,5},{16,-32767}, {2,0},{5,3},{7,5},{16,-32767}, {2,0},{5,3},{7,5},{16,-32767}, {2,0},{5,3},{7,5},{16,-32767} }; const struct al_table2 alloc_1[] = { {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, {4,0},{5,3},{3,-3},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255},{10,-511}, {11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {3,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{16,-32767}, {2,0},{5,3},{7,5},{16,-32767}, {2,0},{5,3},{7,5},{16,-32767}, {2,0},{5,3},{7,5},{16,-32767}, {2,0},{5,3},{7,5},{16,-32767}, {2,0},{5,3},{7,5},{16,-32767}, {2,0},{5,3},{7,5},{16,-32767}, {2,0},{5,3},{7,5},{16,-32767} }; const struct al_table2 alloc_2[] = { {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255}, {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383}, {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255}, {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63} }; const struct al_table2 alloc_3[] = { {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255}, {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383}, {4,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127},{9,-255}, {10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191},{15,-16383}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63} }; const struct al_table2 alloc_4[] = { {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191}, {4,0},{5,3},{7,5},{3,-3},{10,9},{4,-7},{5,-15},{6,-31},{7,-63},{8,-127}, {9,-255},{10,-511},{11,-1023},{12,-2047},{13,-4095},{14,-8191}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {3,0},{5,3},{7,5},{10,9},{4,-7},{5,-15},{6,-31},{7,-63}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9}, {2,0},{5,3},{7,5},{10,9} }; lastfm-player-1.1.4_p1910/src/mpglib/huffman.h0000644000175000001440000003733110352604465020007 0ustar davidusers/* * huffman tables ... recalcualted to work with my optimzed * decoder scheme (MH) * * probably we could save a few bytes of memory, because the * smaller tables are often the part of a bigger table */ struct newhuff { const unsigned int linbits; const short * const table; }; static const short tab0[] = { 0 }; static const short tab1[] = { -5, -3, -1, 17, 1, 16, 0 }; static const short tab2[] = { -15, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 17, -1, 1, 16, 0 }; static const short tab3[] = { -13, -11, -9, -5, -3, -1, 34, 2, 18, -1, 33, 32, 16, 17, -1, 1, 0 }; static const short tab5[] = { -29, -25, -23, -15, -7, -5, -3, -1, 51, 35, 50, 49, -3, -1, 19, 3, -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, 17, -1, 1, 16, 0 }; static const short tab6[] = { -25, -19, -13, -9, -5, -3, -1, 51, 3, 35, -1, 50, 48, -1, 19, 49, -3, -1, 34, 2, 18, -3, -1, 33, 32, 1, -1, 17, -1, 16, 0 }; static const short tab7[] = { -69, -65, -57, -39, -29, -17, -11, -7, -3, -1, 85, 69, -1, 84, 83, -1, 53, 68, -3, -1, 37, 82, 21, -5, -1, 81, -1, 5, 52, -1, 80, -1, 67, 51, -5, -3, -1, 36, 66, 20, -1, 65, 64, -11, -7, -3, -1, 4, 35, -1, 50, 3, -1, 19, 49, -3, -1, 48, 34, 18, -5, -1, 33, -1, 2, 32, 17, -1, 1, 16, 0 }; static const short tab8[] = { -65, -63, -59, -45, -31, -19, -13, -7, -5, -3, -1, 85, 84, 69, 83, -3, -1, 53, 68, 37, -3, -1, 82, 5, 21, -5, -1, 81, -1, 52, 67, -3, -1, 80, 51, 36, -5, -3, -1, 66, 20, 65, -3, -1, 4, 64, -1, 35, 50, -9, -7, -3, -1, 19, 49, -1, 3, 48, 34, -1, 2, 32, -1, 18, 33, 17, -3, -1, 1, 16, 0 }; static const short tab9[] = { -63, -53, -41, -29, -19, -11, -5, -3, -1, 85, 69, 53, -1, 83, -1, 84, 5, -3, -1, 68, 37, -1, 82, 21, -3, -1, 81, 52, -1, 67, -1, 80, 4, -7, -3, -1, 36, 66, -1, 51, 64, -1, 20, 65, -5, -3, -1, 35, 50, 19, -1, 49, -1, 3, 48, -5, -3, -1, 34, 2, 18, -1, 33, 32, -3, -1, 17, 1, -1, 16, 0 }; static const short tab10[] = { -125,-121,-111, -83, -55, -35, -21, -13, -7, -3, -1, 119, 103, -1, 118, 87, -3, -1, 117, 102, 71, -3, -1, 116, 86, -1, 101, 55, -9, -3, -1, 115, 70, -3, -1, 85, 84, 99, -1, 39, 114, -11, -5, -3, -1, 100, 7, 112, -1, 98, -1, 69, 53, -5, -1, 6, -1, 83, 68, 23, -17, -5, -1, 113, -1, 54, 38, -5, -3, -1, 37, 82, 21, -1, 81, -1, 52, 67, -3, -1, 22, 97, -1, 96, -1, 5, 80, -19, -11, -7, -3, -1, 36, 66, -1, 51, 4, -1, 20, 65, -3, -1, 64, 35, -1, 50, 3, -3, -1, 19, 49, -1, 48, 34, -7, -3, -1, 18, 33, -1, 2, 32, 17, -1, 1, 16, 0 }; static const short tab11[] = { -121,-113, -89, -59, -43, -27, -17, -7, -3, -1, 119, 103, -1, 118, 117, -3, -1, 102, 71, -1, 116, -1, 87, 85, -5, -3, -1, 86, 101, 55, -1, 115, 70, -9, -7, -3, -1, 69, 84, -1, 53, 83, 39, -1, 114, -1, 100, 7, -5, -1, 113, -1, 23, 112, -3, -1, 54, 99, -1, 96, -1, 68, 37, -13, -7, -5, -3, -1, 82, 5, 21, 98, -3, -1, 38, 6, 22, -5, -1, 97, -1, 81, 52, -5, -1, 80, -1, 67, 51, -1, 36, 66, -15, -11, -7, -3, -1, 20, 65, -1, 4, 64, -1, 35, 50, -1, 19, 49, -5, -3, -1, 3, 48, 34, 33, -5, -1, 18, -1, 2, 32, 17, -3, -1, 1, 16, 0 }; static const short tab12[] = { -115, -99, -73, -45, -27, -17, -9, -5, -3, -1, 119, 103, 118, -1, 87, 117, -3, -1, 102, 71, -1, 116, 101, -3, -1, 86, 55, -3, -1, 115, 85, 39, -7, -3, -1, 114, 70, -1, 100, 23, -5, -1, 113, -1, 7, 112, -1, 54, 99, -13, -9, -3, -1, 69, 84, -1, 68, -1, 6, 5, -1, 38, 98, -5, -1, 97, -1, 22, 96, -3, -1, 53, 83, -1, 37, 82, -17, -7, -3, -1, 21, 81, -1, 52, 67, -5, -3, -1, 80, 4, 36, -1, 66, 20, -3, -1, 51, 65, -1, 35, 50, -11, -7, -5, -3, -1, 64, 3, 48, 19, -1, 49, 34, -1, 18, 33, -7, -5, -3, -1, 2, 32, 0, 17, -1, 1, 16 }; static const short tab13[] = { -509,-503,-475,-405,-333,-265,-205,-153,-115, -83, -53, -35, -21, -13, -9, -7, -5, -3, -1, 254, 252, 253, 237, 255, -1, 239, 223, -3, -1, 238, 207, -1, 222, 191, -9, -3, -1, 251, 206, -1, 220, -1, 175, 233, -1, 236, 221, -9, -5, -3, -1, 250, 205, 190, -1, 235, 159, -3, -1, 249, 234, -1, 189, 219, -17, -9, -3, -1, 143, 248, -1, 204, -1, 174, 158, -5, -1, 142, -1, 127, 126, 247, -5, -1, 218, -1, 173, 188, -3, -1, 203, 246, 111, -15, -7, -3, -1, 232, 95, -1, 157, 217, -3, -1, 245, 231, -1, 172, 187, -9, -3, -1, 79, 244, -3, -1, 202, 230, 243, -1, 63, -1, 141, 216, -21, -9, -3, -1, 47, 242, -3, -1, 110, 156, 15, -5, -3, -1, 201, 94, 171, -3, -1, 125, 215, 78, -11, -5, -3, -1, 200, 214, 62, -1, 185, -1, 155, 170, -1, 31, 241, -23, -13, -5, -1, 240, -1, 186, 229, -3, -1, 228, 140, -1, 109, 227, -5, -1, 226, -1, 46, 14, -1, 30, 225, -15, -7, -3, -1, 224, 93, -1, 213, 124, -3, -1, 199, 77, -1, 139, 184, -7, -3, -1, 212, 154, -1, 169, 108, -1, 198, 61, -37, -21, -9, -5, -3, -1, 211, 123, 45, -1, 210, 29, -5, -1, 183, -1, 92, 197, -3, -1, 153, 122, 195, -7, -5, -3, -1, 167, 151, 75, 209, -3, -1, 13, 208, -1, 138, 168, -11, -7, -3, -1, 76, 196, -1, 107, 182, -1, 60, 44, -3, -1, 194, 91, -3, -1, 181, 137, 28, -43, -23, -11, -5, -1, 193, -1, 152, 12, -1, 192, -1, 180, 106, -5, -3, -1, 166, 121, 59, -1, 179, -1, 136, 90, -11, -5, -1, 43, -1, 165, 105, -1, 164, -1, 120, 135, -5, -1, 148, -1, 119, 118, 178, -11, -3, -1, 27, 177, -3, -1, 11, 176, -1, 150, 74, -7, -3, -1, 58, 163, -1, 89, 149, -1, 42, 162, -47, -23, -9, -3, -1, 26, 161, -3, -1, 10, 104, 160, -5, -3, -1, 134, 73, 147, -3, -1, 57, 88, -1, 133, 103, -9, -3, -1, 41, 146, -3, -1, 87, 117, 56, -5, -1, 131, -1, 102, 71, -3, -1, 116, 86, -1, 101, 115, -11, -3, -1, 25, 145, -3, -1, 9, 144, -1, 72, 132, -7, -5, -1, 114, -1, 70, 100, 40, -1, 130, 24, -41, -27, -11, -5, -3, -1, 55, 39, 23, -1, 113, -1, 85, 7, -7, -3, -1, 112, 54, -1, 99, 69, -3, -1, 84, 38, -1, 98, 53, -5, -1, 129, -1, 8, 128, -3, -1, 22, 97, -1, 6, 96, -13, -9, -5, -3, -1, 83, 68, 37, -1, 82, 5, -1, 21, 81, -7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20, -19, -11, -5, -1, 65, -1, 4, 64, -3, -1, 35, 50, 19, -3, -1, 49, 3, -1, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16, 0 }; static const short tab15[] = { -495,-445,-355,-263,-183,-115, -77, -43, -27, -13, -7, -3, -1, 255, 239, -1, 254, 223, -1, 238, -1, 253, 207, -7, -3, -1, 252, 222, -1, 237, 191, -1, 251, -1, 206, 236, -7, -3, -1, 221, 175, -1, 250, 190, -3, -1, 235, 205, -1, 220, 159, -15, -7, -3, -1, 249, 234, -1, 189, 219, -3, -1, 143, 248, -1, 204, 158, -7, -3, -1, 233, 127, -1, 247, 173, -3, -1, 218, 188, -1, 111, -1, 174, 15, -19, -11, -3, -1, 203, 246, -3, -1, 142, 232, -1, 95, 157, -3, -1, 245, 126, -1, 231, 172, -9, -3, -1, 202, 187, -3, -1, 217, 141, 79, -3, -1, 244, 63, -1, 243, 216, -33, -17, -9, -3, -1, 230, 47, -1, 242, -1, 110, 240, -3, -1, 31, 241, -1, 156, 201, -7, -3, -1, 94, 171, -1, 186, 229, -3, -1, 125, 215, -1, 78, 228, -15, -7, -3, -1, 140, 200, -1, 62, 109, -3, -1, 214, 227, -1, 155, 185, -7, -3, -1, 46, 170, -1, 226, 30, -5, -1, 225, -1, 14, 224, -1, 93, 213, -45, -25, -13, -7, -3, -1, 124, 199, -1, 77, 139, -1, 212, -1, 184, 154, -7, -3, -1, 169, 108, -1, 198, 61, -1, 211, 210, -9, -5, -3, -1, 45, 13, 29, -1, 123, 183, -5, -1, 209, -1, 92, 208, -1, 197, 138, -17, -7, -3, -1, 168, 76, -1, 196, 107, -5, -1, 182, -1, 153, 12, -1, 60, 195, -9, -3, -1, 122, 167, -1, 166, -1, 192, 11, -1, 194, -1, 44, 91, -55, -29, -15, -7, -3, -1, 181, 28, -1, 137, 152, -3, -1, 193, 75, -1, 180, 106, -5, -3, -1, 59, 121, 179, -3, -1, 151, 136, -1, 43, 90, -11, -5, -1, 178, -1, 165, 27, -1, 177, -1, 176, 105, -7, -3, -1, 150, 74, -1, 164, 120, -3, -1, 135, 58, 163, -17, -7, -3, -1, 89, 149, -1, 42, 162, -3, -1, 26, 161, -3, -1, 10, 160, 104, -7, -3, -1, 134, 73, -1, 148, 57, -5, -1, 147, -1, 119, 9, -1, 88, 133, -53, -29, -13, -7, -3, -1, 41, 103, -1, 118, 146, -1, 145, -1, 25, 144, -7, -3, -1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 71, -7, -3, -1, 40, 130, -1, 24, 129, -7, -3, -1, 116, 8, -1, 128, 86, -3, -1, 101, 55, -1, 115, 70, -17, -7, -3, -1, 39, 114, -1, 100, 23, -3, -1, 85, 113, -3, -1, 7, 112, 54, -7, -3, -1, 99, 69, -1, 84, 38, -3, -1, 98, 22, -3, -1, 6, 96, 53, -33, -19, -9, -5, -1, 97, -1, 83, 68, -1, 37, 82, -3, -1, 21, 81, -3, -1, 5, 80, 52, -7, -3, -1, 67, 36, -1, 66, 51, -1, 65, -1, 20, 4, -9, -3, -1, 35, 50, -3, -1, 64, 3, 19, -3, -1, 49, 48, 34, -9, -7, -3, -1, 18, 33, -1, 2, 32, 17, -3, -1, 1, 16, 0 }; static const short tab16[] = { -509,-503,-461,-323,-103, -37, -27, -15, -7, -3, -1, 239, 254, -1, 223, 253, -3, -1, 207, 252, -1, 191, 251, -5, -1, 175, -1, 250, 159, -3, -1, 249, 248, 143, -7, -3, -1, 127, 247, -1, 111, 246, 255, -9, -5, -3, -1, 95, 245, 79, -1, 244, 243, -53, -1, 240, -1, 63, -29, -19, -13, -7, -5, -1, 206, -1, 236, 221, 222, -1, 233, -1, 234, 217, -1, 238, -1, 237, 235, -3, -1, 190, 205, -3, -1, 220, 219, 174, -11, -5, -1, 204, -1, 173, 218, -3, -1, 126, 172, 202, -5, -3, -1, 201, 125, 94, 189, 242, -93, -5, -3, -1, 47, 15, 31, -1, 241, -49, -25, -13, -5, -1, 158, -1, 188, 203, -3, -1, 142, 232, -1, 157, 231, -7, -3, -1, 187, 141, -1, 216, 110, -1, 230, 156, -13, -7, -3, -1, 171, 186, -1, 229, 215, -1, 78, -1, 228, 140, -3, -1, 200, 62, -1, 109, -1, 214, 155, -19, -11, -5, -3, -1, 185, 170, 225, -1, 212, -1, 184, 169, -5, -1, 123, -1, 183, 208, 227, -7, -3, -1, 14, 224, -1, 93, 213, -3, -1, 124, 199, -1, 77, 139, -75, -45, -27, -13, -7, -3, -1, 154, 108, -1, 198, 61, -3, -1, 92, 197, 13, -7, -3, -1, 138, 168, -1, 153, 76, -3, -1, 182, 122, 60, -11, -5, -3, -1, 91, 137, 28, -1, 192, -1, 152, 121, -1, 226, -1, 46, 30, -15, -7, -3, -1, 211, 45, -1, 210, 209, -5, -1, 59, -1, 151, 136, 29, -7, -3, -1, 196, 107, -1, 195, 167, -1, 44, -1, 194, 181, -23, -13, -7, -3, -1, 193, 12, -1, 75, 180, -3, -1, 106, 166, 179, -5, -3, -1, 90, 165, 43, -1, 178, 27, -13, -5, -1, 177, -1, 11, 176, -3, -1, 105, 150, -1, 74, 164, -5, -3, -1, 120, 135, 163, -3, -1, 58, 89, 42, -97, -57, -33, -19, -11, -5, -3, -1, 149, 104, 161, -3, -1, 134, 119, 148, -5, -3, -1, 73, 87, 103, 162, -5, -1, 26, -1, 10, 160, -3, -1, 57, 147, -1, 88, 133, -9, -3, -1, 41, 146, -3, -1, 118, 9, 25, -5, -1, 145, -1, 144, 72, -3, -1, 132, 117, -1, 56, 131, -21, -11, -5, -3, -1, 102, 40, 130, -3, -1, 71, 116, 24, -3, -1, 129, 128, -3, -1, 8, 86, 55, -9, -5, -1, 115, -1, 101, 70, -1, 39, 114, -5, -3, -1, 100, 85, 7, 23, -23, -13, -5, -1, 113, -1, 112, 54, -3, -1, 99, 69, -1, 84, 38, -3, -1, 98, 22, -1, 97, -1, 6, 96, -9, -5, -1, 83, -1, 53, 68, -1, 37, 82, -1, 81, -1, 21, 5, -33, -23, -13, -7, -3, -1, 52, 67, -1, 80, 36, -3, -1, 66, 51, 20, -5, -1, 65, -1, 4, 64, -1, 35, 50, -3, -1, 19, 49, -3, -1, 3, 48, 34, -3, -1, 18, 33, -1, 2, 32, -3, -1, 17, 1, 16, 0 }; static const short tab24[] = { -451,-117, -43, -25, -15, -7, -3, -1, 239, 254, -1, 223, 253, -3, -1, 207, 252, -1, 191, 251, -5, -1, 250, -1, 175, 159, -1, 249, 248, -9, -5, -3, -1, 143, 127, 247, -1, 111, 246, -3, -1, 95, 245, -1, 79, 244, -71, -7, -3, -1, 63, 243, -1, 47, 242, -5, -1, 241, -1, 31, 240, -25, -9, -1, 15, -3, -1, 238, 222, -1, 237, 206, -7, -3, -1, 236, 221, -1, 190, 235, -3, -1, 205, 220, -1, 174, 234, -15, -7, -3, -1, 189, 219, -1, 204, 158, -3, -1, 233, 173, -1, 218, 188, -7, -3, -1, 203, 142, -1, 232, 157, -3, -1, 217, 126, -1, 231, 172, 255,-235, -143, -77, -45, -25, -15, -7, -3, -1, 202, 187, -1, 141, 216, -5, -3, -1, 14, 224, 13, 230, -5, -3, -1, 110, 156, 201, -1, 94, 186, -9, -5, -1, 229, -1, 171, 125, -1, 215, 228, -3, -1, 140, 200, -3, -1, 78, 46, 62, -15, -7, -3, -1, 109, 214, -1, 227, 155, -3, -1, 185, 170, -1, 226, 30, -7, -3, -1, 225, 93, -1, 213, 124, -3, -1, 199, 77, -1, 139, 184, -31, -15, -7, -3, -1, 212, 154, -1, 169, 108, -3, -1, 198, 61, -1, 211, 45, -7, -3, -1, 210, 29, -1, 123, 183, -3, -1, 209, 92, -1, 197, 138, -17, -7, -3, -1, 168, 153, -1, 76, 196, -3, -1, 107, 182, -3, -1, 208, 12, 60, -7, -3, -1, 195, 122, -1, 167, 44, -3, -1, 194, 91, -1, 181, 28, -57, -35, -19, -7, -3, -1, 137, 152, -1, 193, 75, -5, -3, -1, 192, 11, 59, -3, -1, 176, 10, 26, -5, -1, 180, -1, 106, 166, -3, -1, 121, 151, -3, -1, 160, 9, 144, -9, -3, -1, 179, 136, -3, -1, 43, 90, 178, -7, -3, -1, 165, 27, -1, 177, 105, -1, 150, 164, -17, -9, -5, -3, -1, 74, 120, 135, -1, 58, 163, -3, -1, 89, 149, -1, 42, 162, -7, -3, -1, 161, 104, -1, 134, 119, -3, -1, 73, 148, -1, 57, 147, -63, -31, -15, -7, -3, -1, 88, 133, -1, 41, 103, -3, -1, 118, 146, -1, 25, 145, -7, -3, -1, 72, 132, -1, 87, 117, -3, -1, 56, 131, -1, 102, 40, -17, -7, -3, -1, 130, 24, -1, 71, 116, -5, -1, 129, -1, 8, 128, -1, 86, 101, -7, -5, -1, 23, -1, 7, 112, 115, -3, -1, 55, 39, 114, -15, -7, -3, -1, 70, 100, -1, 85, 113, -3, -1, 54, 99, -1, 69, 84, -7, -3, -1, 38, 98, -1, 22, 97, -5, -3, -1, 6, 96, 53, -1, 83, 68, -51, -37, -23, -15, -9, -3, -1, 37, 82, -1, 21, -1, 5, 80, -1, 81, -1, 52, 67, -3, -1, 36, 66, -1, 51, 20, -9, -5, -1, 65, -1, 4, 64, -1, 35, 50, -1, 19, 49, -7, -5, -3, -1, 3, 48, 34, 18, -1, 33, -1, 2, 32, -3, -1, 17, 1, -1, 16, 0 }; static const short tab_c0[] = { -29, -21, -13, -7, -3, -1, 11, 15, -1, 13, 14, -3, -1, 7, 5, 9, -3, -1, 6, 3, -1, 10, 12, -3, -1, 2, 1, -1, 4, 8, 0 }; static const short tab_c1[] = { -15, -7, -3, -1, 15, 14, -1, 13, 12, -3, -1, 11, 10, -1, 9, 8, -7, -3, -1, 7, 6, -1, 5, 4, -3, -1, 3, 2, -1, 1, 0 }; static const struct newhuff ht[] = { { /* 0 */ 0 , tab0 } , { /* 2 */ 0 , tab1 } , { /* 3 */ 0 , tab2 } , { /* 3 */ 0 , tab3 } , { /* 0 */ 0 , tab0 } , { /* 4 */ 0 , tab5 } , { /* 4 */ 0 , tab6 } , { /* 6 */ 0 , tab7 } , { /* 6 */ 0 , tab8 } , { /* 6 */ 0 , tab9 } , { /* 8 */ 0 , tab10 } , { /* 8 */ 0 , tab11 } , { /* 8 */ 0 , tab12 } , { /* 16 */ 0 , tab13 } , { /* 0 */ 0 , tab0 } , { /* 16 */ 0 , tab15 } , { /* 16 */ 1 , tab16 } , { /* 16 */ 2 , tab16 } , { /* 16 */ 3 , tab16 } , { /* 16 */ 4 , tab16 } , { /* 16 */ 6 , tab16 } , { /* 16 */ 8 , tab16 } , { /* 16 */ 10, tab16 } , { /* 16 */ 13, tab16 } , { /* 16 */ 4 , tab24 } , { /* 16 */ 5 , tab24 } , { /* 16 */ 6 , tab24 } , { /* 16 */ 7 , tab24 } , { /* 16 */ 8 , tab24 } , { /* 16 */ 9 , tab24 } , { /* 16 */ 11, tab24 } , { /* 16 */ 13, tab24 } }; static const struct newhuff htc[] = { { /* 1 , 1 , */ 0 , tab_c0 } , { /* 1 , 1 , */ 0 , tab_c1 } }; lastfm-player-1.1.4_p1910/src/mpglib/VbrTag.c0000644000175000001440000006453010352604465017544 0ustar davidusers/* * Xing VBR tagging for LAME. * * Copyright (c) 1999 A.L. Faber * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* $Id: VbrTag.c,v 1.77 2004/03/23 01:57:16 olcios Exp $ */ #ifdef HAVE_CONFIG_H # include #endif #include "machine.h" #include "bitstream.h" #include "lame.h" #include "VbrTag.h" #include "version.h" #include #ifdef WITH_DMALLOC #include #endif #ifdef __sun__ /* woraround for SunOS 4.x, it has SEEK_* defined here */ #include #endif #ifdef _DEBUG /* #define DEBUG_VBRTAG */ #endif /* * 4 bytes for Header Tag * 4 bytes for Header Flags * 100 bytes for entry (NUMTOCENTRIES) * 4 bytes for FRAME SIZE * 4 bytes for STREAM_SIZE * 4 bytes for VBR SCALE. a VBR quality indicator: 0=best 100=worst * 20 bytes for LAME tag. for example, "LAME3.12 (beta 6)" * ___________ * 140 bytes */ #define VBRHEADERSIZE (NUMTOCENTRIES+4+4+4+4+4) #define LAMEHEADERSIZE (VBRHEADERSIZE + 9 + 1 + 1 + 8 + 1 + 1 + 3 + 1 + 1 + 2 + 4 + 2 + 2) /* the size of the Xing header (MPEG1 and MPEG2) in kbps */ #define XING_BITRATE1 128 #define XING_BITRATE2 64 #define XING_BITRATE25 32 const static char VBRTag[]={"Xing"}; const static char VBRTag2[]={"Info"}; /* Lookup table for fast CRC computation * See 'CRC_update_lookup' * Uses the polynomial x^16+x^15+x^2+1 */ unsigned int crc16_lookup[256] = { 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 }; /*********************************************************************** * Robert Hegemann 2001-01-17 ***********************************************************************/ static void addVbr(VBR_seek_info_t * v, int bitrate) { int i; v->sum += bitrate; v->seen ++; if (v->seen < v->want) { return; } if (v->pos < v->size) { v->bag[v->pos] = v->sum; v->pos ++; v->seen = 0; } if (v->pos == v->size) { for (i = 1; i < v->size; i += 2) { v->bag[i/2] = v->bag[i]; } v->want *= 2; v->pos /= 2; } } static void Xing_seek_table(VBR_seek_info_t * v, unsigned char *t) { int i, index; int seek_point; if (v->pos <= 0) return; for (i = 1; i < NUMTOCENTRIES; ++i) { float j = i/(float)NUMTOCENTRIES, act, sum; index = (int)(floor(j * v->pos)); if (index > v->pos-1) index = v->pos-1; act = v->bag[index]; sum = v->sum; seek_point = (int)(256. * act / sum); if (seek_point > 255) seek_point = 255; t[i] = seek_point; } } void print_seeking(unsigned char *t) { int i; printf("seeking table "); for (i = 0; i < NUMTOCENTRIES; ++i) { printf(" %d ", t[i]); } printf("\n"); } /**************************************************************************** * AddVbrFrame: Add VBR entry, used to fill the VBR the TOC entries * Paramters: * nStreamPos: how many bytes did we write to the bitstream so far * (in Bytes NOT Bits) **************************************************************************** */ void AddVbrFrame(lame_global_flags *gfp) { lame_internal_flags *gfc = gfp->internal_flags; int kbps = bitrate_table[gfp->version][gfc->bitrate_index]; assert(gfc->VBR_seek_table.bag); addVbr(&gfc->VBR_seek_table, kbps); gfp->nVbrNumFrames++; } /*-------------------------------------------------------------*/ static int ExtractI4(unsigned char *buf) { int x; /* big endian extract */ x = buf[0]; x <<= 8; x |= buf[1]; x <<= 8; x |= buf[2]; x <<= 8; x |= buf[3]; return x; } static void CreateI4(unsigned char *buf, int nValue) { /* big endian create */ buf[0]=(nValue>>24)&0xff; buf[1]=(nValue>>16)&0xff; buf[2]=(nValue>> 8)&0xff; buf[3]=(nValue )&0xff; } static void CreateI2(unsigned char *buf, int nValue) { /* big endian create */ buf[0]=(nValue>> 8)&0xff; buf[1]=(nValue )&0xff; } /*-------------------------------------------------------------*/ /* Same as GetVbrTag below, but only checks for the Xing tag. requires buf to contain only 40 bytes */ /*-------------------------------------------------------------*/ int CheckVbrTag(unsigned char *buf) { int h_id, h_mode, h_sr_index; /* get selected MPEG header data */ h_id = (buf[1] >> 3) & 1; h_sr_index = (buf[2] >> 2) & 3; h_mode = (buf[3] >> 6) & 3; /* determine offset of header */ if( h_id ) { /* mpeg1 */ if( h_mode != 3 ) buf+=(32+4); else buf+=(17+4); } else { /* mpeg2 */ if( h_mode != 3 ) buf+=(17+4); else buf+=(9+4); } if( buf[0] != VBRTag[0] && buf[0] != VBRTag2[0] ) return 0; /* fail */ if( buf[1] != VBRTag[1] && buf[1] != VBRTag2[1]) return 0; /* header not found*/ if( buf[2] != VBRTag[2] && buf[2] != VBRTag2[2]) return 0; if( buf[3] != VBRTag[3] && buf[3] != VBRTag2[3]) return 0; return 1; } int GetVbrTag(VBRTAGDATA *pTagData, unsigned char *buf) { int i, head_flags; int h_bitrate,h_id, h_mode, h_sr_index; int enc_delay,enc_padding; /* get Vbr header data */ pTagData->flags = 0; /* get selected MPEG header data */ h_id = (buf[1] >> 3) & 1; h_sr_index = (buf[2] >> 2) & 3; h_mode = (buf[3] >> 6) & 3; h_bitrate = ((buf[2]>>4)&0xf); h_bitrate = bitrate_table[h_id][h_bitrate]; /* check for FFE syncword */ if ((buf[1]>>4)==0xE) pTagData->samprate = samplerate_table[2][h_sr_index]; else pTagData->samprate = samplerate_table[h_id][h_sr_index]; /* if( h_id == 0 ) */ /* pTagData->samprate >>= 1; */ /* determine offset of header */ if( h_id ) { /* mpeg1 */ if( h_mode != 3 ) buf+=(32+4); else buf+=(17+4); } else { /* mpeg2 */ if( h_mode != 3 ) buf+=(17+4); else buf+=(9+4); } if( buf[0] != VBRTag[0] && buf[0] != VBRTag2[0] ) return 0; /* fail */ if( buf[1] != VBRTag[1] && buf[1] != VBRTag2[1]) return 0; /* header not found*/ if( buf[2] != VBRTag[2] && buf[2] != VBRTag2[2]) return 0; if( buf[3] != VBRTag[3] && buf[3] != VBRTag2[3]) return 0; buf+=4; pTagData->h_id = h_id; head_flags = pTagData->flags = ExtractI4(buf); buf+=4; /* get flags */ if( head_flags & FRAMES_FLAG ) { pTagData->frames = ExtractI4(buf); buf+=4; } if( head_flags & BYTES_FLAG ) { pTagData->bytes = ExtractI4(buf); buf+=4; } if( head_flags & TOC_FLAG ) { if( pTagData->toc != NULL ) { for(i=0;itoc[i] = buf[i]; } buf+=NUMTOCENTRIES; } pTagData->vbr_scale = -1; if( head_flags & VBR_SCALE_FLAG ) { pTagData->vbr_scale = ExtractI4(buf); buf+=4; } pTagData->headersize = ((h_id+1)*72000*h_bitrate) / pTagData->samprate; buf+=21; enc_delay = buf[0] << 4; enc_delay += buf[1] >> 4; enc_padding= (buf[1] & 0x0F)<<8; enc_padding += buf[2]; /* check for reasonable values (this may be an old Xing header, */ /* not a INFO tag) */ if (enc_delay<0 || enc_delay > 3000) enc_delay=-1; if (enc_padding<0 || enc_padding > 3000) enc_padding=-1; pTagData->enc_delay=enc_delay; pTagData->enc_padding=enc_padding; #ifdef DEBUG_VBRTAG fprintf(stderr,"\n\n********************* VBR TAG INFO *****************\n"); fprintf(stderr,"tag :%s\n",VBRTag); fprintf(stderr,"head_flags :%d\n",head_flags); fprintf(stderr,"bytes :%d\n",pTagData->bytes); fprintf(stderr,"frames :%d\n",pTagData->frames); fprintf(stderr,"VBR Scale :%d\n",pTagData->vbr_scale); fprintf(stderr,"enc_delay = %i \n",enc_delay); fprintf(stderr,"enc_padding= %i \n",enc_padding); fprintf(stderr,"toc:\n"); if( pTagData->toc != NULL ) { for(i=0;itoc[i])); } } fprintf(stderr,"\n***************** END OF VBR TAG INFO ***************\n"); #endif return 1; /* success */ } /**************************************************************************** * InitVbrTag: Initializes the header, and write empty frame to stream * Paramters: * fpStream: pointer to output file stream * nMode : Channel Mode: 0=STEREO 1=JS 2=DS 3=MONO **************************************************************************** */ int InitVbrTag(lame_global_flags *gfp) { int nMode,SampIndex; int i,kbps_header,tot; lame_internal_flags *gfc = gfp->internal_flags; #define MAXFRAMESIZE 2880 /* or 0xB40, the max freeformat 640 32kHz framesize */ /* uint8_t pbtStreamBuffer[MAXFRAMESIZE]; */ nMode = gfp->mode; SampIndex = gfc->samplerate_index; gfp->nVbrNumFrames=0; /*gfp->nVbrFrameBufferSize=0;*/ /* Clear stream buffer */ /* memset(pbtStreamBuffer,0x00,sizeof(pbtStreamBuffer)); */ /* * Xing VBR pretends to be a 48kbs layer III frame. (at 44.1kHz). * (at 48kHz they use 56kbs since 48kbs frame not big enough for * table of contents) * let's always embed Xing header inside a 64kbs layer III frame. * this gives us enough room for a LAME version string too. * size determined by sampling frequency (MPEG1) * 32kHz: 216 bytes@48kbs 288bytes@ 64kbs * 44.1kHz: 156 bytes 208bytes@64kbs (+1 if padding = 1) * 48kHz: 144 bytes 192 * * MPEG 2 values are the same since the framesize and samplerate * are each reduced by a factor of 2. */ if (1==gfp->version) { kbps_header = XING_BITRATE1; } else { if (gfp->out_samplerate < 16000 ) kbps_header = XING_BITRATE25; else kbps_header = XING_BITRATE2; } if (gfp->VBR==vbr_off) kbps_header = gfp->brate; gfp->TotalFrameSize= ((gfp->version+1)*72000*kbps_header) / gfp->out_samplerate; tot = (gfc->sideinfo_len+LAMEHEADERSIZE); if (gfp->TotalFrameSize < tot || gfp->TotalFrameSize > MAXFRAMESIZE ) { /* disable tag, it wont fit */ gfp->bWriteVbrTag = 0; return 0; } /* write dummy VBR tag of all 0's into bitstream */ for (i=0; iTotalFrameSize; ++i) add_dummy_byte(gfp,0); gfc->VBR_seek_table.sum = 0; gfc->VBR_seek_table.seen = 0; gfc->VBR_seek_table.want = 1; gfc->VBR_seek_table.pos = 0; if (gfc->VBR_seek_table.bag == NULL) { gfc->VBR_seek_table.bag = malloc (400*sizeof(int)); if (gfc->VBR_seek_table.bag != NULL) { gfc->VBR_seek_table.size = 400; } else { gfc->VBR_seek_table.size = 0; ERRORF (gfc,"Error: can't allocate VbrFrames buffer\n"); return -1; } } /* Success */ return 0; } /* fast CRC-16 computation - uses table crc16_lookup 8*/ int CRC_update_lookup(int value, int crc) { int tmp; tmp=crc^value; crc=(crc>>8)^crc16_lookup[tmp & 0xff]; return crc; } void UpdateMusicCRC(uint16_t *crc,unsigned char *buffer, int size){ int i; for (i=0; iinternal_flags; /* FLOAT fVersion = LAME_MAJOR_VERSION + 0.01 * LAME_MINOR_VERSION; */ int nBytesWritten = 0; int nFilesize = 0; /*size of fpStream. Will be equal to size after process finishes. */ int i; int enc_delay=lame_get_encoder_delay(gfp); /* encoder delay */ int enc_padding=lame_get_encoder_padding(gfp); /* encoder padding */ /*recall: gfp->VBR_q is for example set by the switch -V */ /* gfp->quality by -q, -h, -f, etc */ int nQuality = (100 - 10 * gfp->VBR_q - gfp->quality); const char *szVersion = get_lame_very_short_version(); uint8_t nVBR; uint8_t nRevision = 0x00; uint8_t nRevMethod; uint8_t vbr_type_translator[] = {1,5,3,2,4,0,3}; /*numbering different in vbr_mode vs. Lame tag */ uint8_t nLowpass = ( ((gfp->lowpassfreq / 100.0)+.5) > 255 ? 255 : (gfp->lowpassfreq / 100.0)+.5 ); uint32_t nPeakSignalAmplitude = 0; uint16_t nRadioReplayGain = 0; uint16_t nAudiophileReplayGain = 0; uint8_t nNoiseShaping = gfp->internal_flags->noise_shaping; uint8_t nStereoMode = 0; int bNonOptimal = 0; uint8_t nSourceFreq = 0; uint8_t nMisc = 0; uint32_t nMusicLength = 0; int bId3v1Present = ((gfp->internal_flags->tag_spec.flags & CHANGED_FLAG) && !(gfp->internal_flags->tag_spec.flags & V2_ONLY_FLAG)); uint16_t nMusicCRC = 0; /*psy model type: Gpsycho or NsPsytune */ unsigned char bExpNPsyTune = gfp->exp_nspsytune & 1; unsigned char bSafeJoint = (gfp->exp_nspsytune & 2)!=0; unsigned char bNoGapMore = 0; unsigned char bNoGapPrevious = 0; int nNoGapCount = gfp->internal_flags->nogap_total; int nNoGapCurr = gfp->internal_flags->nogap_current; uint8_t nAthType = gfp->ATHtype; /*4 bits. */ uint8_t nFlags = 0; /* if ABR, {store bitrate <=255} else { store "-b"} */ int nABRBitrate; switch (gfp->VBR) { case vbr_abr:{ nABRBitrate = gfp->VBR_mean_bitrate_kbps; break; } case vbr_off:{ nABRBitrate = gfp->brate; break; } default:{ /*vbr modes*/ nABRBitrate = gfp->VBR_min_bitrate_kbps; } } /*revision and vbr method */ if (gfp->VBR>=0 && gfp->VBR < sizeof(vbr_type_translator)) nVBR = vbr_type_translator[gfp->VBR]; else nVBR = 0x00; /*unknown. */ nRevMethod = 0x10 * nRevision + nVBR; /* ReplayGain */ if (gfp->findReplayGain) { if (gfc->RadioGain > 0x1FE) gfc->RadioGain = 0x1FE; if (gfc->RadioGain < -0x1FE) gfc->RadioGain = -0x1FE; nRadioReplayGain = 0x2000; /* set name code */ nRadioReplayGain |= 0xC00; /* set originator code to `determined automatically' */ if (gfc->RadioGain >= 0) nRadioReplayGain |= gfc->RadioGain; /* set gain adjustment */ else { nRadioReplayGain |= 0x200; /* set the sign bit */ nRadioReplayGain |= -gfc->RadioGain; /* set gain adjustment */ } } /* peak sample */ if(gfc->findPeakSample) nPeakSignalAmplitude = abs((int)((((FLOAT8)gfc->PeakSample) / 32767.0 ) * pow(2,23) +.5)); /*nogap */ if (nNoGapCount != -1) { if (nNoGapCurr > 0) bNoGapPrevious = 1; if (nNoGapCurr < nNoGapCount-1) bNoGapMore = 1; } /*flags */ nFlags = nAthType + (bExpNPsyTune << 4) + (bSafeJoint << 5) + (bNoGapMore << 6) + (bNoGapPrevious << 7); if (nQuality < 0) nQuality = 0; /*stereo mode field... a bit ugly.*/ switch(gfp->mode) { case MONO: nStereoMode = 0; break; case STEREO: nStereoMode = 1; break; case DUAL_CHANNEL: nStereoMode = 2; break; case JOINT_STEREO: if (gfp->force_ms) nStereoMode = 4; else nStereoMode = 3; break; case NOT_SET: /* FALLTHROUGH */ default: nStereoMode = 7; break; } /*Intensity stereo : nStereoMode = 6. IS is not implemented */ if (gfp->in_samplerate <= 32000) nSourceFreq = 0x00; else if (gfp->in_samplerate ==48000) nSourceFreq = 0x02; else if (gfp->in_samplerate > 48000) nSourceFreq = 0x03; else nSourceFreq = 0x01; /*default is 44100Hz. */ /*Check if the user overrided the default LAME behaviour with some nasty options */ if (gfp->short_blocks == short_block_forced || gfp->short_blocks == short_block_dispensed || ((gfp->lowpassfreq == -1) && (gfp->highpassfreq == -1)) || /* "-k" */ (gfp->scale_left != gfp->scale_right) || gfp->disable_reservoir || gfp->noATH || gfp->ATHonly || (nAthType == 0) || gfp->in_samplerate <= 32000) bNonOptimal = 1; nMisc = nNoiseShaping + (nStereoMode << 2) + (bNonOptimal << 5) + (nSourceFreq << 6); /*get filesize */ fseek(fpStream, 0, SEEK_END); nFilesize = ftell(fpStream); nMusicLength = nFilesize - id3v2size; /*omit current frame */ if (bId3v1Present) nMusicLength-=128; /*id3v1 present. */ nMusicCRC = gfc->nMusicCRC; /*Write all this information into the stream*/ CreateI4(&pbtStreamBuffer[nBytesWritten], nQuality); nBytesWritten+=4; strncpy(&pbtStreamBuffer[nBytesWritten], szVersion, 9); nBytesWritten+=9; pbtStreamBuffer[nBytesWritten] = nRevMethod ; nBytesWritten++; pbtStreamBuffer[nBytesWritten] = nLowpass; nBytesWritten++; CreateI4(&pbtStreamBuffer[nBytesWritten], nPeakSignalAmplitude); nBytesWritten+=4; CreateI2(&pbtStreamBuffer[nBytesWritten],nRadioReplayGain); nBytesWritten+=2; CreateI2(&pbtStreamBuffer[nBytesWritten],nAudiophileReplayGain); nBytesWritten+=2; pbtStreamBuffer[nBytesWritten] = nFlags; nBytesWritten++; if (nABRBitrate >= 255) pbtStreamBuffer[nBytesWritten] = 0xFF; else pbtStreamBuffer[nBytesWritten] = nABRBitrate; nBytesWritten++; pbtStreamBuffer[nBytesWritten ] = enc_delay >> 4; /* works for win32, does it for unix? */ pbtStreamBuffer[nBytesWritten +1] = (enc_delay << 4) + (enc_padding >> 8); pbtStreamBuffer[nBytesWritten +2] = enc_padding; nBytesWritten+=3; pbtStreamBuffer[nBytesWritten] = nMisc; nBytesWritten++; pbtStreamBuffer[nBytesWritten++] = 0; /*unused in rev0 */ CreateI2(&pbtStreamBuffer[nBytesWritten], gfp->preset); nBytesWritten+=2; CreateI4(&pbtStreamBuffer[nBytesWritten], nMusicLength); nBytesWritten+=4; CreateI2(&pbtStreamBuffer[nBytesWritten], nMusicCRC); nBytesWritten+=2; /*Calculate tag CRC.... must be done here, since it includes *previous information*/ for (i = 0;iinternal_flags; long lFileSize; int nStreamIndex; char abyte,bbyte; uint8_t btToc[NUMTOCENTRIES]; uint8_t pbtStreamBuffer[MAXFRAMESIZE]; int i; uint16_t crc = 0x00; unsigned char id3v2Header[10]; size_t id3v2TagSize; if (gfc->VBR_seek_table.pos <= 0) return -1; /* Clear stream buffer */ memset(pbtStreamBuffer,0x00,sizeof(pbtStreamBuffer)); /* Seek to end of file*/ fseek(fpStream,0,SEEK_END); /* Get file size */ lFileSize=ftell(fpStream); /* Abort if file has zero length. Yes, it can happen :) */ if (lFileSize==0) return -1; /* * The VBR tag may NOT be located at the beginning of the stream. * If an ID3 version 2 tag was added, then it must be skipped to write * the VBR tag data. */ /* seek to the beginning of the stream */ fseek(fpStream,0,SEEK_SET); /* read 10 bytes in case there's an ID3 version 2 header here */ fread(id3v2Header,1,sizeof id3v2Header,fpStream); /* does the stream begin with the ID3 version 2 file identifier? */ if (!strncmp((char *)id3v2Header,"ID3",3)) { /* the tag size (minus the 10-byte header) is encoded into four * bytes where the most significant bit is clear in each byte */ id3v2TagSize=(((id3v2Header[6] & 0x7f)<<21) | ((id3v2Header[7] & 0x7f)<<14) | ((id3v2Header[8] & 0x7f)<<7) | (id3v2Header[9] & 0x7f)) + sizeof id3v2Header; } else { /* no ID3 version 2 tag in this stream */ id3v2TagSize=0; } /* Seek to first real frame */ fseek(fpStream,id3v2TagSize+gfp->TotalFrameSize,SEEK_SET); /* Read the header (first valid frame) */ fread(pbtStreamBuffer,4,1,fpStream); /* the default VBR header. 48 kbps layer III, no padding, no crc */ /* but sampling freq, mode andy copyright/copy protection taken */ /* from first valid frame */ pbtStreamBuffer[0]=(uint8_t) 0xff; abyte = (pbtStreamBuffer[1] & (char) 0xf1); { int bitrate; if (1==gfp->version) { bitrate = XING_BITRATE1; } else { if (gfp->out_samplerate < 16000 ) bitrate = XING_BITRATE25; else bitrate = XING_BITRATE2; } if (gfp->VBR==vbr_off) bitrate = gfp->brate; if (gfp->free_format) bbyte = 0x00; else bbyte = 16*BitrateIndex(bitrate,gfp->version,gfp->out_samplerate); } /* Use as much of the info from the real frames in the * Xing header: samplerate, channels, crc, etc... */ if (gfp->version==1) { /* MPEG1 */ pbtStreamBuffer[1]=abyte | (char) 0x0a; /* was 0x0b; */ abyte = pbtStreamBuffer[2] & (char) 0x0d; /* AF keep also private bit */ pbtStreamBuffer[2]=(char) bbyte | abyte; /* 64kbs MPEG1 frame */ }else{ /* MPEG2 */ pbtStreamBuffer[1]=abyte | (char) 0x02; /* was 0x03; */ abyte = pbtStreamBuffer[2] & (char) 0x0d; /* AF keep also private bit */ pbtStreamBuffer[2]=(char) bbyte | abyte; /* 64kbs MPEG2 frame */ } /* Clear all TOC entries */ memset(btToc,0,sizeof(btToc)); if (gfp->free_format) { int i; for (i = 1; i < NUMTOCENTRIES; ++i) btToc[i] = 255*i/100; } else { Xing_seek_table(&gfc->VBR_seek_table, btToc); } /*print_seeking (btToc);*/ /* Start writing the tag after the zero frame */ nStreamIndex=gfc->sideinfo_len; /* note! Xing header specifies that Xing data goes in the * ancillary data with NO ERROR PROTECTION. If error protecton * in enabled, the Xing data still starts at the same offset, * and now it is in sideinfo data block, and thus will not * decode correctly by non-Xing tag aware players */ if (gfp->error_protection) nStreamIndex -= 2; /* Put Vbr tag */ if (gfp->VBR == vbr_off) { pbtStreamBuffer[nStreamIndex++]=VBRTag2[0]; pbtStreamBuffer[nStreamIndex++]=VBRTag2[1]; pbtStreamBuffer[nStreamIndex++]=VBRTag2[2]; pbtStreamBuffer[nStreamIndex++]=VBRTag2[3]; } else { pbtStreamBuffer[nStreamIndex++]=VBRTag[0]; pbtStreamBuffer[nStreamIndex++]=VBRTag[1]; pbtStreamBuffer[nStreamIndex++]=VBRTag[2]; pbtStreamBuffer[nStreamIndex++]=VBRTag[3]; } /* Put header flags */ CreateI4(&pbtStreamBuffer[nStreamIndex],FRAMES_FLAG+BYTES_FLAG+TOC_FLAG+VBR_SCALE_FLAG); nStreamIndex+=4; /* Put Total Number of frames */ CreateI4(&pbtStreamBuffer[nStreamIndex],gfp->nVbrNumFrames); nStreamIndex+=4; /* Put Total file size */ CreateI4(&pbtStreamBuffer[nStreamIndex],(int)lFileSize); nStreamIndex+=4; /* Put TOC */ memcpy(&pbtStreamBuffer[nStreamIndex],btToc,sizeof(btToc)); nStreamIndex+=sizeof(btToc); if (gfp->error_protection) { /* (jo) error_protection: add crc16 information to header */ CRC_writeheader(gfc, (char*)pbtStreamBuffer); } /*work out CRC so far: initially crc = 0 */ for (i = 0;i< nStreamIndex ;i++) crc = CRC_update_lookup(pbtStreamBuffer[i], crc); /*Put LAME VBR info*/ nStreamIndex+=PutLameVBR(gfp, fpStream, pbtStreamBuffer + nStreamIndex, id3v2TagSize,crc); #ifdef DEBUG_VBRTAG { VBRTAGDATA TestHeader; GetVbrTag(&TestHeader,pbtStreamBuffer); } #endif /*Seek to the beginning of the stream */ fseek(fpStream,id3v2TagSize,SEEK_SET); /* Put it all to disk again */ if (fwrite(pbtStreamBuffer,(unsigned int)gfp->TotalFrameSize,1,fpStream)!=1) { return -1; } return 0; /* success */ } lastfm-player-1.1.4_p1910/src/mpglib/VbrTag.h0000644000175000001440000000603410352604465017544 0ustar davidusers/* * Xing VBR tagging for LAME. * * Copyright (c) 1999 A.L. Faber * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef LAME_VRBTAG_H #define LAME_VRBTAG_H #include "lame.h" #if HAVE_INTTYPES_H # include #else # if HAVE_STDINT_H # include # endif #endif /* ----------------------------------------------------------- * A Vbr header may be present in the ancillary * data field of the first frame of an mp3 bitstream * The Vbr header (optionally) contains * frames total number of audio frames in the bitstream * bytes total number of bytes in the bitstream * toc table of contents * toc (table of contents) gives seek points * for random access * the ith entry determines the seek point for * i-percent duration * seek point in bytes = (toc[i]/256.0) * total_bitstream_bytes * e.g. half duration seek point = (toc[50]/256.0) * total_bitstream_bytes */ #define FRAMES_FLAG 0x0001 #define BYTES_FLAG 0x0002 #define TOC_FLAG 0x0004 #define VBR_SCALE_FLAG 0x0008 #define NUMTOCENTRIES 100 #define FRAMES_AND_BYTES (FRAMES_FLAG | BYTES_FLAG) /*structure to receive extracted header */ /* toc may be NULL*/ typedef struct { int h_id; /* from MPEG header, 0=MPEG2, 1=MPEG1 */ int samprate; /* determined from MPEG header */ int flags; /* from Vbr header data */ int frames; /* total bit stream frames from Vbr header data */ int bytes; /* total bit stream bytes from Vbr header data*/ int vbr_scale; /* encoded vbr scale from Vbr header data*/ unsigned char toc[NUMTOCENTRIES]; /* may be NULL if toc not desired*/ int headersize; /* size of VBR header, in bytes */ int enc_delay; /* encoder delay */ int enc_padding; /* encoder paddign added at end of stream */ } VBRTAGDATA; int CheckVbrTag(unsigned char *buf); int GetVbrTag(VBRTAGDATA *pTagData, unsigned char *buf); int SeekPoint(unsigned char TOC[NUMTOCENTRIES], int file_bytes, float percent); int InitVbrTag(lame_global_flags *gfp); int PutVbrTag(lame_global_flags *gfp,FILE *fid,int nVbrScale); //int PutLameVBR(lame_global_flags *gfp, FILE *fpStream, uint8_t *pbtStreamBuffer, uint32_t id3v2size, uint16_t crc); void AddVbrFrame(lame_global_flags *gfp); //void UpdateMusicCRC(uint16_t *crc,unsigned char *buffer, int size); #endif lastfm-player-1.1.4_p1910/src/mpglib/lame-analysis.h0000644000175000001440000000511710352604465021117 0ustar davidusers/* * GTK plotting routines source file * * Copyright (c) 1999 Mark Taylor * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef LAME_GTKANAL_H #define LAME_GTKANAL_H #include "encoder.h" #define READ_AHEAD 40 /* number of frames to read ahead */ #define MAXMPGLAG READ_AHEAD /* if the mpg123 lag becomes bigger than this we have to stop */ #define NUMBACK 6 /* number of frames we can back up */ #define NUMPINFO (NUMBACK+READ_AHEAD+1) typedef struct { int frameNum; /* current frame number */ int frameNum123; int num_samples; /* number of pcm samples read for this frame */ double frametime; /* starting time of frame, in seconds */ double pcmdata[2][1600]; double pcmdata2[2][1152+1152-DECDELAY]; double xr[2][2][576]; double mpg123xr[2][2][576]; double ms_ratio[2]; double ms_ener_ratio[2]; /* L,R, M and S values */ double energy[2][4][BLKSIZE]; double pe[2][4]; double thr[2][4][SBMAX_l]; double en[2][4][SBMAX_l]; double thr_s[2][4][3*SBMAX_s]; double en_s[2][4][3*SBMAX_s]; double ers[2][4]; double sfb[2][2][SBMAX_l]; double sfb_s[2][2][3*SBMAX_s]; double LAMEsfb[2][2][SBMAX_l]; double LAMEsfb_s[2][2][3*SBMAX_s]; int LAMEqss[2][2]; int qss[2][2]; int big_values[2][2]; int sub_gain[2][2][3]; double xfsf[2][2][SBMAX_l]; double xfsf_s[2][2][3*SBMAX_s]; int over[2][2]; double tot_noise[2][2]; double max_noise[2][2]; double over_noise[2][2]; double var_noise[2][2]; int blocktype[2][2]; int scalefac_scale[2][2]; int preflag[2][2]; int mpg123blocktype[2][2]; int mixed[2][2]; int mainbits[2][2]; int sfbits[2][2]; int LAMEmainbits[2][2]; int LAMEsfbits[2][2]; int framesize,stereo,js,ms_stereo,i_stereo,emph,bitrate,sampfreq,maindata; int crc,padding; int scfsi[2],mean_bits,resvsize; int totbits; } plotting_data; extern plotting_data *pinfo; #endif lastfm-player-1.1.4_p1910/src/mpglib/tabinit.c0000644000175000001440000001115710352604465020006 0ustar davidusers/* $Id: tabinit.c,v 1.11 2001/01/05 15:20:34 aleidinger Exp $ */ #ifdef HAVE_CONFIG_H # include #endif #include #include "tabinit.h" #include "mpg123.h" #ifdef WITH_DMALLOC #include #endif real decwin[512+32]; static real cos64[16],cos32[8],cos16[4],cos8[2],cos4[1]; real *pnts[] = { cos64,cos32,cos16,cos8,cos4 }; const double dewin[512] = { 0.000000000,-0.000015259,-0.000015259,-0.000015259, -0.000015259,-0.000015259,-0.000015259,-0.000030518, -0.000030518,-0.000030518,-0.000030518,-0.000045776, -0.000045776,-0.000061035,-0.000061035,-0.000076294, -0.000076294,-0.000091553,-0.000106812,-0.000106812, -0.000122070,-0.000137329,-0.000152588,-0.000167847, -0.000198364,-0.000213623,-0.000244141,-0.000259399, -0.000289917,-0.000320435,-0.000366211,-0.000396729, -0.000442505,-0.000473022,-0.000534058,-0.000579834, -0.000625610,-0.000686646,-0.000747681,-0.000808716, -0.000885010,-0.000961304,-0.001037598,-0.001113892, -0.001205444,-0.001296997,-0.001388550,-0.001480103, -0.001586914,-0.001693726,-0.001785278,-0.001907349, -0.002014160,-0.002120972,-0.002243042,-0.002349854, -0.002456665,-0.002578735,-0.002685547,-0.002792358, -0.002899170,-0.002990723,-0.003082275,-0.003173828, -0.003250122,-0.003326416,-0.003387451,-0.003433228, -0.003463745,-0.003479004,-0.003479004,-0.003463745, -0.003417969,-0.003372192,-0.003280640,-0.003173828, -0.003051758,-0.002883911,-0.002700806,-0.002487183, -0.002227783,-0.001937866,-0.001617432,-0.001266479, -0.000869751,-0.000442505, 0.000030518, 0.000549316, 0.001098633, 0.001693726, 0.002334595, 0.003005981, 0.003723145, 0.004486084, 0.005294800, 0.006118774, 0.007003784, 0.007919312, 0.008865356, 0.009841919, 0.010848999, 0.011886597, 0.012939453, 0.014022827, 0.015121460, 0.016235352, 0.017349243, 0.018463135, 0.019577026, 0.020690918, 0.021789551, 0.022857666, 0.023910522, 0.024932861, 0.025909424, 0.026840210, 0.027725220, 0.028533936, 0.029281616, 0.029937744, 0.030532837, 0.031005859, 0.031387329, 0.031661987, 0.031814575, 0.031845093, 0.031738281, 0.031478882, 0.031082153, 0.030517578, 0.029785156, 0.028884888, 0.027801514, 0.026535034, 0.025085449, 0.023422241, 0.021575928, 0.019531250, 0.017257690, 0.014801025, 0.012115479, 0.009231567, 0.006134033, 0.002822876, -0.000686646,-0.004394531,-0.008316040,-0.012420654, -0.016708374,-0.021179199,-0.025817871,-0.030609131, -0.035552979,-0.040634155,-0.045837402,-0.051132202, -0.056533813,-0.061996460,-0.067520142,-0.073059082, -0.078628540,-0.084182739,-0.089706421,-0.095169067, -0.100540161,-0.105819702,-0.110946655,-0.115921021, -0.120697021,-0.125259399,-0.129562378,-0.133590698, -0.137298584,-0.140670776,-0.143676758,-0.146255493, -0.148422241,-0.150115967,-0.151306152,-0.151962280, -0.152069092,-0.151596069,-0.150497437,-0.148773193, -0.146362305,-0.143264771,-0.139450073,-0.134887695, -0.129577637,-0.123474121,-0.116577148,-0.108856201, -0.100311279,-0.090927124,-0.080688477,-0.069595337, -0.057617187,-0.044784546,-0.031082153,-0.016510010, -0.001068115, 0.015228271, 0.032379150, 0.050354004, 0.069168091, 0.088775635, 0.109161377, 0.130310059, 0.152206421, 0.174789429, 0.198059082, 0.221984863, 0.246505737, 0.271591187, 0.297210693, 0.323318481, 0.349868774, 0.376800537, 0.404083252, 0.431655884, 0.459472656, 0.487472534, 0.515609741, 0.543823242, 0.572036743, 0.600219727, 0.628295898, 0.656219482, 0.683914185, 0.711318970, 0.738372803, 0.765029907, 0.791213989, 0.816864014, 0.841949463, 0.866363525, 0.890090942, 0.913055420, 0.935195923, 0.956481934, 0.976852417, 0.996246338, 1.014617920, 1.031936646, 1.048156738, 1.063217163, 1.077117920, 1.089782715, 1.101211548, 1.111373901, 1.120223999, 1.127746582, 1.133926392, 1.138763428, 1.142211914, 1.144287109, 1.144989014 }; void make_decode_tables(long scaleval) { int i,j,k,kr,divv; real *table,*costab; for(i=0;i<5;i++) { kr=0x10>>i; divv=0x40>>i; costab = pnts[i]; for(k=0;k #endif #ifdef USE_LAYER_1 #include #include "common.h" #include "decode_i386.h" #ifdef WITH_DMALLOC #include #endif void I_step_one(unsigned int balloc[], unsigned int scale_index[2][SBLIMIT],struct frame *fr) { unsigned int *ba=balloc; unsigned int *sca = (unsigned int *) scale_index; assert ( fr->stereo == 1 || fr->stereo == 2 ); if(fr->stereo==2) { int i; int jsbound = fr->jsbound; for (i=0;istereo == 1 || fr->stereo == 2 ); if(fr->stereo == 2) { int jsbound = fr->jsbound; register real *f0 = fraction[0]; register real *f1 = fraction[1]; ba = balloc; for (sample=smpb,i=0;idown_sample_sblimit;i<32;i++) fraction[0][i] = fraction[1][i] = 0.0; } else { register real *f0 = fraction[0]; ba = balloc; for (sample=smpb,i=0;idown_sample_sblimit;i<32;i++) fraction[0][i] = 0.0; } } /*int do_layer1(struct frame *fr,int outmode,struct audio_info_struct *ai) */ int do_layer1(PMPSTR mp, unsigned char *pcm_sample,int *pcm_point) { int clip=0; unsigned int balloc[2*SBLIMIT]; unsigned int scale_index[2][SBLIMIT]; real fraction[2][SBLIMIT]; struct frame *fr=&(mp->fr); int i,stereo = fr->stereo; int single = fr->single; fr->jsbound = (fr->mode == MPG_MD_JOINT_STEREO) ? (fr->mode_ext<<2)+4 : 32; if (stereo == 1 || single == 3) single = 0; I_step_one(balloc,scale_index,fr); for (i=0;i= 0) { clip += synth_1to1_mono( mp, (real *) fraction[single],pcm_sample,pcm_point); } else { int p1 = *pcm_point; clip += synth_1to1( mp, (real *) fraction[0],0,pcm_sample,&p1); clip += synth_1to1( mp, (real *) fraction[1],1,pcm_sample,pcm_point); } } return clip; } #endif lastfm-player-1.1.4_p1910/src/mpglib/layer1.h0000644000175000001440000000160110352604465017547 0ustar davidusers/* ** Copyright (C) 2000 Albert L. Faber ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef LAYER1_H_INCLUDED #define LAYER1_H_INCLUDED int do_layer1(PMPSTR mp, unsigned char *pcm_sample,int *pcm_point); #endif lastfm-player-1.1.4_p1910/src/mpglib/layer2.c0000644000175000001440000002127610352604465017555 0ustar davidusers/* * Mpeg Layer-2 audio decoder * -------------------------- * copyright (c) 1995 by Michael Hipp, All rights reserved. See also 'README' * */ /* $Id: layer2.c,v 1.19 2002/12/08 17:02:30 takehiro Exp $ */ #ifdef HAVE_CONFIG_H # include #endif #ifdef USE_LAYER_2 #include "common.h" #include "layer2.h" #include "l2tables.h" #include "decode_i386.h" #ifdef WITH_DMALLOC #include #endif static int grp_3tab[32 * 3] = { 0, }; /* used: 27 */ static int grp_5tab[128 * 3] = { 0, }; /* used: 125 */ static int grp_9tab[1024 * 3] = { 0, }; /* used: 729 */ void init_layer2(void) { static const double mulmul[27] = { 0.0 , -2.0/3.0 , 2.0/3.0 , 2.0/7.0 , 2.0/15.0 , 2.0/31.0, 2.0/63.0 , 2.0/127.0 , 2.0/255.0 , 2.0/511.0 , 2.0/1023.0 , 2.0/2047.0 , 2.0/4095.0 , 2.0/8191.0 , 2.0/16383.0 , 2.0/32767.0 , 2.0/65535.0 , -4.0/5.0 , -2.0/5.0 , 2.0/5.0, 4.0/5.0 , -8.0/9.0 , -4.0/9.0 , -2.0/9.0 , 2.0/9.0 , 4.0/9.0 , 8.0/9.0 }; static const int base[3][9] = { { 1 , 0, 2 , } , { 17, 18, 0 , 19, 20 , } , { 21, 1, 22, 23, 0, 24, 25, 2, 26 } }; int i,j,k,l,len; real *table; static const int tablen[3] = { 3 , 5 , 9 }; static int *itable,*tables[3] = { grp_3tab , grp_5tab , grp_9tab }; for(i=0;i<3;i++) { itable = tables[i]; len = tablen[i]; for(j=0;jstereo-1; int sblimit = fr->II_sblimit; int jsbound = fr->jsbound; int sblimit2 = fr->II_sblimit<alloc; int i; static unsigned int scfsi_buf[64]; unsigned int *scfsi,*bita; int sc,step; bita = bit_alloc; if(stereo) { for (i=jsbound;i;i--,alloc1+=(1<bits); *bita++ = (char) getbits(step); } for (i=sblimit-jsbound;i;i--,alloc1+=(1<bits); bita[1] = bita[0]; bita+=2; } bita = bit_alloc; scfsi=scfsi_buf; for (i=sblimit2;i;i--) if (*bita++) *scfsi++ = (char) getbits_fast(2); } else /* mono */ { for (i=sblimit;i;i--,alloc1+=(1<bits); bita = bit_alloc; scfsi=scfsi_buf; for (i=sblimit;i;i--) if (*bita++) *scfsi++ = (char) getbits_fast(2); } bita = bit_alloc; scfsi=scfsi_buf; for (i=sblimit2;i;i--) if (*bita++) switch (*scfsi++) { case 0: *scale++ = getbits_fast(6); *scale++ = getbits_fast(6); *scale++ = getbits_fast(6); break; case 1 : *scale++ = sc = getbits_fast(6); *scale++ = sc; *scale++ = getbits_fast(6); break; case 2: *scale++ = sc = getbits_fast(6); *scale++ = sc; *scale++ = sc; break; default: /* case 3 */ *scale++ = getbits_fast(6); *scale++ = sc = getbits_fast(6); *scale++ = sc; break; } } void II_step_two(unsigned int *bit_alloc,real fraction[2][4][SBLIMIT],int *scale,struct frame *fr,int x1) { int i,j,k,ba; int stereo = fr->stereo; int sblimit = fr->II_sblimit; int jsbound = fr->jsbound; struct al_table2 *alloc2,*alloc1 = fr->alloc; unsigned int *bita=bit_alloc; int d1,step; for (i=0;ibits; for (j=0;jbits; if( (d1=alloc2->d) < 0) { real cm=muls[k][scale[x1]]; fraction[j][0][i] = ((real) ((int)getbits(k) + d1)) * cm; fraction[j][1][i] = ((real) ((int)getbits(k) + d1)) * cm; fraction[j][2][i] = ((real) ((int)getbits(k) + d1)) * cm; } else { static int *table[] = { 0,0,0,grp_3tab,0,grp_5tab,0,0,0,grp_9tab }; unsigned int idx,*tab,m=scale[x1]; idx = (unsigned int) getbits(k); tab = (unsigned int *) (table[d1] + idx + idx + idx); fraction[j][0][i] = muls[*tab++][m]; fraction[j][1][i] = muls[*tab++][m]; fraction[j][2][i] = muls[*tab][m]; } scale+=3; } else fraction[j][0][i] = fraction[j][1][i] = fraction[j][2][i] = 0.0; } } for (i=jsbound;ibits; bita++; /* channel 1 and channel 2 bitalloc are the same */ if ( (ba=*bita++) ) { k=(alloc2 = alloc1+ba)->bits; if( (d1=alloc2->d) < 0) { real cm; cm=muls[k][scale[x1+3]]; fraction[1][0][i] = (fraction[0][0][i] = (real) ((int)getbits(k) + d1) ) * cm; fraction[1][1][i] = (fraction[0][1][i] = (real) ((int)getbits(k) + d1) ) * cm; fraction[1][2][i] = (fraction[0][2][i] = (real) ((int)getbits(k) + d1) ) * cm; cm=muls[k][scale[x1]]; fraction[0][0][i] *= cm; fraction[0][1][i] *= cm; fraction[0][2][i] *= cm; } else { static int *table[] = { 0,0,0,grp_3tab,0,grp_5tab,0,0,0,grp_9tab }; unsigned int idx,*tab,m1,m2; m1 = scale[x1]; m2 = scale[x1+3]; idx = (unsigned int) getbits(k); tab = (unsigned int *) (table[d1] + idx + idx + idx); fraction[0][0][i] = muls[*tab][m1]; fraction[1][0][i] = muls[*tab++][m2]; fraction[0][1][i] = muls[*tab][m1]; fraction[1][1][i] = muls[*tab++][m2]; fraction[0][2][i] = muls[*tab][m1]; fraction[1][2][i] = muls[*tab][m2]; } scale+=6; } else { fraction[0][0][i] = fraction[0][1][i] = fraction[0][2][i] = fraction[1][0][i] = fraction[1][1][i] = fraction[1][2][i] = 0.0; } /* should we use individual scalefac for channel 2 or is the current way the right one , where we just copy channel 1 to channel 2 ?? The current 'strange' thing is, that we throw away the scalefac values for the second channel ...!! -> changed .. now we use the scalefac values of channel one !! */ } /* if(sblimit > (fr->down_sample_sblimit) ) */ /* sblimit = fr->down_sample_sblimit; */ for(i=sblimit;ilsf) table = 4; else table = translate[fr->sampling_frequency][2-fr->stereo][fr->bitrate_index]; sblim = sblims[table]; fr->alloc = (struct al_table2*)tables[table]; fr->II_sblimit = sblim; } int do_layer2( PMPSTR mp,unsigned char *pcm_sample,int *pcm_point) /*int do_layer2(struct frame *fr,int outmode,struct audio_info_struct *ai) */ { int clip=0; int i,j; real fraction[2][4][SBLIMIT]; /* pick_table clears unused subbands */ unsigned int bit_alloc[64]; int scale[192]; struct frame *fr=&(mp->fr); int stereo = fr->stereo; int single = fr->single; II_select_table(fr); fr->jsbound = (fr->mode == MPG_MD_JOINT_STEREO) ? (fr->mode_ext<<2)+4 : fr->II_sblimit; if(stereo == 1 || single == 3) single = 0; II_step_one(bit_alloc, scale, fr); for (i=0;i>2); for (j=0;j<3;j++) { if(single >= 0) { clip += synth_1to1_mono(mp, fraction[single][j],pcm_sample,pcm_point); } else { int p1 = *pcm_point; clip += synth_1to1(mp, fraction[0][j],0,pcm_sample,&p1); clip += synth_1to1(mp, fraction[1][j],1,pcm_sample,pcm_point); } } } return clip; } #endif lastfm-player-1.1.4_p1910/src/mpglib/layer2.h0000644000175000001440000000223510352604465017554 0ustar davidusers/* ** Copyright (C) 2000 Albert L. Faber ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef USE_LAYER_2 #ifndef LAYER2_H_INCLUDED #define LAYER2_H_INCLUDED struct al_table2 { short bits; short d; }; void init_layer2(void); void II_step_one(unsigned int *bit_alloc,int *scale,struct frame *fr); void II_step_two(unsigned int *bit_alloc,real fraction[2][4][SBLIMIT],int *scale,struct frame *fr,int x1); int do_layer2( PMPSTR mp,unsigned char *pcm_sample,int *pcm_point); #endif #endif lastfm-player-1.1.4_p1910/src/mpglib/layer3.c0000644000175000001440000013023410352604465017551 0ustar davidusers/* * Mpeg Layer-3 audio decoder * -------------------------- * copyright (c) 1995,1996,1997 by Michael Hipp. * All rights reserved. See also 'README' */ /* $Id: layer3.c,v 1.47 2004/01/10 10:27:28 takehiro Exp $ */ #ifdef HAVE_CONFIG_H # include #endif #include #include "common.h" #include "huffman.h" #include "lame-analysis.h" #include "decode_i386.h" #ifdef WITH_DMALLOC #include #endif #define MPEG1 static real ispow[8207]; static real aa_ca[8],aa_cs[8]; static real COS1[12][6]; static real win[4][36]; static real win1[4][36]; static real gainpow2[256+118+4]; static real COS9[9]; static real COS6_1,COS6_2; static real tfcos36[9]; static real tfcos12[3]; struct bandInfoStruct { short longIdx[23]; short longDiff[22]; short shortIdx[14]; short shortDiff[13]; }; int longLimit[9][23]; int shortLimit[9][14]; const struct bandInfoStruct bandInfo[9] = { /* MPEG 1.0 */ { {0,4,8,12,16,20,24,30,36,44,52,62,74, 90,110,134,162,196,238,288,342,418,576}, {4,4,4,4,4,4,6,6,8, 8,10,12,16,20,24,28,34,42,50,54, 76,158}, {0,4*3,8*3,12*3,16*3,22*3,30*3,40*3,52*3,66*3, 84*3,106*3,136*3,192*3}, {4,4,4,4,6,8,10,12,14,18,22,30,56} } , { {0,4,8,12,16,20,24,30,36,42,50,60,72, 88,106,128,156,190,230,276,330,384,576}, {4,4,4,4,4,4,6,6,6, 8,10,12,16,18,22,28,34,40,46,54, 54,192}, {0,4*3,8*3,12*3,16*3,22*3,28*3,38*3,50*3,64*3, 80*3,100*3,126*3,192*3}, {4,4,4,4,6,6,10,12,14,16,20,26,66} } , { {0,4,8,12,16,20,24,30,36,44,54,66,82,102,126,156,194,240,296,364,448,550,576} , {4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102, 26} , {0,4*3,8*3,12*3,16*3,22*3,30*3,42*3,58*3,78*3,104*3,138*3,180*3,192*3} , {4,4,4,4,6,8,12,16,20,26,34,42,12} } , /* MPEG 2.0 */ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576}, {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 } , {0,4*3,8*3,12*3,18*3,24*3,32*3,42*3,56*3,74*3,100*3,132*3,174*3,192*3} , {4,4,4,6,6,8,10,14,18,26,32,42,18 } } , /* docs: 332. mpg123: 330 */ { {0,6,12,18,24,30,36,44,54,66,80,96,114,136,162,194,232,278,332,394,464,540,576}, {6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36 } , {0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,136*3,180*3,192*3} , {4,4,4,6,8,10,12,14,18,24,32,44,12 } } , { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576}, {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54 }, {0,4*3,8*3,12*3,18*3,26*3,36*3,48*3,62*3,80*3,104*3,134*3,174*3,192*3}, {4,4,4,6,8,10,12,14,18,24,30,40,18 } } , /* MPEG 2.5 */ { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576} , {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54}, {0,12,24,36,54,78,108,144,186,240,312,402,522,576}, {4,4,4,6,8,10,12,14,18,24,30,40,18} }, { {0,6,12,18,24,30,36,44,54,66,80,96,116,140,168,200,238,284,336,396,464,522,576} , {6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54}, {0,12,24,36,54,78,108,144,186,240,312,402,522,576}, {4,4,4,6,8,10,12,14,18,24,30,40,18} }, { {0,12,24,36,48,60,72,88,108,132,160,192,232,280,336,400,476,566,568,570,572,574,576}, {12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2}, {0, 24, 48, 72,108,156,216,288,372,480,486,492,498,576}, {8,8,8,12,16,20,24,28,36,2,2,2,26} } , }; static int mapbuf0[9][152]; static int mapbuf1[9][156]; static int mapbuf2[9][44]; static int *map[9][3]; static int *mapend[9][3]; static unsigned int n_slen2[512]; /* MPEG 2.0 slen for 'normal' mode */ static unsigned int i_slen2[256]; /* MPEG 2.0 slen for intensity stereo */ static real tan1_1[16],tan2_1[16],tan1_2[16],tan2_2[16]; static real pow1_1[2][16],pow2_1[2][16],pow1_2[2][16],pow2_2[2][16]; static unsigned int get1bit(void) { unsigned char rval; rval = *wordpointer << bitindex; bitindex++; wordpointer += (bitindex>>3); bitindex &= 7; return rval>>7; } /* * init tables for layer-3 */ void init_layer3(int down_sample_sblimit) { int i,j,k; for(i=-256;i<118+4;i++) gainpow2[i+256] = pow((double)2.0,-0.25 * (double) (i+210) ); for(i=0;i<8207;i++) ispow[i] = pow((double)i,(double)4.0/3.0); for (i=0;i<8;i++) { static double Ci[8]={-0.6,-0.535,-0.33,-0.185,-0.095,-0.041,-0.0142,-0.0037}; double sq=sqrt(1.0+Ci[i]*Ci[i]); aa_cs[i] = 1.0/sq; aa_ca[i] = Ci[i]/sq; } for(i=0;i<18;i++) { win[0][i] = win[1][i] = 0.5 * sin( M_PI / 72.0 * (double) (2*(i+0) +1) ) / cos ( M_PI * (double) (2*(i+0) +19) / 72.0 ); win[0][i+18] = win[3][i+18] = 0.5 * sin( M_PI / 72.0 * (double) (2*(i+18)+1) ) / cos ( M_PI * (double) (2*(i+18)+19) / 72.0 ); } for(i=0;i<6;i++) { win[1][i+18] = 0.5 / cos ( M_PI * (double) (2*(i+18)+19) / 72.0 ); win[3][i+12] = 0.5 / cos ( M_PI * (double) (2*(i+12)+19) / 72.0 ); win[1][i+24] = 0.5 * sin( M_PI / 24.0 * (double) (2*i+13) ) / cos ( M_PI * (double) (2*(i+24)+19) / 72.0 ); win[1][i+30] = win[3][i] = 0.0; win[3][i+6 ] = 0.5 * sin( M_PI / 24.0 * (double) (2*i+1) ) / cos ( M_PI * (double) (2*(i+6 )+19) / 72.0 ); } for(i=0;i<9;i++) COS9[i] = cos( M_PI / 18.0 * (double) i); for(i=0;i<9;i++) tfcos36[i] = 0.5 / cos ( M_PI * (double) (i*2+1) / 36.0 ); for(i=0;i<3;i++) tfcos12[i] = 0.5 / cos ( M_PI * (double) (i*2+1) / 12.0 ); COS6_1 = cos( M_PI / 6.0 * (double) 1); COS6_2 = cos( M_PI / 6.0 * (double) 2); for(i=0;i<12;i++) { win[2][i] = 0.5 * sin( M_PI / 24.0 * (double) (2*i+1) ) / cos ( M_PI * (double) (2*i+7) / 24.0 ); for(j=0;j<6;j++) COS1[i][j] = cos( M_PI / 24.0 * (double) ((2*i+7)*(2*j+1)) ); } for(j=0;j<4;j++) { static int len[4] = { 36,36,12,36 }; for(i=0;i 0) { if( i & 1 ) p1 = pow(base,(i+1.0)*0.5); else p2 = pow(base,i*0.5); } pow1_1[j][i] = p1; pow2_1[j][i] = p2; pow1_2[j][i] = M_SQRT2 * p1; pow2_2[j][i] = M_SQRT2 * p2; } } for(j=0;j<9;j++) { struct bandInfoStruct *bi = (struct bandInfoStruct *)&bandInfo[j]; int *mp; int cb,lwin; short *bdf; mp = map[j][0] = mapbuf0[j]; bdf = bi->longDiff; for(i=0,cb = 0; cb < 8 ; cb++,i+=*bdf++) { *mp++ = (*bdf) >> 1; *mp++ = i; *mp++ = 3; *mp++ = cb; } bdf = bi->shortDiff+3; for(cb=3;cb<13;cb++) { int l = (*bdf++) >> 1; for(lwin=0;lwin<3;lwin++) { *mp++ = l; *mp++ = i + lwin; *mp++ = lwin; *mp++ = cb; } i += 6*l; } mapend[j][0] = mp; mp = map[j][1] = mapbuf1[j]; bdf = bi->shortDiff+0; for(i=0,cb=0;cb<13;cb++) { int l = (*bdf++) >> 1; for(lwin=0;lwin<3;lwin++) { *mp++ = l; *mp++ = i + lwin; *mp++ = lwin; *mp++ = cb; } i += 6*l; } mapend[j][1] = mp; mp = map[j][2] = mapbuf2[j]; bdf = bi->longDiff; for(cb = 0; cb < 22 ; cb++) { *mp++ = (*bdf++) >> 1; *mp++ = cb; } mapend[j][2] = mp; } for(j=0;j<9;j++) { for(i=0;i<23;i++) { longLimit[j][i] = (bandInfo[j].longIdx[i] - 1 + 8) / 18 + 1; if(longLimit[j][i] > (down_sample_sblimit) ) longLimit[j][i] = down_sample_sblimit; } for(i=0;i<14;i++) { shortLimit[j][i] = (bandInfo[j].shortIdx[i] - 1) / 18 + 1; if(shortLimit[j][i] > (down_sample_sblimit) ) shortLimit[j][i] = down_sample_sblimit; } } for(i=0;i<5;i++) { for(j=0;j<6;j++) { for(k=0;k<6;k++) { int n = k + j * 6 + i * 36; i_slen2[n] = i|(j<<3)|(k<<6)|(3<<12); } } } for(i=0;i<4;i++) { for(j=0;j<4;j++) { for(k=0;k<4;k++) { int n = k + j * 4 + i * 16; i_slen2[n+180] = i|(j<<3)|(k<<6)|(4<<12); } } } for(i=0;i<4;i++) { for(j=0;j<3;j++) { int n = j + i * 3; i_slen2[n+244] = i|(j<<3) | (5<<12); n_slen2[n+500] = i|(j<<3) | (2<<12) | (1<<15); } } for(i=0;i<5;i++) { for(j=0;j<5;j++) { for(k=0;k<4;k++) { int l; for(l=0;l<4;l++) { int n = l + k * 4 + j * 16 + i * 80; n_slen2[n] = i|(j<<3)|(k<<6)|(l<<9)|(0<<12); } } } } for(i=0;i<5;i++) { for(j=0;j<5;j++) { for(k=0;k<4;k++) { int n = k + j * 4 + i * 20; n_slen2[n+400] = i|(j<<3)|(k<<6)|(1<<12); } } } } /* * read additional side information */ #ifdef MPEG1 static void III_get_side_info_1(struct III_sideinfo *si,int stereo, int ms_stereo,long sfreq,int single) { int ch, gr; int powdiff = (single == 3) ? 4 : 0; si->main_data_begin = getbits(9); if (stereo == 1) si->private_bits = getbits_fast(5); else si->private_bits = getbits_fast(3); for (ch=0; chch[ch].gr[0].scfsi = -1; si->ch[ch].gr[1].scfsi = getbits_fast(4); } for (gr=0; gr<2; gr++) { for (ch=0; chch[ch].gr[gr]); gr_infos->part2_3_length = getbits(12); gr_infos->big_values = getbits_fast(9); if(gr_infos->big_values > 288) { fprintf(stderr,"big_values too large! %i\n",gr_infos->big_values); gr_infos->big_values = 288; } { unsigned int qss = getbits_fast(8); gr_infos->pow2gain = gainpow2+256 - qss + powdiff; } if(ms_stereo) gr_infos->pow2gain += 2; gr_infos->scalefac_compress = getbits_fast(4); /* window-switching flag == 1 for block_Type != 0 .. and block-type == 0 -> win-sw-flag = 0 */ if(get1bit()) { int i; gr_infos->block_type = getbits_fast(2); gr_infos->mixed_block_flag = get1bit(); gr_infos->table_select[0] = getbits_fast(5); gr_infos->table_select[1] = getbits_fast(5); /* * table_select[2] not needed, because there is no region2, * but to satisfy some verifications tools we set it either. */ gr_infos->table_select[2] = 0; for(i=0;i<3;i++) { unsigned int sbg = (getbits_fast(3)<<3); gr_infos->full_gain[i] = gr_infos->pow2gain + sbg; } if(gr_infos->block_type == 0) { fprintf(stderr,"Blocktype == 0 and window-switching == 1 not allowed.\n"); /* error seems to be very good recoverable, so don't exit */ /* exit(1); */ } /* region_count/start parameters are implicit in this case. */ gr_infos->region1start = 36>>1; gr_infos->region2start = 576>>1; } else { int i,r0c,r1c; for (i=0; i<3; i++) gr_infos->table_select[i] = getbits_fast(5); r0c = getbits_fast(4); r1c = getbits_fast(3); gr_infos->region1start = bandInfo[sfreq].longIdx[r0c+1] >> 1 ; gr_infos->region2start = bandInfo[sfreq].longIdx[r0c+1+r1c+1] >> 1; gr_infos->block_type = 0; gr_infos->mixed_block_flag = 0; } gr_infos->preflag = get1bit(); gr_infos->scalefac_scale = get1bit(); gr_infos->count1table_select = get1bit(); } } } #endif /* * Side Info for MPEG 2.0 / LSF */ static void III_get_side_info_2(struct III_sideinfo *si,int stereo, int ms_stereo,long sfreq,int single) { int ch; int powdiff = (single == 3) ? 4 : 0; si->main_data_begin = getbits(8); if (stereo == 1) si->private_bits = get1bit(); else si->private_bits = getbits_fast(2); for (ch=0; chch[ch].gr[0]); unsigned int qss; gr_infos->part2_3_length = getbits(12); gr_infos->big_values = getbits_fast(9); if(gr_infos->big_values > 288) { fprintf(stderr,"big_values too large! %i\n",gr_infos->big_values); gr_infos->big_values = 288; } qss=getbits_fast(8); gr_infos->pow2gain = gainpow2+256 - qss + powdiff; if(ms_stereo) gr_infos->pow2gain += 2; gr_infos->scalefac_compress = getbits(9); /* window-switching flag == 1 for block_Type != 0 .. and block-type == 0 -> win-sw-flag = 0 */ if(get1bit()) { int i; gr_infos->block_type = getbits_fast(2); gr_infos->mixed_block_flag = get1bit(); gr_infos->table_select[0] = getbits_fast(5); gr_infos->table_select[1] = getbits_fast(5); /* * table_select[2] not needed, because there is no region2, * but to satisfy some verifications tools we set it either. */ gr_infos->table_select[2] = 0; for(i=0;i<3;i++) { unsigned int sbg = (getbits_fast(3)<<3); gr_infos->full_gain[i] = gr_infos->pow2gain + sbg; } if(gr_infos->block_type == 0) { fprintf(stderr,"Blocktype == 0 and window-switching == 1 not allowed.\n"); /* error seems to be very good recoverable, so don't exit */ /* exit(1); */ } /* region_count/start parameters are implicit in this case. */ /* check this again! */ if(gr_infos->block_type == 2) { if (sfreq == 8) gr_infos->region1start = 36; else gr_infos->region1start = 36>>1; } else if(sfreq == 8) /* check this for 2.5 and sfreq=8 */ gr_infos->region1start = 108>>1; else gr_infos->region1start = 54>>1; gr_infos->region2start = 576>>1; } else { int i,r0c,r1c; for (i=0; i<3; i++) gr_infos->table_select[i] = getbits_fast(5); r0c = getbits_fast(4); r1c = getbits_fast(3); gr_infos->region1start = bandInfo[sfreq].longIdx[r0c+1] >> 1 ; gr_infos->region2start = bandInfo[sfreq].longIdx[r0c+1+r1c+1] >> 1; gr_infos->block_type = 0; gr_infos->mixed_block_flag = 0; } gr_infos->scalefac_scale = get1bit(); gr_infos->count1table_select = get1bit(); } } /* * read scalefactors */ #ifdef MPEG1 static int III_get_scale_factors_1(int *scf,struct gr_info_s *gr_infos) { static const unsigned char slen[2][16] = { {0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4}, {0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3} }; int numbits; int num0 = slen[0][gr_infos->scalefac_compress]; int num1 = slen[1][gr_infos->scalefac_compress]; if (gr_infos->block_type == 2) { int i=18; numbits = (num0 + num1) * 18; if (gr_infos->mixed_block_flag) { for (i=8;i;i--) *scf++ = getbits_fast(num0); i = 9; numbits -= num0; /* num0 * 17 + num1 * 18 */ } for (;i;i--) *scf++ = getbits_fast(num0); for (i = 18; i; i--) *scf++ = getbits_fast(num1); *scf++ = 0; *scf++ = 0; *scf++ = 0; /* short[13][0..2] = 0 */ } else { int i; int scfsi = gr_infos->scfsi; if(scfsi < 0) { /* scfsi < 0 => granule == 0 */ for(i=11;i;i--) *scf++ = getbits_fast(num0); for(i=10;i;i--) *scf++ = getbits_fast(num1); numbits = (num0 + num1) * 10 + num0; } else { numbits = 0; if(!(scfsi & 0x8)) { for (i=6;i;i--) *scf++ = getbits_fast(num0); numbits += num0 * 6; } else { scf += 6; } if(!(scfsi & 0x4)) { for (i=5;i;i--) *scf++ = getbits_fast(num0); numbits += num0 * 5; } else { scf += 5; } if(!(scfsi & 0x2)) { for(i=5;i;i--) *scf++ = getbits_fast(num1); numbits += num1 * 5; } else { scf += 5; } if(!(scfsi & 0x1)) { for (i=5;i;i--) *scf++ = getbits_fast(num1); numbits += num1 * 5; } else { scf += 5; } } *scf++ = 0; /* no l[21] in original sources */ } return numbits; } #endif static int III_get_scale_factors_2(int *scf,struct gr_info_s *gr_infos,int i_stereo) { unsigned char *pnt; int i,j; unsigned int slen; int n = 0; int numbits = 0; static const unsigned char stab[3][6][4] = { { { 6, 5, 5,5 } , { 6, 5, 7,3 } , { 11,10,0,0} , { 7, 7, 7,0 } , { 6, 6, 6,3 } , { 8, 8,5,0} } , { { 9, 9, 9,9 } , { 9, 9,12,6 } , { 18,18,0,0} , {12,12,12,0 } , {12, 9, 9,6 } , { 15,12,9,0} } , { { 6, 9, 9,9 } , { 6, 9,12,6 } , { 15,18,0,0} , { 6,15,12,0 } , { 6,12, 9,6 } , { 6,18,9,0} } }; if(i_stereo) /* i_stereo AND second channel -> do_layer3() checks this */ slen = i_slen2[gr_infos->scalefac_compress>>1]; else slen = n_slen2[gr_infos->scalefac_compress]; gr_infos->preflag = (slen>>15) & 0x1; n = 0; if( gr_infos->block_type == 2 ) { n++; if(gr_infos->mixed_block_flag) n++; } pnt = (unsigned char *)stab[n][(slen>>12)&0x7]; for(i=0;i<4;i++) { int num = slen & 0x7; slen >>= 3; if(num) { for(j=0;j<(int)(pnt[i]);j++) *scf++ = getbits_fast(num); numbits += pnt[i] * num; } else { for(j=0;j<(int)(pnt[i]);j++) *scf++ = 0; } } n = (n << 1) + 1; for(i=0;iscalefac_scale; real *xrpnt = (real *) xr; int l[3],l3; int part2remain = gr_infos->part2_3_length - part2bits; int *me; { int i; for(i=(&xr[SBLIMIT][0]-xrpnt)>>1;i>0;i--) { *xrpnt++ = 0.0; *xrpnt++ = 0.0; } xrpnt = (real *) xr; } { int bv = gr_infos->big_values; int region1 = gr_infos->region1start; int region2 = gr_infos->region2start; l3 = ((576>>1)-bv)>>1; /* * we may lose the 'odd' bit here !! * check this later again */ if(bv <= region1) { l[0] = bv; l[1] = 0; l[2] = 0; } else { l[0] = region1; if(bv <= region2) { l[1] = bv - l[0]; l[2] = 0; } else { l[1] = region2 - l[0]; l[2] = bv - region2; } } } /* MDH crash fix */ { int i; for (i = 0; i < 3; i++) { if (l[i] < 0) { fprintf(stderr, "mpg123: Bogus region length (%d)\n", l[i]); l[i] = 0; } } } /* end MDH crash fix */ if(gr_infos->block_type == 2) { /* * decoding with short or mixed mode BandIndex table */ int i,max[4]; int step=0,lwin=0,cb=0; register real v = 0.0; register int *m,mc; if(gr_infos->mixed_block_flag) { max[3] = -1; max[0] = max[1] = max[2] = 2; m = map[sfreq][0]; me = mapend[sfreq][0]; } else { max[0] = max[1] = max[2] = max[3] = -1; /* max[3] not really needed in this case */ m = map[sfreq][1]; me = mapend[sfreq][1]; } mc = 0; for(i=0;i<2;i++) { int lp = l[i]; struct newhuff *h = (struct newhuff *)(ht+gr_infos->table_select[i]); for(;lp;lp--,mc--) { register int x,y; if( (!mc) ) { mc = *m++; xrpnt = ((real *) xr) + (*m++); lwin = *m++; cb = *m++; if(lwin == 3) { v = gr_infos->pow2gain[(*scf++) << shift]; step = 1; } else { v = gr_infos->full_gain[lwin][(*scf++) << shift]; step = 3; } } { register short *val = (short *)h->table; while((y=*val++)<0) { if (get1bit()) val -= y; part2remain--; } x = y >> 4; y &= 0xf; } if(x == 15) { max[lwin] = cb; part2remain -= h->linbits+1; x += getbits((int)h->linbits); if(get1bit()) *xrpnt = -ispow[x] * v; else *xrpnt = ispow[x] * v; } else if(x) { max[lwin] = cb; if(get1bit()) *xrpnt = -ispow[x] * v; else *xrpnt = ispow[x] * v; part2remain--; } else *xrpnt = 0.0; xrpnt += step; if(y == 15) { max[lwin] = cb; part2remain -= h->linbits+1; y += getbits((int)h->linbits); if(get1bit()) *xrpnt = -ispow[y] * v; else *xrpnt = ispow[y] * v; } else if(y) { max[lwin] = cb; if(get1bit()) *xrpnt = -ispow[y] * v; else *xrpnt = ispow[y] * v; part2remain--; } else *xrpnt = 0.0; xrpnt += step; } } for(;l3 && (part2remain > 0);l3--) { struct newhuff *h = (struct newhuff *)(htc+gr_infos->count1table_select); register short *val = (short *)h->table,a; while((a=*val++)<0) { part2remain--; if(part2remain < 0) { part2remain++; a = 0; break; } if (get1bit()) val -= a; } for(i=0;i<4;i++) { if(!(i & 1)) { if(!mc) { mc = *m++; xrpnt = ((real *) xr) + (*m++); lwin = *m++; cb = *m++; if(lwin == 3) { v = gr_infos->pow2gain[(*scf++) << shift]; step = 1; } else { v = gr_infos->full_gain[lwin][(*scf++) << shift]; step = 3; } } mc--; } if( (a & (0x8>>i)) ) { max[lwin] = cb; part2remain--; if(part2remain < 0) { part2remain++; break; } if(get1bit()) *xrpnt = -v; else *xrpnt = v; } else *xrpnt = 0.0; xrpnt += step; } } while( m < me ) { if(!mc) { mc = *m++; xrpnt = ((real *) xr) + *m++; if( (*m++) == 3) step = 1; else step = 3; m++; /* cb */ } mc--; *xrpnt = 0.0; xrpnt += step; *xrpnt = 0.0; xrpnt += step; /* we could add a little opt. here: * if we finished a band for window 3 or a long band * further bands could copied in a simple loop without a * special 'map' decoding */ } gr_infos->maxband[0] = max[0]+1; gr_infos->maxband[1] = max[1]+1; gr_infos->maxband[2] = max[2]+1; gr_infos->maxbandl = max[3]+1; { int rmax = max[0] > max[1] ? max[0] : max[1]; rmax = (rmax > max[2] ? rmax : max[2]) + 1; gr_infos->maxb = rmax ? shortLimit[sfreq][rmax] : longLimit[sfreq][max[3]+1]; } } else { /* * decoding with 'long' BandIndex table (block_type != 2) */ int *pretab = (int *)(gr_infos->preflag ? pretab1 : pretab2); int i,max = -1; int cb = 0; register int *m = map[sfreq][2]; register real v = 0.0; register int mc = 0; #if 0 me = mapend[sfreq][2]; #endif /* * long hash table values */ for(i=0;i<3;i++) { int lp = l[i]; struct newhuff *h = (struct newhuff *)(ht+gr_infos->table_select[i]); for(;lp;lp--,mc--) { int x,y; if(!mc) { mc = *m++; v = gr_infos->pow2gain[((*scf++) + (*pretab++)) << shift]; cb = *m++; } { register short *val = (short *)h->table; while((y=*val++)<0) { if (get1bit()) val -= y; part2remain--; } x = y >> 4; y &= 0xf; } if (x == 15) { max = cb; part2remain -= h->linbits+1; x += getbits((int)h->linbits); if(get1bit()) *xrpnt++ = -ispow[x] * v; else *xrpnt++ = ispow[x] * v; } else if(x) { max = cb; if(get1bit()) *xrpnt++ = -ispow[x] * v; else *xrpnt++ = ispow[x] * v; part2remain--; } else *xrpnt++ = 0.0; if (y == 15) { max = cb; part2remain -= h->linbits+1; y += getbits((int)h->linbits); if(get1bit()) *xrpnt++ = -ispow[y] * v; else *xrpnt++ = ispow[y] * v; } else if(y) { max = cb; if(get1bit()) *xrpnt++ = -ispow[y] * v; else *xrpnt++ = ispow[y] * v; part2remain--; } else *xrpnt++ = 0.0; } } /* * short (count1table) values */ for(;l3 && (part2remain > 0);l3--) { struct newhuff *h = (struct newhuff *)(htc+gr_infos->count1table_select); register short *val = (short *)h->table,a; while((a=*val++)<0) { part2remain--; if(part2remain < 0) { part2remain++; a = 0; break; } if (get1bit()) val -= a; } for(i=0;i<4;i++) { if(!(i & 1)) { if(!mc) { mc = *m++; cb = *m++; v = gr_infos->pow2gain[((*scf++) + (*pretab++)) << shift]; } mc--; } if ( (a & (0x8>>i)) ) { max = cb; part2remain--; if(part2remain < 0) { part2remain++; break; } if(get1bit()) *xrpnt++ = -v; else *xrpnt++ = v; } else *xrpnt++ = 0.0; } } /* * zero part */ for(i=(&xr[SBLIMIT][0]-xrpnt)>>1;i;i--) { *xrpnt++ = 0.0; *xrpnt++ = 0.0; } gr_infos->maxbandl = max+1; gr_infos->maxb = longLimit[sfreq][gr_infos->maxbandl]; } while( part2remain > 16 ) { getbits(16); /* Dismiss stuffing Bits */ part2remain -= 16; } if(part2remain > 0) getbits(part2remain); else if(part2remain < 0) { fprintf(stderr,"mpg123: Can't rewind stream by %d bits!\n",-part2remain); return 1; /* -> error */ } return 0; } /* * III_stereo: calculate real channel values for Joint-I-Stereo-mode */ static void III_i_stereo(real xr_buf[2][SBLIMIT][SSLIMIT],int *scalefac, struct gr_info_s *gr_infos,int sfreq,int ms_stereo,int lsf) { real (*xr)[SBLIMIT*SSLIMIT] = (real (*)[SBLIMIT*SSLIMIT] ) xr_buf; struct bandInfoStruct *bi = (struct bandInfoStruct *)&bandInfo[sfreq]; real *tabl1,*tabl2; if(lsf) { int p = gr_infos->scalefac_compress & 0x1; if(ms_stereo) { tabl1 = pow1_2[p]; tabl2 = pow2_2[p]; } else { tabl1 = pow1_1[p]; tabl2 = pow2_1[p]; } } else { if(ms_stereo) { tabl1 = tan1_2; tabl2 = tan2_2; } else { tabl1 = tan1_1; tabl2 = tan2_1; } } if (gr_infos->block_type == 2) { int lwin,do_l = 0; if( gr_infos->mixed_block_flag ) do_l = 1; for (lwin=0;lwin<3;lwin++) /* process each window */ { /* get first band with zero values */ int is_p,sb,idx,sfb = gr_infos->maxband[lwin]; /* sfb is minimal 3 for mixed mode */ if(sfb > 3) do_l = 0; for(;sfb<12;sfb++) { is_p = scalefac[sfb*3+lwin-gr_infos->mixed_block_flag]; /* scale: 0-15 */ if(is_p != 7) { real t1,t2; sb = bi->shortDiff[sfb]; idx = bi->shortIdx[sfb] + lwin; t1 = tabl1[is_p]; t2 = tabl2[is_p]; for (; sb > 0; sb--,idx+=3) { real v = xr[0][idx]; xr[0][idx] = v * t1; xr[1][idx] = v * t2; } } } #if 1 /* in the original: copy 10 to 11 , here: copy 11 to 12 maybe still wrong??? (copy 12 to 13?) */ is_p = scalefac[11*3+lwin-gr_infos->mixed_block_flag]; /* scale: 0-15 */ sb = bi->shortDiff[12]; idx = bi->shortIdx[12] + lwin; #else is_p = scalefac[10*3+lwin-gr_infos->mixed_block_flag]; /* scale: 0-15 */ sb = bi->shortDiff[11]; idx = bi->shortIdx[11] + lwin; #endif if(is_p != 7) { real t1,t2; t1 = tabl1[is_p]; t2 = tabl2[is_p]; for ( ; sb > 0; sb--,idx+=3 ) { real v = xr[0][idx]; xr[0][idx] = v * t1; xr[1][idx] = v * t2; } } } /* end for(lwin; .. ; . ) */ if (do_l) { /* also check l-part, if ALL bands in the three windows are 'empty' * and mode = mixed_mode */ int sfb = gr_infos->maxbandl; int idx = bi->longIdx[sfb]; for ( ; sfb<8; sfb++ ) { int sb = bi->longDiff[sfb]; int is_p = scalefac[sfb]; /* scale: 0-15 */ if(is_p != 7) { real t1,t2; t1 = tabl1[is_p]; t2 = tabl2[is_p]; for ( ; sb > 0; sb--,idx++) { real v = xr[0][idx]; xr[0][idx] = v * t1; xr[1][idx] = v * t2; } } else idx += sb; } } } else /* ((gr_infos->block_type != 2)) */ { int sfb = gr_infos->maxbandl; int is_p,idx = bi->longIdx[sfb]; for ( ; sfb<21; sfb++) { int sb = bi->longDiff[sfb]; is_p = scalefac[sfb]; /* scale: 0-15 */ if(is_p != 7) { real t1,t2; t1 = tabl1[is_p]; t2 = tabl2[is_p]; for ( ; sb > 0; sb--,idx++) { real v = xr[0][idx]; xr[0][idx] = v * t1; xr[1][idx] = v * t2; } } else idx += sb; } is_p = scalefac[20]; /* copy l-band 20 to l-band 21 */ if(is_p != 7) { int sb; real t1 = tabl1[is_p],t2 = tabl2[is_p]; for ( sb = bi->longDiff[21]; sb > 0; sb--,idx++ ) { real v = xr[0][idx]; xr[0][idx] = v * t1; xr[1][idx] = v * t2; } } } /* ... */ } static void III_antialias(real xr[SBLIMIT][SSLIMIT],struct gr_info_s *gr_infos) { int sblim; if(gr_infos->block_type == 2) { if(!gr_infos->mixed_block_flag) return; sblim = 1; } else { sblim = gr_infos->maxb-1; } /* 31 alias-reduction operations between each pair of sub-bands */ /* with 8 butterflies between each pair */ { int sb; real *xr1=(real *) xr[1]; for(sb=sblim;sb;sb--,xr1+=10) { int ss; real *cs=aa_cs,*ca=aa_ca; real *xr2 = xr1; for(ss=7;ss>=0;ss--) { /* upper and lower butterfly inputs */ register real bu = *--xr2,bd = *xr1; *xr2 = (bu * (*cs) ) - (bd * (*ca) ); *xr1++ = (bd * (*cs++) ) + (bu * (*ca++) ); } } } } /* DCT insipired by Jeff Tsay's DCT from the maplay package this is an optimized version with manual unroll. References: [1] S. Winograd: "On Computing the Discrete Fourier Transform", Mathematics of Computation, Volume 32, Number 141, January 1978, Pages 175-199 */ static void dct36(real *inbuf,real *o1,real *o2,real *wintab,real *tsbuf) { { register real *in = inbuf; in[17]+=in[16]; in[16]+=in[15]; in[15]+=in[14]; in[14]+=in[13]; in[13]+=in[12]; in[12]+=in[11]; in[11]+=in[10]; in[10]+=in[9]; in[9] +=in[8]; in[8] +=in[7]; in[7] +=in[6]; in[6] +=in[5]; in[5] +=in[4]; in[4] +=in[3]; in[3] +=in[2]; in[2] +=in[1]; in[1] +=in[0]; in[17]+=in[15]; in[15]+=in[13]; in[13]+=in[11]; in[11]+=in[9]; in[9] +=in[7]; in[7] +=in[5]; in[5] +=in[3]; in[3] +=in[1]; { #define MACRO0(v) { \ real tmp; \ out2[9+(v)] = (tmp = sum0 + sum1) * w[27+(v)]; \ out2[8-(v)] = tmp * w[26-(v)]; } \ sum0 -= sum1; \ ts[SBLIMIT*(8-(v))] = out1[8-(v)] + sum0 * w[8-(v)]; \ ts[SBLIMIT*(9+(v))] = out1[9+(v)] + sum0 * w[9+(v)]; #define MACRO1(v) { \ real sum0,sum1; \ sum0 = tmp1a + tmp2a; \ sum1 = (tmp1b + tmp2b) * tfcos36[(v)]; \ MACRO0(v); } #define MACRO2(v) { \ real sum0,sum1; \ sum0 = tmp2a - tmp1a; \ sum1 = (tmp2b - tmp1b) * tfcos36[(v)]; \ MACRO0(v); } register const real *c = COS9; register real *out2 = o2; register real *w = wintab; register real *out1 = o1; register real *ts = tsbuf; real ta33,ta66,tb33,tb66; ta33 = in[2*3+0] * c[3]; ta66 = in[2*6+0] * c[6]; tb33 = in[2*3+1] * c[3]; tb66 = in[2*6+1] * c[6]; { real tmp1a,tmp2a,tmp1b,tmp2b; tmp1a = in[2*1+0] * c[1] + ta33 + in[2*5+0] * c[5] + in[2*7+0] * c[7]; tmp1b = in[2*1+1] * c[1] + tb33 + in[2*5+1] * c[5] + in[2*7+1] * c[7]; tmp2a = in[2*0+0] + in[2*2+0] * c[2] + in[2*4+0] * c[4] + ta66 + in[2*8+0] * c[8]; tmp2b = in[2*0+1] + in[2*2+1] * c[2] + in[2*4+1] * c[4] + tb66 + in[2*8+1] * c[8]; MACRO1(0); MACRO2(8); } { real tmp1a,tmp2a,tmp1b,tmp2b; tmp1a = ( in[2*1+0] - in[2*5+0] - in[2*7+0] ) * c[3]; tmp1b = ( in[2*1+1] - in[2*5+1] - in[2*7+1] ) * c[3]; tmp2a = ( in[2*2+0] - in[2*4+0] - in[2*8+0] ) * c[6] - in[2*6+0] + in[2*0+0]; tmp2b = ( in[2*2+1] - in[2*4+1] - in[2*8+1] ) * c[6] - in[2*6+1] + in[2*0+1]; MACRO1(1); MACRO2(7); } { real tmp1a,tmp2a,tmp1b,tmp2b; tmp1a = in[2*1+0] * c[5] - ta33 - in[2*5+0] * c[7] + in[2*7+0] * c[1]; tmp1b = in[2*1+1] * c[5] - tb33 - in[2*5+1] * c[7] + in[2*7+1] * c[1]; tmp2a = in[2*0+0] - in[2*2+0] * c[8] - in[2*4+0] * c[2] + ta66 + in[2*8+0] * c[4]; tmp2b = in[2*0+1] - in[2*2+1] * c[8] - in[2*4+1] * c[2] + tb66 + in[2*8+1] * c[4]; MACRO1(2); MACRO2(6); } { real tmp1a,tmp2a,tmp1b,tmp2b; tmp1a = in[2*1+0] * c[7] - ta33 + in[2*5+0] * c[1] - in[2*7+0] * c[5]; tmp1b = in[2*1+1] * c[7] - tb33 + in[2*5+1] * c[1] - in[2*7+1] * c[5]; tmp2a = in[2*0+0] - in[2*2+0] * c[4] + in[2*4+0] * c[8] + ta66 - in[2*8+0] * c[2]; tmp2b = in[2*0+1] - in[2*2+1] * c[4] + in[2*4+1] * c[8] + tb66 - in[2*8+1] * c[2]; MACRO1(3); MACRO2(5); } { real sum0,sum1; sum0 = in[2*0+0] - in[2*2+0] + in[2*4+0] - in[2*6+0] + in[2*8+0]; sum1 = (in[2*0+1] - in[2*2+1] + in[2*4+1] - in[2*6+1] + in[2*8+1] ) * tfcos36[4]; MACRO0(4); } } } } /* * new DCT12 */ static void dct12(real *in,real *rawout1,real *rawout2,register real *wi,register real *ts) { #define DCT12_PART1 \ in5 = in[5*3]; \ in5 += (in4 = in[4*3]); \ in4 += (in3 = in[3*3]); \ in3 += (in2 = in[2*3]); \ in2 += (in1 = in[1*3]); \ in1 += (in0 = in[0*3]); \ \ in5 += in3; in3 += in1; \ \ in2 *= COS6_1; \ in3 *= COS6_1; \ #define DCT12_PART2 \ in0 += in4 * COS6_2; \ \ in4 = in0 + in2; \ in0 -= in2; \ \ in1 += in5 * COS6_2; \ \ in5 = (in1 + in3) * tfcos12[0]; \ in1 = (in1 - in3) * tfcos12[2]; \ \ in3 = in4 + in5; \ in4 -= in5; \ \ in2 = in0 + in1; \ in0 -= in1; { real in0,in1,in2,in3,in4,in5; register real *out1 = rawout1; ts[SBLIMIT*0] = out1[0]; ts[SBLIMIT*1] = out1[1]; ts[SBLIMIT*2] = out1[2]; ts[SBLIMIT*3] = out1[3]; ts[SBLIMIT*4] = out1[4]; ts[SBLIMIT*5] = out1[5]; DCT12_PART1 { real tmp0,tmp1 = (in0 - in4); { real tmp2 = (in1 - in5) * tfcos12[1]; tmp0 = tmp1 + tmp2; tmp1 -= tmp2; } ts[(17-1)*SBLIMIT] = out1[17-1] + tmp0 * wi[11-1]; ts[(12+1)*SBLIMIT] = out1[12+1] + tmp0 * wi[6+1]; ts[(6 +1)*SBLIMIT] = out1[6 +1] + tmp1 * wi[1]; ts[(11-1)*SBLIMIT] = out1[11-1] + tmp1 * wi[5-1]; } DCT12_PART2 ts[(17-0)*SBLIMIT] = out1[17-0] + in2 * wi[11-0]; ts[(12+0)*SBLIMIT] = out1[12+0] + in2 * wi[6+0]; ts[(12+2)*SBLIMIT] = out1[12+2] + in3 * wi[6+2]; ts[(17-2)*SBLIMIT] = out1[17-2] + in3 * wi[11-2]; ts[(6+0)*SBLIMIT] = out1[6+0] + in0 * wi[0]; ts[(11-0)*SBLIMIT] = out1[11-0] + in0 * wi[5-0]; ts[(6+2)*SBLIMIT] = out1[6+2] + in4 * wi[2]; ts[(11-2)*SBLIMIT] = out1[11-2] + in4 * wi[5-2]; } in++; { real in0,in1,in2,in3,in4,in5; register real *out2 = rawout2; DCT12_PART1 { real tmp0,tmp1 = (in0 - in4); { real tmp2 = (in1 - in5) * tfcos12[1]; tmp0 = tmp1 + tmp2; tmp1 -= tmp2; } out2[5-1] = tmp0 * wi[11-1]; out2[0+1] = tmp0 * wi[6+1]; ts[(12+1)*SBLIMIT] += tmp1 * wi[1]; ts[(17-1)*SBLIMIT] += tmp1 * wi[5-1]; } DCT12_PART2 out2[5-0] = in2 * wi[11-0]; out2[0+0] = in2 * wi[6+0]; out2[0+2] = in3 * wi[6+2]; out2[5-2] = in3 * wi[11-2]; ts[(12+0)*SBLIMIT] += in0 * wi[0]; ts[(17-0)*SBLIMIT] += in0 * wi[5-0]; ts[(12+2)*SBLIMIT] += in4 * wi[2]; ts[(17-2)*SBLIMIT] += in4 * wi[5-2]; } in++; { real in0,in1,in2,in3,in4,in5; register real *out2 = rawout2; out2[12]=out2[13]=out2[14]=out2[15]=out2[16]=out2[17]=0.0; DCT12_PART1 { real tmp0,tmp1 = (in0 - in4); { real tmp2 = (in1 - in5) * tfcos12[1]; tmp0 = tmp1 + tmp2; tmp1 -= tmp2; } out2[11-1] = tmp0 * wi[11-1]; out2[6 +1] = tmp0 * wi[6+1]; out2[0+1] += tmp1 * wi[1]; out2[5-1] += tmp1 * wi[5-1]; } DCT12_PART2 out2[11-0] = in2 * wi[11-0]; out2[6 +0] = in2 * wi[6+0]; out2[6 +2] = in3 * wi[6+2]; out2[11-2] = in3 * wi[11-2]; out2[0+0] += in0 * wi[0]; out2[5-0] += in0 * wi[5-0]; out2[0+2] += in4 * wi[2]; out2[5-2] += in4 * wi[5-2]; } } /* * III_hybrid */ static void III_hybrid( PMPSTR mp, real fsIn[SBLIMIT][SSLIMIT],real tsOut[SSLIMIT][SBLIMIT], int ch,struct gr_info_s *gr_infos) { real *tspnt = (real *) tsOut; real (*block)[2][SBLIMIT*SSLIMIT] = mp->hybrid_block; int *blc = mp->hybrid_blc; real *rawout1,*rawout2; int bt; int sb = 0; { int b = blc[ch]; rawout1=block[b][ch]; b=-b+1; rawout2=block[b][ch]; blc[ch] = b; } if(gr_infos->mixed_block_flag) { sb = 2; dct36(fsIn[0],rawout1,rawout2,win[0],tspnt); dct36(fsIn[1],rawout1+18,rawout2+18,win1[0],tspnt+1); rawout1 += 36; rawout2 += 36; tspnt += 2; } bt = gr_infos->block_type; if(bt == 2) { for (; sb<(int)gr_infos->maxb; sb+=2,tspnt+=2,rawout1+=36,rawout2+=36) { dct12(fsIn[sb],rawout1,rawout2,win[2],tspnt); dct12(fsIn[sb+1],rawout1+18,rawout2+18,win1[2],tspnt+1); } } else { for (; sb<(int)gr_infos->maxb; sb+=2,tspnt+=2,rawout1+=36,rawout2+=36) { dct36(fsIn[sb],rawout1,rawout2,win[bt],tspnt); dct36(fsIn[sb+1],rawout1+18,rawout2+18,win1[bt],tspnt+1); } } for(;sbstereo; int single = fr->single; int ms_stereo; int sfreq = fr->sampling_frequency; int granules; int ch,gr,databits; if(stereo == 1) { /* stream is mono */ single = 0; } if(fr->mode == MPG_MD_JOINT_STEREO) { ms_stereo = fr->mode_ext & 0x2; } else ms_stereo = 0; if(fr->lsf) { granules = 1; III_get_side_info_2(&sideinfo,stereo,ms_stereo,sfreq,single); } else { granules = 2; #ifdef MPEG1 III_get_side_info_1(&sideinfo,stereo,ms_stereo,sfreq,single); #else fprintf(stderr,"Not supported\n"); #endif } databits=0; for (gr=0 ; gr < granules ; ++gr) { for (ch=0; ch < stereo ; ++ch) { struct gr_info_s *gr_infos = &(sideinfo.ch[ch].gr[gr]); databits += gr_infos->part2_3_length; } } return databits-8*sideinfo.main_data_begin; } int do_layer3( PMPSTR mp,unsigned char *pcm_sample,int *pcm_point, int (*synth_1to1_mono_ptr)(PMPSTR,real *,unsigned char *,int *), int (*synth_1to1_ptr)(PMPSTR,real *,int,unsigned char *, int *) ) { int gr, ch, ss,clip=0; int scalefacs[2][39]; /* max 39 for short[13][3] mode, mixed: 38, long: 22 */ /* struct III_sideinfo sideinfo; */ struct frame *fr=&(mp->fr); int stereo = fr->stereo; int single = fr->single; int ms_stereo,i_stereo; int sfreq = fr->sampling_frequency; int stereo1,granules; if(set_pointer(mp, (int)sideinfo.main_data_begin) == MP3_ERR) return 0; if(stereo == 1) { /* stream is mono */ stereo1 = 1; single = 0; } else if(single >= 0) /* stream is stereo, but force to mono */ stereo1 = 1; else stereo1 = 2; if(fr->mode == MPG_MD_JOINT_STEREO) { ms_stereo = fr->mode_ext & 0x2; i_stereo = fr->mode_ext & 0x1; } else ms_stereo = i_stereo = 0; if(fr->lsf) { granules = 1; } else { granules = 2; } for (gr=0;grlsf) part2bits = III_get_scale_factors_2(scalefacs[0],gr_infos,0); else { #ifdef MPEG1 part2bits = III_get_scale_factors_1(scalefacs[0],gr_infos); #else fprintf(stderr,"Not supported\n"); #endif } if(III_dequantize_sample(hybridIn[0], scalefacs[0],gr_infos,sfreq,part2bits)) return clip; } if(stereo == 2) { struct gr_info_s *gr_infos = &(sideinfo.ch[1].gr[gr]); long part2bits; if(fr->lsf) part2bits = III_get_scale_factors_2(scalefacs[1],gr_infos,i_stereo); else { #ifdef MPEG1 part2bits = III_get_scale_factors_1(scalefacs[1],gr_infos); #else fprintf(stderr,"Not supported\n"); #endif } if(III_dequantize_sample(hybridIn[1],scalefacs[1],gr_infos,sfreq,part2bits)) return clip; if(ms_stereo) { int i; for(i=0;ilsf); if(ms_stereo || i_stereo || (single == 3) ) { if(gr_infos->maxb > sideinfo.ch[0].gr[gr].maxb) sideinfo.ch[0].gr[gr].maxb = gr_infos->maxb; else gr_infos->maxb = sideinfo.ch[0].gr[gr].maxb; } switch(single) { case 3: { register int i; register real *in0 = (real *) hybridIn[0],*in1 = (real *) hybridIn[1]; for(i=0;i<(int)(SSLIMIT*gr_infos->maxb);i++,in0++) *in0 = (*in0 + *in1++); /* *0.5 done by pow-scale */ } break; case 1: { register int i; register real *in0 = (real *) hybridIn[0],*in1 = (real *) hybridIn[1]; for(i=0;i<(int)(SSLIMIT*gr_infos->maxb);i++) *in0++ = *in1++; } break; } } for(ch=0;ch= 0) { clip += (*synth_1to1_mono_ptr)(mp, hybridOut[0][ss],pcm_sample,pcm_point); } else { int p1 = *pcm_point; clip += (*synth_1to1_ptr)(mp, hybridOut[0][ss],0,pcm_sample,&p1); clip += (*synth_1to1_ptr)(mp, hybridOut[1][ss],1,pcm_sample,pcm_point); } } } return clip; } lastfm-player-1.1.4_p1910/src/mpglib/layer3.h0000644000175000001440000000215010352604465017551 0ustar davidusers/* ** Copyright (C) 2000 Albert L. Faber ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef LAYER3_H_INCLUDED #define LAYER3_H_INCLUDED void init_layer3(int); int do_layer3_sideinfo(struct frame *fr); int do_layer3( PMPSTR mp,unsigned char *pcm_sample,int *pcm_point, int (*synth_1to1_mono_ptr)(PMPSTR,real *,unsigned char *,int *), int (*synth_1to1_ptr)(PMPSTR,real *,int,unsigned char *, int *) ); #endif lastfm-player-1.1.4_p1910/src/mpglib/machine.h0000644000175000001440000000605310352604465017764 0ustar davidusers/* * Machine dependent defines/includes for LAME. * * Copyright (c) 1999 A.L. Faber * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef LAME_MACHINE_H #define LAME_MACHINE_H #include # include # include #if defined(__riscos__) && defined(FPA10) # include "ymath.h" #else # include #endif #include #ifdef HAVE_ERRNO_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #if defined(macintosh) # include # include #else # include # include #endif /* * 3 different types of pow() functions: * - table lookup * - pow() * - exp() on some machines this is claimed to be faster than pow() */ #define POW20(x) (assert(-Q_MAX2 <= x && x < Q_MAX), pow20[x+Q_MAX2]) /*#define POW20(x) pow(2.0,((double)(x)-210)*.25) */ /*#define POW20(x) exp( ((double)(x)-210)*(.25*LOG2) ) */ #define IPOW20(x) (assert(0 <= x && x < Q_MAX), ipow20[x]) /*#define IPOW20(x) exp( -((double)(x)-210)*.1875*LOG2 ) */ /*#define IPOW20(x) pow(2.0,-((double)(x)-210)*.1875) */ #define IIPOW20(x) (assert(0 <= x && x < Q_MAX2), iipow20[x]) /* in case this is used without configure */ #ifndef inline # define inline #endif #if defined(_MSC_VER) # undef inline # define inline _inline #elif defined(__SASC) || defined(__GNUC__) || defined(__ICC) || defined(__ECC) /* if __GNUC__ we always want to inline, not only if the user requests it */ # undef inline # define inline __inline #endif #if defined(_MSC_VER) # pragma warning( disable : 4244 ) /*# pragma warning( disable : 4305 ) */ #endif /* * FLOAT for variables which require at least 32 bits * FLOAT8 for variables which require at least 64 bits * * On some machines, 64 bit will be faster than 32 bit. Also, some math * routines require 64 bit float, so setting FLOAT=float will result in a * lot of conversions. */ typedef float FLOAT; #ifndef FLOAT8 /* NOTE: RH: 7/00: if FLOAT8=float, it breaks resampling and VBR code */ typedef double FLOAT8; # ifdef DBL_MAX # define FLOAT8_MAX DBL_MAX # else # define FLOAT8_MAX 1e99 /* approx */ # endif #else # ifdef FLT_MAX # define FLOAT8_MAX FLT_MAX # else # define FLOAT8_MAX 1e37 /* approx */ # endif #endif /* sample_t must be floating point, at least 32 bits */ typedef FLOAT sample_t; typedef sample_t stereo_t [2]; #endif /* end of machine.h */ lastfm-player-1.1.4_p1910/src/aboutdialog.h0000644000175000001440000000322210352604467017375 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include "ui_about.h" class AboutDialog : public QDialog { Q_OBJECT public: AboutDialog( QWidget *parent = 0, QString dataPath = 0 ); private: Ui::AboutDialog ui; }; lastfm-player-1.1.4_p1910/src/stationdialog.cpp0000644000175000001440000003752110352604467020310 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include "stationdialog.h" #include "playlist.h" #include "settings.h" #include "webserviceconnector.h" StationDialog::StationDialog( QWidget *parent, WebserviceConnector *webserviceConnector ) : QDialog( parent ) , ws( webserviceConnector ) { if ( parent == 0 ) return; ui.setupUi( this ); menu = new QMenu( this ); deleteAction = new QAction( tr( "&Delete item" ), this ); resetAction = new QAction( tr( "&Reset counter" ), this ); clearAction = new QAction( tr( "&Clear history" ), this ); menu->addAction( deleteAction ); menu->addAction( resetAction ); menu->addSeparator(); menu->addAction( clearAction ); QStringList headerLabels; headerLabels.append( "Station" ); headerLabels.append( "Listens" ); ui.historyTree->setHeaderLabels( headerLabels ); ui.historyTree->setRootIsDecorated( true ); ui.historyTree->header()->resizeSection( 0, 290 ); ui.listenButton->setEnabled( false ); ui.resultBox->setEnabled( false ); ui.resultLabel->setVisible( true ); ui.resultList->setVisible( false ); connect( deleteAction, SIGNAL( triggered() ), this, SLOT( deleteItem() ) ); connect( resetAction, SIGNAL( triggered() ), this, SLOT( resetItem() ) ); connect( clearAction, SIGNAL( triggered() ), this, SLOT( clearItems() ) ); connect( ui.historyTree, SIGNAL( itemDoubleClicked( QTreeWidgetItem*, int ) ), this, SLOT( changeStationThreaded( QTreeWidgetItem*, int ) ) ); connect( ui.historyTree, SIGNAL( customContextMenuRequested ( QPoint ) ), this, SLOT( historyMenu( QPoint ) ) ); connect( ui.searchCombo, SIGNAL( activated( int ) ), this, SLOT( searchModeChanged( int ) ) ); connect( ui.listenButton, SIGNAL( clicked() ), this, SLOT( searchResultStation() ) ); connect( ui.searchButton, SIGNAL( clicked() ), this, SLOT( search() ) ); connect( ui.websiteButton, SIGNAL( clicked() ), this, SLOT( websiteSearch() ) ); connect( ui.playlistRemoveButton, SIGNAL( clicked() ), this, SLOT( playlistRemove() ) ); connect( ui.playlistClearButton, SIGNAL( clicked() ), this, SLOT( playlistClear() ) ); connect( ui.tagsList, SIGNAL( itemDoubleClicked( QListWidgetItem* ) ), this, SLOT( changeStationThreaded( QListWidgetItem* ) ) ); connect( ui.personalButton, SIGNAL( clicked() ), this, SLOT( personalStation() ) ); connect( ui.neighbourButton, SIGNAL( clicked() ), this, SLOT( neighbourStation() ) ); connect( ui.lovedButton, SIGNAL( clicked() ), this, SLOT( lovedStation() ) ); connect( ws, SIGNAL( searchArtistResult( QString, bool, QStringList ) ), this, SLOT( searchArtistResult( QString, bool, QStringList ) ) ); connect( ws, SIGNAL( searchTagResult( QStringList ) ), this, SLOT( searchTagResult( QStringList ) ) ); connect( ws, SIGNAL( playlistResult( Playlist ) ), this, SLOT( playlistResult( Playlist ) ) ); connect( ws, SIGNAL( changeStationPrepared() ), this, SLOT( changeStationPrepared() ) ); connect( ws, SIGNAL( tagsForUserResult( QStringList ) ), this, SLOT( tagsForUserResult( QStringList ) ) ); } void StationDialog::updateInformation() { updateHistory(); ui.personalButton->setEnabled( ws->isSubscriber() ); ui.lovedButton->setEnabled( ws->isSubscriber() ); ui.tagsList->clear(); if ( ws->isSubscriber() ) ws->tagsForUser( Settings::instance()->username() ); } void StationDialog::updateHistory() { ui.historyTree->clear(); QTreeWidgetItem *personal = new QTreeWidgetItem( ui.historyTree, 0 ); personal->setText( 0, "Personal Stations" ); QTreeWidgetItem *profile = new QTreeWidgetItem( ui.historyTree, 0 ); profile->setText( 0, "Neighbour Stations" ); QTreeWidgetItem *loved = new QTreeWidgetItem( ui.historyTree, 0 ); loved->setText( 0, "Loved Tracks Stations" ); QTreeWidgetItem *groups = new QTreeWidgetItem( ui.historyTree, 0 ); groups->setText( 0, "Group Stations" ); QTreeWidgetItem *similar = new QTreeWidgetItem( ui.historyTree, 0 ); similar->setText( 0, "Similar Artist Stations" ); QTreeWidgetItem *tags = new QTreeWidgetItem( ui.historyTree, 0 ); tags->setText( 0, "Global Tag Stations" ); QTreeWidgetItem *usertags = new QTreeWidgetItem( ui.historyTree, 0 ); usertags->setText( 0, "Personal Tag Stations" ); QTreeWidgetItem *custom = new QTreeWidgetItem( ui.historyTree, 0 ); custom->setText( 0, "Custom Stations" ); QStringList stations = Settings::instance()->stationItems(); for ( int i = 0; i < stations.size(); i++ ) { QTreeWidgetItem *parent = 0; QStringList parts = stations.at( i ).split( "/", QString::SkipEmptyParts ); QString group = parts.at( 0 ); if ( parts.size() > 2 && group != "usertags" ) group = parts.at( 2 ); if ( group == "similarartists" ) parent = similar; if ( group == "personal" ) parent = personal; if ( group == "neighbours" ) parent = profile; if ( group == "group" ) parent = groups; if ( group == "globaltags" ) parent = tags; if ( group == "usertags" ) parent = usertags; if ( group == "loved" ) parent = loved; if ( group == "artists" ) parent = custom; if ( parent ) { QString stationName = Settings::instance()->stationName( stations.at( i ) ); if ( parent != custom || !stationName.isEmpty() ) { if ( stationName.isEmpty() ) { if ( group == "usertags" ) stationName = QUrl::fromPercentEncoding( parts.at( 2 ).toAscii() ); else stationName = QUrl::fromPercentEncoding( parts.at( 1 ).toAscii() ); } QTreeWidgetItem *subitem = new QTreeWidgetItem( parent, 0 ); subitem->setText( 0, stationName ); subitem->setText( 1, QString().setNum( Settings::instance()->stationCounter( stations.at( i ) ) ) ); subitem->setText( 2, stations.at( i ) ); } } } ui.historyTree->setItemExpanded( similar, true ); ui.historyTree->setItemExpanded( personal, true ); ui.historyTree->setItemExpanded( profile, true ); ui.historyTree->setItemExpanded( groups, true ); ui.historyTree->setItemExpanded( tags, true ); ui.historyTree->setItemExpanded( usertags, true ); ui.historyTree->setItemExpanded( loved, true ); ui.historyTree->setItemExpanded( custom, true ); if ( similar->childCount() == 0 ) delete similar; if ( personal->childCount() == 0 ) delete personal; if ( profile->childCount() == 0 ) delete profile; if ( groups->childCount() == 0 ) delete groups; if ( tags->childCount() == 0 ) delete tags; if ( usertags->childCount() == 0 ) delete usertags; if ( loved->childCount() == 0 ) delete loved; if ( custom->childCount() == 0 ) delete custom; } void StationDialog::tagsForUserResult( const QStringList &result ) { ui.tagsList->clear(); for ( int i = 0; i < result.count(); i++ ) { ui.tagsList->addItem( result[i] ); } } void StationDialog::websiteSearch() { Settings::instance()->startBrowser( "http://www.last.fm/radio/" ); } void StationDialog::searchModeChanged( int index ) { switch ( index ) { case 0: ui.infoLabel->setText( "

Last.fm can play radio stations by finding music similar to a given artist. Find a station by typing an artist name above.

" ); break; case 1: ui.infoLabel->setText( "

Last.fm can play radio stations by finding music tagged by our users. Find a station by typing a tag name above.

" ); break; } } void StationDialog::search() { ui.searchButton->setText( "Searching..." ); ui.searchButton->setEnabled( false ); switch ( ui.searchCombo->currentIndex() ) { case 0: ws->searchArtist( ui.searchEdit->text() ); break; case 1: ws->searchTag( ui.searchEdit->text() ); break; } } void StationDialog::searchArtistResult( const QString &artist, bool streamable, const QStringList &result ) { ui.searchEdit->setText( artist ); ui.listenButton->setEnabled( false ); ui.resultLabel->setVisible( true ); ui.resultList->setVisible( false ); if ( artist.isEmpty() ) { ui.resultLabel->setText( "Artist not found!" ); } else if ( !streamable ) { ui.resultLabel->setText( "Artist not streamable!" ); } else { ui.listenButton->setEnabled( true ); ui.resultLabel->setVisible( true ); ui.resultList->setVisible( false ); resultToken = artist; ui.resultLabel->setText( artist ); for ( int i = 0; i < result.count() && i < 10; i++ ) { ui.resultLabel->setText( ui.resultLabel->text() + ", " + result[i] ); } } ui.searchButton->setText( "Go" ); ui.searchButton->setEnabled( true ); ui.resultBox->setEnabled( true ); ui.resultBox->setVisible( true ); } void StationDialog::searchTagResult( const QStringList &result ) { if ( result.isEmpty() ) { ui.listenButton->setEnabled( false ); ui.resultLabel->setVisible( true ); ui.resultList->setVisible( false ); ui.resultLabel->setText( "No matching tags found!" ); } else { ui.listenButton->setEnabled( true ); ui.resultLabel->setVisible( false ); ui.resultList->clear(); ui.resultList->setVisible( true ); for ( int i = 0; i < result.count() && i < 20; i++ ) { ui.resultList->addItem( result[i] ); } ui.resultList->setCurrentItem( ui.resultList->item( 0 ) ); ui.resultList->setItemSelected( ui.resultList->item( 0 ), true ); ui.resultLabel->setText( ui.resultLabel->text().remove( 0, 2 ) ); } ui.searchButton->setText( "Go" ); ui.searchButton->setEnabled( true ); ui.resultBox->setEnabled( true ); ui.resultBox->setVisible( true ); } void StationDialog::personalStation() { ws->changeStation( "user/" + Settings::instance()->username() + "/personal" ); } void StationDialog::neighbourStation() { ws->changeStation( "user/" + Settings::instance()->username() + "/neighbours" ); } void StationDialog::lovedStation() { ws->changeStation( "user/" + Settings::instance()->username() + "/loved" ); } void StationDialog::searchResultStation() { switch ( ui.searchCombo->currentIndex() ) { case 0: ws->changeStation( "artist/" + QUrl( resultToken ).toEncoded() + "/similarartists" ); break; case 1: ws->changeStation( "globaltags/" + QUrl( ui.resultList->currentItem()->text() ).toEncoded() ); break; } } void StationDialog::playlistResult( const Playlist& playlist ) { ui.playList->clear(); playlistData.first.clear(); playlistData.second.clear(); if ( playlist.count() ) { ui.playlistRemoveButton->setEnabled( true ); ui.playlistClearButton->setEnabled( true ); } else { ui.playlistRemoveButton->setEnabled( false ); ui.playlistClearButton->setEnabled( false ); } for ( int i = 0; i < playlist.count(); i++ ) { QPair, QString> track = playlist.trackAt( i ); playlistData.first.append( track.first.second ); playlistData.second.append( track.second ); ui.playList->addItem( track.first.first ); } } void StationDialog::playlistRemove() { if ( ui.playList->currentItem() ) { QString id = playlistData.first.at( ui.playList->currentRow() ); ws->playlistRemove( id ); ui.playList->takeItem( ui.playList->currentRow() ); } if ( ui.playList->count() == 0 ) { ui.playlistRemoveButton->setEnabled( false ); ui.playlistClearButton->setEnabled( false ); } } void StationDialog::playlistClear() { ui.playList->clear(); ws->playlistClear(); ui.playlistRemoveButton->setEnabled( false ); ui.playlistClearButton->setEnabled( false ); } void StationDialog::changeStationHistory() { ws->changeStation( ui.historyTree->currentItem()->text( 2 ) ); } void StationDialog::changeStationTags() { ws->changeStation( QString( "lastfm://usertags/%1/%2" ) .arg( QString( QUrl( Settings::instance()->username() ).toEncoded() ) ) .arg( QString( QUrl( ui.tagsList->currentItem()->text() ).toEncoded() ) ) ); } void StationDialog::changeStationThreaded( QTreeWidgetItem *item, int column ) { QTimer::singleShot( 0, this, SLOT( changeStationHistory() ) ); } void StationDialog::changeStationThreaded( QListWidgetItem *item ) { QTimer::singleShot( 0, this, SLOT( changeStationTags() ) ); } void StationDialog::changeStationPrepared() { close(); updateHistory(); } void StationDialog::historyMenu( const QPoint & pos ) { if ( ui.historyTree->currentItem() && !ui.historyTree->currentItem()->childCount() ) menu->exec( QCursor::pos() ); } void StationDialog::deleteItem() { if ( ui.historyTree->currentItem() ) { Settings::instance()->deleteStationCounter( ui.historyTree->currentItem()->text( 2 ) ); delete ui.historyTree->currentItem(); } } void StationDialog::resetItem() { if ( ui.historyTree->currentItem() ) { Settings::instance()->resetStationCounter( ui.historyTree->currentItem()->text( 2 ) ); ui.historyTree->currentItem()->setText( 1, "0" ); } } void StationDialog::clearItems() { if ( QMessageBox::question( this, "Clear history?", "This will clear your entire station history! Are you sure?", QMessageBox::Yes, QMessageBox::No ) ) { Settings::instance()->clearStationCounters(); ui.historyTree->clear(); } } lastfm-player-1.1.4_p1910/src/settings.cpp0000644000175000001440000002661110352604467017305 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #ifdef WIN32 #include #endif #ifdef Q_WS_MAC #include #endif #include "settings.h" #include "md5/md5.h" #include "rtaudio/RtAudio.h" QString browserPath; QString browserUrl; Settings *Settings::s_instance = 0; Settings::Settings( QWidget *parent ) : QDialog( parent ) , m_debug( false ) , m_actAsProxy( false ) , m_initial( false ) { s_instance = this; ui.setupUi( this ); connect( ui.okButton, SIGNAL( clicked() ), this, SLOT( okPressed() ) ); connect( ui.signUpButton, SIGNAL( clicked() ), this, SLOT( signUp() ) ); config = new QSettings( QSettings::IniFormat, QSettings::UserScope, "last.fm", "player", this ); config->setFallbacksEnabled( false ); #ifndef Q_WS_X11 ui.browserLabel->setVisible( false ); ui.browserEdit->setVisible( false ); #endif try { RtAudio *audio = new RtAudio(); for ( int i = 1; i < audio->getDeviceCount(); i++ ) { RtAudioDeviceInfo info; info = audio->getDeviceInfo( i ); qDebug() << "Found" << QString::fromStdString( info.name ); if ( info.nativeFormats & RTAUDIO_SINT16 ) qDebug() << "Supports 16 bit"; if ( info.nativeFormats & RTAUDIO_SINT24 ) qDebug() << "Supports 24 bit"; if ( info.nativeFormats & RTAUDIO_SINT32 ) qDebug() << "Supports 32 bit"; for ( uint j = 0; j < info.sampleRates.size(); j++ ) qDebug() << "Supports samplerate" << info.sampleRates.at( j ); QString name = QString::fromStdString( info.name ); ui.cardBox->addItem( name ); } delete audio; } catch ( RtError &error ) { error.printMessage(); } if ( ui.cardBox->count() == 0 ) ui.cardBox->addItem( "Default Card" ); #ifdef Q_WS_X11 ui.systemBox->addItem( "ALSA" ); ui.systemBox->addItem( "OSS" ); ui.systemBox->addItem( "External Mediaplayer" ); #endif #ifdef Q_WS_MAC ui.systemBox->addItem( "CoreAudio" ); ui.systemBox->addItem( "External Mediaplayer" ); #endif #ifdef WIN32 ui.systemBox->addItem( "DirectSound" ); ui.systemBox->addItem( "External Mediaplayer" ); #endif ui.usernameEdit->setText( username() ); if ( !password().isEmpty() ) ui.passwordEdit->setText( "********" ); ui.proxyBox->setChecked( proxyUsage() ); ui.proxyHostEdit->setText( proxyHost() ); ui.proxyPortEdit->setText( QString().setNum( proxyPort() ) ); ui.proxyUsernameEdit->setText( proxyUsername() ); ui.proxyPasswordEdit->setText( proxyPassword() ); ui.systemBox->setCurrentIndex( soundSystem() ); ui.cardBox->setCurrentIndex( soundCard() ); ui.browserEdit->setText( browser() ); originalUsername = username(); originalPassword = password(); originalProxy = proxyHost(); qDebug( QString( "Settings filename: " + config->fileName() ).toLocal8Bit() ); } QString Settings::dataPath() { return m_dataPath; } void Settings::setDataPath( QString path ) { m_dataPath = path; } bool Settings::debug() { return m_debug; } void Settings::setVolume( int volume ) { config->setValue( "player/volume", volume ); } int Settings::volume( ) { return config->value( "player/volume", 50 ).toInt(); } void Settings::setDebug( bool debug ) { m_debug = debug; } bool Settings::actAsProxy() { return m_actAsProxy; } void Settings::setActAsProxy( bool proxy ) { m_actAsProxy = proxy; } void Settings::setInitial( bool initial ) { m_initial = initial; } int Settings::positionX() { return config->value( "player/posx", 0 ).toInt(); } void Settings::setPositionX( int x ) { config->setValue( "player/posx", x ); config->sync(); } int Settings::positionY() { return config->value( "player/posy", 0 ).toInt(); } void Settings::setPositionY( int y ) { config->setValue( "player/posy", y ); config->sync(); } int Settings::soundSystem() { return config->value( "player/soundsystem", 0 ).toInt(); } void Settings::setSoundSystem( int system ) { config->setValue( "player/soundsystem", system ); config->sync(); } int Settings::soundCard() { return config->value( "player/soundcard", 0 ).toInt(); } void Settings::setSoundCard( int card ) { if ( card < 0 ) card = 0; config->setValue( "player/soundcard", card ); config->sync(); } QString Settings::username() { return config->value( "player/username" ).toString(); } QString Settings::password() { QString password = config->value( "player/password" ).toString(); if ( password.length() < 32 && !password.isEmpty() ) { setPassword( password ); password = QString::fromStdString( MD5( password.toStdString() ).hexdigest() ); } return password; } QString Settings::proxyUsername() { return config->value( "player/proxyusername" ).toString(); } QString Settings::proxyPassword() { return config->value( "player/proxypassword" ).toString(); } QString Settings::proxyHost() { return config->value( "player/proxyhost" ).toString(); } int Settings::proxyPort() { return config->value( "player/proxyport" ).toInt(); } bool Settings::proxyUsage() { return ( config->value( "player/proxyusage" ).toInt() == 1 ); } QString Settings::browser() { return config->value( "player/browser" ).toString(); } int Settings::bufferSize() { return config->value( "player/buffer" ).toInt(); } void Settings::setUsername( QString username ) { config->setValue( "player/username", username ); } void Settings::setPassword( QString password ) { if ( !password.isEmpty() && password != "********" ) { password = QString::fromStdString( MD5( password.toStdString() ).hexdigest() ); config->setValue( "player/password", password ); } } void Settings::setProxyUsername( QString username ) { config->setValue( "player/proxyusername", username ); } void Settings::setProxyPassword( QString password ) { config->setValue( "player/proxypassword", password ); } void Settings::setProxyHost( QString host ) { config->setValue( "player/proxyhost", host ); } void Settings::setProxyPort( int port ) { config->setValue( "player/proxyport", port ); } void Settings::setProxyUsage( bool enabled ) { config->setValue( "player/proxyusage", enabled ? "1" : "0" ); } void Settings::setBrowser( QString browser ) { config->setValue( "player/browser", browser ); } void Settings::setBufferSize( int value ) { config->setValue( "player/buffer", value ); } int Settings::stationCounter( QString url ) { return config->value( "history/" + url ).toInt(); } QString Settings::stationName( QString url ) { QString name = config->value( "stations/" + url ).toString(); if ( !name.isEmpty() ) { QStringList parts = name.split( ",", QString::SkipEmptyParts ); name = ""; for ( int i = 0; i < parts.count() && i < 5; i++ ) { if ( i ) name += ", "; name += parts.at( i ); } } return name; } void Settings::incrementStationCounter( QString url, QString stationName ) { if ( stationName != 0 ) { config->setValue( "stations/" + url, stationName ); } config->setValue( "history/" + url, stationCounter( url ) + 1 ); config->sync(); } void Settings::deleteStationCounter( QString url ) { config->remove( "history/" + url ); config->sync(); } void Settings::resetStationCounter( QString url ) { config->setValue( "history/" + url, 0 ); config->sync(); } void Settings::clearStationCounters() { config->remove( "history" ); config->sync(); } QStringList Settings::stationItems() { config->sync(); config->beginGroup( "history" ); QStringList items = config->allKeys(); config->endGroup(); return items; } void Settings::okPressed() { setUsername( ui.usernameEdit->text() ); setPassword( ui.passwordEdit->text() ); setProxyUsername( ui.proxyUsernameEdit->text() ); setProxyPassword( ui.proxyPasswordEdit->text() ); setProxyHost( ui.proxyHostEdit->text() ); setProxyPort( ui.proxyPortEdit->text().toInt() ); setProxyUsage( ui.proxyBox->isChecked() ); setSoundSystem( ui.systemBox->currentIndex() ); setSoundCard( ui.cardBox->currentIndex() ); setBrowser( ui.browserEdit->text() ); config->sync(); emit configChanged(); } void Settings::signUp() { startBrowser( "http://www.last.fm/signup.php" ); } void Settings::startBrowser( QString url ) { #ifdef Q_WS_X11 if ( browser().isEmpty() ) { exec(); } browserPath = browser(); #endif if ( !url.isEmpty() ) { browserUrl = url; BrowserThread *thread = new BrowserThread(); thread->start(); } } void BrowserThread::run() { qDebug( "Starting Browser!" ); #ifdef WIN32 WCHAR val[1024]; memset( val, 0, 1024 ); MultiByteToWideChar( CP_UTF8, 0, browserUrl.toAscii(), browserUrl.length(), val, 1024 ); ShellExecute( 0, 0, val, NULL, NULL, SW_SHOW ); #endif #ifdef Q_WS_MAC ICInstance icInstance; OSType psiSignature = 'Psi '; OSStatus error = ICStart( &icInstance, psiSignature ); ConstStr255Param hint( 0x0 ); const char* data = browserUrl.toLocal8Bit().data(); long length = browserUrl.length(); long start( 0 ); long end( length ); ICLaunchURL( icInstance, hint, data, length, &start, &end ); ICStop( icInstance ); #endif #ifdef Q_WS_X11 QStringList params; params.append( QString( QUrl( browserUrl ).toEncoded() ) ); QStringList options; options = browserPath.split( " " ); if ( options.size() == 0 ) options.append( browserPath ); for ( int i = 1; i < options.size(); i++ ) { qDebug( QString( "param: " + options.at( i ) ).toLocal8Bit() ); params.append( "\"" + options.at( i ) + "\"" ); } QProcess::execute( options.at( 0 ), params ); #endif } lastfm-player-1.1.4_p1910/src/song.h0000644000175000001440000000616210352604467016057 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include class Song { public: void fetch( const Song& song ); bool sameAs( const Song& song ); void clear(); const QString artist() const { return m_artist; } void setArtist( QString artist ) { m_artist = artist; } const QString album() const { return m_album; } void setAlbum( QString album ) { m_album = album; } const QString track() const { return m_track; } void setTrack( QString track ) { m_track = track; } const QString station() const { return m_station; } void setStation( QString station ) { m_station = station; } const QUrl cover() const { return m_cover; } void setCover( QString cover ) { m_cover = QUrl( cover ); } void setCover( QUrl cover ) { m_cover = cover; } const int progress() const { return m_progress; } void setProgress( int progress ) { m_progress = progress; } const int duration() const { return m_duration; } void setDuration( int duration ) { m_duration = duration; } const QString artistUrl() const { return m_artistUrl; } void setArtistUrl( QString url ) { m_artistUrl = url; } const QString albumUrl() const { return m_albumUrl; } void setAlbumUrl( QString url ) { m_albumUrl = url; } const QString trackUrl() const { return m_trackUrl; } void setTrackUrl( QString url ) { m_trackUrl = url; } private: QString m_artist; QString m_album; QString m_track; QString m_station; QUrl m_cover; int m_duration; int m_progress; QString m_artistUrl; QString m_albumUrl; QString m_trackUrl; }; lastfm-player-1.1.4_p1910/src/imagefader.cpp0000644000175000001440000000630010352604467017522 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "imagefader.h" #include #include #include static const double UpdateInterval = 1000 / 25; ImageFader::ImageFader( QObject *parent ) : QObject( parent ) { m_fadeTimer = new QTimer; connect( m_fadeTimer, SIGNAL( timeout() ), this, SLOT( fade() ) ); } void ImageFader::startFade( const QImage &from, const QImage &to, unsigned int ms ) { m_fadeProgress = 0; m_fadeStep = UpdateInterval / ms; m_src = from; m_dst = to; m_tmp = m_src.copy(); m_fadeTimer->start( UpdateInterval ); } void ImageFader::fade() { m_fadeProgress += m_fadeStep; if ( m_fadeProgress >= 1 ) { m_fadeProgress = 1; m_fadeTimer->stop(); } // ------------------------------------------------ const int a = qRound(m_fadeProgress*256); const int ia = 256 - a; const int sw = m_dst.width(); const int sh = m_dst.height(); switch(m_dst.depth()) { case 32: { for (int sy = 0; sy < sh; sy++) { for (int sx = 0; sx < sw; sx++) { if(!m_src.valid(sx, sy)) { qDebug() << "Source image borked, giving up fade effect."; m_fadeTimer->stop(); emit fadeProgress( QPixmap::fromImage( m_dst ) ); return; } QRgb bp = m_src.pixel(sx, sy); QRgb fp = m_dst.pixel(sx, sy); m_tmp.setPixel(sx, sy, qRgb( (qRed(bp)*ia + qRed(fp)*a)>>8, (qGreen(bp)*ia + qGreen(fp)*a)>>8, (qBlue(bp)*ia + qBlue(fp)*a)>>8 ) ); } } } default: break; } // --------------------------------------------- // qDebug() << "Fading to " << m_fadeProgress; emit fadeProgress( QPixmap::fromImage( m_tmp ) ); } lastfm-player-1.1.4_p1910/src/urldialog.cpp0000644000175000001440000000400010352604467017413 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include "urldialog.h" #include "webserviceconnector.h" UrlDialog::UrlDialog( QWidget *parent, WebserviceConnector *webserviceConnector ) : QDialog( parent, Qt::Dialog ) , ws( webserviceConnector ) { ui.setupUi( this ); connect( ui.okButton, SIGNAL( clicked() ), this, SLOT( okPressed() ) ); } void UrlDialog::okPressed() { if ( !ui.urlEdit->text().startsWith( "lastfm://" ) ) QMessageBox::critical( 0, "Error", "This is not a valid Last.fm address!\n" ); else ws->changeStation( ui.urlEdit->text().remove( 0, 9 ) ); } lastfm-player-1.1.4_p1910/src/playlist.cpp0000644000175000001440000000333610352604467017305 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "playlist.h" QPair, QString> Playlist::trackAt( int id ) const { QPair, QString> track; track.first.first = m_tracks.at( id ); track.first.second = m_ids.at( id ); track.second = m_urls.at( id ); return track; } lastfm-player-1.1.4_p1910/src/player.h0000644000175000001440000001041610352604467016402 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include "ui_player.h" #include "ui_playermini.h" #include "webserviceconnector.h" #include "playback.h" #include "stationdialog.h" class QMovie; class CoverLoader; class ImageFader; class Song; class Player : public QMainWindow { Q_OBJECT public: Player( QWidget *parent = 0, QString cli = QString( "" ), QString dataPath = QString( "" ) ); ~Player(); protected: void closeEvent( QCloseEvent *event ); private: Ui::PlayerForm ui; Ui::PlayerMiniForm ui_mini; WebserviceConnector *ws; StationDialog *station; Playback *playback; QTimer *m_timer; CoverLoader *coverLoader; QBuffer *coverBuffer; QTcpServer *serverSocket; QTcpSocket *webSocket; QUrl streamUrl; QString m_dataPath; QMenu *menu; QAction *stationAction; QAction *urlAction; QAction *recordAction; QAction *discoveryAction; QAction *refreshAction; QAction *configureAction; QAction *externalPlayerAction; QAction *onTopAction; QAction *aboutAction; ImageFader *fader; QMovie *animation; bool m_proxyConnected; void loadDefaultCover(); void dragEnterEvent( QDragEnterEvent *event ); void dropEvent( QDropEvent *event ); public slots: void handshake(); void skipSong(); void loveSong(); void banSong(); void recordToProfile( bool checked ); void discoveryMode( bool checked ); void setStayOnTop( bool checked ); void useExternalPlayer( bool checked ); private slots: void init(); void switchMode(); void openAbout(); void openConfig(); void openMenu(); void openStationDialog(); void openUrlDialog(); void openTagDialog(); void openJournalMenu(); void webConnect(); void webRequest(); void handshakeResult( bool connected, bool loginDone, const QUrl& streamUrl ); void metaDataResult( const Song& song, bool discovery ); void changeStationPrepared(); void changeStationResult( bool error ); void skipResult(); void banResult(); void animationStart(); void animationStop(); void coverLoaded( bool error ); void updateTimeBar(); void playbackStarting(); void playbackFinished(); void volumeChanged( int value ); void stopPlayback(); void browseArtist(); void browseAlbum(); void browseTrack(); void journalArtist(); void journalAlbum(); void journalTrack(); void proxyClientConnected(); void proxyClientDisconnected(); }; lastfm-player-1.1.4_p1910/src/webserviceconnector.cpp0000644000175000001440000010352210352604467021513 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include "webserviceconnector.h" #include "settings.h" #include "playback.h" #include "playlist.h" #include "song.h" QString session; bool forcedUpdate; Song currentSong; WebserviceConnector *WebserviceConnector::s_instance = 0; WebserviceConnector::WebserviceConnector() : m_connected( false ) , m_refreshTimer( 0 ) { s_instance = this; } QString WebserviceConnector::parameter( QString keyName, QString data ) { QStringList list = data.split( "\n" ); for ( int i = 0; i < list.size(); i++ ) { QStringList values = list[ i ].split( "=" ); if ( values[0] == keyName ) { values.removeAt( 0 ); return QString().fromUtf8( values.join( "=" ).toAscii() ); } } return QString( "" ); } QStringList WebserviceConnector::parameterArray( QString keyName, QString data ) { QStringList result; QStringList list = data.split( "\n" ); for ( int i = 0; i < list.size(); i++ ) { QStringList values = list[ i ].split( "=" ); if ( values[0].startsWith( keyName ) ) { values.removeAt( 0 ); result.append( QString().fromUtf8( values.join( "=" ).toAscii() ) ); } } return result; } QStringList WebserviceConnector::parameterKeys( QString keyName, QString data ) { QStringList result; QStringList list = data.split( "\n" ); for ( int i = 0; i < list.size(); i++ ) { QStringList values = list[ i ].split( "=" ); if ( values[0].startsWith( keyName ) ) { values = values[0].split( "[" ); values = values[1].split( "]" ); result.append( values[0] ); } } return result; } void WebserviceConnector::stackAppend( QHttp *http ) { for ( int i = 0; i < httpStack.size(); i++ ) { QHttp *http = stackGet( i ); if ( http && http->state() == QHttp::Unconnected ) delete http; } if ( httpStack.size() == 0 ) emit actionStarted(); httpStack.insert( http->currentId(), http ); } QHttp* WebserviceConnector::stackGet( int id ) { if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) return httpStack.value( id - 1 ); else return httpStack.value( id ); } void WebserviceConnector::stackRemove( int id, bool keepAnimation ) { QHttp *http; if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http = httpStack.take( id - 1 ); else http = httpStack.take( id ); if ( http ) http->close(); if ( httpStack.size() == 0 && !keepAnimation ) emit actionFinished(); } void WebserviceConnector::errorCode( int id, QString errorMessage ) { switch ( id ) { case 1: QMessageBox::information( 0, "Error", "There is not enough content to play this station.\n" ); break; case 2: QMessageBox::information( 0, "Error", "This group does not have enough members for radio.\n" ); break; case 3: QMessageBox::information( 0, "Error", "This artist does not have enough fans for radio.\n" ); break; case 4: QMessageBox::information( 0, "Error", "This item is not available for streaming.\n" ); break; case 5: QMessageBox::information( 0, "Error", "This feature is only available to subscribers.\n" ); break; case 6: QMessageBox::information( 0, "Error", "There are not enough neighbours for this radio.\n" ); break; case 7: QMessageBox::information( 0, "Error", "This stream has stopped! Please try another station!\n" ); break; default: QMessageBox::information( 0, "Error", errorMessage.isEmpty() ? "There was an error!" : errorMessage ); } } void WebserviceConnector::handshake( QString username, QString password ) { QString platform; #ifdef WIN32 platform = "win32"; #endif #ifdef Q_WS_X11 platform = "linux"; #endif #ifdef Q_WS_MAC platform = "mac"; #endif QHttp *http = new QHttp( Settings::instance()->debug() ? "wsdev.audioscrobbler.com" : "ws.audioscrobbler.com", 80, this ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); connect( http, SIGNAL( responseHeaderReceived( QHttpResponseHeader ) ), this, SLOT( handshakeHeaderReceived( QHttpResponseHeader ) ) ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( handshakeFinished( int, bool ) ) ); http->get( QString( "/radio/handshake.php?version=%1&platform=%2&username=%3&passwordmd5=%4&debug=%5" ) .arg( "1.1.4" ) .arg( platform ) .arg( QString( QUrl( username ).toEncoded() ) ) .arg( password ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::handshakeHeaderReceived( const QHttpResponseHeader &resp ) { if ( resp.statusCode() == 503 ) { QMessageBox::information( 0, "Attention", resp.reasonPhrase() ); QApplication::quit(); } } void WebserviceConnector::handshakeFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http ) return; if ( error ) { qDebug() << http->error(); qDebug() << http->errorString(); } QString result( http->readAll() ); // if ( Settings::instance()->debug() ) qDebug() << "Handshake:" << result; if ( error ) { emit handshakeResult( false, false, QUrl() ); return; } session = parameter( "session", result ); if ( session.toLower() == "failed" ) session = ""; QString streamUrl = parameter( "stream_url", result ); QString updateUrl = parameter( "update_url", result ); baseHost = parameter( "base_url", result ); basePath = parameter( "base_path", result ); m_subscriber = parameter( "subscriber", result ) == "1"; bool banned = parameter( "banned", result ) == "1"; if ( !updateUrl.isEmpty() && banned ) { QMessageBox::critical( 0, "Update available", "This outdated version of the Last.fm Player is not compatible with our service anymore!\nGo to www.last.fm and download the latest version!", "O&K", QString(), QString(), 0, 1 ); Settings::instance()->startBrowser( updateUrl ); QApplication::quit(); } else if ( !updateUrl.isEmpty() ) { QMessageBox::information( 0, "Update available", "There is a new version of the player available!\nGo to www.last.fm to download the latest version!", "O&K", QString(), QString(), 0, 1 ); Settings::instance()->startBrowser( updateUrl ); } stackRemove( id ); m_connected = !session.isEmpty(); emit handshakeResult( true, !session.isEmpty(), QUrl( streamUrl ) ); } bool WebserviceConnector::isConnected() { return m_connected; } bool WebserviceConnector::isSubscriber() { return m_subscriber; } void WebserviceConnector::searchArtist( QString artist ) { QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( searchArtistFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( "/1.0/artist/%1/similar.xml?debug=%2" ) .arg( QString( QUrl( artist ).toEncoded() ) ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::searchTag( QString tag ) { QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( searchTagFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( "/1.0/tag/%1/search.xml?debug=%2" ) .arg( QString( QUrl( tag ).toEncoded() ) ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::tagsForArtist( QString artist ) { QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( tagsForArtistFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( "/1.0/artist/%1/toptags.xml?debug=%2" ) .arg( QString( QUrl( artist ).toEncoded() ) ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::tagsForUser( QString user ) { QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( tagsForUserFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( "/1.0/user/%1/tags.xml?debug=%2" ) .arg( QString( QUrl( user ).toEncoded() ) ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::tagsForUserArtist( QString user, QString artist ) { QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( tagsForUserArtistFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( "/1.0/user/%1/artisttags.xml?artist=%2&debug=%3" ) .arg( QString( QUrl( user ).toEncoded() ) ) .arg( QString( QUrl( artist ).toEncoded() ) ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::tagsForUserTrack( QString user, QString artist, QString track ) { QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( tagsForUserTrackFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( "/1.0/user/%1/tracktags.xml?artist=%2&track=%3&debug=%4" ) .arg( QString( QUrl( user ).toEncoded() ) ) .arg( QString( QUrl( artist ).toEncoded() ) ) .arg( QString( QUrl( track ).toEncoded() ) ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::tagsForUserAlbum( QString user, QString artist, QString album ) { QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( tagsForUserAlbumFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( "/1.0/user/%1/albumtags.xml?artist=%2&album=%3&debug=%4" ) .arg( QString( QUrl( user ).toEncoded() ) ) .arg( QString( QUrl( artist ).toEncoded() ) ) .arg( QString( QUrl( album ).toEncoded() ) ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::setTag( Song *song, int mode, QString tag ) { qDebug() << "Adding tags"; QString modeToken = ""; switch ( mode ) { case 0: modeToken = QString( "artist=%1" ).arg( QString( QUrl( song->artist() ).toEncoded() ) ); break; case 1: modeToken = QString( "album=%1&artist=%2" ) .arg( QString( QUrl( song->album() ).toEncoded() ) ) .arg( QString( QUrl( song->artist() ).toEncoded() ) ); break; case 2: modeToken = QString( "track=%1&artist=%2" ) .arg( QString( QUrl( song->track() ).toEncoded() ) ) .arg( QString( QUrl( song->artist() ).toEncoded() ) ); break; } QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( setTagFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); /* http->get( QString( "/player/tag.php?s=%1&tag=%2&%3" ) .arg( session ) .arg( QString( QUrl( tag ).toEncoded() ) ) .arg( modeToken ) );*/ QString token = QString( "s=%1&tag=%2&" + modeToken ) .arg( session ) .arg( QString( QUrl( tag ).toEncoded() ) ); QHttpRequestHeader header( "POST", "/player/tag.php" ); header.setValue( "Host", "wsdev.audioscrobbler.com" ); header.setContentType( "application/x-www-form-urlencoded" ); http->request( header, token.toUtf8() ); stackAppend( http ); } void WebserviceConnector::searchArtistFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QDomDocument document; document.setContent( http->readAll() ); QStringList artists; if ( document.elementsByTagName( "similarartists" ).length() == 0 ) { stackRemove( id ); emit searchArtistResult( QString( "" ), false, artists ); return; } QString artist = document.elementsByTagName( "similarartists" ).item( 0 ).attributes().namedItem( "artist" ).nodeValue(); bool streamable = ( document.elementsByTagName( "similarartists" ).item( 0 ).attributes().namedItem( "streamable" ).nodeValue() == "1" ); QDomNodeList values = document.elementsByTagName( "artist" ); for ( int i = 0; i < values.count(); i++ ) { QDomNode item = values.item( i ).namedItem( "name" ); artists << item.toElement().text(); } stackRemove( id ); emit searchArtistResult( artist, streamable, artists ); } void WebserviceConnector::searchTagFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QDomDocument document; document.setContent( http->readAll() ); QStringList tags; if ( document.elementsByTagName( "tags" ).length() == 0 ) { stackRemove( id ); emit searchTagResult( tags ); return; } QDomNodeList values = document.elementsByTagName( "tag" ); for ( int i = 0; i < values.count(); i++ ) { QDomNode item = values.item( i ).namedItem( "name" ); tags << item.toElement().text(); } stackRemove( id ); emit searchTagResult( tags ); } void WebserviceConnector::tagsForArtistFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QDomDocument document; document.setContent( http->readAll() ); QStringList tags; if ( document.elementsByTagName( "toptags" ).length() == 0 ) { stackRemove( id ); emit tagsForArtistResult( tags ); return; } QDomNodeList values = document.elementsByTagName( "tag" ); for ( int i = 0; i < values.count(); i++ ) { QDomNode item = values.item( i ).namedItem( "name" ); tags << item.toElement().text(); } stackRemove( id ); emit tagsForArtistResult( tags ); } void WebserviceConnector::tagsForUserFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QDomDocument document; document.setContent( http->readAll() ); QStringList tags; if ( document.elementsByTagName( "toptags" ).length() == 0 ) { stackRemove( id ); emit tagsForUserResult( tags ); return; } QDomNodeList values = document.elementsByTagName( "tag" ); for ( int i = 0; i < values.count(); i++ ) { QDomNode item = values.item( i ).namedItem( "name" ); tags << item.toElement().text(); } stackRemove( id ); emit tagsForUserResult( tags ); } void WebserviceConnector::tagsForUserArtistFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QDomDocument document; document.setContent( http->readAll() ); QStringList tags; if ( document.elementsByTagName( "artisttags" ).length() == 0 ) { stackRemove( id ); emit tagsForUserArtistResult( tags ); return; } QDomNodeList values = document.elementsByTagName( "tag" ); for ( int i = 0; i < values.count(); i++ ) { QDomNode item = values.item( i ).namedItem( "name" ); tags << item.toElement().text(); } stackRemove( id ); emit tagsForUserArtistResult( tags ); } void WebserviceConnector::tagsForUserTrackFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QDomDocument document; document.setContent( http->readAll() ); QStringList tags; if ( document.elementsByTagName( "tracktags" ).length() == 0 ) { stackRemove( id ); emit tagsForUserTrackResult( tags ); return; } QDomNodeList values = document.elementsByTagName( "tag" ); for ( int i = 0; i < values.count(); i++ ) { QDomNode item = values.item( i ).namedItem( "name" ); tags << item.toElement().text(); } stackRemove( id ); emit tagsForUserTrackResult( tags ); } void WebserviceConnector::tagsForUserAlbumFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QDomDocument document; document.setContent( http->readAll() ); QStringList tags; if ( document.elementsByTagName( "albumtags" ).length() == 0 ) { stackRemove( id ); emit tagsForUserAlbumResult( tags ); return; } QDomNodeList values = document.elementsByTagName( "tag" ); for ( int i = 0; i < values.count(); i++ ) { QDomNode item = values.item( i ).namedItem( "name" ); tags << item.toElement().text(); } stackRemove( id ); emit tagsForUserAlbumResult( tags ); } void WebserviceConnector::setTagFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QString result( http->readAll() ); qDebug() << "Tagging:" << result; stackRemove( id ); } void WebserviceConnector::metaData( bool force ) { if ( m_refreshTimer != 0 ) { m_refreshTimer->stop(); delete m_refreshTimer; m_refreshTimer = 0; } qDebug( "Retrieving MetaData!" ); if ( force ) forcedUpdate = true; QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( metaDataFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( basePath + "/np.php?session=%1&debug=%2" ) .arg( session ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::metaDataFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QString result( http->readAll() ); if ( Settings::instance()->debug() ) qDebug() << "Metadata:" << result.left( 8000 ); Song song; song.setStation( parameter( "station", result ) ); song.setArtist( parameter( "artist", result ) ); song.setAlbum( parameter( "album", result ) ); song.setTrack( parameter( "track", result ) ); song.setCover( parameter( "albumcover_medium", result ) ); // song.setProgress( parameter( "trackprogress", result ).toInt() ); song.setDuration( parameter( "trackduration", result ).toInt() ); song.setProgress( 0 ); song.setArtistUrl( parameter( "artist_url", result ) ); song.setAlbumUrl( parameter( "album_url", result ) ); song.setTrackUrl( parameter( "track_url", result ) ); bool discovery = parameter( "discovery", result ) != "-1"; QStringList ids = parameterKeys( "playlist_track[", result ); QStringList tracks = parameterArray( "playlist_track[", result ); QStringList urls = parameterArray( "playlist_track_url[", result ); Playlist playlist; for ( int i = 0; i < tracks.count(); i++ ) playlist.appendTrack( tracks.at( i ), urls.at( i ), ids.at( i ) ); int errCode = parameter( "error", result ).toInt(); if ( errCode > 0 ) { errorCode( errCode, parameter( "error_message", result ) ); stackRemove( id ); if ( error < 100 ) return; } if ( m_refreshTimer != 0 ) { m_refreshTimer->stop(); delete m_refreshTimer; m_refreshTimer = 0; } if ( song.station().isEmpty() || parameter( "streaming", result ) == "false" || ( forcedUpdate && song.sameAs( currentSong ) ) ) { qDebug( "Returned same or invalid metadata, will refetch!" ); m_refreshTimer = new QTimer( this ); connect( m_refreshTimer, SIGNAL( timeout() ), this, SLOT( metaData() ) ); m_refreshTimer->setInterval( 2000 ); m_refreshTimer->start(); stackRemove( id, true ); return; } forcedUpdate = false; currentSong.fetch( song ); stackRemove( id ); emit playlistResult( playlist ); emit metaDataResult( song, discovery ); } void WebserviceConnector::skip() { qDebug( "Sending SKIP!" ); QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( skipFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( basePath + "/control.php?session=%1&command=skip&debug=%2" ) .arg( session ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::love() { qDebug( "Sending LOVE!" ); QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( loveFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( basePath + "/control.php?session=%1&command=love&debug=%2" ) .arg( session ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::ban() { qDebug( "Sending BAN!" ); QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( banFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( basePath + "/control.php?session=%1&command=ban&debug=%2" ) .arg( session ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::skipFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QString result( http->readAll() ); if ( Settings::instance()->debug() ) qDebug() << "Skip:" << result; stackRemove( id, true ); emit skipResult(); } void WebserviceConnector::loveFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QString result( http->readAll() ); if ( Settings::instance()->debug() ) qDebug() << "Love:" << result; stackRemove( id ); emit loveResult(); } void WebserviceConnector::banFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QString result( http->readAll() ); if ( Settings::instance()->debug() ) qDebug() << "Ban:" << result; stackRemove( id, true ); emit banResult(); } void WebserviceConnector::recordToProfile( bool enabled ) { if ( enabled ) qDebug( "Enabling Record-To-Profile!" ); else qDebug( "Disabling Record-To-Profile!" ); QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( defaultFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( basePath + "/control.php?session=%1&command=%2&debug=%3" ) .arg( session ) .arg( enabled ? QString( "rtp" ) : QString( "nortp" ) ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::discoveryMode( bool enabled ) { if ( enabled ) qDebug( "Enabling Discovery-Mode!" ); else qDebug( "Disabling Discovery-Mode!" ); QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( defaultFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( basePath + "/adjust.php?session=%1&url=lastfm://settings/discovery/%2&debug=%3" ) .arg( session ) .arg( enabled ? "on" : "off" ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::changeStation( QString url ) { qDebug( "Changing station!" ); QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( changeStationFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( basePath + "/adjust.php?session=%1&url=lastfm://%2&debug=%3" ) .arg( session ) .arg( url.contains( "%" ) ? url : QString( QUrl( url ).toEncoded() ) ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); Playback::instance()->setDirectSkip(); if ( !url.startsWith( "play" ) ) { emit changeStationPrepared(); } } void WebserviceConnector::changeStationFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QString result( http->readAll() ); if ( Settings::instance()->debug() ) qDebug() << "Station-change:" << result; int errCode = parameter( "error", result ).toInt(); if ( errCode > 0 ) { errorCode( errCode ); } QString url = parameter( "url", result ); if ( url.startsWith( "lastfm://" ) ) { url.remove( 0, 9 ); QString stationName = parameter( "stationname", result ); if ( !url.startsWith( "play" ) ) { Settings::instance()->incrementStationCounter( url, stationName ); emit changeStationPrepared(); } } stackRemove( id, errCode == 0 ); emit changeStationResult( errCode != 0 ); } void WebserviceConnector::playlistRemove( QString id ) { qDebug( QString( "Removing track from playlist: " + id ).toLocal8Bit() ); QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( defaultFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( basePath + "/adjust.php?session=%1&url=lastfm://play/removetrack/%2&debug=%3" ) .arg( session ) .arg( id ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::playlistClear() { qDebug( "Clearing playlist!" ); QHttp *http = new QHttp( baseHost, 80, this ); connect( http, SIGNAL( requestFinished( int, bool ) ), this, SLOT( defaultFinished( int, bool ) ) ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) http->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); http->get( QString( basePath + "/adjust.php?session=%1&url=lastfm://play/clear&debug=%2" ) .arg( session ) .arg( Settings::instance()->debug() ? "1" : "0" ) ); stackAppend( http ); } void WebserviceConnector::defaultFinished( int id, bool error ) { QHttp *http = stackGet( id ); if ( !http || error || http->bytesAvailable() <= 0 ) { stackRemove( id ); return; } QString result( http->readAll() ); if ( Settings::instance()->debug() ) qDebug() << result; stackRemove( id ); } lastfm-player-1.1.4_p1910/src/settings.ui0000644000175000001440000002564010352604467017141 0ustar davidusers SettingsForm 0 0 391 460 0 0 0 0 391 460 391 460 Settings 20 410 351 34 0 6 Sign Up Qt::Horizontal 40 20 OK Cancel 20 230 351 131 Proxy Settings true false 240 32 31 17 <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Port:</p></body></html> 20 62 71 16 <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Username:</p></body></html> 20 32 71 16 <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Host:</p></body></html> 20 92 71 16 <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Password:</p></body></html> 100 90 231 21 QLineEdit::Password 100 30 131 21 QLineEdit::Normal 100 60 231 21 QLineEdit::Normal 280 30 51 21 00000; QLineEdit::Normal 20 120 351 101 Sound 20 30 66 16 Soundcard: 20 62 66 16 System: 100 28 231 21 100 60 231 21 100 376 261 21 QLineEdit::Normal 30 378 61 17 <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Browser:</p></body></html> 20 10 351 101 Last.fm Account 20 30 71 16 Username: 20 60 71 16 Password: 100 30 231 21 100 60 231 21 QLineEdit::Password usernameEdit passwordEdit proxyHostEdit proxyPortEdit proxyUsernameEdit proxyPasswordEdit browserEdit okButton cancelButton signUpButton okButton clicked() SettingsForm accept() 278 359 15 168 cancelButton clicked() SettingsForm reject() 369 359 15 168 lastfm-player-1.1.4_p1910/src/stationdialog.h0000644000175000001440000000572210352604467017753 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include "ui_stationdialog.h" class Playlist; class WebserviceConnector; class StationDialog : public QDialog { Q_OBJECT public: StationDialog( QWidget *parent = 0, WebserviceConnector *webserviceConnector = 0 ); void updateInformation(); private: Ui::StationDialog ui; QMenu *menu; QAction *deleteAction; QAction *resetAction; QAction *clearAction; WebserviceConnector *ws; QString resultToken; QPair playlistData; private slots: void updateHistory(); void tagsForUserResult( const QStringList &result ); void changeStationHistory(); void changeStationTags(); void websiteSearch(); void searchModeChanged( int index ); void search(); void searchResultStation(); void searchArtistResult( const QString &artist, bool streamable, const QStringList &result ); void searchTagResult( const QStringList &result ); void changeStationPrepared(); void changeStationThreaded( QTreeWidgetItem *item, int column ); void changeStationThreaded( QListWidgetItem *item ); void personalStation(); void neighbourStation(); void lovedStation(); void playlistResult( const Playlist& playlist ); void playlistRemove(); void playlistClear(); void historyMenu( const QPoint & pos ); void deleteItem(); void resetItem(); void clearItems(); }; lastfm-player-1.1.4_p1910/src/coverloader.cpp0000644000175000001440000000743210352604467017752 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "coverloader.h" #include #include #include #include #include CoverLoader::CoverLoader( QObject *parent, QString cachePath ) : QObject( parent ), m_transport( new QHttp( this ) ), m_cachePath( cachePath ) { if ( !QDir( cachePath ).exists() ) QDir().mkpath( cachePath ); connect( m_transport, SIGNAL( done( bool ) ), this, SLOT( slotRequestFinished( bool ) ) ); } void CoverLoader::setHost( const QString &host ) { m_host = host; m_transport->setHost( host ); } void CoverLoader::setProxy( const QString &host, int port, const QString &user, const QString &password ) { m_transport->setProxy( host, port, user, password ); } void CoverLoader::get( const QString &path, QIODevice *to ) { m_cacheKey = m_host + path; m_cacheKey.replace( '/', '_' ); if ( to && ( !to->isReadable() && !to->isWritable() ) ) { to->close(); to->open( QIODevice::ReadWrite ); } m_buffer = to; // qDebug() << "Attempting to fetch cover " << m_cacheKey; // qDebug() << "Have cached copy for " << m_cacheKey << "?"; if ( haveCachedCopy() && to ) { QByteArray data = getCachedCopy(); if ( !data.isEmpty() ) { // qDebug() << "Reusing cached copy for " << m_cacheKey; to->write( getCachedCopy() ); emit requestFinished( false ); return; } } // qDebug() << "Didn't find a cached copy for " << m_cacheKey << ", downloading."; m_transport->get( path, to ); } void CoverLoader::slotRequestFinished( bool error ) { if ( !error && !haveCachedCopy() && m_buffer->size() > 0 ) { m_buffer->reset(); QByteArray data = m_buffer->readAll(); // qDebug() << "Storing copy of " << m_cacheKey << " in cache (" << data.size() << "bytes)"; putCachedCopy( data ); } emit requestFinished( error ); } bool CoverLoader::haveCachedCopy() const { return QFile::exists( pathToCachedCopy() ) && QFileInfo( pathToCachedCopy() ).isReadable(); } QByteArray CoverLoader::getCachedCopy() const { QFile f( pathToCachedCopy() ); if ( !f.open( QIODevice::ReadOnly ) ) { return QByteArray(); } return f.readAll(); } void CoverLoader::putCachedCopy( const QByteArray &data ) { QFile f( pathToCachedCopy() ); if ( !f.open( QIODevice::WriteOnly ) ) { return; } f.write( data ); } QString CoverLoader::pathToCachedCopy() const { return m_cachePath + m_cacheKey; } lastfm-player-1.1.4_p1910/src/playback.h0000644000175000001440000000541210352604467016674 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include #include "mpglib/interface.h" class QTcpServer; class WaveThread : public QThread { public: void run(); }; class Playback : public QObject { Q_OBJECT static Playback *s_instance; public: Playback(); ~Playback(); static Playback *instance() { return s_instance; } bool startPlayback( QUrl url ); bool isPlaying(); void stopPlayback(); void setVolume( int percentage ); void setDirectSkip(); private: WaveThread *waveThread; QTcpServer *proxyServerSocket; QList proxySocketList; bool playing; bool m_directSkip; char inBuf[8192]; bool initSound(); void showErrorCode( int error ); signals: void bufferStatus( int size ); void bufferUsed( int size ); void playbackStarting(); void playbackFinished(); void proxyClientConnected(); void proxyClientDisconnected(); private slots: void dataAvailable( const QHttpResponseHeader& resp ); void responseHeaderReceived( const QHttpResponseHeader& resp ); void stateChanged( int state ); void proxyClientConnect(); void proxyClientDisconnect(); }; lastfm-player-1.1.4_p1910/src/aboutdialog.cpp0000644000175000001440000000336010352604467017733 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include "aboutdialog.h" AboutDialog::AboutDialog( QWidget *parent, QString dataPath ) : QDialog( parent, Qt::Dialog ) { ui.setupUi( this ); QPixmap image; image.load( dataPath + "data/icon.png" ); ui.logoLabel->setPixmap( image ); } lastfm-player-1.1.4_p1910/src/stationdialog.ui0000644000175000001440000004273010352604467020141 0ustar davidusers StationDialog 0 0 422 391 Radio Control 8 6 QTabWidget::Rounded Personal 8 6 0 6 0 6 Personal Radio Neighbour Radio Loved Tracks Radio Qt::Vertical QSizePolicy::Fixed 20 6 <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Your personal tag radio stations:</p></body></html> Search 8 6 0 0 0 0 <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; color:#000000;">Search for a station</span></p></body></html> 0 0 0 0 Advanced Search on the Website Qt::Vertical QSizePolicy::Fixed 20 8 Similar Artist Radio Tags 0 6 Go Qt::Vertical QSizePolicy::Fixed 20 8 7 1 0 0 <html><head><meta name="qrichtext" content="1" /></head><body style=" white-space: pre-wrap; font-family:Sans Serif; font-weight:400; font-style:normal; text-decoration:none;"><p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Last.fm can play radio stations by finding music similar to a given artist. Find a station by typing an artist name above.</p></body></html> Qt::AlignTop true Qt::Vertical QSizePolicy::Fixed 20 8 7 7 0 0 16 160 16777215 16777215 Search Results 8 6 0 2 3 3 0 0 Qt::AlignTop true 3 1 0 0 16777215 16777215 Qt::Vertical QSizePolicy::Fixed 20 4 0 6 Qt::Horizontal 40 20 0 0 0 0 Listen Now Qt::Horizontal 40 20 History 8 6 Qt::CustomContextMenu QAbstractItemView::NoEditTriggers 16 2 true 0 1 Previews 8 6 0 6 0 5 0 0 96 16 QFrame::NoFrame QFrame::Raised 10 40 83 26 Clear List 10 10 83 26 Remove 9 9 515 352 10 10 511 351 searchEdit returnPressed() searchButton click() 300 156 400 158 resultList doubleClicked(QModelIndex) listenButton click() 178 275 184 345 lastfm-player-1.1.4_p1910/src/imagebutton.cpp0000644000175000001440000000464610352604467017767 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "imagebutton.h" ImageButton::ImageButton( QWidget *parent ) : QLabel( parent ) { state = true; } void ImageButton::setImages( QString prefix, QString normal, QString down, QString hover, QString disabled ) { this->normal.load ( prefix + normal ); this->down.load ( prefix + down ); this->hover.load ( prefix + hover ); this->disabled.load( prefix + disabled ); this->setPixmap( this->normal ); } void ImageButton::setEnabled( bool enabled ) { state = enabled; this->setPixmap( enabled ? this->normal : this->disabled ); } void ImageButton::mousePressEvent( QMouseEvent *e ) { if ( state ) this->setPixmap( down ); } void ImageButton::mouseReleaseEvent( QMouseEvent * e ) { if ( state ) { setEnabled( state ); emit clicked(); } } void ImageButton::enterEvent( QEvent * e ) { if ( state ) this->setPixmap( hover ); } void ImageButton::leaveEvent( QEvent *e ) { setEnabled( state ); } lastfm-player-1.1.4_p1910/src/player.cpp0000644000175000001440000010360710352604467016742 0ustar davidusers/*************************************************************************** * Copyright (C) 2005 by Christian Muehlhaeuser, Last.fm Ltd. * * chris@last.fm * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include #include #include #include #include "player.h" #include "song.h" #include "aboutdialog.h" #include "urldialog.h" #include "tagdialog.h" #include "coverloader.h" #include "imagefader.h" #include "settings.h" #ifdef WIN32 #include #endif bool handshaked = false; bool minimode = false; QString commandLine; Song playingSong; bool transmitCli( QString parameter ) { QTcpSocket *socket = new QTcpSocket( 0 ); socket->connectToHost( QHostAddress::LocalHost, 32213 ); if ( socket->waitForConnected( 500 ) ) { socket->write( parameter.toLocal8Bit(), parameter.length() ); socket->flush(); socket->close(); return true; } return false; } #ifdef Q_WS_MAC #include static pascal OSErr getURLEvent( const AppleEvent *appEvent, AppleEvent *reply, long handlerRefcon ) { DescType type; Size size; char buf[1024]; AEGetParamPtr( appEvent, keyDirectObject, typeChar, &type, &buf, 1023, &size ); buf[ size ] = '\0'; if ( handshaked ) transmitCli( QString::fromUtf8( buf ).remove( 0, 9 ) ); else commandLine = QString::fromUtf8( buf ).remove( 0, 9 ); } #endif Player::Player( QWidget *parent, QString cli, QString dataPath ) : QMainWindow( parent ) , ws( new WebserviceConnector() ) , station( new StationDialog( this, ws ) ) , playback( 0 ) , m_timer( 0 ) , coverBuffer( 0 ) , animation( 0 ) , m_proxyConnected( false ) { #ifdef Q_WS_MAC AEInstallEventHandler( 'GURL', 'GURL', NewAEEventHandlerUPP( getURLEvent ), 0, false ); #endif setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); m_dataPath = QFileInfo( dataPath ).absolutePath() + "/"; coverLoader = new CoverLoader( this, m_dataPath + "cache/" ); #ifndef Q_WS_MAC if ( !cli.isEmpty() && cli.length() > 9 ) commandLine = cli.remove( 0, 9 ); #endif // check for another instance running if ( transmitCli( commandLine ) ) QApplication::quit(); else { serverSocket = new QTcpServer( this ); connect( serverSocket, SIGNAL( newConnection() ), this, SLOT( webConnect() ) ); serverSocket->listen( QHostAddress::LocalHost, 32213 ); ui.setupUi( this ); QIcon icon( m_dataPath + "data/icon.png" ); setWindowIcon( icon ); menu = new QMenu( this ); stationAction = new QAction( tr( "Change &Station..." ), this ); urlAction = new QAction( tr( "Enter Station &Address..." ), this ); recordAction = new QAction( tr( "Record to &Profile" ), this ); discoveryAction = new QAction( tr( "&Discovery Mode" ), this ); refreshAction = new QAction( tr( "&Refresh" ), this ); configureAction = new QAction( tr( "&Settings..." ), this ); externalPlayerAction = new QAction( tr( "Use &External Player" ), this ); onTopAction = new QAction( tr( "Stay on &Top" ), this ); aboutAction = new QAction( tr( "&About" ), this ); #ifdef Q_WS_MAC stationAction->setShortcut( tr( "Ctrl+N" ) ); urlAction->setShortcut( tr( "Ctrl+G" ) ); refreshAction->setShortcut( tr( "Ctrl+R" ) ); QMenu *fileMenu = menuBar()->addMenu( tr( "&File" ) ); fileMenu->addAction( configureAction ); fileMenu->addAction( aboutAction ); QMenu *streamMenu = menuBar()->addMenu( tr( "&Controls" ) ); streamMenu->addAction( stationAction ); streamMenu->addAction( urlAction ); streamMenu->addSeparator(); streamMenu->addAction( recordAction ); streamMenu->addAction( discoveryAction ); streamMenu->addSeparator(); streamMenu->addAction( refreshAction ); #endif stationAction->setEnabled( false ); urlAction->setEnabled( false ); recordAction->setEnabled( false ); discoveryAction->setEnabled( false ); refreshAction->setEnabled( false ); recordAction->setCheckable( true ); recordAction->setChecked( true ); discoveryAction->setCheckable( true ); discoveryAction->setChecked( false ); onTopAction->setCheckable( true ); onTopAction->setChecked( false ); externalPlayerAction->setCheckable( true ); menu->addAction( stationAction ); menu->addAction( urlAction ); menu->addSeparator(); menu->addAction( recordAction ); menu->addAction( discoveryAction ); menu->addAction( refreshAction ); menu->addSeparator(); menu->addAction( configureAction ); menu->addAction( externalPlayerAction ); menu->addAction( onTopAction ); menu->addSeparator(); menu->addAction( aboutAction ); int externalSystem = -1; #ifdef WIN32 externalSystem = 1; #endif #ifdef Q_WS_X11 externalSystem = 2; #endif externalPlayerAction->setChecked( Settings::instance()->soundSystem() == externalSystem ); init(); connect( stationAction, SIGNAL( triggered() ), this, SLOT( openStationDialog() ) ); connect( urlAction, SIGNAL( triggered() ), this, SLOT( openUrlDialog() ) ); connect( recordAction, SIGNAL( toggled( bool ) ), this, SLOT( recordToProfile( bool ) ) ); connect( discoveryAction, SIGNAL( toggled( bool ) ), this, SLOT( discoveryMode( bool ) ) ); connect( refreshAction, SIGNAL( triggered() ), ws, SLOT( metaData() ) ); connect( configureAction, SIGNAL( triggered() ), this, SLOT( openConfig() ) ); connect( externalPlayerAction, SIGNAL( toggled( bool ) ), this, SLOT( useExternalPlayer( bool ) ) ); connect( onTopAction, SIGNAL( toggled( bool ) ), this, SLOT( setStayOnTop( bool ) ) ); connect( aboutAction, SIGNAL( triggered() ), this, SLOT( openAbout() ) ); connect( ws, SIGNAL( handshakeResult( bool, bool, QUrl ) ), this, SLOT( handshakeResult( bool, bool, QUrl ) ) ); connect( ws, SIGNAL( metaDataResult( Song, bool ) ), this, SLOT( metaDataResult( Song, bool ) ) ); connect( ws, SIGNAL( skipResult() ), this, SLOT( skipResult() ) ); connect( ws, SIGNAL( banResult() ), this, SLOT( banResult() ) ); connect( ws, SIGNAL( changeStationPrepared() ), this, SLOT( changeStationPrepared() ) ); connect( ws, SIGNAL( changeStationResult( bool ) ), this, SLOT( changeStationResult( bool ) ) ); connect( ws, SIGNAL( actionStarted() ), this, SLOT( animationStart() ) ); connect( ws, SIGNAL( actionFinished() ), this, SLOT( animationStop() ) ); connect( Settings::instance(), SIGNAL( configChanged() ), this, SLOT( handshake() ) ); Settings::instance()->setDataPath( m_dataPath ); if ( Settings::instance()->positionX() > 0 && Settings::instance()->positionY() > 0 ) move( Settings::instance()->positionX(), Settings::instance()->positionY() ); if ( Settings::instance()->username().isEmpty() || Settings::instance()->password().isEmpty() ) { Settings::instance()->setInitial( true ); QApplication::setQuitOnLastWindowClosed( false ); Settings::instance()->exec(); QApplication::setQuitOnLastWindowClosed( true ); } if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) qDebug( "Using proxy!" ); handshake(); } } Player::~Player() { delete playback; } void Player::init() { if ( minimode ) { fader = new ImageFader(); loadDefaultCover(); connect( coverLoader, SIGNAL( requestFinished( bool ) ), this, SLOT( coverLoaded( bool ) ) ); connect( fader, SIGNAL( fadeProgress( const QPixmap & ) ), ui_mini.coverImage, SLOT( setPixmap( const QPixmap & ) ) ); QPixmap image; image.load( m_dataPath + "data/artist.png" ); ui_mini.artistImage->setPixmap( image ); image.load( m_dataPath + "data/album.png" ); ui_mini.albumImage->setPixmap( image ); image.load( m_dataPath + "data/song.png" ); ui_mini.songImage->setPixmap( image ); image.load( m_dataPath + "data/volume_left.png" ); ui_mini.volumeLow->setPixmap( image ); image.load( m_dataPath + "data/volume_right.png" ); ui_mini.volumeHigh->setPixmap( image ); image.load( m_dataPath + "data/curve.png" ); ui_mini.curveLabel->setPixmap( image ); ui_mini.stopButton->setImages( m_dataPath, "data/buttons/play_enabled.png", "data/buttons/play_enabled.png", "data/buttons/play_hover.png", "data/buttons/stop_disabled.png" ); ui_mini.skipButton->setImages( m_dataPath, "data/buttons/skip_enabled.png", "data/buttons/skip_down.png", "data/buttons/skip_hover.png", "data/buttons/skip_disabled.png" ); ui_mini.loveButton->setImages( m_dataPath, "data/buttons/love_enabled.png", "data/buttons/love_down.png", "data/buttons/love_hover.png", "data/buttons/love_disabled.png" ); ui_mini.banButton->setImages ( m_dataPath, "data/buttons/ban_enabled.png", "data/buttons/ban_down.png", "data/buttons/ban_hover.png", "data/buttons/ban_disabled.png" ); ui_mini.menuButton->setImages( m_dataPath, "data/buttons/config_enabled.png", "data/buttons/config_down.png", "data/buttons/config_hover.png", "data/buttons/config_disabled.png" ); ui_mini.tagButton->setImages ( m_dataPath, "data/buttons/tag_normal.png", "data/buttons/tag_down.png", "data/buttons/tag_hover.png", "data/buttons/tag_disabled.png" ); ui_mini.blogButton->setImages( m_dataPath, "data/buttons/blog_normal.png", "data/buttons/blog_down.png", "data/buttons/blog_hover.png", "data/buttons/blog_disabled.png" ); ui_mini.stationLabel->setText( "Connecting to server..." ); ui_mini.volumeSlider->setValue( Settings::instance()->volume() ); volumeChanged( Settings::instance()->volume() ); ui_mini.skipButton->setEnabled( false ); ui_mini.loveButton->setEnabled( false ); ui_mini.banButton->setEnabled ( false ); ui_mini.tagButton->setEnabled ( false ); ui_mini.blogButton->setEnabled( false ); connect( ui_mini.volumeSlider, SIGNAL( sliderMoved( int ) ), this, SLOT( volumeChanged( int ) ) ); connect( ui_mini.skipButton, SIGNAL( clicked() ), this, SLOT( skipSong() ) ); connect( ui_mini.loveButton, SIGNAL( clicked() ), this, SLOT( loveSong() ) ); connect( ui_mini.banButton, SIGNAL( clicked() ), this, SLOT( banSong() ) ); connect( ui_mini.stopButton, SIGNAL( clicked() ), this, SLOT( stopPlayback() ) ); connect( ui_mini.menuButton, SIGNAL( clicked() ), this, SLOT( openMenu() ) ); connect( ui_mini.coverImage, SIGNAL( clicked() ), this, SLOT( browseAlbum() ) ); connect( ui_mini.artistLabel, SIGNAL( clicked() ), this, SLOT( browseArtist() ) ); connect( ui_mini.albumLabel, SIGNAL( clicked() ), this, SLOT( browseAlbum() ) ); connect( ui_mini.trackLabel, SIGNAL( clicked() ), this, SLOT( browseTrack() ) ); connect( ui_mini.tagButton, SIGNAL( clicked() ), this, SLOT( openTagDialog() ) ); connect( ui_mini.blogButton, SIGNAL( clicked() ), this, SLOT( openJournalMenu() ) ); } else { fader = new ImageFader(); loadDefaultCover(); connect( coverLoader, SIGNAL( requestFinished( bool ) ), this, SLOT( coverLoaded( bool ) ) ); connect( fader, SIGNAL( fadeProgress( const QPixmap & ) ), ui.coverImage, SLOT( setPixmap( const QPixmap & ) ) ); QPixmap image; image.load( m_dataPath + "data/artist.png" ); ui.artistImage->setPixmap( image ); image.load( m_dataPath + "data/album.png" ); ui.albumImage->setPixmap( image ); image.load( m_dataPath + "data/song.png" ); ui.songImage->setPixmap( image ); image.load( m_dataPath + "data/volume_left.png" ); ui.volumeLow->setPixmap( image ); image.load( m_dataPath + "data/volume_right.png" ); ui.volumeHigh->setPixmap( image ); image.load( m_dataPath + "data/curve.png" ); ui.curveLabel->setPixmap( image ); ui.stopButton->setImages( m_dataPath, "data/buttons/play_enabled.png", "data/buttons/play_enabled.png", "data/buttons/play_hover.png", "data/buttons/stop_disabled.png" ); ui.skipButton->setImages( m_dataPath, "data/buttons/skip_enabled.png", "data/buttons/skip_down.png", "data/buttons/skip_hover.png", "data/buttons/skip_disabled.png" ); ui.loveButton->setImages( m_dataPath, "data/buttons/love_enabled.png", "data/buttons/love_down.png", "data/buttons/love_hover.png", "data/buttons/love_disabled.png" ); ui.banButton->setImages ( m_dataPath, "data/buttons/ban_enabled.png", "data/buttons/ban_down.png", "data/buttons/ban_hover.png", "data/buttons/ban_disabled.png" ); ui.menuButton->setImages( m_dataPath, "data/buttons/config_enabled.png", "data/buttons/config_down.png", "data/buttons/config_hover.png", "data/buttons/config_disabled.png" ); ui.tagButton->setImages ( m_dataPath, "data/buttons/tag_normal.png", "data/buttons/tag_down.png", "data/buttons/tag_hover.png", "data/buttons/tag_disabled.png" ); ui.blogButton->setImages( m_dataPath, "data/buttons/blog_normal.png", "data/buttons/blog_down.png", "data/buttons/blog_hover.png", "data/buttons/blog_disabled.png" ); ui.stationLabel->setText( "Connecting to server..." ); ui.volumeSlider->setValue( Settings::instance()->volume() ); volumeChanged( Settings::instance()->volume() ); ui.skipButton->setEnabled( false ); ui.loveButton->setEnabled( false ); ui.banButton->setEnabled ( false ); ui.tagButton->setEnabled ( false ); ui.blogButton->setEnabled( false ); connect( ui.volumeSlider, SIGNAL( sliderMoved( int ) ), this, SLOT( volumeChanged( int ) ) ); connect( ui.skipButton, SIGNAL( clicked() ), this, SLOT( skipSong() ) ); connect( ui.loveButton, SIGNAL( clicked() ), this, SLOT( loveSong() ) ); connect( ui.banButton, SIGNAL( clicked() ), this, SLOT( banSong() ) ); connect( ui.stopButton, SIGNAL( clicked() ), this, SLOT( stopPlayback() ) ); connect( ui.menuButton, SIGNAL( clicked() ), this, SLOT( openMenu() ) ); connect( ui.coverImage, SIGNAL( clicked() ), this, SLOT( browseAlbum() ) ); connect( ui.artistLabel, SIGNAL( clicked() ), this, SLOT( browseArtist() ) ); connect( ui.albumLabel, SIGNAL( clicked() ), this, SLOT( browseAlbum() ) ); connect( ui.trackLabel, SIGNAL( clicked() ), this, SLOT( browseTrack() ) ); connect( ui.tagButton, SIGNAL( clicked() ), this, SLOT( openTagDialog() ) ); connect( ui.blogButton, SIGNAL( clicked() ), this, SLOT( openJournalMenu() ) ); } } void Player::handshake() { if ( playback != 0 ) { delete playback; playbackFinished(); } ws->handshake( Settings::instance()->username(), Settings::instance()->password() ); playback = new Playback(); connect( playback, SIGNAL( playbackStarting() ), this, SLOT( playbackStarting() ) ); connect( playback, SIGNAL( playbackFinished() ), this, SLOT( playbackFinished() ) ); connect( playback, SIGNAL( bufferStatus( int ) ), this, SLOT( bufferStatus( int ) ) ); connect( playback, SIGNAL( bufferUsed( int ) ), this, SLOT( bufferUsed( int ) ) ); connect( playback, SIGNAL( proxyClientConnected() ), this, SLOT( proxyClientConnected() ) ); connect( playback, SIGNAL( proxyClientDisconnected() ), this, SLOT( proxyClientDisconnected() ) ); } void Player::switchMode() { } void Player::loadDefaultCover() { QPixmap image; image.load( m_dataPath + "data/nocover.gif" ); ui.coverImage->setPixmap( image ); } void Player::handshakeResult( bool connected, bool loginDone, const QUrl &streamUrl ) { handshaked = connected; if ( loginDone ) { station->updateInformation(); if ( !minimode ) ui.stationLabel->setText( "Please start a station!" ); stationAction->setEnabled( true ); urlAction->setEnabled( true ); recordAction->setEnabled( true ); refreshAction->setEnabled( true ); discoveryAction->setEnabled( ws->isSubscriber() ); this->streamUrl = streamUrl; if ( !commandLine.isEmpty() && commandLine.length() > 9 ) { ws->changeStation( commandLine ); commandLine = ""; } else openStationDialog(); } else { if ( connected ) QMessageBox::critical( 0, "Error", "Login failed! Please check your username and password!\n" ); else QMessageBox::critical( 0, "Error", "Can't connect to server! Make sure your Internet connection is working and your proxy settings are correct!\n" ); Settings::instance()->exec(); } } void Player::metaDataResult( const Song &song, bool discovery ) { if ( !playback->isPlaying() ) return; playingSong.fetch( song ); if ( ws->isSubscriber() ) discoveryAction->setEnabled( discovery ); if ( playingSong.duration() == 0 ) { if ( minimode ) { ui_mini.timeBar->setMaximum( 100 ); ui_mini.timeBar->setValue( 100 ); } else { ui.timeBar->setMaximum( 100 ); ui.timeBar->setValue( 100 ); } } else { if ( minimode ) { ui_mini.timeBar->setMaximum( playingSong.duration() ); ui_mini.timeBar->setValue( 0 ); } else { ui.timeBar->setMaximum( playingSong.duration() ); ui.timeBar->setValue( 0 ); } } if ( m_timer != 0 ) { m_timer->stop(); delete m_timer; } m_timer = new QTimer( this ); connect( m_timer, SIGNAL( timeout() ), this, SLOT( updateTimeBar() ) ); m_timer->setInterval( 1000 ); m_timer->start(); if ( minimode ) { ui_mini.tagButton->setEnabled ( true ); ui_mini.blogButton->setEnabled( true ); ui_mini.skipButton->setEnabled( true ); ui_mini.loveButton->setEnabled( true ); ui_mini.banButton->setEnabled ( true ); ui_mini.wholeLabel->setText( playingSong.artist() + " " + playingSong.track() ); } else { ui.tagButton->setEnabled ( true ); ui.blogButton->setEnabled( true ); ui.skipButton->setEnabled( true ); ui.loveButton->setEnabled( true ); ui.banButton->setEnabled ( true ); ui.artistLabel->setText( playingSong.artist() ); ui.albumLabel->setText( playingSong.album() ); ui.trackLabel->setText( playingSong.track() ); if ( Settings::instance()->debug() ) ui.stationLabel->setText( "Debug: " + playingSong.station() + "" ); else ui.stationLabel->setText( "" + playingSong.station() + "" ); } if ( coverBuffer != 0 ) delete coverBuffer; coverBuffer = new QBuffer(); if ( playingSong.cover().isEmpty() ) loadDefaultCover(); else { coverLoader->setHost( playingSong.cover().host() ); if ( Settings::instance()->proxyUsage() && !Settings::instance()->proxyHost().isEmpty() ) coverLoader->setProxy( Settings::instance()->proxyHost(), Settings::instance()->proxyPort(), Settings::instance()->proxyUsername(), Settings::instance()->proxyPassword() ); if ( !playingSong.cover().encodedQuery().isEmpty() ) { qDebug( QString( playingSong.cover().host() + playingSong.cover().path() + "?" + QString( playingSong.cover().encodedQuery() ) ).toLocal8Bit() ); coverLoader->get( playingSong.cover().path() + "?" + QString( playingSong.cover().encodedQuery() ), coverBuffer ); } else { qDebug( QString( playingSong.cover().host() + playingSong.cover().path() ).toLocal8Bit() ); coverLoader->get( playingSong.cover().path(), coverBuffer ); } } } void Player::skipResult() { } void Player::banResult() { } void Player::updateTimeBar() { playingSong.setProgress( playingSong.progress() + 1 ); if ( minimode ) ui_mini.timeBar->setValue( playingSong.progress() ); else ui.timeBar->setValue( playingSong.progress() ); } void Player::animationStart() { if ( !animation || animation->state() != QMovie::Running ) { if ( animation ) delete animation; animation = new QMovie( m_dataPath + "data/progress_active.gif" ); animation->setSpeed( 150 ); if ( minimode ) ui_mini.logoLabel->setMovie( animation ); else ui.logoLabel->setMovie( animation ); animation->start(); connect( animation, SIGNAL( finished() ), this, SLOT( animationStart() ) ); } } void Player::animationStop() { QPixmap image; image.load( m_dataPath + "data/progress_inactive.gif" ); if ( minimode ) ui_mini.logoLabel->setPixmap( image ); else ui.logoLabel->setPixmap( image ); if ( animation ) delete animation; animation = 0; } void Player::coverLoaded( bool error ) { if ( !playback->isPlaying() ) return; qDebug( "Cover returned!" ); if ( !error && coverBuffer->size() > 0 ) { qDebug( "Setting cover!" ); QPixmap cover; cover.loadFromData( coverBuffer->buffer() ); if ( !cover.isNull() ) { fader->startFade( ui.coverImage->pixmap()->toImage(), cover.scaled( 130, 130, Qt::IgnoreAspectRatio, Qt::SmoothTransformation ).toImage(), 1500 ); return; } } qDebug( "Cover empty!" ); QPixmap cover; cover.load( m_dataPath + "data/nocover.gif" ); if ( minimode ) fader->startFade( ui_mini.coverImage->pixmap()->toImage(), cover.toImage(), 1500 ); else fader->startFade( ui.coverImage->pixmap()->toImage(), cover.toImage(), 1500 ); } void Player::skipSong() { if ( minimode ) { ui_mini.skipButton->setEnabled( false ); ui_mini.loveButton->setEnabled( false ); ui_mini.banButton->setEnabled ( false ); } else { ui.skipButton->setEnabled( false ); ui.loveButton->setEnabled( false ); ui.banButton->setEnabled ( false ); } playback->setDirectSkip(); ws->skip(); } void Player::loveSong() { if ( minimode ) ui_mini.loveButton->setEnabled( false ); else ui.loveButton->setEnabled( false ); ws->love(); } void Player::banSong() { if ( minimode ) { ui_mini.skipButton->setEnabled( false ); ui_mini.loveButton->setEnabled( false ); ui_mini.banButton->setEnabled ( false ); } else { ui.skipButton->setEnabled( false ); ui.loveButton->setEnabled( false ); ui.banButton->setEnabled ( false ); } playback->setDirectSkip(); ws->ban(); } void Player::recordToProfile( bool checked ) { ws->recordToProfile( checked ); } void Player::discoveryMode( bool checked ) { ws->discoveryMode( checked ); } void Player::setStayOnTop( bool checked ) { if ( checked ) setWindowFlags( windowFlags() | Qt::WindowStaysOnTopHint ); else setWindowFlags( windowFlags() ^ Qt::WindowStaysOnTopHint ); show(); QIcon icon( m_dataPath + "data/icon.png" ); setWindowIcon( icon ); } void Player::useExternalPlayer( bool checked ) { if ( checked ) { int externalSystem = -1; #ifdef WIN32 externalSystem = 1; #endif #ifdef Q_WS_X11 externalSystem = 2; #endif Settings::instance()->setSoundSystem( externalSystem ); } else { Settings::instance()->setSoundSystem( 0 ); } handshake(); } void Player::playbackStarting() { qDebug( "Playback starting!" ); if ( minimode ) { ui_mini.stopButton->setImages( m_dataPath, "data/buttons/stop_enabled.png", "data/buttons/stop_down.png", "data/buttons/stop_hover.png", "data/buttons/stop_disabled.png" ); ui_mini.stopButton->setEnabled( true ); } else { ui.stopButton->setImages( m_dataPath, "data/buttons/stop_enabled.png", "data/buttons/stop_down.png", "data/buttons/stop_hover.png", "data/buttons/stop_disabled.png" ); ui.stopButton->setEnabled( true ); } } void Player::playbackFinished() { qDebug( "Playback finished!" ); if ( m_timer != 0 ) { m_timer->stop(); delete m_timer; } m_timer = 0; if ( minimode ) { ui_mini.wholeLabel->setText( "" ); ui_mini.stopButton->setImages( m_dataPath, "data/buttons/play_enabled.png", "data/buttons/play_enabled.png", "data/buttons/play_hover.png", "data/buttons/stop_disabled.png" ); ui_mini.skipButton->setEnabled( false ); ui_mini.loveButton->setEnabled( false ); ui_mini.banButton->setEnabled ( false ); ui_mini.timeBar->setValue( 0 ); } else { ui.artistLabel->setText( "" ); ui.albumLabel->setText( "" ); ui.trackLabel->setText( "" ); ui.stationLabel->setText( "Please start a station!" ); ui.stopButton->setImages( m_dataPath, "data/buttons/play_enabled.png", "data/buttons/play_enabled.png", "data/buttons/play_hover.png", "data/buttons/stop_disabled.png" ); ui.skipButton->setEnabled( false ); ui.loveButton->setEnabled( false ); ui.banButton->setEnabled ( false ); ui.timeBar->setValue( 0 ); } loadDefaultCover(); animationStop(); } void Player::webConnect() { qDebug( "New WebRequest incoming!" ); webSocket = serverSocket->nextPendingConnection(); connect( webSocket, SIGNAL( readyRead() ), this, SLOT( webRequest() ) ); } void Player::webRequest() { QByteArray in = webSocket->readAll(); ws->changeStation( QString( in ) ); delete webSocket; } void Player::openAbout() { AboutDialog about( this, m_dataPath ); about.exec(); } void Player::openConfig() { Settings::instance()->exec(); } void Player::openMenu() { menu->exec( QCursor::pos() ); } void Player::openStationDialog() { station->show(); } void Player::openUrlDialog() { UrlDialog urlDialog( this, ws ); urlDialog.exec(); } void Player::openTagDialog() { if ( playback->isPlaying() ) { TagDialog tagDialog( this, playingSong, ws ); tagDialog.execSmall(); } } void Player::openJournalMenu() { if ( playback->isPlaying() ) { QMenu journalMenu( this ); QAction *artistAction = new QAction( tr( "Write a Journal about this Artist" ), this ); QAction *albumAction = new QAction( tr( "Write a Journal about this Album" ), this ); QAction *trackAction = new QAction( tr( "Write a Journal about this Track" ), this ); journalMenu.addAction( artistAction ); journalMenu.addAction( albumAction ); journalMenu.addAction( trackAction ); connect( artistAction, SIGNAL( triggered() ), this, SLOT( journalArtist() ) ); connect( albumAction, SIGNAL( triggered() ), this, SLOT( journalAlbum() ) ); connect( trackAction, SIGNAL( triggered() ), this, SLOT( journalTrack() ) ); journalMenu.exec( QCursor::pos() ); } } void Player::changeStationPrepared() { if ( !minimode ) ui.stationLabel->setText( "Connecting to station, please wait..." ); } void Player::changeStationResult( bool error ) { if ( error && !playback->isPlaying() ) playbackFinished(); if ( !error && !playback->isPlaying() ) { #ifdef WIN32 if ( Settings::instance()->actAsProxy() && !m_proxyConnected ) { QString m3uUrl = m_dataPath + "data/default.m3u"; WCHAR val[1024]; memset( val, 0, 1024 ); MultiByteToWideChar( CP_UTF8, 0, m3uUrl.toAscii(), m3uUrl.length(), val, 1024 ); ShellExecute( 0, 0, val, NULL, NULL, SW_SHOW ); } #endif int res = QMessageBox::Ok; #ifndef WIN32 while ( Settings::instance()->actAsProxy() && !m_proxyConnected && res != QMessageBox::Cancel) { res = QMessageBox::information( this, "Attention", "Start your media-player with the following url, now: http://localhost:32214", QMessageBox::Ok, QMessageBox::Cancel ); } #endif if ( !Settings::instance()->actAsProxy() || res == QMessageBox::Ok ) { if ( !playback->startPlayback( streamUrl ) ) { QMessageBox::information( this, "Error", "Can't open sound-device! Please make sure your settings are correct!" ); animationStop(); } } else animationStop(); } } void Player::volumeChanged( int value ) { playback->setVolume( value ); Settings::instance()->setVolume( value ); } void Player::stopPlayback() { animationStop(); playingSong.clear(); if ( playback->isPlaying() ) playback->stopPlayback(); else station->show(); } void Player::browseArtist() { Settings::instance()->startBrowser( playingSong.artistUrl() ); } void Player::browseAlbum() { Settings::instance()->startBrowser( playingSong.albumUrl() ); return; delete centralWidget(); if ( minimode ) { setWindowFlags( Qt::Window ); show(); ui.setupUi( this ); minimode = false; } else { setWindowFlags( Qt::Window | Qt::FramelessWindowHint ); show(); ui_mini.setupUi( this ); minimode = true; } QTimer::singleShot( 0, this, SLOT( init() ) ); } void Player::browseTrack() { Settings::instance()->startBrowser( playingSong.trackUrl() ); } void Player::journalArtist() { Settings::instance()->startBrowser( QString( "http://www.last.fm/user/%1/journal/&action=create&artistname=%2" ) .arg( Settings::instance()->username() ) .arg( playingSong.artist() ) ); } void Player::journalAlbum() { Settings::instance()->startBrowser( QString( "http://www.last.fm/user/%1/journal/&action=create&artistname=%2&albumname=%3" ) .arg( Settings::instance()->username() ) .arg( playingSong.artist() ) .arg( playingSong.album() ) ); } void Player::journalTrack() { Settings::instance()->startBrowser( QString( "http://www.last.fm/user/%1/journal/&action=create&artistname=%2&trackname=%3" ) .arg( Settings::instance()->username() ) .arg( playingSong.artist() ) .arg( playingSong.track() ) ); } void Player::dragEnterEvent( QDragEnterEvent *event ) { if ( event->mimeData()->hasFormat( "text/plain" ) && event->mimeData()->text().startsWith( "lastfm://" ) ) event->acceptProposedAction(); } void Player::dropEvent( QDropEvent *event ) { QString station = event->mimeData()->text().remove( 0, 9 ).split( "\n" )[0]; ws->changeStation( station ); event->acceptProposedAction(); } void Player::proxyClientConnected() { m_proxyConnected = true; } void Player::proxyClientDisconnected() { m_proxyConnected = false; } void Player::closeEvent( QCloseEvent *event ) { Settings::instance()->setPositionX( pos().x() ); Settings::instance()->setPositionY( pos().y() ); } lastfm-player-1.1.4_p1910/data/0000755000175000001440000000000010352604575015055 5ustar daviduserslastfm-player-1.1.4_p1910/data/song.png0000644000175000001440000000145710352604471016533 0ustar davidusersPNG  IHDRgAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxbܲg#`>@l j@_@|Fb" u@lhrB@,.@\ 뀸!+ tCx&KCx'TG bm v x B6 W1J ,=@lĭ@l Kr` QyPA2.bOC@4 @3Aq,Gs(WT3'1  f z@ ?`< e@<(XPh8ŧx7ȕ@@ C[H b~(  PU<|Brq e??P6,* 44hl1+A دhy@^E3nhX߄& y4rZ7 h@{-541I ؀7>@ CA_hYA%I^@ CA fi.kʠ?aa3x _Az X˂2 24I}G@ P X޿@Áx(C.A?t) lhc o efOd -|^"A^H Z&!Xl}8 l%hXJhy{@|bKۆm`IENDB`lastfm-player-1.1.4_p1910/data/default.m3u0000644000175000001440000000007210352604471017121 0ustar davidusers#EXTM3U #EXTINF:-1,localhost:32214 http://localhost:32214 lastfm-player-1.1.4_p1910/data/volume_right.png0000644000175000001440000000064710352604471020271 0ustar davidusersPNG  IHDR 7gAMAOX2tEXtSoftwareAdobe ImageReadyqe<9IDATxb?`bY ~ߐ2'?;X* @7'@ i` b&48$ R@ Ai bU rW b; 3 d-?@u% .~ &y ,ġP` nz0M"Bňdc q@ OO9@|@<@`7B1 I@ O@C&b$)@ub<@\ [ $@X(lͅ6 ^ IENDB`lastfm-player-1.1.4_p1910/data/icon.png0000644000175000001440000003103610352604471016511 0ustar davidusersPNG  IHDR>agAMAOX2tEXtSoftwareAdobe ImageReadyqe<1IDATxڴ1 @ u鮃PB31N%yyyk$rRcߴ?j2 ~%9Gxz;Z[[wޙ0o C_K$YU+vwyb5-W{ xX]@ r@,b@ %X‚2zB!/@0I >oP8 5ĠfD?xGllˍsjP@>m ćx3j @C ^@(̀EAKX.3^ɳ@@2H52 XBwXǀb7I@5) c3м@h& NPJd ȇGgπ⎻@r70&97[@- i̡GP|Ľ6@" {Ah$> jHl>'" qp/ ot nq ⹈OdmOt)uK :@x#b=8qu>:GlR"?Gd.hHH^9b_Ԗ?G>D>Gv:Z ˁXޑ@NyG/$FF>1#Oȇ@V#d!DF hȬc9 LL`? "?48 Գ@s<0BK$߿ HG>2h P[n= dQZ\ >3} }`3ÿ_}g#ÿ? IT9nF~^FHQQa&?! rG$ D1d艈|3 #( ht,I:% ,+w~<@r ~9V:@s<(XX \"h!A-fK=VSM`&N6H?o[Ag1Q0,U Z&gPhr[( j)nE Z% 4W#\y]\~{ ޼g`d˄/;B-aJ Pdd`Ra`Se`a`S6߀n '+Ӂ 5@"tq\D>Hf_ ?\`j'ï3W=y.A9)&Q6 efwK/K oP'#aER+5(ئq$ r<;ÿ}nn0T`4:F>:9.aR v 2qpBOft R#5tjE>;mA1> loh /X uȇ=dJ@Q+2#\33ts|O?g݀L K`Pd`Ne`T @t@%@HQ@?MvAݸo;2|l10poG;`ba`Nf`v``6徃OfL^d'P6MrC4%+--v&V`vc N,Q9#2{:";d`g`v60Ao$0vjMDIM\ZjMfZ-o2|l+`p}|Z@cxgtc` ``ᄔD> | 5x rhv#_G`]uJH%*҆h`u\.R _#i&>1[J{Z"h3#| _exWcA`]ωu?"AS`Od`4ׄ&"$h%H`Ь!NR" oR~F>/7g\ _0+oYqN*-R ~I%7dP׵{ \ja,OQ8dٿX"?G=0Lr @@E9D>(Ĕ3{c'!҆hGJ]ÿ0_Nyc/P'6?JDJji (L LN <( ?b_=H?g 恆Ef10KKh#@[ Xq" s>.dD>l?;#زE Vh#?a`ks8F$݌W20N\9g(KKbK|"t>@0x=lrS F&:t<{Z,x nױ$oEmesР7=e6_π-m< Um —!>O6\>6̀86gO!$d-0 E~6́c<% Sy ȇf5`1[n2ge%R27C @ o< Y@Lx@M剏|h3|qב3u_1(3pu10I`R2P0|%.}9Lq^ L!N.,G]ޛjkp 'a7|# W p@'3 Ga@K7>úi52XﲘsrF9 `@YB0`u$/i8s>*aJa`C-a>}@t97c`\ L@@X|8#*g @Ji χzs0:On7V[C.Pˊs<]В T`G_p1"ȇV3L9!MbF ;P7aʀ3pdc9 @ :BzL>~YOY=pNĠ/\ bjj10E@r?t|X9ybJ\ 0~d;h#c.fE=K2Sj`'z9A9_((p} i}G%VG_D `Ou!aZ@ ?,Hb9qg-%A%01@N( &~Sdbh0~} ܀Z1eE,!wx$)#;j_Jȇd`u2@t䃸@Vru (?, f:"X2 2p3'\Ef zS# `b$6A!B1E>d~9I5%E&2zـb :a9!;N<}:G>20|%w@\L% |Pco<`sz~R#}M~_! J \KXu}r?π80100E8c|| {.0ga&%AlM )  ROۏ#,ZX,t$EZl7D˖g9)OQ) R"B|POZc~A`Q#Di4b& D|h# Ir 0QlU#`@9au:}t Ysb:·j'4Aj?^x[7 %F>_3 v+P!U`ʕn?L8S8TԂ?0J< %?4N .;Y=P_ mlМ<;F?D{~,.10v(&QJb$ǁ3D ``Hb`rRjŶ}9uX-!c@] Zżp;m!ԶbP{)]Jgbga`'Ho"Zn A> ?|f}"; q >`k _)4 `l۵@8+308E=]bD>$/210×{")_Hn i@,@AbX[^;޼c68* B{@ 2s -!A{t3#|xG?LL E>L\ X?j#v7CT?? F ~Do#UGF@]*Nkv" ̜D>H\ @`T: t,!O\nHm1`YߎoVf?8(#Hr3P3aoH(= RdH:t.xPrڜ|J&olè^#?1Q2;W[vאqF>W-!g \,ё /k],HՀDœh (0a';#0‡_b9]#*@܃|w?jZWp׈eXp:D>L?#E:h]h闼8Q?}q4@gD+@_z#h hjȇVˌo>Av=*F"D@|3+`jcf"&:TK#x]l10h+@9K'[>ְG (A815Sg`_ }@V'" #F>xH @-3a":I[coP @Kv8z#.].z n*1sa`k_لGWDG>@L(׮|$oW0Rã$dĭaXU0n10ѐvO db#D %.gH|Ϛ@"dF>9#u< G=E @b"I~Q@Y=R#y Ԓf6fGfVx, ~`n'&P}b# Pȇ:4 Aň @yZ9_30d @3̌p>@oaPq?T LC!]j.vFkgPV$`6MDgi|@LC+Е,#O"`D5X?!ʅῘȍA[л3HF`2= ˡDGN3B'Gs?,ڭ hcԀF>[cg]$23@a ȇg 5 _G8A @A/|xk/B] Ci\Ԟ#"򑷳rOD"`aEG`ˀEYɏ|)?1`€ MC^ BAG*eOgd$n(btzL #2l +%><T =@] _&w@m"ρ4? >ɍ|-^|=tȏ|Q^#D| B hILzH>{w}lqObnַC,Q=<#glz7iƨ D> P<#WDx=zg^j[ gPWbLJІiA*R e!9/-#yp+9nh2PD`xl}WLWf`r6a`Ԑo_#a0|`}/Ȇn1~]Ï^10t&xu`|_ٙJ3%lq:N @1AO|豫&x/Dp*^cU? x ^_O.#?R)C/A1c !ǰKz [ZkC It|1H Wz@]m}HT&:~b`e`00Jmew*HWy>g ;%z= y>nq}!mЩ cHغzԈ?8[3Oku |} @)b#6 2)JAM#b}𫠟O\/20{A߁à l%q( \l{t le`^1#Fh t 2d?%gzeBA)U3^KA-[ax򖁥v!Ł1 o^H"cL!"~`,-G>u :0:3Q\GȾZ t ӺeC]r?B@s× u.ރL22LPs1dk M|XKO~‘iA O  '*a-s`0jK%[0H3/MH"@oB[Lf`B|G. bWᛇ 䯄:@zRt9F>|xC3s? 8PWlK]ϴaD> { "~[Ta?0ҕc( H| 3 Ў]>@ϳ00]@Gʙ >1/]HGj| l_~0z h" g'm" `w6#)A1dx!e>4V.lila9 WP7-D@h.ycڿr!u.ɜH4$ & "%! s~i0р\Nɀ~: $@Y \$ظ?g(-RJUlJc,Ɠu퓚&00Y6!8| _k€ՁD@l9{I߼  {csG0Tq<@ȍ@4)qTVd`bڞDpGnUf`<| 'wJfzIL70a>b"~8KGợ60A@8Arг;@a/Jx;/#Њ/FUy=dhpW Lw/XD>AO @5A۰2ml`C >cKD?a$& HA7 ]&t?؁RQ!:Xgߨ0Z \)gm 21bDOj>8<}lOg q0|eel"+Jo ?T?$E>ZGڸۃ;-YǮtNѕ ]7030}reYs e+-r) GA /_= ~ƟqD>hO @]7 TK>ÿs75N}ԌA{- ?5Ə_o?m:ZOru ԥ׻{WK',ASUcF>\v^2 $|#9'X<}JG* C+W_f`rRwMo O!ۼрm1={zl;|s>_/ `<H=$e< A큟)I=~w4|Ɗ{V$6xg`~<Y! p b+|v'w)"-ǵ] NǮ0/ D >JK*G>P-5ƞᗙ 64Ǯ Dek?~3|Lt`g c`?1ˁub <&&0bm=0:I|"x 304#zpAS; ] =6sl7Ȃȉ|`K|}B &a51@[, ğ,=A7{.;^L `f)DF>| Rzt~+g@9| H-0 EZI?FfcJ"a}zh3KN  bBb @O"cP[69?u.(0@;m6g`l]-;tۮE9?0|7a,G̀R`dz0W b.$?p!3JgzK^=_`1c=?h)<]E>ė H!,ڮjO5ypbȉ|`/btH@<: HI-HctP g(@i3fbŧvd}KQmswOHW-2"]FN >Av{@Oא hCh׺ L300~6R-W1߿0|Lg'Ƈcw&'} 벣Ɋ|8hh?ޕ LK00o; c:H2|an%aLR2AW=&7vE. mBV# Zuwy1W!KHTq@aށX[3|Jrd 9p c%@HP:f#i"_`x2HMqrנhy2 ?ud>[2RĈs>Ȏ2VSuDG^ ZJ n208t\:}z/UIކ ]G}YOL1-&xkXRM5p `p Ƨo % +*A{ῂ< ?ue;hs?.6pw~N2VRk` EyE>BaUV.~gO\g`"tV@gbZ7@9t h[0v~3|wg,)~Bv$5S{.g Z$@NW;:>s=o'an˙(|3 @4@1s1d(>嗉"d_?HIHT|`W,S#@ |_Lq3 N  iI3Ҕ8-hoh?ˡG Fz7] pJRVV_*&_q~q?!mԢBy€(U ?HNH2|.mhb- w9.aL BT{rϐ"Znl1`:xt(?An<AzAu_?<'shՓ8[AU fA'(Ƅ#?inA[iZQ$ JЦ ТPPnUmcѠ@A9|J N 8_,CM9E@,ZG@# F`=t ) 9: @ L ǿ@/[Ss$"|7#b~ @OESE5 iC8,=SBp@FmbzE>=  .ۈdF>d9 VR@$U7||"s#0E3Y@%X"hgMg`1Z;m08h'!`5#߄ZOEE>I;9@w Dڥ5 &@[!e ct頏D>xf%A X|H+\&!|P(9Ú c@ǡA@ j+r |;gRiz {Pn @ } d P.bY' C VB1qY`|O .Źv{wy 0D@0 m&!J1`Y#NF|); :?"߁:3促nyo@#/09G9IENDB`lastfm-player-1.1.4_p1910/data/curve.png0000644000175000001440000000356310352604471016711 0ustar davidusersPNG  IHDR2&gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxbtV6h  y G?p%-#pb@b}8$-ym8@<:<@ ( <@a1@LP<@ аа@1 }0(i}? |@ |1@ A@ =rg\C5Fu#ÃG5F@xXr=RwbƚF`>9 @aȍ#ʔP,3Fu–Q@\Ԑ'u%60V5.\j \><@32j 1,%אU \_. X GP@½CgG@ƛ n X fuax nzܿC#D#C B0@x='Թ+DpC%?5 @0y 1@22\ #D#׀MnjL'{s D6pF l2wQ擫@`D|`}rl0'/"#dPgx"#1nh] H:#599X @sDGrp0&/"`s`DG40F b H0yzf%/" `>?<@yR1cH,[3?0b > Xzgx 0L1@{$4@`1=X<@n |@y`)"#ӎ+Db+Ǔ@j@ @QF`l3wD0ןmU<zf@^D#@㶁'D5c0Fl` ` +*Vce@^Dmd0 y VY&M+Dm=GL@Th;yM敭E( x}?0o~y hv705y<ob]A-d Pe.T!@b y)i@ }IENDB`lastfm-player-1.1.4_p1910/data/album.png0000644000175000001440000000152410352604471016660 0ustar davidusersPNG  IHDRgAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxbܲg#`>@l j@_@|Fb" u@lhrB@,.@\ 뀸!+ tCx&KCx_P`Ŏ@k` qP A~  Pu` QyP@muq ~W< > NPik  @,8j8Щ@l

ĂP3P= PzÁ?F#/hj а== WB} jA@ * S4~& hIPz ա@, l$ 5vBك$dy 4{ ŒvaEKbP @LЬ@M,BH|7P  Xyt&#io >FHAʿ@L|\4M酦М $'dF@6 hAXĮ@36{R@ =5$`dJ-$7 ١i(@i5fɅ@*y'4ǁbse(+@f(Ha:4Bx4:ECӰ&@ nPMi64g1r\th.;-ݷA6(@ A'hf@'UPW7AfMv GV\-} -! @j蓻U8IENDB`lastfm-player-1.1.4_p1910/data/expand.png0000644000175000001440000000042510352604471017036 0ustar davidusersPNG  IHDRvgAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxb0o= d abM@*$  Hb/ LP=G *p*p <"@LHO|@1)x A@@ď w@[B ~ Xp8@<98W4koIENDB`lastfm-player-1.1.4_p1910/data/nocover.gif0000644000175000001440000001063210352604471017214 0ustar davidusersGIF89aýʺ޳ľĿȿ»¼ʶøѻþ̿ź¸ضڹݽּŹٹ۾ƴ۵˿ܵʾĸɽøƺȼĹ˴Ƚ¶·÷ǹȼǻǻ˾ź˿ʿҸԽٹ׿иسƺҶĸɽ÷ɾʿʾ˾ȷźԹܺǼҿӴŸŹͶ۷Źʽฯѳ·¶ƻǺ̸!, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sɳϟ@ JѣH*]ʴӧPJJj#:t̰j 3LHFFJY!ѺQ[qӮm eaC`o"1q⁇g `yU-'!B$ʔ3oCa8)5Ŵ#3 )i!8221PDrc{F İTo'&bhgnC0C+[t!,T" @aiWgD !CK,6@ `7aY 1` Z0(>gC c ;CC;1Ȑb` 4`,`ixPB2060Tb N\ƀ#!*mӑm5d}LKrHDpG 6Q'tF0H@ 1 "Cr;a6@6*>`AVg^pI@9@[p2fXC<$"AjP d" ` 2\kM+C)5\qC_A0Rʰ- bPǪy0QnYD) N'e7, 6"TCE ApLreW^Cn lт;CQh@'<<81 -JrH zG'YZ7l0@vļ0  W0|H$0d$`B rTH@@ieB>z#PdEAV^dorH%+8 wpDƏ4$Ra`dJÛ&,@8B)3(`OC P,, ,-r P"X a||`-@@HPE4;-A &G`S4!K"BH33ZC 3"*8J@aG :> d@x0xJB P !}G>0 ip! %A  K)g \&V0⸁!K0 5D,-8 `vM@ha!>3 B %v С h)SQP@Cb oxB@l@&A$ HǐBn0˾M\Q&&8`Z r0A=pP \'\<1RANH) h4y& u8b8a $`RԢjz`býˆ"|ᄅpy;L޶W0.&D,$JTCm^ [xEp"Jz 8ջUthxPJ!Be.  Ì=E0.1 5(UJ D Q4` ?! ! PB+-[ g`@5N@&[Yd:My SLUjuEa"Ls!8 Ϻ]Ӻ*8(A@aC ]p(Fܐ hh kB2$`!LB; &PB&T?K B x$aE(wAs .䧇2B Vp B(́"t\ 7؂0 c % B7p% 7"#^lab<(e h`1  ?f0]@%L$,@-aX@L"(rZ%Aԙp.E_9 bh)4pC=e.@@eX0K p#j4pBIp$Zo|c5K pe |a>0xyπ4zU@{HFĀ )( E :1GP0 &&aC*00B?s0 °^;p `57P YY q,w՗dA'6P<@`t`" )?{A" Ӈ 6p@ ( ص- {^ GXY S"@  V;P [ (0 H0fmP3qB:R@ZP(`j1Q8UPBs:pXc1:HeP2` xzpW 5^D@`)gn*J@UPR9t=~x$wSp* N`"1@6(p  DH1 FbP $0PoI("PU NP6`i$L 1 1@N @@00 <@H{G'*3}0m3 }`YIVPu%n!+^ 4?K@?q!Y"!0p0"p kX?(^I A|}0 \(ѓ?E~ Ї (RnaW c &h 1M $س 0\ HkAyE` R g9 )0C  c @&%1p08RVax2 hPH :OD@o ,ɖ. p P`q`=8IR7@7 z@HVr`p `:Z" P%B `;KR qšm)0P%Wrpbǟ>@]@5loq@nP ; f @@ gg =r 0( " 5!PH)StYQYP P 0 b V`@3RĠ1eH {Ps=q<<+Q8"qddH"sMWH`b;d[f{hjl۶npr;t[v{xz|۷~;B;lastfm-player-1.1.4_p1910/data/progress_active.gif0000644000175000001440000007141110352604471020742 0ustar davidusersGIF89a !! NETSCAPE2.0,  ڋm!, ҢŒ쑙I8ͻ`xh d!, ҞI8ͻ`x—,xHC(aA!, Ҟܖ쑙I8ͻ`xt3$F Q4Y!, Ɩʙ)`%dihl뾯T4 +A@@T!, 塞הܣʽ¼Ң- &dihlA D+ 8@1 RC!, ʔҼܾü장צᢩ/&dihl/RU:DciHIA1 (VH`($0!, ü𖝓ПƼΞʢ4`'dihlǔ@NA8$@*1 *Dz:4. aQH!, ן󦬣üΙBpH,ȤR(:%FU"f6n`|B $CE2 BP! IA!, ןО󢩟ü۬FpH,HaII1&YH%-V]c &C@Iv X86d&` &T " IA!, 쟦𒚏мަ̬ξüL@pH, q)D(N Yrh$<E9B ' &' _ #%TA!, üަܱזПТQ@04H@CBPIgI]Pcud 5PTi@"ߓaXY +(+%h OYA!, 앝渽ʾýЬ󒚏оü埦ҙT0cxpd`A05(`[t y*IQ$V<>2L*V7!? &m > -14>/mB%0#.A!, ѓ𔛑ϡ嚡ұע۸ʜüגּw0= 9r)<$ 4b.' U(! KIl3|<|ƒhɴ:V80c+>V>L2 L% .)/-> L > $34>! V6 5?VA!, üљ𦬣ПΚҸ˧~Pd99xr)V>-L % L% ? 61,>J >(5>* o7L ! 3:B) ?A!, љО̱ѾüĻΨ墩ſҸ󖞓~00QF%r)4d>Ac.l"U1.K@N2 |ƀjص) V2c>V>5L(37L( ) 0>J >,!<>- o *K6$8 =:?A!, ̧孳ÙΦʼױܖƻü d$r)\c./_*"U(=KAn0|b ѵ3V ; c/>V>L &67L&3 ? 42>{ > !' >) oB8<1(B+ *=#A!, О孳Й𦬣욡ƕ֨üѢ渽󖞓Аxi$(r)H 4Cc. _&;"UHmK 0!|k1ǺK#%c3> Bt>L ,7.J>,) ? 51>!?>*:<> o'B8=?+ 6 -A!, ˞幾Й픜μǖ星Ҝü08ur)> Sc. 0;$UHEK 0!|k6Fbz;m  =/>.=c4> B>L)8J>) 9 ?1>"?>,:<>2 o%*B0  ?- & (A!, О乾ý𔜑㷼ɱҘޜٗ08y&%r)M> TCb.; S6UEKC!41|k ><0m= >> B>L=&6J& ?=*)>3?>,9:>.=o#B(85B-"*/A!, üˬ乾׼𦬣씜Ʊ樯㘟ќ󪰧@E)T'99NwS01&_&DD?d߶Vg#py> <yB= >>;/ty6> B>X=*J>*= +>t8?> #>!=v-,B9:%B14$.A!, ɾüˬ乾풚씜樯盢ѻ喞PI+T& E.Eh,aOhyb;DJ=j(> = }B < >>0xZ6> B>Z<"4J>"<2>k:?>%>#<z 'B)$B1 8/A!, Ⱦüˬ乾怜μ씜Ө֢һ㳸xI *0H͔wFBGQZ>æM*Xh O>@K dGw tB`4>= ;B= <8>>(|9> B><,:J>,=<2>|?>%>1<~8+B=7B0.3A! , ɾü⧮乾Й픜ז֨ѻƪU (p-2B^API)3:j g\E CDxx>xBc54>=B|y#">; <$B; =&>>8>B> =+*J >+;?=!3>6?>"%>,=&'B9;74#B20A!, ֧丽пýЦגּ𘟕¼Ƣѯ󗟔朣ڕCE(B 6B^`PI *$F÷R=@+hx;>xB>%)>'B|y<> <$#B= >>!<> B= >"0PB">* ?=/> ?> >9B&6 J-213A!, ֧пýЦଲϢ𺿹ﯵ¼ѷ윣㕝P8EF2*BP;1IӰ2D{g?hx >xB>9>.8 B |y<> <B =;>>#B="> ,PB>&( =)>"?>Ƃ;B -%0!$:5A!, ֧乾НۡЦ»練ޓڗƺ𯵭歳̜㞥隷Px96; &BPC2°<+wQ?bx#>xB>(>9B|y <> ><0>B="'PB>&, =)>"?5= >8> B ?+ 3*$A!, ֧乾ە旟﷼Ƚ¼󭳪!AB&8BƮP5-!b**o{?Dhx8>xB>7>6(B|y  > B=6>>!:>B=#)PB6> 3*=.> ? x= >2,>x + 95%A!, 乾ەЬΓ󞥛̽¼ž﷼㜣嗟ý4*34GPq+Jw Kfky?@Bx#> xB >$> 4B|y > &B =<>>J9>B= :B'PB0>?=,> ?>1*>( .)6A!, 乾Т˧᧭溿Ѯ֗ý̽¼⒚%+pd,BCsPo*@ Re3kє?Xx>xB > %>:5 J'B (> x =.>>JtB=;B 2PB0>?="> #>(>?&8)A!, ѕ̦퓛اᔜ⧭亿ۨ󞥜ޚ¼ѿýr%('qtBPm1a ZG$+c?L%@8xxo+>4* ;>J  9!>>B:>B7?PB2>">= ?&>3$> ? 85%#A!, ѭ˓Υ؛湾󙠖їƨȷ޼¼ý丽5%2&8Z(!SXVe'F |pTXh~fN+$>,0}Q 8>7B15<>> ?>B23PB,>>6 ?">&9/>? '!A!, ؚરЬ຿їڧ䙠䔜¼ᡨۿý}P4BB׫ [@:Z'U|c ϴz 4'I%>9,B$<.>> ?>3B 1 0OB>>7?;>6(> ? '"!A!, й¼󺿹󞥜ȭý᧭ۗƙ㧭wps bD #! $|Scd["AVV2~0|p03>${? %")>> >B1 6 -MB*>> ?'><2>?5(A!, ؘΚ䔜Ѽۧᔜȧý¼󣩠qpHX ['q00ڠg-PIH+B.h Y |nv 1<<*v5<B) v KB4PuKB01l1 t l1$!1u-&/A!, 󕝒ȗռᢩܙ󬲩ڹД¼޿ýQpHrDB(dh/X4LX$ L4XGL,֪> U6|D,B|*,| A!, Γý¼՚˪ח⹾ژ󢩟ޕLpHNJJ h1hRX \TL$$T'**> , |D *B+*A!, Β¼ýṾ☟Ц؞КI@pHf0! IhI!(YHHNрL,NF"6>(.|D'BBCA!, 󓛐᪰չƙпý˽¼ОApH&nh=$@X$ |DtuA!, Γýឥн¼8@pH> f6h%Xp>ŅL44>M<0(~!, ޔڦƘԢК¼ؙ.&h f6h%Xp>ŅL44>M<0(~!, 󓛐᪰չƙпý˽¼ОApH&nh=$@X$ |DtuA!, Β¼ýṾ☟Ц؞КI@pHf0! IhI!(YHHNрL,NF"6>(.|D'BBCA!, Γý¼՚˪ח⹾ژ󢩟ޕLpHNJJ h1hRX \TL$$T'**> , |D *B+*A!, 󕝒ȗռᢩܙ󬲩ڹД¼޿ýQpHrDB(dh/X4LX$ L4XGL,֪> U6|D,B|*,| A!, Θ¼Цýֽ»ۗ㹾󙠖䔜ȼY@pHNFKFh%1 XDb L4PuKB01l1 t l1$!1u-&/A!, Γ՘˪Ƨ¼󦬣۞ý㢩b@pH~:W<-ha0!X PnLL1n̢nU(!7nB,d'KB7d7 t3l$74."7l862&1A!, تڔмޢ¼♠ѿýѬj@pHr@ʠf.ZhS91X$K+5: h\'99,f59/B0!f 1KB9f29{ 7p9+9p: & 6*A!, ؘΚ䔜Ѽۧᔜȧý¼󣩠qpHX ['q00ڠg-PIH+B.h Y |nv 1<<*v5<B) v KB${? %")>> >B1 6 -MB*>> ?'><2>?5(A!, ؚરЬ຿їڧ䙠䔜¼ᡨۿý}P4BB׫ [@:Z'U|c ϴz 4'I%>9,B$<.>> ?>3B 1 0OB>>7?;>6(> ? '"!A!, ѭ˓Υ؛湾󙠖їƨȷ޼¼ý丽5%2&8Z(!SXVe'F |pTXh~fN+$>,0}Q 8>7B15<>> ?>B23PB,>>6 ?">&9/>? '!A!, ѕ̦퓛اᔜ⧭亿ۨ󞥜ޚ¼ѿýr%('qtBPm1a ZG$+c?L%@8xxo+>4* ;>J  9!>>B:>B7?PB2>">= ?&>3$> ? 85%#A!, 乾Т˧᧭溿Ѯ֗ý̽¼⒚%+pd,BCsPo*@ Re3kє?Xx>xB > %>:5 J'B (> x =.>>JtB=;B 2PB0>?="> #>(>?&8)A!, 乾ەЬΓ󞥛̽¼ž﷼㜣嗟ý4*34GPq+Jw Kfky?@Bx#> xB >$> 4B|y > &B =<>>J9>B= :B'PB0>?=,> ?>1*>( .)6A!, ֧乾ە旟﷼Ƚ¼󭳪!AB&8BƮP5-!b**o{?Dhx8>xB>7>6(B|y  > B=6>>!:>B=#)PB6> 3*=.> ? x= >2,>x + 95%A!, ֧乾НۡЦ»練ޓڗƺ𯵭歳̜㞥隷Px96; &BPC2°<+wQ?bx#>xB>(>9B|y <> ><0>B="'PB>&, =)>"?5= >8> B ?+ 3*$A!, ֧пýЦଲϢ𺿹ﯵ¼ѷ윣㕝P8EF2*BP;1IӰ2D{g?hx >xB>9>.8 B |y<> <B =;>>#B="> ,PB>&( =)>"?>Ƃ;B -%0!$:5A!, ֧丽пýЦגּ𘟕¼Ƣѯ󗟔朣ڕCE(B 6B^`PI *$F÷R=@+hx;>xB>%)>'B|y<> <$#B= >>!<> B= >"0PB">* ?=/> ?> >9B&6 J-213A!, ɾü⧮乾Й픜ז֨ѻƪU (p-2B^API)3:j g\E CDxx>xBc54>=B|y#">; <$B; =&>>8>B> =+*J >+;?=!3>6?>"%>,=&'B9;74#B20A!, Ⱦüˬ乾怜μ씜Ө֢һ㳸xI *0H͔wFBGQZ>æM*Xh O>@K dGw tB`4>= ;B= <8>>(|9> B><,:J>,=<2>|?>%>1<~8+B=7B0.3A!, ɾüˬ乾풚씜樯盢ѻ喞PI+T& E.Eh,aOhyb;DJ=j(> = }B < >>0xZ6> B>Z<"4J>"<2>k:?>%>#<z 'B)$B1 8/A!, üˬ乾׼𦬣씜Ʊ樯㘟ќ󪰧@E)T'99NwS01&_&DD?d߶Vg#py> <yB= >>;/ty6> B>X=*J>*= +>t8?> #>!=v-,B9:%B14$.A!, О乾ý𔜑㷼ɱҘޜٗ08y&%r)M> TCb.; S6UEKC!41|k ><0m= >> B>L=&6J& ?=*)>3?>,9:>.=o#B(85B-"*/A!, ˞幾Й픜μǖ星Ҝü08ur)> Sc. 0;$UHEK 0!|k6Fbz;m  =/>.=c4> B>L)8J>) 9 ?1>"?>,:<>2 o%*B0  ?- & (A!, О孳Й𦬣욡ƕ֨üѢ渽󖞓Аxi$(r)H 4Cc. _&;"UHmK 0!|k1ǺK#%c3> Bt>L ,7.J>,) ? 51>!?>*:<> o'B8=?+ 6 -A!, ̧孳ÙΦʼױܖƻü d$r)\c./_*"U(=KAn0|b ѵ3V ; c/>V>L &67L&3 ? 42>{ > !' >) oB8<1(B+ *=#A!, љО̱ѾüĻΨ墩ſҸ󖞓~00QF%r)4d>Ac.l"U1.K@N2 |ƀjص) V2c>V>5L(37L( ) 0>J >,!<>- o *K6$8 =:?A!, üљ𦬣ПΚҸ˧~Pd99xr)V>-L % L% ? 61,>J >(5>* o7L ! 3:B) ?A!, ѓ𔛑ϡ嚡ұע۸ʜüגּw0= 9r)<$ 4b.' U(! KIl3|<|ƒhɴ:V80c+>V>L2 L% .)/-> L > $34>! V6 5?VA!, Г짮Δü墩ʙ柦זݼ䬲Сoa pr)$!a.g6Ux-KFF7u|;k=k )V6"c(>V<>2L*V7!? &m > -14>/mB%0#.A!, Ц̭ܾüƭ䢩Пθk@PF)r)4;tb.]7r:U! K*4v1bמƄeZklZ@ kjzy 05JMJ߆N1߆6߆ހٿ z w ݀ l ݁ g߀ ݂ϰ݂@݂܁B[݂܂Sm؀[m؀Z[ނ܅RBހڅ A߀׃ ހ ց l߂e݀ڀӀ{݂ڂ r ݂܅¾6ނ܅ۀ¾8P܅QPقR79ys if CS\[RA>YkkY@ kjzx 0ջ5JZ* !! 'PMJ9$%#""##"!!0N1d#$%#""##"! O5S$$%#""##"!0&=ؾ zd$$%#""(?"! IخJv#$%#""/1!DI|k9$%#""4:!!J߄%g$%#""::!N߄|ϰY%#-w:#߄9@*#)9vB[ "b7@Sm!"n6I[m!#n6O[[!#d6RB&"+6t_AO!!43i .钀! >3Sflªk/! 83Kef /-I_m{N&DHş4t ; ,")6I49 PՃ$ mRPz8-kS7:xt iªg CT\\SB=XijX? jgzu 0޿3JrJBDDBGjKJWEEDPJ1|DEEDi3mEDPIC[ζ z{EDIZD cҲBCeqDEEDNQDc׋C kWEED SXDDdבC KbEED WXDgבCĦqEDMWFבCZ?IDIWCCB>ZBDwVCBAC=BNkCDVDfCBBVkDDUDjBUYBDxUCCECBMAGDLUCuCBA=iDDRUE|CCB T㞁D C\UCkzB }iODDC WTCCgxB F^ޝDCQPCetB}whCKaC BBdABXk ZCBSLBP4dCBW5M͐JCBF}LMYBAS}L5ĸ5 rl c_ ?OVVM=l8mk+VprZ/ip y8CQ^Q_7D zi{,;Xhs|t~\k/?q%BR`qbrIT "|т(?4# %ċ+}ˀ ~9?ir$Ѵ+ :vE'! #=iB )A %%$"# !39 ;{%$$%$"#"! eC $&$$%%#"#"!v-"$%$"#! wحk'$%$"#"! ;+tx9"$%#"!##"!#%nC&$%$"rH!1ӣz$%$"$!j߄U& %$$%#"%!#} A$%%$"!!#ϼ&!$%$"*! r߆)<t%%#"!Edd f'K̍D%$" '!&$"!&n #""/(*5!""?8GGEDC3=3A!""?[n&|8B!"#@޹gԀx*{86!##?ݹZyyxxwvvtg4%##3ܸ#*##" ܷhR);#!!۶m]½g!#[usٴmd?-! /سa~B1!׳[  !! (ղ `s{!c! "԰p_>,…W As$OIxs ?,O~ pO-lN5 EٲS =M<׋" rO FʎJ >|M/̷zy5|}FJ)|-?¾D +5995,  !1>?3" $*|{9ݡ=io$Խܩ* :ʉaFAC AEZ~@ )^BEED BS7 ;FEED CzA$GEDC+CED CBϦkHEDCYMCr9CEDCCDFICCB@GEDCcDCRCBɛEDEADC}בBCn%~FEEDFADEגBCBv^EDBADDCוBCIIJ&CEDKADCגBCB8EDC`zyACzגBC$g`EDDBݿACؖBC#JFDDCܾACIBCA#ADDOڽ@C$BA'5CDD\ټ@CBVbba``\CB+0@CDD[ػ@CqɀCB*5ACDD[׹@B{ƆB*46CDD[ֹ@CBq{DB0$ADDRԸ@CBAB A(݀DCӷ@CE|kBCB @XDBҵ@BsBCB F|DErеABxBCB_-BDCQϳACBwABA=QDCͳACCBqABD}CDCJ̱ACHtABAr!yDDCH˰ACAsB^)‡CBoCBBAKBA?چBCBIBCBGJBiDtօBCB A_RBBAitׂBCBAi+BCBAh1 CдmBCB A]G:πHBCB ADI D—fI@AB A@E^G-²1vtBD&xs*;? (1651(h8mk  &r@Xp.p"U 5B)SQ .SUoAW0&bu!FbuyjS . BuAr.v  =3FW U 3"@[ڇ Ew2buB$Hdv{lT0ics#HSɨj$Rʳgics4 CC42CKMC,$ KB$Ĵ4L _` SX Aʰ¬>G}yEJH C? cQDDNZCSED/GN=REDguDLpKKEDpܝMCCvBQkߛ|CWHuCܙ]}LBCؖ`LBNlՔuCBBFpIDCp̒M}BBl?LCCfpCKfG@ױKCBBFJ9 [нuHBBFlR >ſ:Cvp?s8mkadFKgnDLaj£enJRoxOTjn!icm#8?GͫY) $-˫???icm4hL CB$B#BL$,K$L DC<,MC$,L$2CDLBDMܯ,Mݺicm822?"?@"@?+@"28?+@2+?@,29?@"?+@ %?9??WF2989FE,\?2%91@F@+++@E89"?F2P+ 9EF?F?2+,89982,zVVVWit32dd %?Teu{iVD/'NqzV3 IzՈU$ HV,oڄ;@ PFZ?R)A n:łQs  # =Cgiހ݆ށށߏ ރߔ ߞޅߘ އߝ޴އߟݩވߠ ܦމߠރ݀i݊ߠޅ߀݀ۘDފߠވ݃݀q"ދߠމހ݀Eދߠދ݀"xދߠލ݀ۀפ7ދߠޏ݀ۀ؀c ɂދߠސڀ؁*s݌ߟބޅ܁ڀ؂֠'ތߟބރ݁ۀـ؂Oތߟބ߂݃ـ؂ս>ߟބހ݅ـ؂jދߌߋބހ݇؃Bދߋބހ݉ڂ tߊ ݇ބހ݋؀ ?ފߊ ݆ބހ݌h݊ߊ ބހݎ Ҁ,ߊބހݐҀNtމߊބހݑـҀϥߊ݂ބހݒՀҀЀ0Hވߋ݁ބ߀ݒҀЁsߋބݒ܁рЁοކߌބݒ܁׀рЁ<Jޅߋބݒ܃Ёnߌބݒ܄ԀЁ̱ πބߌހݒ܅׃+*ރ߄ނ݀ݒ܇с̀LP߃ݒ܈̀twނ߃ހݒ܉̀ʪށ߃ ݒ܋̀߃ ށݒ܍Ѐ̀5+߄݂ޒ܎̀KB߄ݒ܎Ӂ^Y߆ݒ܏Հ pj݆ݑܐ̀}|݅߇ݑܒ Ɖ ߆ݑہ܏΀Ɣ ߅݂܁ۄځ؁؎ Ş ߄݁ڀ؁Ҁڌ Ť߆ރ݁؂܋ƀ ނ݁؂ Ҁьπƀĭ̀ ށ݁؃ Ҁь΀Ɓí ߆ ހ݁ۀ؃ՀҀӌƁç  ݃؂ Ҁٌƀâ ݆ ޅ؂Ӂ܌̀ƀĀ ޑ܁ۆڎƀā ݐܝɁān݅ ހݏܝƀā~]݆ ހݏܝՀāpJ ހݏܝсā`2 ހۆܝۀāO݈ ހۅ׀ڝۂā; ݈ ހۄրٛۂǂ#݉ހۃ׀ٙۂԂ Zހۃۀܖۃ€~6ރހڄ׀ܔۅR݈އހڄՀܒۄ3݇ފ݁ހ څԀܐۅ΁Vތހ ڄӀҀ܎ۇȀ| (ޏހ ڃ܁ҁЀ܌ۆԀFޓ ڂ܃րЀ܊ۇYސ ؀ځ܅րЁ܈ۇĀޑ݁ۀ؁ڀ܆ւ܆ۈ<ޑ݃ڀ؁܈̀܄ۈ=݄ـ؂܊Հ̀܂ۉʿ_݆؃،Հ̀܀ۉ#U݈ڂ׍Ձۋǿ}܌݊ۀ׎ԁۉϿ0d݌ڏˀɉÿݎ׀ّʀۇ̿1Y ވݐٔɀۆр ݑܘȀۅ¿' C܄ݑܚрۅŁj܂ݑܝۅɁ"܀ݑܝۅˁC[ݓܝۋˁېܝۋˁʀێܝۋˁ=BހیܝۋɁjqހڊܝۋŁ ށڈܝۊ¿ށ څܝۊ,ށ ۂܝۉÿ-ށ؂ـܝۈ 5 ؁؝ۇ¿ @ ؂ٚۆ A ؂ ܔۄ 7؂ܐۂÁ .؂܊ڀā , {؂ Ҁ ڂ܁ Nǂ ҀЀ̀΂Ą m * ҀЁƁŀā? lҀЁ̀ ƀā 2ҀЁ̀ ƀāEVҀЁ̀ ƀākqҀЁ̀ ƀā,'{Ё̀ ƀā4&lЁ̀ ƀā~4T̀ ƀāa(6x̀ ƀāACrƀāJ" -Pyƀā T6 !8NbqscQ<' %?Seu̿ziVD. 'NpzU2IzԈT$ HV+oك:@PFZ?R)A n:Qs#ށ =C%еhL0()**)(*=3ـ׀ԟ'}#$%"#"!X[@؀׀ՀO"$%"#"!!"#"! L#Xۀ׀Ձ>'$%"# ''#"! LW{ׁՁiC$%"#'ƕ-!L߀c߀ՁBu$%" #."!L37ـՁt%!$%" #4i!LՁ>]$%" #;!L'܁Ҁh#$%" B!L ^Ҁо+B$%" F!L݀ҀрNt$%"N!LJՀҀрϥـ6$%" !W!LҀрЀ0H$%" q!ZHԀрЀs4$%" !^рЁπ#$%" !^WЁπ<JV$%" !^Ёπ΀n#"$$%" !^ہπ΀̱ $$%"  !U@π΀+*J$%" '. --!"Ձ΀̀LP%"%"$Z! :ڀ΀̀tv$%"H! ; T΀̀ʪv%"!c!! "Ҁ̀ 9 %"Y!  ]̀5+'#%%"0 PìʀKB"$%"! n؀ʀ^X$" 'Gʀpj#"N&ʀɀ~|c"|ʀɀNJ@"ʀɀǔ)"-e{}|{{zyxwtTʀɀƞ("; ٛ#Ҁɀƥʀ+"ڀ׀Ձ ɀǀ("#4ڀ׀Ձр֯ɀǀŮ'" ##<܀ׁՁҀյҀǁĮˁ *"##ـׁՁҀ ܕǁĨ )"#_ڀ؁Հ ۿ8ǀģ("#Q̀ˀʀǁ 7ǀŀ8""#ހ''&#"#!ǀŀ×Q"#ހ̀ŀÍmw#cހŀĀ]#"0ހ݀ 4ŀāqJ"#"! ހ݁\ŀāa2$!#"!Kހ݁`s+ zŀāP+ #"! ހ݁1KŀāÀ;Z##"!ހ݁ LɀāÀ$#"!|ހ݁0؀L9āÀ Z! ! Dހ݁=րK oāÀ50! 2O^ba_`߀ހ݁ڀ'KāÀ€Sw! 6ށ݁ڀ~K'ǁÀ€3 ! /݁ڀH  g΁À€U5! +݁ڀӀH ˀÀ€~(! 3ڀӁH  7À€G!! -ڀG  ~À€Xj! *ڀ{ӀπP )ǁ€"! nҁH  |ρ€=l! n΀H *ƀ€=%! uրnрG  ~̀€`}! lրǹG 3€$U7! aۀmG€~ !! WՀvmC  V€1c G  P1lʀѭ€#GչQl̀ɀH  m€2XFH lɁU  EĀ ` l˽* (ǀ'BC utˀl1gg̀"(  tD["nހ&nހ,r>B߀ހ݁: kpހ݁S ) ހ݁u Cˀހ݁ڀ4 lǀ-݁ڀЃ!QĀ-݁ڀ V 2€6ڀءC '}ÁAڀ׀ њC )yAڀ׀ր ͙Q  3{ǀ€8׀րՀլ~<  "bǁ€/׀րՀ˧K  4nÁ€-{׀րՀҀѺh> /WwĀÀ€NƀրՀҀЀʾƀĀÀ€n *րՀҀЁπłĀÀ€@ kՀҀЁπ΀̀ʀɀǀŁĀÀ€2ҀрЀπ΀̀ʀɀǀŁĀÀ€FVҀрЀπ΀̀ʀɀǀŁĀÀ€lqɀрЀπ΀̀ʀɀǀŁĀÀ€-'{ˀЀπ΀̀ʀɀǀŁĀÁ5&lπ΀̀ʀɀǀŁĀÁ5T΀̀ʀɀǀŁĀÁb(5x̀ʀɀǀŁĀÁBCrʁǀŁĀÁK" -PyÀǀŁĀÀU6 !9NbstdR=' %>SdtɽyhUC. 'Mo߱xT2Hy υS# H T+n Հ9@ N FW ?P) ߯? n ߀ : ߀O s߀݀ #߀݀ۼ;C ԾhRJK M]t݀di(ҳ[HC=9;>ABBAABBA?=9CD C@;A\݀؀ V>=CED C?C=MЀ΁(s:ED>[[IAEDA=GG?AD;fނoLDCD;f؀S@C=YˁɀoGBED =R}w>EDƀŀ-H=EDB?EDB;_€(*g?EDB<;HNML€mu?EED;cY׀ݜA@Ch݀;CB@Mvz{;D:݀;CB@F€`?D8݀;C@;9:=BCBADĿMAD8݀ ;C>OymF@CBABſ JAD9݀ ;C?Yڀ؀ҁ̟IACBA@ľǀ KAD9݀ ;CBAр΁́ʁЌ>CCBA?ľIBD9݀ ;C@Sр΁ˀʁɭFABA?ĽHBD9݀ڀ؀ߕ;C?ZӀ΁ˀʁȀȲG@BA?ÀJBD9݀ڀ؀ޔ;CAGЀ΁ˀʁȀ͛?ABA?¼KBD9ހ ؀ޔ;C>uс̀ˀʀȀ͸Z>BA@¼KBD9ހ ؀ޓ;C>iƀY=BAAX@D9ހ ؀ݓ;CYiH @CDC@ ؀ ے;CA<;;@CB;s[1FCD?e߁ ؀ ے;C?EvO>CB <JLAD< ؀ ے;C?SԻf;CB AA7s[ XCCDB=`ށ݀ Ӏّ=QhtwvuvӀҀؑ=<<;8UӀҀؑˁeZAEBDC=OӀҀ֏@΀Տ΀Ԏĺc:B>Q<HBDCA<΀̀ӎW !SW?DCB;wрӐt >DCB:pˀӅ;CBA=~a>B =n-`݀c=DC BEbdG>CBA=}o>ACB A=|Q?B=P$A݀ a;CB A<ƒ>B?Ed݀ S? X؀߮G=CBA@AB @?}{؀ݪJ=CB @>}؀ ܱOA9@؀ ۹[;BCB =Dbm؀m>k*ӀҀЀԷp?=BCB?;V1ӀҀЀπͦb=>BCB?;P;ҀЀρ ȡa>BCB A?;@Lw+Ѐρ́ʁȀ ©hGB<:=@@A@@>;;ADX)vρ́ʁȀǀ"Ƶ{^HFCA??==??ACEGUpḰɀȀǀĀȿe (́ɀȀŀĀ ; fȁɀȀŀ€x0ɀȀǀŀ€ARȀǀŀ€dkǀŀ€|*%u€ŀ€1$f€ u1O€ [%2q |=?lvE  *KszO2 5I\ly{l]L8% t8mk@ &AVhyȸo[H1(Ps[6J|[' I],p@AXGc@[ )H n#:[s #ECuiiD"R(x 7u 2s'_>&C%?,`u;IKK 6+_Ry%C,_Dx[msbN5iN/`9nD\+^'`QC 0]BmCb5J&\f#!TK $=>I!X!Y L@= &\2W )<_ h" =/G.G!g6 BXSc.8dqH *Hc}ȻlP4lastfm-player-1.1.4_p1910/data/progress_inactive.gif0000644000175000001440000000061510352604471021267 0ustar davidusersGIF89a »!, ptk p|&B\ Qk!AK4M\L0A .LkPBCpx:xBc,: 6B|y8:4.85B46 ::9!:7B6 : 68aB :94';6$ 6/:35;9d*:2 B449)37V+A;lastfm-player-1.1.4_p1910/data/buttons/0000755000175000001440000000000010352604605016545 5ustar daviduserslastfm-player-1.1.4_p1910/data/buttons/stop_enabled.png0000644000175000001440000000434310352604470021716 0ustar davidusersPNG  IHDR$$gAMAOX2tEXtSoftwareAdobe ImageReadyqe<uIDATxb?`0@ ( P+@EKr|jGpzogH4:$;jL30Z+dN^ iZQRqxy2]?>)RT€QZt l$|z@@kX@DWq@ ~cae`bd``geZ , <@1V@%P:(mbAB#D-ċh0 z+ -0 @J3;&pB[e@}L#DfH ޘs/]zw ں`-͋y;zN0O30sH\20C o'.c4?(wJHq C\f=p/ h`a``[ïo#9-)da+a7u u&B ~}pM !"n E \ŀ+3231~^TpF?>.e`Wa`c0=C7 ( %~l#  i T LtXXXc /.gXQs?$;)#qB a?_t?> @ B?JGhߊ5[/( <ʀԇiX2 B+JZ 6a2e=6E;3 *e@ss @!eP`Ԟafԏ+`r`ba-A-`ß584MJzB(1(YH17Ta'0A` `&^.`rTxdz?%%Wd5a& @\O>eW]ؐuexd ϻ!%>|N9)9O {f.Ae wPSԂ|@NjgAAtix9>l4\12S\:i8o3@ݦ_$6NĂRKPǖA(/_ S3cbT6A4`7.N#7cQu-"`3A0?\ @( 3ßp#/0=`7BZz "0-1E@'D00)J2|_pTkl΂| MPgfpPz TT@,f x"Z_6;Y-=P =&@fgB `Qvz I 3'9& $6K~!! lB,fL[&ޟ@-:9cD`k狁X44En!s #}?U=BwYTo t0䤁4? T ׁ>Iɔ8FhЍwfhbIENDB`lastfm-player-1.1.4_p1910/data/buttons/play_hover.png0000644000175000001440000000431410352604470021425 0ustar davidusersPNG  IHDR$$bKGD pHYs  tIME 0OYIDATXõˏu{l&8cC<&BYE(l"*b "D vl "&Č===]dQ1ȕJS}W;E }@&" @] `h `h`0>s@ u0XE`]LnVn-v`'ݭ*Ky&MicG p5+` SD ("6 b-Ba.?yζcV80I& p(F* >6HZbTR5T6/CJ`*H"bJshp/]rQ6`̨cp/o q`Whbmc2 =x7}݃?2]iϝ6=W,q T5p`3߻̌Ky'KĻx\Ϙ$@JarړR06l#ilٺTX#{S20~gcꉧa?_3 #|~MS$y߃ pij#|3>P//U2rх SW*(=b-})i۶sykݸpՀ]~cߘz7oHr>E6ϼHסGEX\: ƭnjq0nA30*SzxܦQ _j#`&Wpy\ys&) '\zK$b^f>esMi3B{/Ar>T@c_PC62"& 3F1; lȎ*i'R+iC>?X+{(@ZA_mGف;Pյ~VCSo|9`D$pM*iJ֌%ƘrUlئ{(NL Qg?9')Ԑߎ e6N\3beJ4 F=(^~ffξ=UgbLJ(B=q 0j50'Jj"͐/A6>]3%jCw+-*۶N?o[V;3p4 mYb-ϼQW:+(L3f!IXX093blO=z</ ac8#};|0,/gȘKl71 !we6I ryrxw zj'ƝK0;;jU(Iα~ E% ޕ;!߆!Jf#o瀋Dbvys7=4.;I)dvi+"{w' H "JU#1N2m 5 IZZ<pqa6Hm`<4oP8μgмH5I2a.~bJ{)gWS@n6tlQ+w7{͵WVLQD V2FXKjl6qzxhܮ6T!`+psε@0aIĨ 狔K Tzhgnu;jfnP\bᣎ{ݜOJ7\(V^5{Cꮕ.`U锝n6ju:-Z-"iEIENDB`lastfm-player-1.1.4_p1910/data/buttons/love_enabled.png0000644000175000001440000000474510352604470021704 0ustar davidusersPNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe< wIDATxڌP6`A35 %][z9d&3 ث[se->b$J@ < h A40 -_. Bmb3H׿ Z'!:t{l \E@l9B;ç0*7 A'd dk71|pFF~~ccvbRoO2޺ 4w) 0ÿ!T0x_YL}v3 (3h1f0023}{uLFqo7)M 2(1vQK ^:WvwN_P(Z +^,NdIbˉGXxytaX`0 *;N0y0^,()\}<}YX5\Vaj$#| tg N_cJ{ &`4dI`<IQB|& s#? Mpq/ &p@~2|_S 8je X0B4@E`Ez;/Wo&߿濿!P:1#0Ѥ;٢u ._kɿP0 K*f^hL Oexpٖ:sfE;9 @LC`,*d%W8@w]ϭaxn'ɖzbTï7o-*@ssfl/e PYY3Hp#9(R;gFxw&/_DU>U3xeԲ:`6Q9~?~e^;O0 !01_P TP bJTPZ[n-@`+;Ob`Y4$`)7(7AMZł*ѷ| (l XhRi@r5AZ8*THTwh ~HL`}8CMyqIENDB`lastfm-player-1.1.4_p1910/data/buttons/stop_hover.png0000644000175000001440000000533510352604470021451 0ustar davidusersPNG  IHDR$$gAMAOX2tEXtSoftwareAdobe ImageReadyqe< oIDATxt DĐLI\p``ֈsN\kECfΨx𡯞ĈA B-Y,Ģ@,b@,uT-A |@_B1*sQ( t! RI b VbeJ@}"c/ 2##of&, ~ϡ ͿȎ d PH*kk= t?0:D!22av߂:%4:@CBv 1P9DM5 Ku31/??af'a#x${ oS BpH Ra$$$!JX~iwO1|{Gg`a``c4d`f;>|[a! Q6v.@! XQ M3А9 5z~}l61|>{Ϗ/иfIl N ! bB |c+5DeY9n(i @(c:F2@`>p @3+;#~î Ze~300HJ):j P M SE ^܁ S2*k{Np~'8] G33߯_ l GYXXUMȂЬMr:`x@-#;0$YOYv2,L@3|'f,-b8]BO Adbx0d9 " Lh0a-0~20CÔ L >6 ?ex)+ =h{@,@ MP[%h P361pʃsjE7LgH9cR}Fa>`ի ? &h/ICB/8-HB>]V~>VA~V!($ϠP&.q';O \x}ׯP7A( AË 9pH1 X9)X~8@[Eh@L(MA/&v#踿1:}?3XӧD @ q\,YA 8PB/n Xsׯ@ Ap=O3¿nư N'fA㻏C3WAV_Ă(큎a4+@kjQĄ8~ZT9%PeUp @[~ *e@j/p@rcbdj]@f6pM&& Tps6i񎉙?` \ƚ J`%=,0kg] %/q ~<}A~z䒒N.ffaa^'bvK^zlf`lH2[G LNmr]>'= 26qY..F;UG?rE\{ N@?tK4y34Opc+- L@A^NE5?@ =W *#52E3, S,cbti2R@m 7| &hv{]<t8 c̏W"1h& R<͠s*a ~ 憆DKd6[8},Vb@^ ޾@ |\YP&PȊ˛,-Ё  T}eo@ @|&h q=?86ߏ_W?v`w0a b120ʈ2cef#16OD@@|{P! @+3Rc_7'A rp9eq؞}؄x? qڊrX r{MUP L zED$@!s]Iܥ `zNF߿Yyg,B l KAF&pubJCPV}܋@|ְcp `{~h4F`#gJ޽m'P [@U@fvEEdeB4W@p l X`0HAMϟ>}|_?3}vv bvw8`~w hCs3h +Fh |PA{~Ti7ǟQ<`*:}lϐҨ'46Ґ'a=ؐO|CzQN`IENDB`lastfm-player-1.1.4_p1910/data/buttons/skip_disabled.png0000644000175000001440000000470610352604470022057 0ustar davidusersPNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe< XIDATxڌ@Æ!AwqbI+6%ѿeC`1 zn WGse->b!f> :@h9 A4+d w3 X>6N`QV&61(eea`df`~ t_z} +@|b p9X  @wOO3;x Jg`a``c4d`f;ÿفf tЬB ~ vba~}l61|>{Ϗ/`G2q3pj+38120 1 e`0"[@bo Yfa[ ]X(] da`WUfHba?p @!;>@, x8u!ív_Gq~A(?A0'? ~b[`*K]_ϩc3;CↁR.`x;q&` a`V5d? Ą !q G>oc`bbfMbwA.v Q ,Ng7CK(cac0e8:BT>@vpV (ؿ102$1K3(0hNkƈa7;!GK^-ӁGu1|] ̌ cZt@M  g62|p@f~Y COg.1|fa[:D5F؁' _e JW N:0^,N8UxRa2 ZH!m ?9 FV;N0yX2JOv`% @LB2|ŽFa3i| w($WMa4Bg N_*̠ % /EA ~~&6|T|#$4k!9a9 ^$NI1e!Rs7ǿA@?8N~2|O = %$.Cu!i`y  V$}?G sPC&) t_/!!&*̠9 *W"07?`eVAqmP&N<0@,~LYZB ^mdrDe`q6 10,jU gTP &` !,*d%:@.AzB_j 2p+AB'Ky ov;AT oJͪ93VfRU2_5UOcRGÕb{03UDӿd<&?߿3pk2ph*N|jj\ '} K1H> YZῡ V_p@1Fꒉ ؘp7JJR)@qmn&2; oAk+ʹe+ h/`< &`y S' ndvA|l)8`>_n1lPL៷9t(jLW". n0%(q> O`9 ?|É _oB98Gj%_ia+з TǷ`s{!۾,7 2} Ab%00ނؠBD0'A0?\it(؁ ?p[/0'`w@^F3Z hd>/e JgJ;_HP f;PKb{2n'~ Ԁ`3?%jLgZ7umE`> Anp :E9 .;89Mi'U lL_P 12WT7&0[ws-!@A? jZW,6ן*T @ dd6@,D4AA gޒAB8l*T+HTh3 LLf0["nOIENDB`lastfm-player-1.1.4_p1910/data/buttons/ban_enabled.png0000644000175000001440000000421410352604470021466 0ustar davidusersPNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxڌP6`A35 %][z9d&3 ث[se->b$J@ < h A40 -_. Bmb3H׿ Z'!:t{l \E@l9B;s9c>103r3J0pj3p30q2_@`p@u~Z#ËU[/%>A430Ar^N=U ~'F~?_.0l@;@Wn2\ϭgx{0Ą=@o Xe ß}`} |)r޼ !,ΉriL L Ll ?axTvJF`JB M%8eVWŃfu g#.c`Jex2,`AQ tH%P; C!7>]au;W2ĥ @ߋN.g`s1e !5 @B 4!X]h ăf >{Q,@G31[ bu`#0}/0gcV YSF|cxt38Vei ߷a3?`lb p̧SXn1<#gA, >fu0  @1?h8|X~gxt܍faЙMd0hd}68\hVO{HxbB XOTV$>|+ZCCd3c0Bگ$ L ?}e8s@C\Y0ٿҐ:U?PBd &```ab`V,x (Mc`:RA :?>UOf-lX *@ssfÿ*`qT>pF &'sV` -!*򙁎Ғ@Qp C;:$rD^#֐@ `43I2Sd' 6"{F`:]՘7 0 <Lmo?x4ْ$G   @f`i sAs30J0|vAq;10H3QbxnNjy@(6 ) ͜A$ߐ1^`F ti ? \1Y` , $@qa%`}uCVW_ ]b&{@5 H9@1f@J~ ؄P lfZ l o?b8Tځww2Vd`+?%PM(P b{2z0e0Jmxvpe[25:##R t3$!wS< Z`B 9ߺY dsj ={6:ؠ2 q0~|`4z.qDQQ̦WV@U)J 7dd 6D7 ԷZ&cOrz \10{. ETsfP X0@%ѷ|I@?Ҁ&4kq* C'eπEu@Cb2 @IENDB`lastfm-player-1.1.4_p1910/data/buttons/tag_hover.png0000644000175000001440000000134010352604470021227 0ustar davidusersPNG  IHDR2gAMAOX2tEXtSoftwareAdobe ImageReadyqe<rIDATxblnFFF&F Y뷯~baaedĢ￿28? @,@dc4 AF3=q-@]qHP HE77 YX&2@LP520L :\g+b3|#AvᎽX-yy+-.f`bعϟR._H#ԙ0u23w㧏@q]C;kYX9a@B|²c>6 ,0>nܾs Ru-ЩXؾ ܻ=if?$ܸ}SYQ @:!>ܱ6hqwi &h{P[]Z̔ Oj!P|**cjpY%3D ` #M5Ec$`GÜ]@e&FF= KSKؑ `]$DŽIBZ*bb` 夿I)02gd?#@P+׮G u߿r@ IIENDB`lastfm-player-1.1.4_p1910/data/buttons/blog_disabled.png0000644000175000001440000000116110352604470022024 0ustar davidusersPNG  IHDR2gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxbx PB҉;Vc7"Ă]X{6~>~ ?~1}!H; a'wpmڠ610rb7ڀ j*6  ײq~GHP f6  D``@#vm@@0e?<8FdfaaW&&f @:L k 6b#Y rkIENDB`lastfm-player-1.1.4_p1910/data/buttons/stop_down.png0000644000175000001440000000440710352604470021274 0ustar davidusersPNG  IHDR$$gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxb?`0@ 0Ob68+L. ຬFz`h6M4͚O!܏CЃ$P +R,N2I)2MkYGny ) ጌJLerbJ <1#Zf_'"A\@C(dbbc h9;33ߟ~b` =Vf/ /] ieDAr@ɞ Css0Ż \c5߀Q ,O߿Fм8 B*_ =`:```U{m=}߿#41;'4!~"62<6[ ` Z`12ˁ3 ;0| VV`qb 020n=l-`.Q:jQ>@Wѵ<33@03 !Klfv 0Jkay6#@7)1Q |{`bejeR r`TPc&\&F&[ a!15?F v6/t vf33R 1a(@(!ti)W-3A " L<_/2LlКh 3oq X!$ ƙ } 361pʃ j ;{-S-a&m\ L~>Li Dbfabfdddf`bnKW>cۣ\g**$Xx8 ؀ 3in`ad ) -Q#Jt`xf0f aRb$M=10~JЂ@ Dfbouv# /S`('I ~|sP+B9J& @DĨ~$OI$[`!(  $1 |L83T] iR6t EFf`.V<y"YXXI*e}fO0B8ȡL:SA`QL"%n'021=RBa>"ٟ́*`*A u$@!C&)0h?Q+3 %MK,r Z\vQ<@!;$ VP2iPb$;׫`G 0Ą@a`Wq>#d WHCNmr]>'+d~g6Q , LJtc>bD떨 q^F1`B/Ұ 6@n f z1IENDB`lastfm-player-1.1.4_p1910/data/buttons/tag_disabled.png0000644000175000001440000000120210352604470021650 0ustar davidusersPNG  IHDR2gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxb<|?π022Id@O?߿ĄK'PH"@,@ &!&4@( 3eE_?12<~HD DH#HK?}d<@A mT Ha!ϟBՁ@0 2"t/0"i xp˗? yׯ_"ŒH!@p;4ً?Ipsq|?$ :ɟ`ː߿DD$ݛ_‚ a%(<N=k@1Asߐ$yy$52x%D, ~gt4û{Gf>69 n =NCM&~n߾3F@'ڏ/aGgK70X+ ~|+dd``G2q3pj+38120 1 Ţh* oY @8=(d<adx0í.y L L82)(] da`WUfHba?p'@!e@0S2*k{Np!0ɰ03G33߯_ {LaiJ[ yQp\ϩc^fv.KFv`H20e YFN!``) ҇&7{Ch`a``[A0zM?8>LY cWjcЬw0} -AV (ABDT8UWʍL ߼c2?KXU?`z!VBrV V,Y-`B>]>^7{0z}r/k2p0*C4PwBJCā?(t~|cxt87!)1+Va`D7#+',A v`w|Ep9Ž!Atï׀"3X/^WI??(b}&̽@{a `?L&(){}߀(*@ /?pd#$а0NGc "CtHP/BP@ XH%)/,FH0B䲿>VMVb2L ߷f_ 3֦~\ ]P.j{ D|e n*V:@ABZῡ V_p3%L lyl\9]O_ P䒒N.k~)L #t?`C?ԕݒ- ?>b`bwj8DFfF>1< v@9LL៷9AMG@_amk@תE\{J b8M^ M?J  m2RK|UPǖA(/_ S3cbT6A4`wBT@ 2?0Xp%Ih& R<ğ~C8 X2'~t2A$ç >7!@6,Ǡ u`w (}T @,f "!Q6;Y-=P =&@fgB `Qvz ?`q?c:ݽQ[ϟ?@0Ը l>lB<ڟA| F!0 #eG9&/L+X},8Y@l F LΆ67 12!`&^[!gql zwjMV:80K \%Kf<bP##~ ѣ@ހ{z[_p0丁4 to_!%g@L @m @\рDKIENDB`lastfm-player-1.1.4_p1910/data/buttons/love_hover.png0000644000175000001440000000615110352604470021426 0ustar davidusersPNG  IHDR$$gAMAOX2tEXtSoftwareAdobe ImageReadyqe< IDATxt DĐLI\p``ֈsN\kECfΨx𡯞ĈA B-Y,Ģ@,b@,uT-A |@_B1*sQ( t! RI b VbeJ@}"c/ 2##of&, ~ϡ ͿȎ d PH*kk= t?0:D!22av߂:%4:@CBv 1P9DM5 Ku31/??af'a#x${ oS BpH Ra$$$!JX~iwO1|{Gg`a``c4d`f;>|[a! Q6v.@! XQ M3А9 5z~}l61|>{Ϗ/иfIl N ! bB |c+5DeY9n(i @(c:F2@`>p @3+;#~î Ze~300HJ):j P M SE ^܁ S2*k{Np~'8] G33߯_ l GYXXUMȂЬMr:?~10shedF N\ 3|'f,-b8]BO Adbx0d9?6 ?ex)+ =h{@,@ MP¯nذva5c`e Û]>] v0:<0~3|:} ``w) 0PoW*+(h ;@ Xſ8$ ̬ ϖmdx0 1@9fjOc1 X>avcМ soe[0|Y;' 1}IJJA"-u0+rPb&pn'LJ\N ǀ5F_c??w`?`TǷ Q& L/O_3BOA>?oUePk)a`F2—@T0}k ٘@5ŧO @ Xij"P2|$tz@X} Lt$8&Fn`R*b}&p:_?1C @b1cxԀD)dI[` XTgb7c Z3Š`M~;? _++߿Z}15+#0Ѥ;٢u ._V9d ` oP X2 [̬ Oexpَt͊vPZc`@ M[bڀE0#XJ@[rNmQy ޼lQAH.:T{D䠯#P3/j0kmp0 ;g 2g/3 Ler.&&`r`baN"I wL,og2dT7q,2\N*a4c^mp;{ ,\ PcoOpsO# @LvkPTJYL\ƕ#3Q`H],+63\e-"dh._!],_XXW ݒ+_p6C]8'jX3i WK".å"?aGu 0326Waf m @]N[ ? ,#0; P{h90Zݾtw) 70Y1Z?᯴0PTA]$P-j1 CH@ÛO߲ 2[L>Z n&.Qp1Ѵ9X2:3;dTYr >j4v bae'6PF0'A0?\} }N/7c@LAICVUU䈫 @{ђw:8},Vb@^ ޾􈘘YPHng򰱑}~>@QeL> m{J?|q l 7~<3`.4gmXh eD12cL3ɿ? 1y*&*j+@߃ i\2ƾɏ_>js34ge7~3πoP!._[Xs 7TY< zED$@!s]Iܥ `zNF߿Yyg,B l KA%U 65]l`(H+SQ>{ h.zְcp `{~h4F`#gJ޽b@]p+4J#ЁLWrFVF8,Dn@GC^ k86@*gwO)|AL߀c`(L:'?>`u ,$4އg ZA lVf 4 N$8!'Ҁo?Cy%T#tP7#)Ҩ'46Ґ'a=ؐO|Cz$UIENDB`lastfm-player-1.1.4_p1910/data/buttons/config_hover.png0000644000175000001440000000621010352604470021722 0ustar davidusersPNG  IHDR((mgAMAOX2tEXtSoftwareAdobe ImageReadyqe< IDATxb?43*_$6Lh@,d8 BŐ/( u,Q s's17P9fPo@A@/!!"; ^ bA$ bv$"9@?BžA)I`b!"@!@, @,IJ@35iX@FL̏YXn011?y/-:7h F,8X 89 A ?YX^pprbcrccC( ld:j@@@װX짿1(t3,  89 ffax r i 89 Vb ?~FF ba;çӧ69 n =NCM&~n߾*߾}́`E/d ZҜ4@b/Hï-b&g0j8372+/߀!Ջ"bl\\ H9pGr3#9rP"fa[ ]Xفy {VEoHǮ Ze~3013Tų4mE@02B?4ԡњS2*kZ:& '8] G33߯_ ,l_5M?@1!=6h& ͭn;zNÿ@Cٹr0\n6-u 2bB^n<9ٲ /\9(#1b \* z }5/k2ce? }fX3 4YV@A6N / ,?A,r6zO3~# 0Fb}&!:gءnb LJ@?LR14Bw6JOg.ihj vdǁ zK@}?"lV,JS;ï7n5u+߿]/Xkލ `ej8Q 61n41?rJ( Ff@ "@ql ,A7+'bP(gvn$k(104n{Vֿ:p B[?%>D XxT_aTUR-lQAH.y`,#R ԇ=nj w`/RIs_W\P6N./.#ف3#`߂ \ƚ  p)2\I,cjn؋[>K`I17Ta'{+ ڡu?Bv _ l1r@"xޣ ?bx:5 ]@3t 0bfb#&*uQ́!by KPWeepF U _.dxi =1Qv"j`{+p5DjP9''TؠdM%PbNEYp+޼GB n =*@u3a]Q8`ݫϟ+n 2[nρL~c"ԙOq!!.~6PY 7B@1!; *_h P  E@ AKU3#++w u] ;vBa &._X:uY. K0D02K ~@ ,5D",lŁ_xyU?vBl0@!4"#h}~Ѡf:% jly߽gWF-`020ʈ2ce/`]qAi)P @)@# 'yP t'܀p6[|` l>3`7Hq  9Ɂt6@Xˆnʯ_? mw^<&[$@d946@lPoȐbU 2h \GH!舖,(?}zgSފ6AmV @ hff?odTIHȞ(=G/0lMo I|U> Ϗ r0_`+0Hws᷏HeLb3F͈q`ɇ4p)GBؐz PVb_0q #HHCb$J@ < h A40 -_. Bmb3H׿ Z'!:t{l \E@l9B;ç!s ?o>0 ,@Ÿ#@1 ;`pO_s&v7 O[ KHߥ|#@!j , * 2|pVY00rsRA :XT23Jur]>JCb6˱ ~WOeVWO "<w $΃UT3k5l+UeX~-PrMp%ޣ u`&P\ & 010XCS\uWS+>RU`_> sP_)D9Hb U n1:؈?`ulL8]%}~%XKAqmn&2; oAk+ʹe+ h'` &`y VS'  $#>߰ s`+mL!web ́C@f`i m5P/ , #0Qӹ+~~d ˉç>8]`!sV('1f0DP@O߂bڀeA%{!۾,7 2} f) &p[/m 6 c̏W)$ v`<V L@|X5BZ^0 `( mA$ç >WrW @L`Twgai*_DU>5E˨f]Y pXZ2ď'{T\320"ZB @Qvz 0{f98l@asu`3?%jLgZ7umE`> Anp :ﷁ?h~Q`ft` AFU*d o.^AlPoXQo0+O@IF+%@C2RpԴXm?U,}[x׌&y 4-[Si RJ@uǁvd_"IENDB`lastfm-player-1.1.4_p1910/data/buttons/skip_hover.png0000644000175000001440000000611410352604470021426 0ustar davidusersPNG  IHDR$$gAMAOX2tEXtSoftwareAdobe ImageReadyqe< IDATxt DĐLI\p``ֈsN\kECfΨx𡯞Ă1 B-Y,Ģ@,b@,uT-A |@_B1*sQ(!@,xBj$+2 %`Ȋ߿ e/ˌLX2cbb ?:344";  XBBUXX)VC11 h qv H;mVV@׀q/`X !@l ĖY쇿1(t 3f@? v0H='OnS'K@|hC X 4d$!!` @Q@wOO3;x =?3&?7ÿo߁;߾ Isp!%д *Vh 1n@GgK70X+ ~|5HeffVfpbqe`bÏ_^x!*.v8T@1HOE 1А1bOD[fa[ ]XQPl2hE o? )t-@E':4 @Npzqb'dN]phW9QGt% ~1q0+heaaTy ^4! B4s"1sEc.`x;q&`F/As &ht =E 6 BM{C dӢbPm-e`dfư]BxMЊ_`1|ƃ `o~j7 &hu emc~}pMYY إS45ca7;!GK^-ӁG+cRߏ_0cfdëWO. |-!iT;ٲ /\ xm?l`u@" Og.1|fa[:D5>~e^ML~P `FePL#~|cxt87̭.3 IdB /n3\ f!'`nfH12qϛ FǷ( X ꦿ   ;Y7 O[ KHߥ|@!j {X>x5l ӧD @ q\LdI`xŭ!_ 1\ a $K1-)~ 2`?ׯ@ Ap=O)[CGD0hjC 9`txH¿o/`QP܌a &hN *>BG sPO&) t_/!!&*̠9 *Wo`A QҬ?QwQL(?>1!?Plg"&&Da @L 71L`I, B^mdr8GKplN>}aY>#7J̐ n TpˏXJur]>RUb6) ~[MAT.eyHHp9ޣp1 Br_ ,~i_A鐉h(=q̵*c4Uw (ڮ$#j20UPQڤ@LP;&f?߿3pk2ph*  ~TObVWGӥB7;!%,`,H17Ta'' &h5AJ`&^.`(i=d$[?|PZ1\7[Cň\{f2ㄅ%@{% -y ~`C?ԕCJ?!JF!Bf.H|1,dė0w -[b ́$À ZDT6 uU89o2`". n1tJ_2CݠEFϔ޽{jNK@A+?1Ĉe,aF!abО'D ghT X}jFb3DŽ4* 1>hn䃵M`fG@4 iX6ߐ@IENDB`lastfm-player-1.1.4_p1910/data/buttons/blog_normal.png0000644000175000001440000000127110352604470021547 0ustar davidusersPNG  IHDR2gAMAOX2tEXtSoftwareAdobe ImageReadyqe<KIDATxb\f@4`bd}@|篟̌ xT20B= z6wG׿ kXѣO=@ĸd0P߷TU^>A>v\\4̷g|n+X aaf/#\+H#6{7n_WS412`d @L0O3qYXYٴ4888?t& f3 沰<{JjL,,o߽j NM`+3ۙ>0h D @ \ #+ s=yC`xKBEaNN;&%!`k ?&HA `v21r+жgеl(8Ba:`>~ Y#P?p`h'5Lޅivj nܵL@23NDXT^F^S]HE v$0BБ a``ĒM۠3L=0 X3, pi9  ?ۼ ${ df6y@'IENDB`lastfm-player-1.1.4_p1910/data/buttons/skip_down.png0000644000175000001440000000476010352604470021257 0ustar davidusersPNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe< IDATxl1@P߿ \Ar|ZPOPd3fʾ^B隶1lZPa"O0PpFFF%&&30FSHht оT| ca T H5S8"+,OUf;r#TeRc)kFXNWn!2>ղ)C-p9@E@ 212gca+ïk~k}(!_C7;ÿ? r߿M % s-3H3#ÿm?W3 `"df_3 *zEL|zA ~]H1aPP$F#| ´Ƿ,.`ƻ-(gefx;#۶9 }g`b`z8L , I ? Pp߁09H30p1|XuT?~30h9r I!22 PEؘ }f '@\+8gm?,`u@!2ϧ3~x f0-g?c`~P`d :D v3[C@ h?ޯpI*X 7K&5A )$\p14W`>!49O?XX@+Ďt=(^X.j__*`G{pTlJH|<}RT>_5Aaȃ30^ `bfjdL , _g3$Pʦ 7[1BBRcHc[6 u"Ż<QOq6#!p#R"g\xHan:RHM_PE4@,4???K;* 2|p0ad`/t̂H3?: ?N˕T 1\-d VVSd_=[] R?1\cxT_)af.vp= )AÝّV˕P,QT,TZ>KY W= O? P6BX@of.RrP_MKUA$A~)@5!$4q @9V1~H PJR)P zbD.=G2/7` "X` L䀏@buN2 > .&hc ϯė0w _X$%x] N[Q `~ 꽕ʈ?tJ_2W*gZIENDB`lastfm-player-1.1.4_p1910/data/buttons/blog_hover.png0000644000175000001440000000134210352604470021401 0ustar davidusersPNG  IHDR2gAMAOX2tEXtSoftwareAdobe ImageReadyqe<tIDATxb׿1 _@,>; +##>|<|L߼} -'ׯ@90` >b@@`t 733P@1A SZ ū_rd@AumffF&0`VQRq&PNFp T'wLpǂ)S-,,@lo޾j `A4Bd:{{=ymNfmM~} @Heŋ7o@!3 ]@ ka9.))(9 YqB !`̅~ ?0!R!}o~q:\3Pۯ@C@1AD `!,L,̗]iSv'@PcDIx_~!-) ] PdB' &_[n #@ƀ|Ƅi dc`v$ *f=@@t{3( [o1 󁦧dIENDB`lastfm-player-1.1.4_p1910/data/buttons/tag_normal.png0000644000175000001440000000131210352604470021373 0ustar davidusersPNG  IHDR2gAMAOX2tEXtSoftwareAdobe ImageReadyqe<\IDATxbt_ǀ02022idLL? ?~󛙙`D@ X@Ғ DBY@ 3'-MYYX6o @Lc@@ZAڠ<r VV&f b3 KWMh &6A0#Dߞ>W؟?YY h k!V}ھ~ DWo P #2BF`(fca/0J@AD @ !f#!ڀL \};!NHT'3,lvGh'ܰû@Gt8>!>D B"n.Ё &h{PEQ*0SPL<,%P_B=@fm.z,APw-(̀&(N@)2h2dO@Uߐ иp7aAbZ̦$B@ 6IENDB`lastfm-player-1.1.4_p1910/data/buttons/ban_disabled.png0000644000175000001440000000421210352604470021641 0ustar davidusersPNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxڌ!P&(!t P( &XD^bY < rG;j:\zYu#X5PZh9l|@/@k]  F]\L w=f s ۞ vg`de| 2w=є5G23_w.b ?fF`6\:?+* r(;zViXAjH0}/SPb S/`Ë;nUv1<:!}>KPP *ƥ XPHS&,d>7v%m /Y@Gpri? ß~ bV,p (n̈J47 :B"܇Ag6} 10 (Abj8obOC_ TWp 02?`Eg"14$@@63#$JrМ_xW070/( P1~ @!@LB׿?4 X@Q t#7@1[5u6`R 99$Z;$X:AT ŏ߂U2V|Z` M OZ[PCT2%-Q 砸;:$rD^#֐@ `43I2Sdj^F߀U%#0~.MtGtHjZ OT/ %FyI1砺8Wgx0. x2arZ j \Q3?m w#v!.gUg&5N@P7F`\矯DJF߿E4c'`6TA`s GsA~b2Z4.Pqk20sr1|?q XF3!0$&×O='P'977Rh ρ6ف<ۏ|iW@گ .^e"p+/V@%w=ȝb{2z0%6<;Gz_reVd`t2dnSwPϑ lzП?lap،bd[ e+׌\"؝RdϏ7GƿB4`G0* &-`++`fT.12WTVw M-_F `4؊,/8՜!T+? %P O`= _32B6@, o! gޒ=)7fI}^z_4coj/tIENDB`lastfm-player-1.1.4_p1910/data/buttons/play_enabled.png0000644000175000001440000000327710352604470021703 0ustar davidusersPNG  IHDR$$bKGD&tv8 pHYs  tIME +1n|XLIDATXkUWun3C,-Zb!Rlm؋!f5M%5cE1MR#[lbcjZk %m@0@+f콾p)WsrNY}zJy? Q_<t$-bCrV%C""2'>s.|k t=/XB,K [c ~$kX:h#1kNr\l/5Y#/~ H~lyC]{(LuDs.}5LX<6D8#bT:!QtwGZRKسE*ŧ"b@Z4)Q S$o;(R7t:i!J-ӻM*Ƹi,TowQlنUla@q?2yT}ofP)A@6 #łb-BMs6b(԰(QE4{ɢS6٤&~bt&Sw}X ? <OEGo>e&4)2wL|~%*K"]>3SFvCRU:i*sgq'ȻBa4+ "w?;׆>c۾STĥjݾ9Yٌvua@ĉezUL+A~͞Q {?okr sοQM^%ܲ \Q[+̜ŋ硩(PD!~` >9ۿbkM(P[To͋( @rj;hX?V )m&ItB`$ )c"uXd؜ҙq1 ^-#aIWb[:"K؛ vgI?Ac{{IENDB`lastfm-player-1.1.4_p1910/data/buttons/config_disabled.png0000644000175000001440000000460210352604470022351 0ustar davidusersPNG  IHDR((mgAMAOX2tEXtSoftwareAdobe ImageReadyqe< IDATxb?`0@ zРw @` /BR)"pH*Κ3<ا># ("a4НKv.K1IX8%#`:?ff@ _R@eDAĺ@pr0ACß?R@1S 3_ @{Пr D(@@ vbI`ȁ¿~~gtû{Gf>69 n =NCM&~n߾= P}:C q ?8̀ /mb| ß_ 10AСl N ! bB |%O٭nDȁ@vܛ݇nUt0|,C{rm LL(e%. 1hMoaMb5w!a 20pArZf@rP';8C|ptdgx[ 4G`e`:F,KEWOA>~e^1, ,i8P`Ë rR Q`i ,, >}Aʞ ?w``pa '؁V (s,A`A* YrS`qA߯ _oc߀>1:} X33Jz,b,?ecx$C/Wo3\ϭg97 … LNta @XlOrr9q *]p5ә˸k `- Ǎ X0/;p}?"lV,JS;ï7n5u,_  l*Kl؉4̝pU2V0Q9t@LЊl L\@%B j N`o0mrv"?Fn`2BFW@Qg%>D XxT_aTUR-lQAH.CW@Xov` ԞcfJEHo5x;#QRN@& V7(ߣ+ >&V?߿3pk2ph*F0p%bX6ëb/Voe,/I5C?~{ 8?`s t %m oe!z|#7h._!]@Ste@`0.`3?ԕCY\çzw×7b$w> 7f;( t`нUπr >F @~ h; Rٱv>IENDB`lastfm-player-1.1.4_p1910/data/buttons/tag_down.png0000644000175000001440000000145210352604470021057 0ustar davidusersPNG  IHDR2gAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxb e  KJ22b߿ Qя?gd[VZ @:YYYB@߿ ~>|⸝ܷ d?@ DhkayE  @ѿfbbB4-uc?bP1@ρlVfW=f@T'@ :LwϟRo맴ԟAu,ty@m?}**  :C`ߑ#m@g~?ܸ}` NP 32Gwݾ.@g߾T'@t233'RQD ed;󟶺& fJTW7 +@y ѴIKHjkh1g'`h'B~.c&P*addbQVc?@L ]y|/?/9 3?X|8@iZTdknWPLmh3@" X qh_L ?t&@А@̝pA7 0:+d|GZ6T7e)3~(MNBP@:<@D*b]p11 /mb| ß_ AСl N ! bB |% `l* , B!([X8F73ܪ`xĄ]7(] da`WUfHbwFӥ ρ4m`.dU+0t8e$2ѯt) ~e`.rzRk q1񋁙(DZ 33Ȥ3I30gdxۻe Y?(JH b“)Ja#kc`bE)+Q"Ǡ5A6- }  Ç)+>o<fJP3:9[%hH@O_AӞ Hf`ޥ `,qt$9Ły!ٲ /\9(#1b \* z }5/k2cee`Zt89g !~b&pn%:Cd?-hk`z# ßO_'+'#\sj!`uʬ@6 0BXsx"cba');+DlP7[7 g N_c ,b,V&6NK/ ,?A,r6zO3~\ \~dL6yŁV 8Md~1I!'ǠXqܵ W+>&2,@!O] @`F_!PTѿID٬X"&v_o1k8X +߿-!PnCW@XB?J!iˑ ;AFA#d`s 0,4?_px?X85ZG9X w'1\D񏁑6Uf+5`&+@61,~3^n.Uw$1|:T 4[TUbypvxx[ _Tǥ8P#΅]CW@*<L g2dT7`[MJbŰlWw^ X^k*i1* Zn1teŁN/CL\Ʀ#g,icx(_ l ʹe+ O0܇ 0`] ]%+2OiK#*/o2y4:HL៷9T^t@`_?%0%l*6@SQJ7r_ia@cxK@-OnZmwcgexݽmp{d" 2} w[ZR _ Aa J?0 @ AKU3#V;f f7M`F`VP&EIOK<|n2Z%8b@3I;_HP| TTW XĻ ``H?`q0ꫀ?v`w0G##*eoˌ0_9. $ 4xߺY ds7o`{ [%A| 1ڊrXr{p~twh0(@wP_  5K\%}`vz E9XdcdT_ 5l`. t'Z=!b"e50pZ$ @u!M&p~ 5B d GfRi@k!+ AMZgTw@ &@ zРw @ zQgGOIENDB`lastfm-player-1.1.4_p1910/data/buttons/love_down.png0000644000175000001440000000512410352604470021251 0ustar davidusersPNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe< IDATxl1@P߿ \Ar|ZPOPd3fʾ^B隶1lZPa"O0PpFFF%&&30FSHht оT|\@C2111M 10+3 Pÿe,4{f @r/bddgf ?7O_~_5?_3 e@%4d|N?~20-tD50<5ts-Зzxd``U{m=}߿A210B9U܌ 0a45!@m GQHp`gcer  .߂X (de`Ta`HfoD>@WaZP0h[PvFms}.qXY<G9/q&w eJP`Ĝd y>,v*9 A#+Hgnb`\h;3ff&`® X,,rLLLYW-3 ;_}c[O _< v h3fu邴 3 ř(f:/ /gBQ݂ECa[Ynf$w_gn0XA- L ~(O _Wq#N/dxXLn, , =y *ȿ>w-ea?؇% \Qn )|(çY&?l 0 Zlwy&`5 i2h*/>}&~^x5yN$H4`v8E02Cs ~00'# ;0JXA ouXT,~9ʁ) Py#@i Y3ȑI?xx3; 6X88=gpgc  6z , ] 0xE X<L rOYY\Ζ ou_PՐ f!f&`N| 8K=X 0K?^0{h$l`:b: TC  P =#0Aǖc,t803|[( F&Hy,dd{,,l /W1:q4Amÿ-cQ`H# XS2Pz@iGҎ Xgq*ÕPj " _&v/03q OK`E@ `̎,,>~𡠟ױː`S#;>5́Ԍ̈:F@3c`T0_ P8nbﳷ z;I&[e0S> GX$M숣2(?{XJ#J  T~m.p) 4|cvJn a1!P L䀏@P*&O[>5D O->O\Ҍl,O +1|[ X1CT{HJ2201@TN`{{+ 6`#.g:} ) >}c}!ÇN`j?ILhih!(b(f`Ql &0kbbDjq5s{Í&bvHn:U[%jF@  \plkR@@l9vM78$"+3IlP`;bDh6 V ڸfE;×7yY0%d~_ l2 @x'`ZK 'sV2<_ L̠9 r1902)H3O(- F}6 ^Ȃ|fxû'?x _g6Pdx,xmE='`p- -8b@rq1?T PG778' f`'   |@, bQPyH? ԓ Lqx/(P:Bh!?@#>^1ÿ Ecq@3+փIENDB`lastfm-player-1.1.4_p1910/data/buttons/config_down.png0000644000175000001440000000501110352604470021544 0ustar davidusersPNG  IHDR((mgAMAOX2tEXtSoftwareAdobe ImageReadyqe< IDATxb?`0@ zРw @:@(GF0+P,@u"џ@kˑ`> yyU-ŞEqazDdCP$ĕ,~<[}oB81Q @.##?ALI е@Y@I %dp 4 gmv`pqJ'!, diKeF+KjHa삹Zo]=_"ցr@ɞ 2ss0Ż \c5߀Ib@9F !=%& Y ߏ L Y L@!WFZ`(#ÿm?W3 i 9U܌ 0 iv30C  0Bd,0aF#30*X8sï˷ beF3'vӁ@|qq4ofphsБ뀎ˁQ03@03 aKlfv 0Jkay6#0tفnSR;+ýg k30.+ P+0HV(` #A  rɖd?';ÛYk^N I6Qah}~'o`Lq.:Iqʋ@g:ER`10ss1|=xULYW,| Z[dӢ~x`=vh3fb3 0C̙LL̬l >~ax8 X ixR[ABTD/- L> +ȑLLAy#! 1|tAI*$D!&"Wg O6;$02*n@`ff `$J`xf00k bd?-hk`z# ßO_ 10=dpff3؃ g߀EXH = w;0= EsK9 *l \d]L _g3б?A,r6zO3~/0#?&bvF7 8Q?0k}20rr9q *]p5әxtMXgeLP G]@aq ßn2f24Cכw 7 8¾K`\χ 0  Ns^ n41?rp- ,Rf]جg?0e`xTvqqk !rQo'6Obxf;?@4`B 0Z/N?71,~3^n.Uw$1|:TEJ XJ|9 ’σ<9s0/D(V33q)9gfc`5Ҡ jf6@aMx .ɭp%bX6ëb/Voet IeX$MȣAÒA21}ޣ ?bx:5$|#c/7` [73݇ 9#qbuF U _.dxi,, L1ZUMI@*9? ŜV (}y|d(f`Sa`l~)@ ct@xfZƹ=FY0vpO:ߟo  5 g6`_g60\@Jt,Pj/8i 5 rAzv?  VA1#}`k$Hy8 F[@ >nV3|qXQ Y6HGQJo~1I0'2(`Wn? '`ZT Q F" x#~dJ7[,`z?{CEAρA(֗MAׯАʫÔUQ, F"ux 3û'<_2<~^6Yq }^[cfQA`pq L t h 舟L̥a`w lm9 H~{%@l \VF2(b`F-￀ L،[,|BH5Б?(D! :0*C~Abc4GXh;  n$eY;IENDB`lastfm-player-1.1.4_p1910/data/buttons/love_disabled.png0000644000175000001440000000472410352604470022056 0ustar davidusersPNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe< fIDATxڌ@`O!ix#HEregvuIo%c@,+p4cnͥU=X%|@K3ь , >Pu pb .6Np`biß?R@03U33o訫 B p;$0؁1 >>)3~(MNBP߷ ~eJYY@a ~ vb!~}l61|>{Ϗ/ L`HZf6nNme'Wf1!?_0vC-e=-0í.y L LL .~20*3V$1x08 u;hxH@0S2*k;NpTaOpʏf gÿ_@0n R @^сXn:?~10so9$nف!m1ga&fUCH@,HAK&7{}L,,Pl Lly@r '3|IAdž@-Q-AíPk :a'![3~^/0uӅ`G02}<03|:} ``cR}Fa>?(YXY-fPgИX cN&^4{ӀAۍAsJ JL{Rt>ïo>~e^`ʈ8 @0'L:,| xӚru.ZK N5 >x5` *_  8ټ?pX~9CRP* ]9[C h~ .* \Oó) B i`y  *>12KPl\ Vp6 P@?O &-ctg8\K ¯iXR1 B+`edxt-ӅȶәK 7+Au8$ |FHobj>l`Q&+wzn u;I ~y =lQAH.: ( 7~lG 9BqP`fy` X}5 C` .h0c =?b̦Z?FU*d b*0[@:?;whMRRpԴXm?U,6;| (l Xh^&zK_p\@ Te_PK]π 1@DX2IENDB`lastfm-player-1.1.4_p1910/data/buttons/ban_down.png0000644000175000001440000000424010352604470021042 0ustar davidusersPNG  IHDR szzgAMAOX2tEXtSoftwareAdobe ImageReadyqe<2IDATxl1@P߿ \Ar|ZPOPd3fʾ^B隶1lZPa"O0PpFFF%&&30FSHht оT|\@C2111M 10+3 Pÿe,4{f @r/bddgf o >d}ßǯ ZNv& !&&Ui?< ``[j`y͋kWZ/'o1|r ~eb`13rr30210;3;a4ȶ㣇(Q$ L8[O?02>|𸼇둳 YYp%  ̙ L|=:*L @113013 0221 ,\ 1YY31 `Ӏt ;*0++ P+0b!FP"bb%(0fbebd , o1hdК@4LW009$33-fG#@1YY䀮+o70h'Π38lV3 6h3feuAJaP0g231 3]+ÒnGSbrLC g`}}> }f '@~tS JCh{vN`C1 l yP"'5@1߷*O[L`da L,,(Ďt=(^X); _\*`Yx%LN࿻Og``̠Lm@@8Gp!* 7 :B"܇Agvq!r 2|9B 3*0R+ТAK6Lp{Hx@_A  dJ1\a1!ĕ2OI, T@012,gQg6GBT\ͨfzR00bb AU +P?,M+,`aO@?f~.u ԝ DēYq `X(#sF yPA;VN~&'sV` -!^`fWBX@gĺvOB숼F!!4 :4q @8 6d0D}lD@;`qa5B/1_߉.e VNmM@?! y 0 #gy(9$`-X_tI 2)rώg,ο6@S+un$ba'kJ`ZCۓ D~ S@!{@|ƑK`o\Pi?{ l} lR?qk@i!d5d ixؐ /g'W,,Q63PK"Lכ cxt7`y̺z?SCA(ЉA0“EVׯ@@]W !dGMAe>` d)ï >613=+&a`d8J}F p9Ӂ]Ͽ`K-6A߾k hvrgx%@l ij5<"` 66` &w`5 l1⚉_P /("u@)0EIENDB`lastfm-player-1.1.4_p1910/data/buttons/ban_hover.png0000644000175000001440000000531210352604470021217 0ustar davidusersPNG  IHDR$$gAMAOX2tEXtSoftwareAdobe ImageReadyqe< \IDATxt DĐLI\p``ֈsN\kECfΨx𡯞Ă1 B-Y,Ģ@,b@,uT-A |@_B1*sQ(!@,xBj$+2 %`Ȋ߿ e/ˌLX2cbb ?:344";  XBBUXX)VC11 h qv H;mVV@׀q/`X !@l ĖY쇿1(t 3f@? v0H='OnS'K@|hC X 4d$!!` @Q@w1?r ?|b`&+mw1|/Bl\H?4-B/@@fġ!rk"01Ū- ϗmb|oq5FY8x8T\E~W/j˲r?P  @Qu4d́2}rzn=1`9h @Lz@lD'#ç׀QFC<ȝAg~ 0 9mBo;30rs7OY}lT: j61|=є5cf0\̌ ~ax2 ſ8zwG1*`x?H )F~߻el,L~P `FeP,k~bfpn*O[Lbdeÿw?>~| 2~E0n rt2çS7fIK:J{QD ß>}&j^9D]@Fa3VGI0&2cXt3pe:~ . 멿~2|y7[T'!J _?÷ @,:.V>|k`!1!MWˀ$&2 ~Ղ/fbBܬ/) )Pd ` oP W Ji'J̐ n-RԄ z2k9bc QAH.:6/9+ {.S`\k*hbx2g֐ҚނR!3БZr$*jxA xv|en*kR!Lr  n ~ ڮ} jJ00W DDԿ , AI],\),,+@LnKP/~082pii/f2j2:4mh@prpe?r*%vT b A`$/ " G Vs<@͜A$ `ԑ(,!c'3jH); wWa?%A",^N=[O=G1ps3J]Оwbnhh@N&`Sؒ''̴T~~+HԦUX]][EW _FF6UFP P5m\?y<vO q@/+/0|zϭG.QXl N M~0q8橘v@|A{P! @+3Rc_7'A rp6;@-=`7=-2* [RdϏ\g'} PȜFchw X(ihQ_o^?5}˟?0M_P+}{F&pubJCPV}܋Zڕ~M=@6Fߤ lD~LݻW60@J 2F@ !32Da!r^ Ĉ6`< C;qrОϟ>}|_?3}vv ec`Y!77-hMϠ9b<@XF6hA&H@qBP9NPFKGo! FXfz_HCz`Cz? ){qIENDB`lastfm-player-1.1.4_p1910/data/artist.png0000644000175000001440000000151510352604471017066 0ustar davidusersPNG  IHDRgAMAOX2tEXtSoftwareAdobe ImageReadyqe<IDATxbܲg#`>@l j@_@|Fb" u@lhrB@,.@\ 뀸!+ tCx&KCx'_@,:@5d#g!Ądx];^onbQ ^ !0f2b. u f!9 {B Y@ PX ~%X`A8 w1#@ P ą@M#+aP o@\Ŀ @C#KPP JRP オ? ĻAfհwC]Uċ#Rx` $WИd }@LМ 4JO@P @LЬ iX MFgpxN# &h^M4w/CA74=bҠ"![Z>@ C7Bmy IR-AʉȹK6H`f b$(C|b sܖ IHIفx< X)U xKh S4 "i:` ;A#f((ӁxGBK$PҺ#NAˇ?T@hXMТn'Oܥ 5Z:9 ^uuLCl @C>PbX-oy9 @V^ yIENDB`lastfm-player-1.1.4_p1910/data/player.ico0000644000175000001440000002267610352604471017055 0ustar davidusers00 %(0` %$H(++d154v699599155{(,+lT 02;??bݾ탎?DCu B&))ExĴs|{*--wBFF[DJI "v|}t}{@-//3zy155DFFUfJI@ABBBBA@E^>|GML :<=WH"BCCCBBBBBBBBBADrION CEFFmSBCCCCCCBBBBBBBBBBA]=¿GML+--3lBCCCCCCCCCBBBBBBBBBBBAhN154pBCCCCCCCCCCCBBBBBBBBBBBAiOĿ=txxsBCCCCCCCCCCCCCBBBBBBA_?R,BBAiOt~| ?AA sBCCCCCCCCCBI$BCCCBBBBGJBBBiO¿DIHCCCCCCCCCCBoWCCCCBBAK BBBA!!!.ycD!D CCCCCCCH"ACCCCAps_BBBBB^>),,v}rC D!D!D CCCCCJ(ACCCH t`ABBBBBAsr{zQ1D!D!D!D!DCCCCACCBq[ABBBBBBBDA--.BBD!D!D!D!D!D CQ/ACBwaABBBBBBBBA~=BBu|gD!D!D!D!E#r[usABmxdBCBBBBBBBBB_?  ɦX;D#D!D!B@Bms]BCCCBBBBBBBBF.D#D#D"C @CE|hkRBCCCCCBBBBBBB@S$%%AD#D#R3@CCCBBBBBBBBABBBBBA(**j667C!D#D#[?@CBqZyyxxwvvt{gDBBBBB043yABBC!D"D#[@@B{gxBBBBB{488@ABC!D"D"[?@Cq[nCBBBB|588555C!D"D"\?@CBV8bGbGaEaDaCaC`C`C\=CCCBBB033u###AD"D"O/@CCCCCCCCCCCCCCCCBA'**bF&D$D"C!ACI&nBCCCCCCCCCCCCAFǜ`DE%D$D"B ACBCCCCCCCCCCCJ!!utE%D%D#D"C!`EzdydAC zfBCCCCCCCCCCgK&&&&C!E$D%D$D"D"D"D"K*AD!C rBCCCCCCCCB8<>???@344"## \8~~>|>x$p+dp9`5{@T@@@@?b@``pxx|> B~~?lastfm-player-1.1.4_p1910/LICENSE0000644000175000001440000000304310352604471015144 0ustar davidusersCopyright (C) 2005 Last.fm Ltd. All Rights Reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of Last.fm Ltd. nor the names of its contributors may be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact support@last.fm. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LAST.FM LIMITED OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. lastfm-player-1.1.4_p1910/README0000644000175000001440000000141710352604471015022 0ustar davidusersYou need Qt 4.x to compile this source-code. Get it from http://www.trolltech.no. Installation instructions Extract the Qt-sources and compile it with the following options (GIF support _is_ important!): ./configure --prefix=/choose/your/prefix/ -qt-gif make su -c make install To compile and run the Last.fm Player, follow these steps: export QTDIR=/choose/your/prefix export PATH=/choose/your/prefix/bin:$PATH export QMAKESPEC= qmake make ./player If you got problems compiling the sources, please contact chris@last.fm or visit our Support Forums at http://www.last.fm/forum/. ----------------------------------------------------------------------------- Special Thanks for patching, supporting and helping with this software go to: * Frerich Raabe lastfm-player-1.1.4_p1910/icon.o0000644000175000001440000002332610352604471015255 0ustar davidusersL&.rsrcp&<&@B PB8B BhB  IDI_ICON1%\&(0` %$H(++d154v699599155{(,+lT 02;??bݾ탎?DCu B&))ExĴs|{*--wBFF[DJI "v|}t}{@-//3zy155DFFUfJI@ABBBBA@E^>|GML :<=WH"BCCCBBBBBBBBBADrION CEFFmSBCCCCCCBBBBBBBBBBA]=¿GML+--3lBCCCCCCCCCBBBBBBBBBBBAhN154pBCCCCCCCCCCCBBBBBBBBBBBAiOĿ=txxsBCCCCCCCCCCCCCBBBBBBA_?R,BBAiOt~| ?AA sBCCCCCCCCCBI$BCCCBBBBGJBBBiO¿DIHCCCCCCCCCCBoWCCCCBBAK BBBA!!!.ycD!D CCCCCCCH"ACCCCAps_BBBBB^>),,v}rC D!D!D CCCCCJ(ACCCH t`ABBBBBAsr{zQ1D!D!D!D!DCCCCACCBq[ABBBBBBBDA--.BBD!D!D!D!D!D CQ/ACBwaABBBBBBBBA~=BBu|gD!D!D!D!E#r[usABmxdBCBBBBBBBBB_?  ɦX;D#D!D!B@Bms]BCCCBBBBBBBBF.D#D#D"C @CE|hkRBCCCCCBBBBBBB@S$%%AD#D#R3@CCCBBBBBBBBABBBBBA(**j667C!D#D#[?@CBqZyyxxwvvt{gDBBBBB043yABBC!D"D#[@@B{gxBBBBB{488@ABC!D"D"[?@Cq[nCBBBB|588555C!D"D"\?@CBV8bGbGaEaDaCaC`C`C\=CCCBBB033u###AD"D"O/@CCCCCCCCCCCCCCCCBA'**bF&D$D"C!ACI&nBCCCCCCCCCCCCAFǜ`DE%D$D"B ACBCCCCCCCCCCCJ!!utE%D%D#D"C!`EzdydAC zfBCCCCCCCCCCgK&&&&C!E$D%D$D"D"D"D"K*AD!C rBCCCCCCCCB8<>???@344"## \8~~>|>x$p+dp9`5{@T@@@@?b@``pxx|> B~~?00 %.rsrclastfm-player-1.1.4_p1910/icon.rc0000644000175000001440000000005310352604471015413 0ustar davidusersIDI_ICON1 ICON DISCARDABLE "player.ico" lastfm-player-1.1.4_p1910/lastfm.iss0000644000175000001440000000454610352604471016156 0ustar davidusers; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! [Setup] AppName=Last.fm Player AppVerName=Last.fm Player 1.0.99 AppPublisher=Last.fm AppPublisherURL=http://www.last.fm AppSupportURL=http://www.last.fm AppUpdatesURL=http://www.last.fm DefaultDirName={pf}\Last.fm Player DefaultGroupName=Last.fm Player OutputDir=C:\ OutputBaseFilename=lastfm-setup Compression=lzma SolidCompression=yes ChangesAssociations=yes [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] Source: "C:\player\trunk\release\player.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\player\trunk\data\*"; DestDir: "{app}\data"; Flags: ignoreversion recursesubdirs createallsubdirs Source: "C:\qt\4.0.1\bin\mingwm10.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\qt\4.0.1\bin\QtCore4.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\qt\4.0.1\bin\QtGui4.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\qt\4.0.1\bin\QtNetwork4.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\qt\4.0.1\bin\QtXml4.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\qt\4.0.1\plugins\imageformats\qgif1.dll"; DestDir: "{app}\imageformats"; Flags: ignoreversion Source: "C:\qt\4.0.1\plugins\imageformats\qjpeg1.dll"; DestDir: "{app}\imageformats"; Flags: ignoreversion ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Registry] Root: HKCR; Subkey: "lastfm"; ValueType: string; ValueName: ""; ValueData: "URL:lastfm"; Flags: uninsdeletevalue Root: HKCR; Subkey: "lastfm"; ValueType: string; ValueName: "URL Protocol"; ValueData: ""; Flags: uninsdeletevalue Root: HKCR; Subkey: "lastfm\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\player.exe"" ""%1"""; Flags: uninsdeletevalue [Icons] Name: "{group}\last.fm Player"; Filename: "{app}\player.exe"; WorkingDir: {app} Name: "{userdesktop}\last.fm Player"; Filename: "{app}\player.exe"; WorkingDir: {app}; Tasks: desktopicon Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\last.fm Player"; WorkingDir: {app}; Filename: "{app}\player.exe"; Tasks: quicklaunchicon [Run] Filename: "{app}\player.exe"; Flags: nowait lastfm-player-1.1.4_p1910/ChangeLog0000644000175000001440000000353610352604471015720 0ustar davidusersLast.fm Player ChangeLog ======================== (C) 2005 Last.fm Ltd. VERSION 1.1.3: * Fix muting problem with certain DirectSound configurations. * Reset focus in Tag Dialog after switching between song / artist / track. VERSION 1.1.2: * Fixed connection problem when using Windows Media Player as the external player. VERSION 1.1.1: * Don't force the user to restart the player after changing the username or sound-configuration. VERSION 1.1: * Ability to redirect sound to an external mediaplayer. * Added a personal page to the StationDialog, quickly linking to your stations and personal tags. * Remember volume setting on shutdown and restore it on startup. * Added tagging and journaling interface. * Neighbour stations show up in the history again. * Don't load the cover / metadata if the connection just dropped. * Store custom stations in the history. * Prevent fetching metadata too often. * Switch to new webservice url scheme. * DirectSound support for Windows. * ALSA support for Linux. * Ability to select a specific soundcard. VERSION 1.0.5: * Use a custom progressbar for all platforms. * Smaller main-window. * Added proper menu bar for MacOS. * Added shortcuts for MacOS. * No more CPU hogging with MacOS. * Stop button prevents further sound playback instantly. VERSION 1.0.4: * Added a play button, which opens the station-dialog. VERSION 1.0.3: * Fixed issue with Internet Explorer, which transmits dragged urls differently than Firefox. * Better buffering mechanism. * Don't poll metadata in intervals, but when the sync signal arrives only. VERSION 1.0.2: * Add support for accessing the servers via a proxy. VERSION 1.0.1: * Eye-candy release with fading covers and sliding labels. VERSION 1.0.0: * First public version. lastfm-player-1.1.4_p1910/COPYING0000644000175000001440000004311310352604471015174 0ustar davidusers GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. lastfm-player-1.1.4_p1910/Info.plist0000644000175000001440000000203110352604471016103 0ustar davidusers CFBundleDevelopmentRegion English CFBundleExecutable player CFBundleIdentifier fm.last.player CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleShortVersionString 1.0.1 CFBundleSignature last CFBundleIconFile player.icns CFBundleName Last.fm Player CFBundleURLTypes CFBundleURLName Last.fm CFBundleURLSchemes lastfm CFBundleVersion 1.0.0 NSAppleScriptEnabled YES lastfm-player-1.1.4_p1910/player.pro0000644000175000001440000000333410352604471016160 0ustar davidusers###################################################################### # Automatically generated by qmake (2.00a) Mon Jul 4 13:35:57 2005 ###################################################################### TEMPLATE = app DEPENDPATH += . INCLUDEPATH += . # Input FORMS = src/player.ui src/settings.ui src/stationdialog.ui src/about.ui \ src/urldialog.ui src/tagdialog.ui src/playermini.ui HEADERS = src/player.h src/settings.h src/webserviceconnector.h src/playback.h \ src/imagebutton.h src/stationdialog.h src/linklabel.h src/aboutdialog.h \ src/urldialog.h src/coverloader.h src/imagefader.h src/slidinglabel.h \ src/progressframe.h src/tagdialog.h SOURCES = src/main.cpp src/player.cpp src/webserviceconnector.cpp \ src/settings.cpp src/playback.cpp src/imagebutton.cpp src/stationdialog.cpp \ src/linklabel.cpp src/aboutdialog.cpp src/playlist.cpp src/song.cpp \ src/urldialog.cpp src/coverloader.cpp src/imagefader.cpp src/slidinglabel.cpp \ src/progressframe.cpp src/tagdialog.cpp \ \ src/rtaudio/RtAudio.cpp \ \ src/md5/md5.cpp \ \ src/mpglib/common.c src/mpglib/dct64_i386.c src/mpglib/decode_i386.c \ src/mpglib/interface.c src/mpglib/layer1.c src/mpglib/layer2.c \ src/mpglib/layer3.c src/mpglib/tabinit.c unix { LIBS += -lasound } win32 { LIBS += -lwinmm -ldsound icon.o INCLUDEPATH += "c:\program files\microsoft directx 9.0 sdk (october 2005)\include" } macx { LIBS += -framework CoreAudio -static LIBS += /usr/local/qt4_static/lib/libQtCore.a \ /usr/local/qt4_static/lib/libQtGui.a \ /usr/local/qt4_static/lib/libQtNetwork.a \ /usr/local/qt4_static/lib/libQtXml.a \ /sw/lib/libjpeg.a } QT += Network Xml CONFIG += static CONFIG -= debug