コード例 #1
0
ファイル: operations.c プロジェクト: mikemakuch/muzikbrowzer
FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOptions *options)
{
	FLAC__bool ok = true, needs_write = false, is_ogg = false;
	FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();

	if(0 == chain)
		die("out of memory allocating chain");

	/*@@@@ lame way of guessing the file type */
	if(strlen(filename) >= 4 && 0 == strcmp(filename+strlen(filename)-4, ".ogg"))
		is_ogg = true;

	if(! (is_ogg? FLAC__metadata_chain_read_ogg(chain, filename) : FLAC__metadata_chain_read(chain, filename)) ) {
		print_error_with_chain_status(chain, "%s: ERROR: reading metadata", filename);
		FLAC__metadata_chain_delete(chain);
		return false;
	}

	switch(options->ops.operations[0].type) {
		case OP__LIST:
			ok = do_major_operation__list(options->prefix_with_filename? filename : 0, chain, options);
			break;
		case OP__APPEND:
			ok = do_major_operation__append(chain, options);
			needs_write = true;
			break;
		case OP__REMOVE:
			ok = do_major_operation__remove(chain, options);
			needs_write = true;
			break;
		case OP__REMOVE_ALL:
			ok = do_major_operation__remove_all(chain, options);
			needs_write = true;
			break;
		case OP__MERGE_PADDING:
			FLAC__metadata_chain_merge_padding(chain);
			needs_write = true;
			break;
		case OP__SORT_PADDING:
			FLAC__metadata_chain_sort_padding(chain);
			needs_write = true;
			break;
		default:
			FLAC__ASSERT(0);
			return false;
	}

	if(ok && needs_write) {
		if(options->use_padding)
			FLAC__metadata_chain_sort_padding(chain);
		ok = FLAC__metadata_chain_write(chain, options->use_padding, options->preserve_modtime);
		if(!ok)
			print_error_with_chain_status(chain, "%s: ERROR: writing FLAC file", filename);
	}

	FLAC__metadata_chain_delete(chain);

	return ok;
}
コード例 #2
0
ファイル: replaygain.c プロジェクト: Erikhht/TCPMP
static const char *store_to_file_pre_(const char *filename, FLAC__Metadata_Chain **chain, FLAC__StreamMetadata **block)
{
	FLAC__Metadata_Iterator *iterator;
	const char *error;
	FLAC__bool found_vc_block = false;

	if(0 == (*chain = FLAC__metadata_chain_new()))
		return "memory allocation error";

	if(!FLAC__metadata_chain_read(*chain, filename)) {
		error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)];
		FLAC__metadata_chain_delete(*chain);
		return error;
	}

	if(0 == (iterator = FLAC__metadata_iterator_new())) {
		FLAC__metadata_chain_delete(*chain);
		return "memory allocation error";
	}

	FLAC__metadata_iterator_init(iterator, *chain);

	do {
		*block = FLAC__metadata_iterator_get_block(iterator);
		if((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
			found_vc_block = true;
	} while(!found_vc_block && FLAC__metadata_iterator_next(iterator));

	if(!found_vc_block) {
		/* create a new block */
		*block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
		if(0 == *block) {
			FLAC__metadata_chain_delete(*chain);
			FLAC__metadata_iterator_delete(iterator);
			return "memory allocation error";
		}
		while(FLAC__metadata_iterator_next(iterator))
			;
		if(!FLAC__metadata_iterator_insert_block_after(iterator, *block)) {
			error = FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(*chain)];
			FLAC__metadata_chain_delete(*chain);
			FLAC__metadata_iterator_delete(iterator);
			return error;
		}
		/* iterator is left pointing to new block */
		FLAC__ASSERT(FLAC__metadata_iterator_get_block(iterator) == *block);
	}

	FLAC__metadata_iterator_delete(iterator);

	FLAC__ASSERT(0 != *block);
	FLAC__ASSERT((*block)->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);

	return 0;
}
コード例 #3
0
ファイル: operations.c プロジェクト: mikemakuch/muzikbrowzer
FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLineOptions *options)
{
	unsigned i;
	FLAC__bool ok = true, needs_write = false, use_padding = options->use_padding;
	FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();

	if(0 == chain)
		die("out of memory allocating chain");

	if(!FLAC__metadata_chain_read(chain, filename)) {
		print_error_with_chain_status(chain, "%s: ERROR: reading metadata", filename);
		return false;
	}

	for(i = 0; i < options->ops.num_operations && ok; i++) {
		/*
		 * Do OP__ADD_SEEKPOINT last to avoid decoding twice if both
		 * --add-seekpoint and --import-cuesheet-from are used.
		 */
		if(options->ops.operations[i].type != OP__ADD_SEEKPOINT)
			ok &= do_shorthand_operation(filename, options->prefix_with_filename, chain, &options->ops.operations[i], &needs_write, options->utf8_convert);

		/* The following seems counterintuitive but the meaning
		 * of 'use_padding' is 'try to keep the overall metadata
		 * to its original size, adding or truncating extra
		 * padding if necessary' which is why we need to turn it
		 * off in this case.  If we don't, the extra padding block
		 * will just be truncated.
		 */
		if(options->ops.operations[i].type == OP__ADD_PADDING)
			use_padding = false;
	}

	/*
	 * Do OP__ADD_SEEKPOINT last to avoid decoding twice if both
	 * --add-seekpoint and --import-cuesheet-from are used.
	 */
	for(i = 0; i < options->ops.num_operations && ok; i++) {
		if(options->ops.operations[i].type == OP__ADD_SEEKPOINT)
			ok &= do_shorthand_operation(filename, options->prefix_with_filename, chain, &options->ops.operations[i], &needs_write, options->utf8_convert);
	}

	if(ok && needs_write) {
		if(use_padding)
			FLAC__metadata_chain_sort_padding(chain);
		ok = FLAC__metadata_chain_write(chain, use_padding, options->preserve_modtime);
		if(!ok)
			print_error_with_chain_status(chain, "%s: ERROR: writing FLAC file", filename);
	}

	FLAC__metadata_chain_delete(chain);

	return ok;
}
コード例 #4
0
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;
}
コード例 #5
0
bool FLACMetadata::ReadMetadata(CFErrorRef *error)
{
	// Start from scratch
	CFDictionaryRemoveAllValues(mMetadata);
	CFDictionaryRemoveAllValues(mChangedMetadata);
	
	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_Iterator *iterator = FLAC__metadata_iterator_new();

	if(NULL == iterator) {
		FLAC__metadata_chain_delete(chain), chain = NULL;		
		return false;
	}
	
	CFDictionarySetValue(mMetadata, kPropertiesFormatNameKey, CFSTR("FLAC"));

	FLAC__metadata_iterator_init(iterator, chain);
	
	FLAC__StreamMetadata *block = NULL;
	
	CFMutableDictionaryRef additionalMetadata = CFDictionaryCreateMutable(kCFAllocatorDefault, 
																		  32,
																		  &kCFTypeDictionaryKeyCallBacks,
																		  &kCFTypeDictionaryValueCallBacks);
	
	do {
		block = FLAC__metadata_iterator_get_block(iterator);
		
		if(NULL == block)
			break;
		
		switch(block->type) {					
			case FLAC__METADATA_TYPE_VORBIS_COMMENT:				
				for(unsigned i = 0; i < block->data.vorbis_comment.num_comments; ++i) {
					
					char *fieldName = NULL;
					char *fieldValue = NULL;
					
					// Let FLAC parse the comment for us
					if(!FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(block->data.vorbis_comment.comments[i], &fieldName, &fieldValue)) {
						// Ignore malformed comments
						continue;
					}
					
					CFStringRef key = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
																	reinterpret_cast<const UInt8 *>(fieldName),
																	strlen(fieldName), 
																	kCFStringEncodingASCII,
																	false,
																	kCFAllocatorMalloc);

					CFStringRef value = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
																	  reinterpret_cast<const UInt8 *>(fieldValue),
																	  strlen(fieldValue), 
																	  kCFStringEncodingUTF8,
																	  false,
																	  kCFAllocatorMalloc);
					
					if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ALBUM"), kCFCompareCaseInsensitive))
						CFDictionarySetValue(mMetadata, kMetadataAlbumTitleKey, value);
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ARTIST"), kCFCompareCaseInsensitive))
						CFDictionarySetValue(mMetadata, kMetadataArtistKey, value);
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ALBUMARTIST"), kCFCompareCaseInsensitive))
						CFDictionarySetValue(mMetadata, kMetadataAlbumArtistKey, value);
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("COMPOSER"), kCFCompareCaseInsensitive))
						CFDictionarySetValue(mMetadata, kMetadataComposerKey, value);
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("GENRE"), kCFCompareCaseInsensitive))
						CFDictionarySetValue(mMetadata, kMetadataGenreKey, value);
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DATE"), kCFCompareCaseInsensitive))
						CFDictionarySetValue(mMetadata, kMetadataReleaseDateKey, value);
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DESCRIPTION"), kCFCompareCaseInsensitive))
						CFDictionarySetValue(mMetadata, kMetadataCommentKey, value);
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("TITLE"), kCFCompareCaseInsensitive))
						CFDictionarySetValue(mMetadata, kMetadataTitleKey, value);
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("TRACKNUMBER"), kCFCompareCaseInsensitive)) {
						int num = CFStringGetIntValue(value);
						CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &num);
						CFDictionarySetValue(mMetadata, kMetadataTrackNumberKey, number);
						CFRelease(number), number = NULL;
					}
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("TRACKTOTAL"), kCFCompareCaseInsensitive)) {
						int num = CFStringGetIntValue(value);
						CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &num);
						CFDictionarySetValue(mMetadata, kMetadataTrackTotalKey, number);
						CFRelease(number), number = NULL;
					}
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("COMPILATION"), kCFCompareCaseInsensitive))
						CFDictionarySetValue(mMetadata, kMetadataCompilationKey, CFStringGetIntValue(value) ? kCFBooleanTrue : kCFBooleanFalse);
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DISCNUMBER"), kCFCompareCaseInsensitive)) {
						int num = CFStringGetIntValue(value);
						CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &num);
						CFDictionarySetValue(mMetadata, kMetadataDiscNumberKey, number);
						CFRelease(number), number = NULL;
					}
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("DISCTOTAL"), kCFCompareCaseInsensitive)) {
						int num = CFStringGetIntValue(value);
						CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &num);
						CFDictionarySetValue(mMetadata, kMetadataDiscTotalKey, number);
						CFRelease(number), number = NULL;
					}
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("ISRC"), kCFCompareCaseInsensitive))
						CFDictionarySetValue(mMetadata, kMetadataISRCKey, value);
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("MCN"), kCFCompareCaseInsensitive))
						CFDictionarySetValue(mMetadata, kMetadataMCNKey, value);
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_REFERENCE_LOUDNESS"), kCFCompareCaseInsensitive)) {
						double num = CFStringGetDoubleValue(value);
						CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &num);
						CFDictionarySetValue(mMetadata, kReplayGainReferenceLoudnessKey, number);
						CFRelease(number), number = NULL;
					}
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_TRACK_GAIN"), kCFCompareCaseInsensitive)) {
						double num = CFStringGetDoubleValue(value);
						CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &num);
						CFDictionarySetValue(mMetadata, kReplayGainTrackGainKey, number);
						CFRelease(number), number = NULL;
					}
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_TRACK_PEAK"), kCFCompareCaseInsensitive)) {
						double num = CFStringGetDoubleValue(value);
						CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &num);
						CFDictionarySetValue(mMetadata, kReplayGainTrackPeakKey, number);
						CFRelease(number), number = NULL;
					}
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_ALBUM_GAIN"), kCFCompareCaseInsensitive)) {
						double num = CFStringGetDoubleValue(value);
						CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &num);
						CFDictionarySetValue(mMetadata, kReplayGainAlbumGainKey, number);
						CFRelease(number), number = NULL;
					}
					else if(kCFCompareEqualTo == CFStringCompare(key, CFSTR("REPLAYGAIN_ALBUM_PEAK"), kCFCompareCaseInsensitive)) {
						double num = CFStringGetDoubleValue(value);
						CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &num);
						CFDictionarySetValue(mMetadata, kReplayGainAlbumPeakKey, number);
						CFRelease(number), number = NULL;
					}
					// Put all unknown tags into the additional metadata
					else
						CFDictionarySetValue(additionalMetadata, key, value);
					
					CFRelease(key), key = NULL;
					CFRelease(value), value = NULL;
					
					fieldName = NULL;
					fieldValue = NULL;
				}
				break;
				
			case FLAC__METADATA_TYPE_PICTURE:
			{
				CFDataRef data = CFDataCreate(kCFAllocatorDefault, block->data.picture.data, block->data.picture.data_length);
				CFDictionarySetValue(mMetadata, kAlbumArtFrontCoverKey, data);
				CFRelease(data), data = NULL;
			}
			break;
				
			case FLAC__METADATA_TYPE_STREAMINFO:
			{
				CFNumberRef sampleRate = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &block->data.stream_info.sample_rate);
				CFDictionarySetValue(mMetadata, kPropertiesSampleRateKey, sampleRate);
				CFRelease(sampleRate), sampleRate = NULL;

				CFNumberRef channelsPerFrame = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &block->data.stream_info.channels);
				CFDictionarySetValue(mMetadata, kPropertiesChannelsPerFrameKey, channelsPerFrame);
				CFRelease(channelsPerFrame), channelsPerFrame = NULL;

				CFNumberRef bitsPerChannel = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &block->data.stream_info.bits_per_sample);
				CFDictionarySetValue(mMetadata, kPropertiesBitsPerChannelKey, bitsPerChannel);
				CFRelease(bitsPerChannel), bitsPerChannel = NULL;
				
				CFNumberRef totalFrames = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongLongType, &block->data.stream_info.total_samples);
				CFDictionarySetValue(mMetadata, kPropertiesTotalFramesKey, totalFrames);
				CFRelease(totalFrames), totalFrames = NULL;

				double length = static_cast<double>(block->data.stream_info.total_samples / block->data.stream_info.sample_rate);
				CFNumberRef duration = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &length);
				CFDictionarySetValue(mMetadata, kPropertiesDurationKey, duration);
				CFRelease(duration), duration = NULL;

				double losslessBitrate = static_cast<double>(block->data.stream_info.sample_rate * block->data.stream_info.channels * block->data.stream_info.bits_per_sample) / 1000;
				CFNumberRef bitrate = CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &losslessBitrate);
				CFDictionarySetValue(mMetadata, kPropertiesBitrateKey, bitrate);
				CFRelease(bitrate), bitrate = NULL;
			}
			break;

			case FLAC__METADATA_TYPE_PADDING:						break;
			case FLAC__METADATA_TYPE_APPLICATION:					break;
			case FLAC__METADATA_TYPE_SEEKTABLE:						break;
			case FLAC__METADATA_TYPE_CUESHEET:						break;
			case FLAC__METADATA_TYPE_UNDEFINED:						break;

			default:												break;
		}
	} while(FLAC__metadata_iterator_next(iterator));

	if(CFDictionaryGetCount(additionalMetadata))
		SetAdditionalMetadata(additionalMetadata);
	
	CFRelease(additionalMetadata), additionalMetadata = NULL;
	
	FLAC__metadata_iterator_delete(iterator), iterator = NULL;
	FLAC__metadata_chain_delete(chain), chain = NULL;
	
	return true;
}
コード例 #6
0
ファイル: flac_tag.c プロジェクト: bbarenblat/easytag
/*
 * Write Flac tag, using the level 2 flac interface
 */
