QString CPlayer::replace_tokens(QString str, bool hidePlayingState) { QString status_text = ""; switch(GetState()) { case PLAYER_STOPPED: if(GetPlayer()=="") status_text = ""; else status_text = "Stopped"; break; case PLAYER_PAUSED: status_text = "Paused"; break; case PLAYER_PLAYING: status_text = (hidePlayingState? "" : "Playing"); break; } replace_token(str,"title" ,GetTitle()); replace_token(str,"artist" ,GetArtist()); replace_token(str,"album" ,GetAlbum()); replace_token(str,"duration" ,GetDuration(),GetDuration()); replace_token(str,"played" ,GetPosition(),GetDuration()); replace_token(str,"remaining",GetDuration() - GetPosition(),GetDuration()); replace_token(str,"status" ,status_text); replace_token(str,"player" ,GetPlayer()); replace_token(str,"file" ,GetFilePath()); replace_token(str,"shuffle" ,(GetShuffle() ? "Shuffle" : "")); replace_token(str,"repeat" ,(GetRepeat() ? "Repeat" : "")); replace_token(str,"rating" ,QString::number(GetRating())); replace_token(str,"lyrics" ,GetLyrics()); return str; }
inline bool MtgCard::operator==(const MtgCard& rhs) { return GetLayout() == rhs.GetLayout() && GetManacost() == rhs.GetManacost() && GetCmc() == rhs.GetCmc() && CompareStringList(GetColors(), rhs.GetColors()) && type == rhs.type && CompareStringList(GetSupertypes(), rhs.GetSupertypes()) && CompareStringList(GetTypes(), rhs.GetTypes()) && CompareStringList(GetSubtypes(), rhs.GetSubtypes()) && GetRarity() == rhs.GetRarity() && text == rhs.GetText() && GetFlavor() == rhs.GetFlavor() && GetArtist() == rhs.GetArtist() && GetNumber() == rhs.GetNumber() && GetPower() == rhs.GetPower() && GetToughness() == rhs.GetToughness() && GetLoyalty() == rhs.GetLoyalty() && GetMultiverseid() == rhs.GetMultiverseid() && CompareIntList(GetVariations(), rhs.GetVariations()) && GetImageName() == rhs.GetImageName() && GetWatermark() == rhs.GetWatermark() && GetBorder() == rhs.GetBorder() && IsTimeshifted() == rhs.IsTimeshifted() && GetHand() == rhs.GetHand() && GetLife() == rhs.GetLife() && IsReserved() == rhs.IsReserved() && GetReleasedate() == rhs.GetReleasedate() && IsStarter() == rhs.IsStarter() && CompareStringPairList(GetRulings(), rhs.GetRulings()) && CompareStringPairList(GetForeignNames(), rhs.GetForeignNames()) && GetOriginalText() == rhs.GetOriginalText() && GetOriginalType() == rhs.GetOriginalType() && CompareStringPairList(GetLegalities(), rhs.GetLegalities()) && GetEdition() == rhs.GetEdition(); }
void AlbumArtManagerDialog::request () { Model_->clear (); const auto& artist = GetArtist (); const auto& album = GetAlbum (); if (artist.isEmpty () || album.isEmpty ()) return; AAMgr_->CheckAlbumArt (artist, album, true); RequestScheduled_ = false; }
Media::AudioInfo UserTune::ToAudioInfo () const { return { GetArtist (), GetSource (), GetTitle (), {}, GetLength (), 0, GetTrack ().toInt (), Util::MakeMap<QString, QVariant> ({ { "URL", GetURI () } }) }; }
void AlbumArtManagerDialog::on_BrowseButton__released () { const auto& filename = QFileDialog::getOpenFileName (this, tr ("Choose album art"), QDir::homePath (), tr ("Images (*.png *.jpg *.jpeg);;All files (*.*)")); if (filename.isEmpty ()) return; QImage image (filename); if (image.isNull ()) return; handleImages ({ GetArtist (), GetAlbum () }, { image }); }
void SyncUnmountableManager::AddFiles (const AddFilesParams& params) { auto coll = Core::Instance ().GetLocalCollection (); const auto& format = params.TCParams_.FormatID_; auto syncer = params.Syncer_; for (const auto& file : params.Files_) { const auto trackId = coll->FindTrack (file); if (trackId < 0) continue; const auto trackNumber = coll->GetCollectionModel ()-> GetTrackData (trackId, LocalCollectionModel::Role::TrackNumber).toInt (); const auto& trackTitle = coll->GetCollectionModel ()-> GetTrackData (trackId, LocalCollectionModel::Role::TrackTitle).toString (); const auto album = coll->GetTrackAlbum (trackId); if (!album) continue; const auto& artists = coll->GetAlbumArtists (album->ID_); if (artists.isEmpty ()) continue; const auto& artist = coll->GetArtist (artists.at (0)); syncer->SetFileInfo (file, { format.isEmpty () ? QFileInfo (file).suffix ().toLower () : format, trackNumber, trackTitle, artist.Name_, album->Name_, album->Year_, album->CoverPath_, QStringList () }); Source2Params_ [file] = params; } SyncManagerBase::AddFiles (params.Files_, params.TCParams_); }
void AlbumArtManagerDialog::handleImages (const Media::AlbumInfo& info, const QList<QImage>& images) { qDebug () << Q_FUNC_INFO << images.size (); if (info.Album_ != GetAlbum () || info.Artist_ != GetArtist ()) return; auto watcher = new QFutureWatcher<ScaleResult> (); connect (watcher, SIGNAL (finished ()), this, SLOT (handleResized ())); auto worker = [info] (const QImage& image) -> ScaleResult { const auto& scaled = image.scaled (200, 200, Qt::KeepAspectRatio, Qt::SmoothTransformation); return { scaled, image, info }; }; watcher->setFuture (QtConcurrent::mapped (images, std::function<ScaleResult (QImage)> (worker))); }
void LocalCollection::HandleExistingInfos (const QList<MediaInfo>& infos) { Q_FOREACH (const auto& info, infos) { const auto& path = info.LocalPath_; const auto trackIdx = FindTrack (path); const auto trackAlbum = GetTrackAlbum (trackIdx); if (!trackAlbum) { qWarning () << Q_FUNC_INFO << "no album for track" << path; continue; } const auto pos = std::find_if (trackAlbum->Tracks_.begin (), trackAlbum->Tracks_.end (), [trackIdx] (decltype (trackAlbum->Tracks_.front ()) track) { return track.ID_ == trackIdx; }); const auto& track = pos != trackAlbum->Tracks_.end () ? *pos : Collection::Track (); const auto& artist = GetArtist (AlbumID2ArtistID_ [trackAlbum->ID_]); if (artist.Name_ == info.Artist_ && trackAlbum->Name_ == info.Album_ && trackAlbum->Year_ == info.Year_ && track.Number_ == info.TrackNumber_ && track.Name_ == info.Title_ && track.Genres_ == info.Genres_) continue; auto stats = GetTrackStats (path); RemoveTrack (path); const auto& newArts = Storage_->AddToCollection ({ info }); HandleNewArtists (newArts); const auto newTrackIdx = FindTrack (path); stats.TrackID_ = newTrackIdx; Storage_->SetTrackStats (stats); } }
void AlbumArtManagerDialog::handleResized () { auto watcher = dynamic_cast<QFutureWatcher<ScaleResult>*> (sender ()); watcher->deleteLater (); for (const auto& result : watcher->future ()) { if (result.Info_.Album_ != GetAlbum () || result.Info_.Artist_ != GetArtist ()) continue; auto item = new QStandardItem (); item->setIcon (QIcon (QPixmap::fromImage (result.Image_))); item->setText (QString::fromUtf8 ("%1×%2") .arg (result.SourceImage_.width ()) .arg (result.SourceImage_.height ())); item->setEditable (false); Model_->appendRow (item); FullImages_ << result.SourceImage_; } }
bool FLACMetadata::WriteMetadata(CFErrorRef *error) { UInt8 buf [PATH_MAX]; if(!CFURLGetFileSystemRepresentation(mURL, false, buf, PATH_MAX)) return false; FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new(); // ENOMEM sux if(NULL == chain) return false; if(!FLAC__metadata_chain_read(chain, reinterpret_cast<const char *>(buf))) { // Attempt to provide a descriptive error message if(NULL != error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); switch(FLAC__metadata_chain_status(chain)) { case FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE: { CFStringRef displayName = CreateDisplayNameForURL(mURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid FLAC file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not a FLAC file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; break; } case FLAC__METADATA_CHAIN_STATUS_BAD_METADATA: { CFStringRef displayName = CreateDisplayNameForURL(mURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid FLAC file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not a FLAC file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file contains bad metadata."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; break; } default: { CFStringRef displayName = CreateDisplayNameForURL(mURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid FLAC file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not a FLAC file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; break; } } *error = CFErrorCreate(kCFAllocatorDefault, AudioMetadataErrorDomain, AudioMetadataFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } FLAC__metadata_chain_delete(chain), chain = NULL; return false; } FLAC__metadata_chain_sort_padding(chain); FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new(); if(NULL == iterator) { FLAC__metadata_chain_delete(chain), chain = NULL; return false; } FLAC__metadata_iterator_init(iterator, chain); // Seek to the vorbis comment block if it exists while(FLAC__METADATA_TYPE_VORBIS_COMMENT != FLAC__metadata_iterator_get_block_type(iterator)) { if(!FLAC__metadata_iterator_next(iterator)) break; // Already at end } FLAC__StreamMetadata *block = NULL; // If there isn't a vorbis comment block add one if(FLAC__METADATA_TYPE_VORBIS_COMMENT != FLAC__metadata_iterator_get_block_type(iterator)) { // The padding block will be the last block if it exists; add the comment block before it if(FLAC__METADATA_TYPE_PADDING == FLAC__metadata_iterator_get_block_type(iterator)) FLAC__metadata_iterator_prev(iterator); block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT); if(NULL == block) { FLAC__metadata_chain_delete(chain), chain = NULL; FLAC__metadata_iterator_delete(iterator), iterator = NULL; return false; } // Add our metadata if(!FLAC__metadata_iterator_insert_block_after(iterator, block)) { if(NULL != error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid FLAC file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Unable to write metadata"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioMetadataErrorDomain, AudioMetadataInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } FLAC__metadata_chain_delete(chain), chain = NULL; FLAC__metadata_iterator_delete(iterator), iterator = NULL; return false; } } else block = FLAC__metadata_iterator_get_block(iterator); // Standard tags SetVorbisComment(block, "ALBUM", GetAlbumTitle()); SetVorbisComment(block, "ARTIST", GetArtist()); SetVorbisComment(block, "ALBUMARTIST", GetAlbumArtist()); SetVorbisComment(block, "COMPOSER", GetComposer()); SetVorbisComment(block, "GENRE", GetGenre()); SetVorbisComment(block, "DATE", GetReleaseDate()); SetVorbisComment(block, "DESCRIPTION", GetComment()); SetVorbisComment(block, "TITLE", GetTitle()); SetVorbisCommentNumber(block, "TRACKNUMBER", GetTrackNumber()); SetVorbisCommentNumber(block, "TRACKTOTAL", GetTrackTotal()); SetVorbisCommentBoolean(block, "COMPILATION", GetCompilation()); SetVorbisCommentNumber(block, "DISCNUMBER", GetDiscNumber()); SetVorbisCommentNumber(block, "DISCTOTAL", GetDiscTotal()); SetVorbisComment(block, "ISRC", GetISRC()); SetVorbisComment(block, "MCN", GetMCN()); // Additional metadata CFDictionaryRef additionalMetadata = GetAdditionalMetadata(); if(NULL != additionalMetadata) { CFIndex count = CFDictionaryGetCount(additionalMetadata); const void * keys [count]; const void * values [count]; CFDictionaryGetKeysAndValues(additionalMetadata, reinterpret_cast<const void **>(keys), reinterpret_cast<const void **>(values)); for(CFIndex i = 0; i < count; ++i) { CFIndex keySize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(reinterpret_cast<CFStringRef>(keys[i])), kCFStringEncodingASCII); char key [keySize + 1]; if(!CFStringGetCString(reinterpret_cast<CFStringRef>(keys[i]), key, keySize + 1, kCFStringEncodingASCII)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.FLAC"); LOG4CXX_WARN(logger, "CFStringGetCString() failed"); continue; } SetVorbisComment(block, key, reinterpret_cast<CFStringRef>(values[i])); } } // ReplayGain info SetVorbisCommentDouble(block, "REPLAYGAIN_REFERENCE_LOUDNESS", GetReplayGainReferenceLoudness(), CFSTR("%2.1f dB")); SetVorbisCommentDouble(block, "REPLAYGAIN_TRACK_GAIN", GetReplayGainReferenceLoudness(), CFSTR("%+2.2f dB")); SetVorbisCommentDouble(block, "REPLAYGAIN_TRACK_PEAK", GetReplayGainTrackGain(), CFSTR("%1.8f")); SetVorbisCommentDouble(block, "REPLAYGAIN_ALBUM_GAIN", GetReplayGainAlbumGain(), CFSTR("%+2.2f dB")); SetVorbisCommentDouble(block, "REPLAYGAIN_ALBUM_PEAK", GetReplayGainAlbumPeak(), CFSTR("%1.8f")); // Write the new metadata to the file if(!FLAC__metadata_chain_write(chain, true, false)) { if(NULL != error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid FLAC file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Unable to write metadata"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioMetadataErrorDomain, AudioMetadataInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } FLAC__metadata_chain_delete(chain), chain = NULL; FLAC__metadata_iterator_delete(iterator), iterator = NULL; return false; } FLAC__metadata_chain_delete(chain), chain = NULL; FLAC__metadata_iterator_delete(iterator), iterator = NULL; MergeChangedMetadataIntoMetadata(); return true; }
bool MP4Metadata::WriteMetadata(CFErrorRef *error) { UInt8 buf [PATH_MAX]; if(!CFURLGetFileSystemRepresentation(mURL, false, buf, PATH_MAX)) return false; // Open the file for modification MP4FileHandle file = MP4Modify(reinterpret_cast<const char *>(buf)); if(MP4_INVALID_FILE_HANDLE == file) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mURL); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid MPEG-4 file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an MPEG file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioMetadataErrorDomain, AudioMetadataInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } return false; } // Read the tags const MP4Tags *tags = MP4TagsAlloc(); if(NULL == tags) { MP4Close(file), file = NULL; if(error) *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, ENOMEM, NULL); return false; } MP4TagsFetch(tags, file); // Album Title CFStringRef str = GetAlbumTitle(); if(str) { CFIndex cStringSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); char cString [cStringSize + 1]; if(!CFStringGetCString(str, cString, cStringSize + 1, kCFStringEncodingUTF8)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFStringGetCString() failed"); return false; } MP4TagsSetAlbum(tags, cString); } else MP4TagsSetAlbum(tags, NULL); // Artist str = GetArtist(); if(str) { CFIndex cStringSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); char cString [cStringSize + 1]; if(!CFStringGetCString(str, cString, cStringSize + 1, kCFStringEncodingUTF8)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFStringGetCString() failed"); return false; } MP4TagsSetArtist(tags, cString); } else MP4TagsSetArtist(tags, NULL); // Album Artist str = GetAlbumArtist(); if(str) { CFIndex cStringSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); char cString [cStringSize + 1]; if(!CFStringGetCString(str, cString, cStringSize + 1, kCFStringEncodingUTF8)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFStringGetCString() failed"); return false; } MP4TagsSetAlbumArtist(tags, cString); } else MP4TagsSetAlbumArtist(tags, NULL); // Genre str = GetGenre(); if(str) { CFIndex cStringSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); char cString [cStringSize + 1]; if(!CFStringGetCString(str, cString, cStringSize + 1, kCFStringEncodingUTF8)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFStringGetCString() failed"); return false; } MP4TagsSetGenre(tags, cString); } else MP4TagsSetGenre(tags, NULL); // Release date str = GetReleaseDate(); if(str) { CFIndex cStringSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); char cString [cStringSize + 1]; if(!CFStringGetCString(str, cString, cStringSize + 1, kCFStringEncodingUTF8)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFStringGetCString() failed"); return false; } MP4TagsSetReleaseDate(tags, cString); } else MP4TagsSetReleaseDate(tags, NULL); // Composer str = GetComposer(); if(str) { CFIndex cStringSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); char cString [cStringSize + 1]; if(!CFStringGetCString(str, cString, cStringSize + 1, kCFStringEncodingUTF8)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFStringGetCString() failed"); return false; } MP4TagsSetComposer(tags, cString); } else MP4TagsSetComposer(tags, NULL); // Comment str = GetComment(); if(str) { CFIndex cStringSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); char cString [cStringSize + 1]; if(!CFStringGetCString(str, cString, cStringSize + 1, kCFStringEncodingUTF8)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFStringGetCString() failed"); return false; } MP4TagsSetComments(tags, cString); } else MP4TagsSetComments(tags, NULL); // Track title str = GetTitle(); if(str) { CFIndex cStringSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); char cString [cStringSize + 1]; if(!CFStringGetCString(str, cString, cStringSize + 1, kCFStringEncodingUTF8)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFStringGetCString() failed"); return false; } MP4TagsSetName(tags, cString); } else MP4TagsSetName(tags, NULL); // Track number and total MP4TagTrack trackInfo; memset(&trackInfo, 0, sizeof(MP4TagTrack)); if(GetTrackNumber()) CFNumberGetValue(GetTrackNumber(), kCFNumberSInt32Type, &trackInfo.index); if(GetTrackTotal()) CFNumberGetValue(GetTrackTotal(), kCFNumberSInt32Type, &trackInfo.total); MP4TagsSetTrack(tags, &trackInfo); // Disc number and total MP4TagDisk discInfo; memset(&discInfo, 0, sizeof(MP4TagDisk)); if(GetDiscNumber()) CFNumberGetValue(GetDiscNumber(), kCFNumberSInt32Type, &discInfo.index); if(GetDiscTotal()) CFNumberGetValue(GetDiscTotal(), kCFNumberSInt32Type, &discInfo.total); MP4TagsSetDisk(tags, &discInfo); // Compilation if(GetCompilation()) { uint8_t comp = CFBooleanGetValue(GetCompilation()); MP4TagsSetCompilation(tags, &comp); } else MP4TagsSetCompilation(tags, NULL); // Lyrics str = GetLyrics(); if(str) { CFIndex cStringSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8); char cString [cStringSize + 1]; if(!CFStringGetCString(str, cString, cStringSize + 1, kCFStringEncodingUTF8)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFStringGetCString() failed"); return false; } MP4TagsSetLyrics(tags, cString); } else MP4TagsSetLyrics(tags, NULL); // Album art CFDataRef artData = GetFrontCoverArt(); if(artData) { MP4TagArtwork artwork; artwork.data = reinterpret_cast<void *>(const_cast<UInt8 *>(CFDataGetBytePtr(artData))); artwork.size = static_cast<uint32_t>(CFDataGetLength(artData)); artwork.type = MP4_ART_UNDEFINED; MP4TagsAddArtwork(tags, &artwork); } // Save our changes MP4TagsStore(tags, file); // Replay Gain // Reference loudness if(GetReplayGainReferenceLoudness()) { float f; if(!CFNumberGetValue(GetReplayGainReferenceLoudness(), kCFNumberFloatType, &f)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFStringGetCString() failed"); return false; } char value [8]; snprintf(value, sizeof(value), "%2.1f dB", f); MP4ItmfItem *item = MP4ItmfItemAlloc("----", 1); if(NULL != item) { item->mean = strdup("com.apple.iTunes"); item->name = strdup("replaygain_reference_loudness"); item->dataList.elements[0].typeCode = MP4_ITMF_BT_UTF8; item->dataList.elements[0].value = reinterpret_cast<uint8_t *>(strdup(value)); item->dataList.elements[0].valueSize = static_cast<uint32_t>(strlen(value)); } } else { MP4ItmfItemList *items = MP4ItmfGetItemsByMeaning(file, "com.apple.iTunes", "replaygain_reference_loudness"); if(items) { for(uint32_t i = 0; i < items->size; ++i) MP4ItmfRemoveItem(file, items->elements + i); } MP4ItmfItemListFree(items), items = NULL; } // Track gain if(GetReplayGainTrackGain()) { float f; if(!CFNumberGetValue(GetReplayGainTrackGain(), kCFNumberFloatType, &f)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFNumberGetValue() failed"); return false; } char value [10]; snprintf(value, sizeof(value), "%+2.2f dB", f); MP4ItmfItem *item = MP4ItmfItemAlloc("----", 1); if(NULL != item) { item->mean = strdup("com.apple.iTunes"); item->name = strdup("replaygain_track_gain"); item->dataList.elements[0].typeCode = MP4_ITMF_BT_UTF8; item->dataList.elements[0].value = reinterpret_cast<uint8_t *>(strdup(value)); item->dataList.elements[0].valueSize = static_cast<uint32_t>(strlen(value)); } } else { MP4ItmfItemList *items = MP4ItmfGetItemsByMeaning(file, "com.apple.iTunes", "replaygain_track_gain"); if(items) { for(uint32_t i = 0; i < items->size; ++i) MP4ItmfRemoveItem(file, items->elements + i); } MP4ItmfItemListFree(items), items = NULL; } // Track peak if(GetReplayGainTrackPeak()) { float f; if(!CFNumberGetValue(GetReplayGainTrackPeak(), kCFNumberFloatType, &f)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFNumberGetValue() failed"); return false; } char value [12]; snprintf(value, sizeof(value), "%1.8f", f); MP4ItmfItem *item = MP4ItmfItemAlloc("----", 1); if(NULL != item) { item->mean = strdup("com.apple.iTunes"); item->name = strdup("replaygain_track_peak"); item->dataList.elements[0].typeCode = MP4_ITMF_BT_UTF8; item->dataList.elements[0].value = reinterpret_cast<uint8_t *>(strdup(value)); item->dataList.elements[0].valueSize = static_cast<uint32_t>(strlen(value)); } } else { MP4ItmfItemList *items = MP4ItmfGetItemsByMeaning(file, "com.apple.iTunes", "replaygain_track_peak"); if(items) { for(uint32_t i = 0; i < items->size; ++i) MP4ItmfRemoveItem(file, items->elements + i); } MP4ItmfItemListFree(items), items = NULL; } // Album gain if(GetReplayGainAlbumGain()) { float f; if(!CFNumberGetValue(GetReplayGainAlbumGain(), kCFNumberFloatType, &f)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFNumberGetValue() failed"); return false; } char value [10]; snprintf(value, sizeof(value), "%+2.2f dB", f); MP4ItmfItem *item = MP4ItmfItemAlloc("----", 1); if(NULL != item) { item->mean = strdup("com.apple.iTunes"); item->name = strdup("replaygain_album_gain"); item->dataList.elements[0].typeCode = MP4_ITMF_BT_UTF8; item->dataList.elements[0].value = reinterpret_cast<uint8_t *>(strdup(value)); item->dataList.elements[0].valueSize = static_cast<uint32_t>(strlen(value)); } } else { MP4ItmfItemList *items = MP4ItmfGetItemsByMeaning(file, "com.apple.iTunes", "replaygain_album_gain"); if(items) { for(uint32_t i = 0; i < items->size; ++i) MP4ItmfRemoveItem(file, items->elements + i); } MP4ItmfItemListFree(items), items = NULL; } // Album peak if(GetReplayGainAlbumPeak()) { float f; if(!CFNumberGetValue(GetReplayGainAlbumPeak(), kCFNumberFloatType, &f)) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioMetadata.MP4"); LOG4CXX_WARN(logger, "CFNumberGetValue() failed"); return false; } char value [12]; snprintf(value, sizeof(value), "%1.8f", f); MP4ItmfItem *item = MP4ItmfItemAlloc("----", 1); if(NULL != item) { item->mean = strdup("com.apple.iTunes"); item->name = strdup("replaygain_album_peak"); item->dataList.elements[0].typeCode = MP4_ITMF_BT_UTF8; item->dataList.elements[0].value = reinterpret_cast<uint8_t *>(strdup(value)); item->dataList.elements[0].valueSize = static_cast<uint32_t>(strlen(value)); } } else { MP4ItmfItemList *items = MP4ItmfGetItemsByMeaning(file, "com.apple.iTunes", "replaygain_album_peak"); if(items) { for(uint32_t i = 0; i < items->size; ++i) MP4ItmfRemoveItem(file, items->elements + i); } MP4ItmfItemListFree(items), items = NULL; } // Clean up MP4TagsFree(tags), tags = NULL; MP4Close(file), file = NULL; MergeChangedMetadataIntoMetadata(); return true; }