Tag::Tag(const QString &filename) : m_filename(filename) { TagLib::MPEG::File mpegfile(filename.toLocal8Bit().constData()); TagLib::ID3v2::Tag* id3v2 = mpegfile.ID3v2Tag(); if (id3v2 && !id3v2->isEmpty()) { readRegularTag(id3v2, m_data); int picnum = 0; TagLib::ID3v2::FrameList frames = id3v2->frameListMap()["APIC"]; // attached picture TagLib::ID3v2::FrameList::ConstIterator it = frames.begin(); while (it != frames.end()) { TagLib::ID3v2::AttachedPictureFrame* apic = static_cast<TagLib::ID3v2::AttachedPictureFrame*>(*it); TagLib::ByteVector bytes = apic->picture(); QImage img = QImage::fromData(reinterpret_cast<const uchar*>(bytes.data()), bytes.size()); if (!img.isNull()) { m_data[QLatin1String("picture") + QString::number(picnum++)] = QVariant(img); } ++it; } } else { TagLib::FileRef fileref(filename.toLocal8Bit().constData()); if (fileref.isNull()) return; TagLib::Tag* tag = fileref.tag(); if (!tag || tag->isEmpty()) return; readRegularTag(tag, m_data); } }
bool writeFileFromVector(const char *path, const TagLib::ByteVector &v) { std::ofstream o(path, std::ofstream::binary); if (!o) { return false; } o.write(v.data(), v.size()); return true; }
/* ** Write cover data to file. ** */ bool CCover::WriteCover(const TagLib::ByteVector& data, const std::wstring& target) { bool written = false; FILE* f = _wfopen(target.c_str(), L"wb"); if (f) { written = (fwrite(data.data(), 1, data.size(), f) == data.size()); fclose(f); } return written; }
QString extractMBIDFromFile(TagLib::MPEG::File *file) { TagLib::ID3v2::Tag *tag = file->ID3v2Tag(); TagLib::ID3v2::FrameList ufid = tag->frameListMap()["UFID"]; if (!ufid.isEmpty()) { for (TagLib::ID3v2::FrameList::Iterator i = ufid.begin(); i != ufid.end(); i++) { TagLib::ID3v2::UniqueFileIdentifierFrame *frame = dynamic_cast<TagLib::ID3v2::UniqueFileIdentifierFrame *>(*i); if (frame && frame->owner() == "http://musicbrainz.org") { TagLib::ByteVector id = frame->identifier(); return QString::fromAscii(id.data(), id.size()); } } } return QString(); }
bool CoverUtils::coverFromXiphComment(TagLib::Ogg::XiphComment *xiphComment, Album *album) { if (!xiphComment) return false; const TagLib::StringList &stringList = xiphComment->fieldListMap()["COVERART"]; if (stringList.isEmpty()) return false; TagLib::ByteVector byteVector = stringList.front().data(TagLib::String::Latin1); QByteArray encodedData; encodedData.setRawData(byteVector.data(), byteVector.size()); QByteArray data = QByteArray::fromBase64(encodedData); QImage image; image.loadFromData(data); if (!isAcceptableImage(image)) return false; qDebug() << "Cover from Xiph!"; return saveImage(image, album); }
TagLib::ByteVector TagLib::EncodeBase64(const TagLib::ByteVector& input) { ByteVector result; CFErrorRef error; SFB::SecTransform encoder = SecEncodeTransformCreate(kSecBase64Encoding, &error); if(nullptr == encoder) { LOGGER_WARNING("org.sbooth.AudioEngine", "SecEncodeTransformCreate failed: " << error); return TagLib::ByteVector::null; } SFB::CFData sourceData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)input.data(), (CFIndex)input.size(), kCFAllocatorNull); if(!sourceData) return TagLib::ByteVector::null; if(!SecTransformSetAttribute(encoder, kSecTransformInputAttributeName, sourceData, &error)) { LOGGER_WARNING("org.sbooth.AudioEngine", "SecTransformSetAttribute failed: " << error); return TagLib::ByteVector::null; } SFB::CFData encodedData = (CFDataRef)SecTransformExecute(encoder, &error); if(!encodedData) { LOGGER_WARNING("org.sbooth.AudioEngine", "SecTransformExecute failed: " << error); return TagLib::ByteVector::null; } result.setData((const char *)CFDataGetBytePtr((CFDataRef)encodedData), (TagLib::uint)CFDataGetLength((CFDataRef)encodedData)); return result; }
void MediaScannerThread::run() { if (multiPass) { qDebug() << "MediaScannerThread: multi non-single pass selected"; multiPass = false; for (int i=0;i<multiPassFileNames.count();i++) { QString fullPath = multiPassFileNames.at(i); TagLib::FileRef testfile(fullPath.toStdString().c_str()); if (!testfile.isNull()) { ID3Tag *tmpTag = 0; if (!tmpTag) { tmpTag = new ID3Tag(); tmpTag->valid = false; } if (TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File *>( testfile.file())) { if (file->ID3v2Tag(false)) { //Check to see if there are images in the tag #ifdef Q_OS_WIN32 //Currently this only works in the windows version of taglib?? if (!file->ID3v2Tag()->frameListMap()["APIC"].isEmpty()) { TagLib::ID3v2::FrameList frameList = file->ID3v2Tag()->frameListMap()["APIC"]; TagLib::ID3v2::FrameList::Iterator iter; for( iter = frameList.begin(); iter != frameList.end(); ++iter ) { TagLib::ID3v2::AttachedPictureFrame::Type t = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->type(); if (t == TagLib::ID3v2::AttachedPictureFrame::Type::FrontCover) { TagLib::ByteVector pic = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->picture(); QImage tmpPicture; tmpPicture.loadFromData((uchar*)pic.data(),pic.size()); tmpTag->frontcover = tmpPicture; } else if (t == TagLib::ID3v2::AttachedPictureFrame::Type::BackCover) { TagLib::ByteVector pic = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->picture(); QImage tmpPicture; tmpPicture.loadFromData((uchar*)pic.data(),pic.size()); tmpTag->backcover = tmpPicture; } } } #endif //Q_OS_WIN32 TagLib::Tag *tag = testfile.tag(); if (!tmpTag->valid) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->filename = fullPath; emit tagReceived("",0,tmpTag); } } } else { TagLib::Tag *tag = testfile.tag(); if (!tmpTag->valid) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->filename = fullPath; emit tagReceived("",0,tmpTag); } } } else { qDebug() << "MediaScannerThread: Invalid File in multipass" << fullPath; int stop = 1; } QThread::msleep(50); } multiPassFileNames.clear(); } else if (singlePass) { qDebug() << "MediaScannerThread: Non-multi single pass selected"; singlePass = false; QString fullPath = singlePassFileName; TagLib::FileRef testfile(fullPath.toStdString().c_str()); if (!testfile.isNull()) { TagLib::Tag *tag = testfile.tag(); //if (tag) //{ ID3Tag *tmpTag = 0; if (!tmpTag) { tmpTag = new ID3Tag(); tmpTag->valid = false; } if (!tmpTag->valid) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->filename = fullPath; emit tagReceived("",0,tmpTag); } //ID3Tag *tmpTag = new ID3Tag(); } else { qDebug() << "MediaScannerThread: Invalid File in singlepass" << fullPath; } } else { qDebug() << "MediaScannerThread: Non-multi non-single pass selected"; bool firstScan = true; int delay=0; bool anyValid = true; while (anyValid) { anyValid = false; if (firstScan) { delay=0; //firstScan = false; } else { delay=20; } for (int i=0;i<m_playLists->count();i++) { for (int j=0;j<m_playLists->at(i)->size();j++) { QString fullPath = m_playLists->at(i)->getFileNum(j)->fullPath(); // qDebug() << fullPath; TagLib::FileRef testfile(fullPath.toStdString().c_str()); if (!testfile.isNull()) { ID3Tag *tmpTag = m_playLists->at(i)->getFileNum(j)->getId3Tag(); if (!tmpTag) { tmpTag = new ID3Tag(); tmpTag->valid = false; anyValid = true; } if (TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File *>( testfile.file())) { if (file->ID3v2Tag(false)) { //Check to see if there are images in the tag #ifdef Q_OS_WIN32 //Currently this only works in the windows version of taglib?? if (!file->ID3v2Tag()->frameListMap()["APIC"].isEmpty()) { TagLib::ID3v2::FrameList frameList = file->ID3v2Tag()->frameListMap()["APIC"]; TagLib::ID3v2::FrameList::Iterator iter; for( iter = frameList.begin(); iter != frameList.end(); ++iter ) { TagLib::ID3v2::AttachedPictureFrame::Type t = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->type(); if (t == TagLib::ID3v2::AttachedPictureFrame::Type::FrontCover) { TagLib::ByteVector pic = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->picture(); QImage tmpPicture; tmpPicture.loadFromData((uchar*)pic.data(),pic.size()); tmpTag->frontcover = tmpPicture; } else if (t == TagLib::ID3v2::AttachedPictureFrame::Type::BackCover) { TagLib::ByteVector pic = (((TagLib::ID3v2::AttachedPictureFrame*)*iter))->picture(); QImage tmpPicture; tmpPicture.loadFromData((uchar*)pic.data(),pic.size()); tmpTag->backcover = tmpPicture; } } } #endif TagLib::Tag *tag = testfile.tag(); if (!tmpTag->valid) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->filename = fullPath; emit tagReceived(m_playLists->at(i)->title(),j,tmpTag); } } } else { TagLib::Tag *tag = testfile.tag(); if (!tmpTag->valid) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->valid = true; tmpTag->filename = fullPath; m_playLists->at(i)->setId3(j,tmpTag); anyValid = true; emit tagReceived(m_playLists->at(i)->title(),j,tmpTag); } else { if (tmpTag->filename != fullPath) { tmpTag->artist = tag->artist().toCString(); tmpTag->title = tag->title().toCString(); tmpTag->filename = fullPath; tmpTag->valid = true; m_playLists->at(i)->setId3(j,tmpTag); anyValid = true; emit tagReceived(m_playLists->at(i)->title(),j,tmpTag); } } } //ID3Tag *tmpTag = new ID3Tag(); } else { qDebug() << "MediaScannerThread: Invalid File in standardpass" << fullPath; } QThread::msleep(delay); } } if (firstScan) { firstScan = false; qDebug() << "Initial scan of id3 tags completed. "; if (anyValid) { qDebug() << "Moving to background mode..."; } else { qDebug() << "All files scanned, exiting thread"; } } else { qDebug() << "Secondary id3 scan complete... continuing..."; } } } /* int done = 1; return; QFile tmpFile("music.conf"); if (!tmpFile.open(QIODevice::ReadOnly | QIODevice::Text)) { printf("Error opening configuration file!!!\n"); return; } QString musicDir(tmpFile.readLine()); printf("Music Directory from configuration file: %s\n",musicDir.toStdString().c_str()); tmpFile.close(); musicDir = musicDir.mid(0,musicDir.length()-1); QDir tmpDir(musicDir); tmpDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); QDir tmpFiles(musicDir); tmpFiles.setFilter(QDir::Files | QDir::NoDotAndDotDot); QStringList infoList = tmpDir.entryList(); foreach (QString info,infoList) { TagLib::FileRef testfile(info.toStdString().c_str()); TagLib::Tag *tag = testfile.tag(); ID3Tag *tmpTag = new ID3Tag(); tmpTag->artist = tag->artist().to8Bit().c_str(); tmpTag->title = tag->title().to8Bit().c_str(); //emit tagReceived(tmpTag); } bool inLoop = true; //while (inLoop) //{ //QThread:: // QThread::sleep(1); // ID3Tag *tmpTag = new ID3Tag(); //tmpTag->artist = //} */ }
TagLib::ByteVector TagLib::DecodeBase64(const TagLib::ByteVector& input) { #if USE_SECURITY_FRAMEWORK ByteVector result; CFErrorRef error; SecTransformRef decoder = SecDecodeTransformCreate(kSecBase64Encoding, &error); if(nullptr == decoder) { CFShow(error); return TagLib::ByteVector::null; } CFDataRef sourceData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)input.data(), input.size(), kCFAllocatorNull); if(nullptr == sourceData) { CFRelease(decoder), decoder = nullptr; return TagLib::ByteVector::null; } if(!SecTransformSetAttribute(decoder, kSecTransformInputAttributeName, sourceData, &error)) { CFShow(error); CFRelease(sourceData), sourceData = nullptr; CFRelease(decoder), decoder = nullptr; return TagLib::ByteVector::null; } CFTypeRef decodedData = SecTransformExecute(decoder, &error); if(nullptr == decodedData) { CFShow(error); CFRelease(sourceData), sourceData = nullptr; CFRelease(decoder), decoder = nullptr; return TagLib::ByteVector::null; } result.setData((const char *)CFDataGetBytePtr((CFDataRef)decodedData), (TagLib::uint)CFDataGetLength((CFDataRef)decodedData)); CFRelease(decodedData), decodedData = nullptr; CFRelease(sourceData), sourceData = nullptr; CFRelease(decoder), decoder = nullptr; return result; #else ByteVector result; BIO *b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO *bio = BIO_new_mem_buf(reinterpret_cast<void *>(const_cast<char *>(input.data())), input.size()); bio = BIO_push(b64, bio); char inbuf [512]; int inlen; while(0 < (inlen = BIO_read(bio, inbuf, 512))) result.append(ByteVector(inbuf, inlen)); BIO_free_all(bio); return result; #endif }
TagLib::ByteVector TagLib::EncodeBase64(const TagLib::ByteVector& input) { #if USE_SECURITY_FRAMEWORK ByteVector result; CFErrorRef error; SecTransformRef encoder = SecEncodeTransformCreate(kSecBase64Encoding, &error); if(nullptr == encoder) { CFShow(error); return TagLib::ByteVector::null; } CFDataRef sourceData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)input.data(), input.size(), kCFAllocatorNull); if(nullptr == sourceData) { CFRelease(encoder), encoder = nullptr; return TagLib::ByteVector::null; } if(!SecTransformSetAttribute(encoder, kSecTransformInputAttributeName, sourceData, &error)) { CFShow(error); CFRelease(sourceData), sourceData = nullptr; CFRelease(encoder), encoder = nullptr; return TagLib::ByteVector::null; } CFTypeRef encodedData = SecTransformExecute(encoder, &error); if(nullptr == encodedData) { CFShow(error); CFRelease(sourceData), sourceData = nullptr; CFRelease(encoder), encoder = nullptr; return TagLib::ByteVector::null; } result.setData((const char *)CFDataGetBytePtr((CFDataRef)encodedData), (TagLib::uint)CFDataGetLength((CFDataRef)encodedData)); CFRelease(encodedData), encodedData = nullptr; CFRelease(sourceData), sourceData = nullptr; CFRelease(encoder), encoder = nullptr; return result; #else ByteVector result; BIO *b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO *bio = BIO_new(BIO_s_mem()); bio = BIO_push(b64, bio); BIO_write(bio, input.data(), input.size()); (void)BIO_flush(bio); void *mem = nullptr; long size = BIO_get_mem_data(bio, &mem); if(0 < size) result.setData(static_cast<const char *>(mem), static_cast<uint>(size)); BIO_free_all(bio); return result; #endif }
static Meta::ReplayGainTagMap readID3v2Tags( TagLib::ID3v2::Tag *tag ) { Meta::ReplayGainTagMap map; { // ID3v2.4.0 native replay gain tag support (as written by Quod Libet, for example). TagLib::ID3v2::FrameList frames = tag->frameListMap()["RVA2"]; frames.append(tag->frameListMap()["XRVA"]); if ( !frames.isEmpty() ) { for ( unsigned int i = 0; i < frames.size(); ++i ) { // we have to parse this frame ourselves // ID3v2 frame header is 10 bytes, so skip that TagLib::ByteVector data = frames[i]->render().mid( 10 ); unsigned int offset = 0; QString desc( data.data() ); offset += desc.count() + 1; unsigned int channel = data.mid( offset, 1 ).toUInt( true ); // channel 1 is the main volume - the only one we care about if ( channel == 1 ) { ++offset; qint16 adjustment512 = data.mid( offset, 2 ).toShort( true ); qreal adjustment = ( (qreal)adjustment512 ) / 512.0; offset += 2; unsigned int peakBits = data.mid( offset, 1 ).toUInt( true ); ++offset; bool ok = false; qreal peak = readRVA2PeakValue( data.mid( offset ), peakBits, &ok ); if ( ok ) { if ( desc.toLower() == "album" ) { map[Meta::ReplayGain_Album_Gain] = adjustment; map[Meta::ReplayGain_Album_Peak] = peakToDecibels( peak ); } else if ( desc.toLower() == "track" || !map.contains( Meta::ReplayGain_Track_Gain ) ) { map[Meta::ReplayGain_Track_Gain] = adjustment; map[Meta::ReplayGain_Track_Peak] = peakToDecibels( peak ); } } } } if ( !map.isEmpty() ) return map; } } { // Foobar2000-style ID3v2.3.0 tags TagLib::ID3v2::FrameList frames = tag->frameListMap()["TXXX"]; for ( TagLib::ID3v2::FrameList::Iterator it = frames.begin(); it != frames.end(); ++it ) { TagLib::ID3v2::UserTextIdentificationFrame* frame = dynamic_cast<TagLib::ID3v2::UserTextIdentificationFrame*>( *it ); if ( frame && frame->fieldList().size() >= 2 ) { QString desc = TStringToQString( frame->description() ).toLower(); if ( desc == "replaygain_album_gain" ) maybeAddGain( frame->fieldList()[1], Meta::ReplayGain_Album_Gain, &map ); if ( desc == "replaygain_album_peak" ) maybeAddPeak( frame->fieldList()[1], Meta::ReplayGain_Album_Peak, &map ); if ( desc == "replaygain_track_gain" ) maybeAddGain( frame->fieldList()[1], Meta::ReplayGain_Track_Gain, &map ); if ( desc == "replaygain_track_peak" ) maybeAddPeak( frame->fieldList()[1], Meta::ReplayGain_Track_Peak, &map ); } } } return map; }
TagLib::ByteVector TagLib::DecodeBase64(const TagLib::ByteVector& input) { SFB::CFError error; SFB::SecTransform decoder(SecDecodeTransformCreate(kSecBase64Encoding, &error)); if(!decoder) { LOGGER_WARNING("org.sbooth.AudioEngine", "SecDecodeTransformCreate failed: " << error); return {}; } SFB::CFData sourceData(CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)input.data(), (CFIndex)input.size(), kCFAllocatorNull)); if(!sourceData) return {}; if(!SecTransformSetAttribute(decoder, kSecTransformInputAttributeName, sourceData, &error)) { LOGGER_WARNING("org.sbooth.AudioEngine", "SecTransformSetAttribute failed: " << error); return {}; } SFB::CFData decodedData((CFDataRef)SecTransformExecute(decoder, &error)); if(!decodedData) return {}; return {(const char *)CFDataGetBytePtr((CFDataRef)decodedData), (size_t)CFDataGetLength((CFDataRef)decodedData)}; }
virtual TagLib::String parse( const TagLib::ByteVector &data ) const { return QStringToTString( m_codec->toUnicode( data.data(), data.size() ) ); }
void DSFHeader::parse(const TagLib::ByteVector &data) { if (data.size() < DSD_HEADER_SIZE + FMT_HEADER_SIZE) { std::cerr <<"DSFHeader::parse(): header size incorrect" << std::endl; return; } const char *hdr = data.data(); size_t offset = 0; // // ******** DSD chunk header ******** // DSD header chunk should start with "DSD ". // if (hdr[0] != 'D' || hdr[1] != 'S' || hdr[2] != 'D' || hdr[3] != ' ') { std::cerr <<"DSD::Header::parse(): DSD header's first 4 bytes != 'DSD '" << std::endl; return; } offset += 4; // The next 8 bytes contain the size of DSD header // (numerical data is stored in little endian) if (data.toLongLong(offset, false) != DSD_HEADER_SIZE) { std::cerr <<"DSD::Header::parse(): DSD header size is incorrect" << std::endl; return; } offset += LONG_INT_SIZE; // The next 8 bytes contains the file size d->fileSize = bytesToUInt64(&hdr[0], offset); offset += LONG_INT_SIZE; // The next 8 bytes contains the offset to id3v2 tag (0 if not exists) d->ID3v2Offset = bytesToUInt64(&hdr[0], offset); offset += LONG_INT_SIZE; // // ********* FMT chunk ******** // // FMT header chunk should start with "fmt ". // if (hdr[offset] != 'f' || hdr[offset + 1] != 'm' || hdr[offset + 2] != 't' || hdr[offset + 3] != ' ') { std::cerr <<"DSD::Header::parse(): FMT header's first 4 bytes != 'fmt '" << std::endl; return; } offset += 4; // The next 8 bytes contain the size of FMT header, which should be 52 if (data.toLongLong(offset, false) != FMT_HEADER_SIZE) { std::cerr <<"DSD::Header::parse(): FMT header size is incorrect" << std::endl; return; } offset += LONG_INT_SIZE; // Format version // There's only version 1 for now... unsigned int ver = data.toUInt(offset, false); if (ver != 1) { std::cerr <<"DSD::Header::parse(): format version != 1" << std::endl; return; } d->version = static_cast<Version>(ver); offset += INT_SIZE; // Format ID if (data.toUInt(offset, false) != 0) { std::cerr <<"DSD::Header::parse(): format ID != 0" << std::endl; return; } offset += INT_SIZE; // Channel Type unsigned int ct = data.toUInt(offset, false); if (ct < 1 || ct > 7) { std::cerr <<"DSD::Header::parse(): channel type out of range" << std::endl; return; } d->channelType = static_cast<ChannelType>(ct); offset += INT_SIZE; // Channel Num d->channelNum = data.toUInt(offset, false); if (d->channelNum < MinType || d->channelNum > MaxType) { std::cerr <<"DSD::Header::parse(): channel num out of range" << std::endl; return; } offset += INT_SIZE; // Sampling frequency d->sampleRate = data.toUInt(offset, false); if (d->sampleRate != 2822400 && d->sampleRate != 5644800) { std::cerr <<"DSD::Header::parse(): invalid sampling frequency" << std::endl; return; } offset += INT_SIZE; // Bits per sample d->bitsPerSample = data.toUInt(offset, false); if (d->bitsPerSample != 1 && d->bitsPerSample != 8) { std::cerr <<"DSD::Header::parse(): bits per sample invalid" << std::endl; return; } offset += INT_SIZE; // Sample count d->sampleCount = bytesToUInt64(&hdr[0], offset); offset += LONG_INT_SIZE; // Block size per channel if (data.toUInt(offset, false) != 4096) { std::cerr <<"DSD::Header::parse(): block size != 4096" << std::endl; return; } //offset += 4; // Reserved // offset += 4; d->isValid = true; }