gboolean Flac_Tag_Write_File_Tag (ET_File *ETFile)
{
    File_Tag *FileTag;
    gchar *filename_utf8, *filename;
    gchar *basename_utf8;
    FLAC__Metadata_Chain *chain;
    FLAC__Metadata_Iterator *iter;
    FLAC__StreamMetadata_VorbisComment_Entry vce_field_vendor_string; // To save vendor string
    gboolean vce_field_vendor_string_found = FALSE;


    if (!ETFile || !ETFile->FileTag)
        return FALSE;

    FileTag       = (File_Tag *)ETFile->FileTag->data;
    filename      = ((File_Name *)ETFile->FileNameCur->data)->value;
    filename_utf8 = ((File_Name *)ETFile->FileNameCur->data)->value_utf8;
    flac_error_msg = NULL;

    /* libFLAC is able to detect (and skip) ID3v2 tags by itself */

    // Create a new chain instance to get all blocks in one time
    chain = FLAC__metadata_chain_new();
    if (chain == NULL || !FLAC__metadata_chain_read(chain,filename))
    {
        if (chain == NULL)
        {
            // Error with "FLAC__metadata_chain_new"
            flac_error_msg = FLAC__Metadata_ChainStatusString[FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR];
        } else
        {
            // Error with "FLAC__metadata_chain_read"
            FLAC__Metadata_ChainStatus status = FLAC__metadata_chain_status(chain);
            flac_error_msg = FLAC__Metadata_ChainStatusString[status];

            FLAC__metadata_chain_delete(chain);
        }

        Log_Print(LOG_ERROR,_("ERROR while opening file: '%s' as FLAC (%s)."),filename_utf8,flac_error_msg);
        return FALSE;
    }

    // Create a new iterator instance for the chain
    iter = FLAC__metadata_iterator_new();
    if (iter == NULL)
    {
        flac_error_msg = FLAC__Metadata_ChainStatusString[FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR];

        Log_Print(LOG_ERROR,_("ERROR while opening file: '%s' as FLAC (%s)."),filename_utf8,flac_error_msg);
        return FALSE;
    }

    // Initialize the iterator to point to the first metadata block in the given chain.
    FLAC__metadata_iterator_init(iter,chain);

    while (FLAC__metadata_iterator_next(iter))
    {
        //g_print("Write: %d %s -> block type: %d\n",j++,g_path_get_basename(filename),FLAC__metadata_iterator_get_block_type(iter));

        // Action to do according the type
        switch ( FLAC__metadata_iterator_get_block_type(iter) )
        {
        //
        // Delete the VORBIS_COMMENT block and convert to padding. But before, save the original vendor string.
        //
        case FLAC__METADATA_TYPE_VORBIS_COMMENT:
        {
            // Get block data
            FLAC__StreamMetadata *block = FLAC__metadata_iterator_get_block(iter);
            FLAC__StreamMetadata_VorbisComment *vc = &block->data.vorbis_comment;

            if (vc->vendor_string.entry != NULL)
            {
                // Get initial vendor string, to don't alterate it by FLAC__VENDOR_STRING when saving file
                vce_field_vendor_string.entry = (FLAC__byte *)g_strdup((gchar *)vc->vendor_string.entry);
                vce_field_vendor_string.length = strlen((gchar *)vce_field_vendor_string.entry);
                vce_field_vendor_string_found = TRUE;
            }

            // Free block data
            FLAC__metadata_iterator_delete_block(iter,true);
            break;
        }

            //
            // Delete all the PICTURE blocks, and convert to padding
            //
#ifndef LEGACY_FLAC // For FLAC >= 1.1.3
        case FLAC__METADATA_TYPE_PICTURE:
        {
            FLAC__metadata_iterator_delete_block(iter,true);
            break;
        }
#endif

        default:
            break;
        }
    }


    //
    // Create and insert a new VORBISCOMMENT block
    //
    {
        FLAC__StreamMetadata *vc_block; // For vorbis comments
        FLAC__StreamMetadata_VorbisComment_Entry field;
        gchar *string;
        GList *list;

        // Allocate a block for Vorbis comments
        vc_block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);

        // Set the original vendor string, else will be use the version of library
        if (vce_field_vendor_string_found)
        {
            // must set 'copy' param to false, because the API will reuse the  pointer of an empty
            // string (yet still return 'true', indicating it was copied); the string is free'd during
            // metadata_chain_delete routine
            FLAC__metadata_object_vorbiscomment_set_vendor_string(vc_block, vce_field_vendor_string, /*copy=*/false);
        }


        /*********
         * Title *
         *********/
        if ( FileTag->title )
        {
            Flac_Write_Delimetered_Tag(vc_block,"TITLE=",FileTag->title);
        }

        /**********
         * Artist *
         **********/
        if ( FileTag->artist )
        {
            Flac_Write_Delimetered_Tag(vc_block,"ARTIST=",FileTag->artist);
        }

        /*********
         * Album *
         *********/
        if ( FileTag->album )
        {
            Flac_Write_Delimetered_Tag(vc_block,"ALBUM=",FileTag->album);
        }

        /***************
         * Disc Number *
         ***************/
        if ( FileTag->disc_number )
        {
            string = g_strconcat("DISCNUMBER=",FileTag->disc_number,NULL);
            field.entry = (FLAC__byte *)string;
            field.length = strlen(string);
            FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
            g_free(string);
        }

        /********
         * Year *
         ********/
        if ( FileTag->year )
        {
            string = g_strconcat("DATE=",FileTag->year,NULL);
            field.entry = (FLAC__byte *)string;
            field.length = strlen(string);
            FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
            g_free(string);
        }

        /*************************
         * Track and Total Track *
         *************************/
        if ( FileTag->track )
        {
            string = g_strconcat("TRACKNUMBER=",FileTag->track,NULL);
            field.entry = (FLAC__byte *)string;
            field.length = strlen(string);
            FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
            g_free(string);
        }
        if ( FileTag->track_total /*&& strlen(FileTag->track_total)>0*/ )
        {
            string = g_strconcat("TRACKTOTAL=",FileTag->track_total,NULL);
            field.entry = (FLAC__byte *)string;
            field.length = strlen(string);
            FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
            g_free(string);
        }

        /*********
         * Genre *
         *********/
        if ( FileTag->genre )
        {
            Flac_Write_Delimetered_Tag(vc_block,"GENRE=",FileTag->genre);
        }

        /***********
         * Comment *
         ***********/
        // We write the comment using the "both" format
        if ( FileTag->comment )
        {
            Flac_Write_Delimetered_Tag(vc_block,"DESCRIPTION=",FileTag->comment);

            Flac_Write_Delimetered_Tag(vc_block,"COMMENT=",FileTag->comment);
        }

        /************
         * Composer *
         ************/
        if ( FileTag->composer )
        {
            Flac_Write_Delimetered_Tag(vc_block,"COMPOSER=",FileTag->composer);
        }

        /*******************
         * Original artist *
         *******************/
        if ( FileTag->orig_artist )
        {
            Flac_Write_Delimetered_Tag(vc_block,"PERFORMER=",FileTag->orig_artist);
        }

        /*************
         * Copyright *
         *************/
        if ( FileTag->copyright )
        {
            string  = g_strconcat("COPYRIGHT=",FileTag->copyright,NULL);
            field.entry = (FLAC__byte *)string;
            field.length = strlen(string);
            FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
            g_free(string);
        }

        /*******
         * URL *
         *******/
        if ( FileTag->url )
        {
            string = g_strconcat("LICENSE=",FileTag->url,NULL);
            field.entry = (FLAC__byte *)string;
            field.length = strlen(string);
            FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
            g_free(string);
        }

        /**************
         * Encoded by *
         **************/
        if ( FileTag->encoded_by )
        {
            string = g_strconcat("ENCODED-BY=",FileTag->encoded_by,NULL);
            field.entry = (FLAC__byte *)string;
            field.length = strlen(string);
            FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
            g_free(string);
        }


        /**************************
         * Set unsupported fields *
         **************************/
        list = FileTag->other;
        while (list)
        {
            if (list->data)
            {
                string = (gchar*)list->data;
                field.entry = (FLAC__byte *)string;
                field.length = strlen(string);
                FLAC__metadata_object_vorbiscomment_insert_comment(vc_block,vc_block->data.vorbis_comment.num_comments,field,true);
            }
            list = list->next;
        }

        // Add the block to the the chain (so we don't need to free the block)
        FLAC__metadata_iterator_insert_block_after(iter, vc_block);
    }



    //
    // Create and insert PICTURE blocks
    //

    /***********
     * Picture *
     ***********/
    // For FLAC >= 1.1.3
