![]() |
Mixxx
|
00001 /*************************************************************************** 00002 soundsourcesndfile.cpp - description 00003 ------------------- 00004 copyright : (C) 2002 by Tue and Ken Haste Andersen 00005 email : 00006 ***************************************************************************/ 00007 00008 /*************************************************************************** 00009 * * 00010 * This program is free software; you can redistribute it and/or modify * 00011 * it under the terms of the GNU General Public License as published by * 00012 * the Free Software Foundation; either version 2 of the License, or * 00013 * (at your option) any later version. * 00014 * * 00015 ***************************************************************************/ 00016 00017 #include <taglib/flacfile.h> 00018 #include <taglib/aifffile.h> 00019 #include <taglib/rifffile.h> 00020 #include <taglib/wavfile.h> 00021 00022 #include "trackinfoobject.h" 00023 #include "soundsourcesndfile.h" 00024 #include <qstring.h> 00025 #include <QtDebug> 00026 00027 /* 00028 Class for reading files using libsndfile 00029 */ 00030 SoundSourceSndFile::SoundSourceSndFile(QString qFilename) : 00031 Mixxx::SoundSource(qFilename) 00032 { 00033 m_bOpened = false; 00034 info = new SF_INFO; 00035 info->format = 0; // Must be set to 0 per the API for reading (non-RAW files) 00036 filelength = 0; 00037 } 00038 00039 SoundSourceSndFile::~SoundSourceSndFile() 00040 { 00041 if (m_bOpened) { 00042 sf_close(fh); 00043 } 00044 delete info; 00045 } 00046 00047 QList<QString> SoundSourceSndFile::supportedFileExtensions() 00048 { 00049 QList<QString> list; 00050 list.push_back("aiff"); 00051 list.push_back("aif"); 00052 list.push_back("wav"); 00053 list.push_back("flac"); 00054 return list; 00055 } 00056 00057 int SoundSourceSndFile::open() { 00058 #ifdef __WINDOWS__ 00059 QByteArray qbaFilename = m_qFilename.toLocal8Bit(); 00060 #else 00061 QByteArray qbaFilename = m_qFilename.toUtf8(); 00062 #endif 00063 fh = sf_open( qbaFilename.data(), SFM_READ, info ); 00064 00065 if (fh == NULL) { // sf_format_check is only for writes 00066 qWarning() << "libsndfile: Error opening file" << m_qFilename << sf_strerror(fh); 00067 return -1; 00068 } 00069 00070 if (sf_error(fh)>0) { 00071 qWarning() << "libsndfile: Error opening file" << m_qFilename << sf_strerror(fh); 00072 return -1; 00073 } 00074 00075 channels = info->channels; 00076 00077 // This is the 'virtual' filelength. No matter how many channels the file 00078 // actually has, we pretend it has 2. 00079 filelength = 2*info->frames; // File length with two interleaved channels 00080 m_iSampleRate = info->samplerate; 00081 m_bOpened = true; 00082 return OK; 00083 } 00084 00085 long SoundSourceSndFile::seek(long filepos) 00086 { 00087 unsigned long filepos2 = (unsigned long)filepos; 00088 if (filelength>0) 00089 { 00090 filepos2 = math_min(filepos2,filelength); 00091 sf_seek(fh, (sf_count_t)filepos2/2, SEEK_SET); 00092 //Note that we don't error check sf_seek because it reports 00093 //benign errors under normal usage (ie. we sometimes seek past the end 00094 //of a song, and it will stop us.) 00095 return filepos2; 00096 } 00097 return 0; 00098 } 00099 00100 /* 00101 read <size> samples into <destination>, and return the number of 00102 samples actually read. A sample is a single float representing a 00103 sample on one channel of the audio. In the case of a monaural file 00104 then size/2 samples are read from the mono file, and they are 00105 doubled into stereo. 00106 */ 00107 unsigned SoundSourceSndFile::read(unsigned long size, const SAMPLE * destination) 00108 { 00109 SAMPLE * dest = (SAMPLE *)destination; 00110 if (filelength > 0) 00111 { 00112 if (channels==2) 00113 { 00114 unsigned long no = sf_read_short(fh, dest, size); 00115 00116 // rryan 2/2009 This code used to lie and say we read 00117 // 'size' samples no matter what. I left this array 00118 // zeroing code here in case the Reader doesn't check 00119 // against this. 00120 for (unsigned long i=no; i<size; ++i) 00121 dest[i] = 0; 00122 00123 return no; 00124 } 00125 else if(channels==1) 00126 { 00127 // We are not dealing with a stereo file. Read fewer 00128 // samples than requested and double them because we 00129 // pretend to every reader that all files are in stereo. 00130 int readNo = sf_read_short(fh, dest, size/2); 00131 00132 // readNo*2 is strictly less than available buffer space 00133 00134 // rryan 2/2009 00135 // Mini-proof of the below: 00136 // size = 20, destination is a 20 element array 0-19 00137 // readNo = 10 (or less, but 10 in this case) 00138 // i = 10-1 = 9, so dest[9*2] and dest[9*2+1], 00139 // so the first iteration touches the very ends of destination 00140 // on the last iteration, dest[0] and dest[1] are assigned to dest[0] 00141 00142 for(int i=(readNo-1); i>=0; i--) { 00143 dest[i*2] = dest[i]; 00144 dest[(i*2)+1] = dest[i]; 00145 } 00146 00147 // We doubled the readNo bytes we read into stereo. 00148 return readNo * 2; 00149 } else { 00150 // We do not support music with more than 2 channels. 00151 return 0; 00152 } 00153 } 00154 00155 // The file has errors or is not open. Tell the truth and return 0. 00156 return 0; 00157 } 00158 00159 int SoundSourceSndFile::parseHeader() 00160 { 00161 QString location = getFilename(); 00162 setType(location.section(".",-1).toLower()); 00163 00164 bool result; 00165 bool is_flac = location.endsWith("flac", Qt::CaseInsensitive); 00166 bool is_wav = location.endsWith("wav", Qt::CaseInsensitive); 00167 00168 #ifdef __WINDOWS__ 00169 /* From Tobias: A Utf-8 string did not work on my Windows XP (German edition) 00170 * If you try this conversion, f.isValid() will return false in many cases 00171 * and processTaglibFile() will fail 00172 * 00173 * The method toLocal8Bit() returns the local 8-bit representation of the string as a QByteArray. 00174 * The returned byte array is undefined if the string contains characters not supported 00175 * by the local 8-bit encoding. 00176 */ 00177 QByteArray qBAFilename = m_qFilename.toLocal8Bit(); 00178 #else 00179 QByteArray qBAFilename = m_qFilename.toUtf8(); 00180 #endif 00181 00182 if (is_flac) { 00183 TagLib::FLAC::File f(qBAFilename.constData()); 00184 result = processTaglibFile(f); 00185 TagLib::ID3v2::Tag* id3v2 = f.ID3v2Tag(); 00186 TagLib::Ogg::XiphComment* xiph = f.xiphComment(); 00187 if (id3v2) { 00188 processID3v2Tag(id3v2); 00189 } 00190 if (xiph) { 00191 processXiphComment(xiph); 00192 } 00193 } else if (is_wav) { 00194 TagLib::RIFF::WAV::File f(qBAFilename.constData()); 00195 result = processTaglibFile(f); 00196 00197 TagLib::ID3v2::Tag* id3v2 = f.tag(); 00198 if (id3v2) { 00199 processID3v2Tag(id3v2); 00200 } 00201 00202 if (getDuration() <= 0) { 00203 // we're using a taglib version which doesn't know how to do wav 00204 // durations, set it with info from sndfile -bkgood 00205 // XXX remove this when ubuntu ships with an sufficiently 00206 // intelligent version of taglib, should happen in 11.10 00207 00208 // Have to open the file for info to be valid. 00209 if (!m_bOpened) { 00210 open(); 00211 } 00212 00213 if (info->samplerate > 0) { 00214 setDuration(info->frames / info->samplerate); 00215 } else { 00216 qDebug() << "WARNING: WAV file with invalid samplerate." 00217 << "Can't get duration using libsndfile."; 00218 } 00219 } 00220 } else { 00221 // Try AIFF 00222 TagLib::RIFF::AIFF::File f(qBAFilename.constData()); 00223 result = processTaglibFile(f); 00224 00225 TagLib::ID3v2::Tag* id3v2 = f.tag(); 00226 if (id3v2) { 00227 processID3v2Tag(id3v2); 00228 } 00229 } 00230 00231 if (result) 00232 return OK; 00233 return ERR; 00234 } 00235 00236 /* 00237 Return the length of the file in samples. 00238 */ 00239 inline long unsigned SoundSourceSndFile::length() 00240 { 00241 return filelength; 00242 }