DWORD CTag_Mp4::Save(LPCTSTR szFileName) { DWORD dwWin32errorCode = ERROR_SUCCESS; if(!m_bEnable) { return -1; } char *pFileName = TstrToDataAlloc(szFileName, -1, NULL, DTC_CODE_UTF8); if (pFileName == NULL) { return -1; } MP4FileHandle mp4file = MP4Modify(pFileName); free(pFileName); if(mp4file == MP4_INVALID_FILE_HANDLE) { return -1; } #ifdef USE_OLD_TAG_API if(m_strMetadata_Name.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Name, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4SetMetadataName(mp4file,buf); free(buf); } } else { MP4DeleteMetadataName(mp4file); } if(m_strMetadata_Artist.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Artist, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4SetMetadataArtist(mp4file,buf); free(buf); } } else { MP4DeleteMetadataArtist(mp4file); } if(m_strMetadata_Album.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Album, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4SetMetadataAlbum(mp4file,buf); free(buf); } } else { MP4DeleteMetadataAlbum(mp4file); } if(m_strMetadata_Group.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Group, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4SetMetadataGrouping(mp4file,buf); free(buf); } } else { MP4DeleteMetadataGrouping(mp4file); } if(m_strMetadata_Composer.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Composer, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4SetMetadataWriter(mp4file,buf); free(buf); } } else { MP4DeleteMetadataWriter(mp4file); } if(m_strMetadata_Genre.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Genre, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4SetMetadataGenre(mp4file,buf); free(buf); } } else { MP4DeleteMetadataGenre(mp4file); } if((m_iMetadata_Track1 == -1) && (m_iMetadata_Track2 == -1)) { MP4DeleteMetadataTrack(mp4file); } else if(m_iMetadata_Track1 == -1) { MP4SetMetadataTrack(mp4file,0,m_iMetadata_Track2); } else if(m_iMetadata_Track2 == -1) { MP4SetMetadataTrack(mp4file,m_iMetadata_Track1,0); } else { MP4SetMetadataTrack(mp4file,m_iMetadata_Track1,m_iMetadata_Track2); } if((m_iMetadata_Disc1 == -1) && (m_iMetadata_Disc2 == -1)) { MP4DeleteMetadataDisk(mp4file); } else if(m_iMetadata_Disc1 == -1) { MP4SetMetadataDisk(mp4file,0,m_iMetadata_Disc2); } else if(m_iMetadata_Disc2 == -1) { MP4SetMetadataDisk(mp4file,m_iMetadata_Disc1,0); } else { MP4SetMetadataDisk(mp4file,m_iMetadata_Disc1,m_iMetadata_Disc2); } if(m_iMetadata_Tempo == -1) { MP4DeleteMetadataTempo(mp4file); } else { MP4SetMetadataTempo(mp4file,m_iMetadata_Tempo); } if(m_strMetadata_Year.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Year, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4SetMetadataYear(mp4file,buf); free(buf); } } else { MP4DeleteMetadataYear(mp4file); } if(m_iMetadata_Compilation != -1) { MP4SetMetadataCompilation(mp4file,1); } else { MP4DeleteMetadataCompilation(mp4file); } if(m_strMetadata_Comment.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Comment, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4SetMetadataComment(mp4file,buf); free(buf); } } else { MP4DeleteMetadataComment(mp4file); } if(m_strMetadata_Tool.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Tool, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4SetMetadataTool(mp4file,buf); free(buf); } } else { MP4DeleteMetadataTool(mp4file); } #else const MP4Tags* tags = MP4TagsAlloc(); if(tags) { MP4TagsFetch(tags, mp4file); if(m_strMetadata_Name.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Name, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4TagsSetName(tags, buf); free(buf); } } else { MP4TagsSetName(tags, NULL); } if(m_strMetadata_Artist.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Artist, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4TagsSetArtist(tags, buf); free(buf); } } else { MP4TagsSetArtist(tags, NULL); } if(m_strMetadata_Album.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Album, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4TagsSetAlbum(tags, buf); free(buf); } } else { MP4TagsSetAlbum(tags, NULL); } if(m_strMetadata_Group.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Group, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4TagsSetGrouping(tags, buf); free(buf); } } else { MP4TagsSetGrouping(tags, NULL); } if(m_strMetadata_Composer.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Composer, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4TagsSetComposer(tags, buf); free(buf); } } else { MP4TagsSetComposer(tags, NULL); } if(m_strMetadata_Genre.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Genre, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4TagsSetGenre(tags, buf); free(buf); } } else { MP4TagsSetGenre(tags, NULL); } if((m_iMetadata_Track1 == -1) && (m_iMetadata_Track2 == -1)) { MP4TagsSetTrack(tags, NULL); } else { MP4TagTrack track; track.index = (m_iMetadata_Track1 == -1) ? 0 : m_iMetadata_Track1; track.total = (m_iMetadata_Track2 == -1) ? 0 : m_iMetadata_Track2; MP4TagsSetTrack(tags, &track); } if((m_iMetadata_Disc1 == -1) && (m_iMetadata_Disc2 == -1)) { MP4TagsSetDisk(tags, NULL); } else { MP4TagDisk disk; disk.index = (m_iMetadata_Disc1 == -1) ? 0 : m_iMetadata_Disc1; disk.total = (m_iMetadata_Disc2 == -1) ? 0 : m_iMetadata_Disc2; MP4TagsSetDisk(tags, &disk); } if(m_iMetadata_Tempo == -1) { MP4TagsSetTempo(tags, NULL); } else { uint16_t tempo = m_iMetadata_Tempo; MP4TagsSetTempo(tags, &tempo); } if(m_strMetadata_Year.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Year, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4TagsSetReleaseDate(tags, buf); free(buf); } } else { MP4TagsSetReleaseDate(tags, NULL); } if(m_iMetadata_Compilation != -1) { uint8_t compilation = 1; MP4TagsSetCompilation(tags, &compilation); } else { MP4TagsSetCompilation(tags, NULL); } if(m_strMetadata_Comment.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Comment, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4TagsSetComments(tags, buf); free(buf); } } else { MP4TagsSetComments(tags, NULL); } if(m_strMetadata_Tool.GetLength()) { char *buf = TstrToDataAlloc(m_strMetadata_Tool, -1, NULL, DTC_CODE_UTF8); if (buf != NULL) { MP4TagsSetEncodingTool(tags, buf); free(buf); } } else { MP4TagsSetEncodingTool(tags, NULL); } MP4TagsStore(tags, mp4file); MP4TagsFree(tags); } #endif MP4Close(mp4file); return dwWin32errorCode; }
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; }