#ifndef LEGACY_FLAC
    {
        Picture *pic = FileTag->picture;
        while (pic)
        {
            if (pic->data)
            {
                const gchar *violation;
                FLAC__StreamMetadata *picture_block; // For picture data
                Picture_Format format;

                // Allocate block for picture data
                picture_block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PICTURE);

                // Type
                picture_block->data.picture.type = pic->type;

                // Mime type
                format = Picture_Format_From_Data(pic);
                FLAC__metadata_object_picture_set_mime_type(picture_block, (gchar *)Picture_Mime_Type_String(format), TRUE);

                // Description
                if (pic->description)
                {
                    FLAC__metadata_object_picture_set_description(picture_block, (FLAC__byte *)pic->description, TRUE);
                }

                // Resolution
                picture_block->data.picture.width  = pic->width;
                picture_block->data.picture.height = pic->height;
                picture_block->data.picture.depth  = 0;

                // Picture data
                FLAC__metadata_object_picture_set_data(picture_block, (FLAC__byte *)pic->data, (FLAC__uint32) pic->size, TRUE);

                if (!FLAC__metadata_object_picture_is_legal(picture_block, &violation))
                {
                    Log_Print(LOG_ERROR,_("Picture block isn't valid: '%s'"),violation);
                    FLAC__metadata_object_delete(picture_block);
                } else
                {
                    // Add the block to the the chain (so we don't need to free the block)
                    FLAC__metadata_iterator_insert_block_after(iter, picture_block);
                }
            }

            pic = pic->next;
        }
    }
#endif

    // Free iter
    FLAC__metadata_iterator_delete(iter);


    //
    // Prepare for writing tag
    //

    // Move all PADDING blocks to the end on the metadata, and merge them into a single block.
    FLAC__metadata_chain_sort_padding(chain);

    // Write tag
    if ( !FLAC__metadata_chain_write(chain, /*padding*/TRUE, PRESERVE_MODIFICATION_TIME) )
    {
        // Error with "FLAC__metadata_chain_write"
        FLAC__Metadata_ChainStatus status = FLAC__metadata_chain_status(chain);
        flac_error_msg = FLAC__Metadata_ChainStatusString[status];

        FLAC__metadata_chain_delete(chain);

        Log_Print(LOG_ERROR,_("ERROR: Failed to write comments to file '%s' (%s)."),filename_utf8,flac_error_msg);
        return FALSE;
    } else
    {
        basename_utf8 = g_path_get_basename(filename_utf8);
        Log_Print(LOG_OK,_("Written tag of '%s'"),basename_utf8);
        g_free(basename_utf8);
    }

    FLAC__metadata_chain_delete(chain);


#ifdef ENABLE_MP3
    /*
     * Write also the ID3 tags (ID3v1 and/or ID3v2) if wanted (as needed by some players)
     */
    if (WRITE_ID3_TAGS_IN_FLAC_FILE)
    {
        Id3tag_Write_File_Tag(ETFile);
    } else
    {
        // Delete the ID3 tags (create a dummy ETFile for the Id3tag_... function)
        ET_File   *ETFile_tmp    = ET_File_Item_New();
        File_Name *FileName_tmp  = ET_File_Name_Item_New();
        File_Tag  *FileTag_tmp   = ET_File_Tag_Item_New();
        // Same file...
        FileName_tmp->value      = g_strdup(filename);
        FileName_tmp->value_utf8 = g_strdup(filename_utf8); // Not necessary to fill 'value_ck'
        ETFile_tmp->FileNameList = g_list_append(NULL,FileName_tmp);
        ETFile_tmp->FileNameCur  = ETFile_tmp->FileNameList;
        // With empty tag...
        ETFile_tmp->FileTagList  = g_list_append(NULL,FileTag_tmp);
        ETFile_tmp->FileTag      = ETFile_tmp->FileTagList;
        Id3tag_Write_File_Tag(ETFile_tmp);
        ET_Free_File_List_Item(ETFile_tmp);
    }
#endif

    return TRUE;
}