Beispiel #1
0
	virtual void OnClick(Point pt, int widget, int click_count)
	{
		switch (widget) {
			case MTSW_LIST_LEFT: { // add to playlist
				int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL);

				if (_msf.playlist < 4) return;
				if (!IsInsideMM(y, 0, BaseMusic::GetUsedSet()->num_available)) return;

				byte *p = _playlists[_msf.playlist];
				for (uint i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) {
					if (p[i] == 0) {
						/* Find the actual song number */
						for (uint j = 0; j < NUM_SONGS_AVAILABLE; j++) {
							if (GetTrackNumber(j) == y + 1) {
								p[i] = j + 1;
								break;
							}
						}
						p[i + 1] = 0;
						this->SetDirty();
						SelectSongToPlay();
						break;
					}
				}
				break;
			}

			case MTSW_LIST_RIGHT: { // remove from playlist
				int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL);

				if (_msf.playlist < 4) return;
				if (!IsInsideMM(y, 0, NUM_SONGS_PLAYLIST)) return;

				byte *p = _playlists[_msf.playlist];
				for (uint i = y; i != NUM_SONGS_PLAYLIST - 1; i++) {
					p[i] = p[i + 1];
				}

				this->SetDirty();
				SelectSongToPlay();
				break;
			}

			case MTSW_CLEAR: // clear
				for (uint i = 0; _playlists[_msf.playlist][i] != 0; i++) _playlists[_msf.playlist][i] = 0;
				this->SetDirty();
				StopMusic();
				SelectSongToPlay();
				break;

			case MTSW_ALL: case MTSW_OLD: case MTSW_NEW:
			case MTSW_EZY: case MTSW_CUSTOM1: case MTSW_CUSTOM2: // set playlist
				SelectPlaylist(widget - MTSW_ALL);
				StopMusic();
				SelectSongToPlay();
				break;
		}
	}
Beispiel #2
0
	virtual void DrawWidget(const Rect &r, int widget) const
	{
		switch (widget) {
			case MW_GAUGE:
				GfxFillRect(r.left, r.top, r.right, r.bottom, 0);

				for (uint i = 0; i != 8; i++) {
					int colour = 0xD0;
					if (i > 4) {
						colour = 0xBF;
						if (i > 6) {
							colour = 0xB8;
						}
					}
					GfxFillRect(r.left, r.bottom - i * 2, r.right, r.bottom - i * 2, colour);
				}
				break;

			case MW_TRACK_NR: {
				GfxFillRect(r.left + 1, r.top + 1, r.right, r.bottom, 0);
				StringID str = STR_MUSIC_TRACK_NONE;
				if (_song_is_active != 0 && _music_wnd_cursong != 0) {
					SetDParam(0, GetTrackNumber(_music_wnd_cursong - 1));
					SetDParam(1, 2);
					str = STR_MUSIC_TRACK_DIGIT;
				}
				DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str);
				break;
			}

			case MW_TRACK_NAME: {
				GfxFillRect(r.left, r.top + 1, r.right - 1, r.bottom, 0);
				StringID str = STR_MUSIC_TITLE_NONE;
				if (_song_is_active != 0 && _music_wnd_cursong != 0) {
					str = STR_MUSIC_TITLE_NAME;
					SetDParamStr(0, GetSongName(_music_wnd_cursong - 1));
				}
				DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_FROMSTRING, SA_HOR_CENTER);
				break;
			}

			case MW_MUSIC_VOL: case MW_EFFECT_VOL: {
				DrawFrameRect(r.left, r.top + 2, r.right, r.bottom - 2, COLOUR_GREY, FR_LOWERED);
				byte volume = (widget == MW_MUSIC_VOL) ? _msf.music_vol : _msf.effect_vol;
				int x = (volume * (r.right - r.left) / 127);
				if (_current_text_dir == TD_RTL) {
					x = r.right - x;
				} else {
					x += r.left;
				}
				DrawFrameRect(x, r.top, x + slider_width, r.bottom, COLOUR_GREY, FR_NONE);
				break;
			}
		}
	}
Beispiel #3
0
	virtual void DrawWidget(const Rect &r, int widget) const
	{
		switch (widget) {
			case WID_MTS_LIST_LEFT: {
				GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK);

				int y = r.top + WD_FRAMERECT_TOP;
				uint vscroll_max = min(this->left_sb->GetPosition() + this->left_sb->GetCapacity(), NUM_SONGS_AVAILABLE);

				for (uint i = this->left_sb->GetPosition(); i < vscroll_max; i++) {
					const char *song_name = GetSongName(i);
					if (StrEmpty(song_name)) continue;

					SetDParam(0, GetTrackNumber(i));
					SetDParam(1, 2);
					SetDParamStr(2, song_name);
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, Center(y, this->resize.step_height, FONT_HEIGHT_SMALL), STR_PLAYLIST_TRACK_NAME);
					y += this->resize.step_height;
				}
				break;
			}

			case WID_MTS_LIST_RIGHT: {
				GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, PC_BLACK);

				int y = r.top + WD_FRAMERECT_TOP;
				uint vscroll_max = min(this->right_sb->GetPosition() + this->right_sb->GetCapacity(), this->GetNumberOfTracksOfTracklist());

				for (uint i = this->right_sb->GetPosition(); i < vscroll_max; i++) {
					uint j = _playlists[_settings_client.music.playlist][i] - 1;
					SetDParam(0, GetTrackNumber(j));
					SetDParam(1, 2);
					SetDParamStr(2, GetSongName(j));
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, Center(y, this->resize.step_height, FONT_HEIGHT_SMALL), STR_PLAYLIST_TRACK_NAME);
					y += this->resize.step_height;
				}
				break;
			}
		}
	}
Beispiel #4
0
	virtual void DrawWidget(const Rect &r, int widget) const
	{
		switch (widget) {
			case MTSW_LIST_LEFT: {
				GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, 0);

				int y = r.top + WD_FRAMERECT_TOP;
				for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
					const char *song_name = GetSongName(i);
					if (StrEmpty(song_name)) continue;

					SetDParam(0, GetTrackNumber(i));
					SetDParam(1, 2);
					SetDParamStr(2, song_name);
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
					y += FONT_HEIGHT_SMALL;
				}
				break;
			}

			case MTSW_LIST_RIGHT: {
				GfxFillRect(r.left + 1, r.top + 1, r.right - 1, r.bottom - 1, 0);

				int y = r.top + WD_FRAMERECT_TOP;
				for (const byte *p = _playlists[_msf.playlist]; *p != 0; p++) {
					uint i = *p - 1;
					SetDParam(0, GetTrackNumber(i));
					SetDParam(1, 2);
					SetDParamStr(2, GetSongName(i));
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_PLAYLIST_TRACK_NAME);
					y += FONT_HEIGHT_SMALL;
				}
				break;
			}
		}
	}
Beispiel #5
0
	virtual void DrawWidget(const Rect &r, int widget) const
	{
		switch (widget) {
			case WID_M_TRACK_NR: {
				GfxFillRect(r.left + 1, r.top + 1, r.right, r.bottom, PC_BLACK);
				if (BaseMusic::GetUsedSet()->num_available == 0) {
					break;
				}
				StringID str = STR_MUSIC_TRACK_NONE;
				if (_song_is_active != 0 && _music_wnd_cursong != 0) {
					SetDParam(0, GetTrackNumber(_music_wnd_cursong - 1));
					SetDParam(1, 2);
					str = STR_MUSIC_TRACK_DIGIT;
				}
				DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str);
				break;
			}

			case WID_M_TRACK_NAME: {
				GfxFillRect(r.left, r.top + 1, r.right - 1, r.bottom, PC_BLACK);
				StringID str = STR_MUSIC_TITLE_NONE;
				if (BaseMusic::GetUsedSet()->num_available == 0) {
					str = STR_MUSIC_TITLE_NOMUSIC;
				} else if (_song_is_active != 0 && _music_wnd_cursong != 0) {
					str = STR_MUSIC_TITLE_NAME;
					SetDParamStr(0, GetSongName(_music_wnd_cursong - 1));
				}
				DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, r.top + WD_FRAMERECT_TOP, str, TC_FROMSTRING, SA_HOR_CENTER);
				break;
			}

			case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: {
				int sw = ScaleGUITrad(slider_width);
				int hsw = sw / 2;
				DrawFrameRect(r.left + hsw, r.top + 2, r.right - hsw, r.bottom - 2, COLOUR_GREY, FR_LOWERED);
				byte volume = (widget == WID_M_MUSIC_VOL) ? _settings_client.music.music_vol : _settings_client.music.effect_vol;
				if (_current_text_dir == TD_RTL) volume = 127 - volume;
				int x = r.left + (volume * (r.right - r.left - sw) / 127);
				DrawFrameRect(x, r.top, x + sw, r.bottom, COLOUR_GREY, FR_NONE);
				break;
			}
		}
	}
Beispiel #6
0
bool
MP4MetadataStagefright::ReadTrackIndex(FallibleTArray<Index::Indice>& aDest, mozilla::TrackID aTrackID)
{
  size_t numTracks = mMetadataExtractor->countTracks();
  int32_t trackNumber = GetTrackNumber(aTrackID);
  if (trackNumber < 0) {
    return false;
  }
  sp<MediaSource> track = mMetadataExtractor->getTrack(trackNumber);
  if (!track.get()) {
    return false;
  }
  sp<MetaData> metadata = mMetadataExtractor->getTrackMetaData(trackNumber);
  int64_t mediaTime;
  if (!metadata->findInt64(kKeyMediaTime, &mediaTime)) {
    mediaTime = 0;
  }
  bool rv = ConvertIndex(aDest, track->exportIndex(), mediaTime);

  return rv;
}
Beispiel #7
0
	virtual void DrawWidget(const Rect &r, int widget) const
	{
		switch (widget) {
			case WID_M_TRACK_NR: {
				GfxFillRect(r.left + 1, r.top + 1, r.right, r.bottom, PC_BLACK);
				StringID str = STR_MUSIC_TRACK_NONE;
				if (_song_is_active != 0 && _music_wnd_cursong != 0) {
					SetDParam(0, GetTrackNumber(_music_wnd_cursong - 1));
					SetDParam(1, 2);
					str = STR_MUSIC_TRACK_DIGIT;
				}
				DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, Center(r.top, r.bottom - r.top, FONT_HEIGHT_SMALL), str);
				break;
			}

			case WID_M_TRACK_NAME: {
				GfxFillRect(r.left, r.top + 1, r.right - 1, r.bottom, PC_BLACK);
				StringID str = STR_MUSIC_TITLE_NONE;
				if (_song_is_active != 0 && _music_wnd_cursong != 0) {
					str = STR_MUSIC_TITLE_NAME;
					SetDParamStr(0, GetSongName(_music_wnd_cursong - 1));
				}
				DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, Center(r.top, r.bottom - r.top, FONT_HEIGHT_SMALL), str, TC_FROMSTRING, SA_HOR_CENTER);
				break;
			}

			case WID_M_MUSIC_VOL: case WID_M_EFFECT_VOL: {
				DrawFrameRect(r.left, r.top + 2, r.right, r.bottom - 2, COLOUR_GREY, FR_LOWERED);
				byte volume = (widget == WID_M_MUSIC_VOL) ? _settings_client.music.music_vol : _settings_client.music.effect_vol;
				int x = (volume * (r.right - r.left) / 127);
				if (_current_text_dir == TD_RTL) {
					x = r.right - x;
				} else {
					x += r.left;
				}
				DrawFrameRect(x, r.top, x + slider_width, r.bottom, COLOUR_GREY, FR_NONE);
				break;
			}
		}
	}
Beispiel #8
0
	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
	{
		switch (widget) {
			case WID_MTS_PLAYLIST: {
				Dimension d = {0, 0};

				for (int i = 0; i < 6; i++) {
					SetDParam(0, STR_MUSIC_PLAYLIST_ALL + i);
					d = maxdim(d, GetStringBoundingBox(STR_PLAYLIST_PROGRAM));
				}
				d.width += padding.width;
				d.height += padding.height;
				*size = maxdim(*size, d);
				break;
			}

			case WID_MTS_LIST_LEFT:
			case WID_MTS_LIST_RIGHT: {
				Dimension d = {0, 0};

				for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
					const char *song_name = GetSongName(i);
					if (StrEmpty(song_name)) continue;

					SetDParam(0, GetTrackNumber(i));
					SetDParam(1, 2);
					SetDParamStr(2, GetSongName(i));
					d = maxdim(d, GetStringBoundingBox(STR_PLAYLIST_TRACK_NAME));
				}
				resize->height = GetMinSizing(NWST_STEP, d.height);
				d.width += WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
				d.height = 7 * resize->height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
				*size = maxdim(*size, d);
				break;
			}
		}
	}
Beispiel #9
0
	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
	{
		switch (widget) {
			case MTSW_PLAYLIST: {
				Dimension d = {0, 0};

				for (int i = 0; i < 6; i++) {
					SetDParam(0, STR_MUSIC_PLAYLIST_ALL + i);
					d = maxdim(d, GetStringBoundingBox(STR_PLAYLIST_PROGRAM));
				}
				d.width += padding.width;
				d.height += padding.height;
				*size = maxdim(*size, d);
				break;
			}

			case MTSW_LIST_LEFT: case MTSW_LIST_RIGHT: {
				Dimension d = {0, 0};

				for (uint i = 0; i < NUM_SONGS_AVAILABLE; i++) {
					const char *song_name = GetSongName(i);
					if (StrEmpty(song_name)) continue;

					SetDParam(0, GetTrackNumber(i));
					SetDParam(1, 2);
					SetDParamStr(2, GetSongName(i));
					Dimension d2 = GetStringBoundingBox(STR_PLAYLIST_TRACK_NAME);
					d.width = max(d.width, d2.width);
					d.height += d2.height;
				}
				d.width += padding.width;
				d.height += padding.height;
				*size = maxdim(*size, d);
				break;
			}
		}
	}
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;
}
Beispiel #11
0
	virtual void OnClick(Point pt, int widget, int click_count)
	{
		switch (widget) {
			case WID_MTS_LIST_LEFT: { // add to playlist
				if (_settings_client.music.playlist < 4) return;
				int id_m = this->left_sb->GetScrolledRowFromWidget(pt.y, this, WID_MTS_LIST_LEFT, WD_FRAMERECT_TOP, this->resize.step_height);
				if (!IsInsideMM(id_m, 0, BaseMusic::GetUsedSet()->num_available)) return;

				byte *p = _playlists[_settings_client.music.playlist];
				for (uint i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) {
					if (p[i] == 0) {
						/* Find the actual song number */
						for (uint j = 0; j < NUM_SONGS_AVAILABLE; j++) {
							if (GetTrackNumber(j) == id_m + 1) {
								p[i] = j + 1;
								break;
							}
						}
						p[i + 1] = 0;
						this->right_sb->SetCount(GetNumberOfTracksOfTracklist());
						this->SetDirty();
						SelectSongToPlay();
						break;
					}
				}
				break;
			}

			case WID_MTS_LIST_RIGHT: { // remove from playlist
				if (_settings_client.music.playlist < 4) return;
				int id_m = this->right_sb->GetScrolledRowFromWidget(pt.y, this, WID_MTS_LIST_RIGHT, WD_FRAMERECT_TOP, this->resize.step_height);
				if (!IsInsideMM(id_m, 0, NUM_SONGS_PLAYLIST)) return;

				byte *p = _playlists[_settings_client.music.playlist];
				for (uint i = id_m; i != NUM_SONGS_PLAYLIST - 1; i++) {
					p[i] = p[i + 1];
				}

				this->right_sb->SetCount(GetNumberOfTracksOfTracklist());
				this->SetDirty();
				SelectSongToPlay();
				break;
			}

			case WID_MTS_CLEAR: // clear
				for (uint i = 0; _playlists[_settings_client.music.playlist][i] != 0; i++) _playlists[_settings_client.music.playlist][i] = 0;
				this->right_sb->SetCount(GetNumberOfTracksOfTracklist());
				this->SetDirty();
				StopMusic();
				SelectSongToPlay();
				break;

			case WID_MTS_ALL:
			case WID_MTS_OLD:
			case WID_MTS_NEW:
			case WID_MTS_EZY:
			case WID_MTS_CUSTOM1:
			case WID_MTS_CUSTOM2: // set playlist
				SelectPlaylist(widget - WID_MTS_ALL);
				this->right_sb->SetCount(GetNumberOfTracksOfTracklist());
				this->SetDirty();
				StopMusic();
				SelectSongToPlay();
				break;
		}
	}
Beispiel #12
0
/**
 * Plays the specified MIDI sequence through the sound driver.
 * @param dwFileOffset		File offset of MIDI sequence data
 * @param bLoop				Whether to loop the sequence
 */
bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
	currentMidi = dwFileOffset;
	currentLoop = bLoop;

	// Tinsel V1 PSX uses a different music format, so i
	// disable it here.
	// TODO: Maybe this should be moved to a better place...
	if (TinselV1PSX) return false;

	if (_vm->_config->_musicVolume != 0) {
		bool mute = false;
		if (ConfMan.hasKey("mute"))
			mute = ConfMan.getBool("mute");

		SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);
	}

	// the index and length of the last tune loaded
	static uint32 dwLastMidiIndex = 0;	// FIXME: Avoid non-const global vars
	//static uint32 dwLastSeqLen;

	uint32 dwSeqLen = 0;	// length of the sequence

	// Support for external music from the music enhancement project
	if (_vm->getFeatures() & GF_ENHANCED_AUDIO_SUPPORT) {
		int trackNumber = GetTrackNumber(dwFileOffset);
		int track = 0;
		if (trackNumber >= 0) {
			if (_vm->getFeatures() & GF_ALT_MIDI)
				track = enhancedAudioSCNVersionALT[trackNumber];
			else if (_vm->getFeatures() & GF_SCNFILES)
				track = enhancedAudioSCNVersion[trackNumber];
			else
				track = enhancedAudioGRAVersion[trackNumber];

			if (track > 0) {
				StopMidi();

				// StopMidi resets these fields, so set them again
				currentMidi = dwFileOffset;
				currentLoop = bLoop;

				// try to play track, but don't fall back to a true CD
				g_system->getAudioCDManager()->play(track, bLoop ? -1 : 1, 0, 0, true);

				// Check if an enhanced audio track is being played.
				// If it is, stop here and don't load a MIDI track
				if (g_system->getAudioCDManager()->isPlaying()) {
					return true;
				}
			}
		} else {
			warning("Unknown MIDI offset %d", dwFileOffset);
		}
	}

	if (dwFileOffset == 0)
		return true;

	if (dwFileOffset != dwLastMidiIndex) {
		Common::File midiStream;

		// open MIDI sequence file in binary mode
		if (!midiStream.open(MIDI_FILE))
			error(CANNOT_FIND_FILE, MIDI_FILE);

		// update index of last tune loaded
		dwLastMidiIndex = dwFileOffset;

		// move to correct position in the file
		midiStream.seek(dwFileOffset, SEEK_SET);

		// read the length of the sequence
		dwSeqLen = midiStream.readUint32LE();

		// make sure buffer is large enough for this sequence
		assert(dwSeqLen > 0 && dwSeqLen <= midiBuffer.size);

		// stop any currently playing tune
		_vm->_midiMusic->stop();

		// read the sequence
		if (midiStream.read(midiBuffer.pDat, dwSeqLen) != dwSeqLen)
			error(FILE_IS_CORRUPT, MIDI_FILE);

		midiStream.close();

		// WORKAROUND for bug #2820054 "DW1: No intro music at first start on Wii",
		// which actually affects all ports, since it's specific to the GRA version.
		//
		// The GRA version does not seem to set the channel volume at all for the first
		// intro track, thus we need to do that here. We only initialize the channels
		// used in that sequence. And we are using 127 as default channel volume.
		//
		// Only in the GRA version dwFileOffset can be "38888", just to be sure, we
		// check for the SCN files feature flag not being set though.
		if (_vm->getGameID() == GID_DW1 && dwFileOffset == 38888 && !(_vm->getFeatures() & GF_SCNFILES)) {
			_vm->_midiMusic->send(0x7F07B0 |  3);
			_vm->_midiMusic->send(0x7F07B0 |  5);
			_vm->_midiMusic->send(0x7F07B0 |  8);
			_vm->_midiMusic->send(0x7F07B0 | 10);
			_vm->_midiMusic->send(0x7F07B0 | 13);
		}

		_vm->_midiMusic->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);

		// Store the length
		//dwLastSeqLen = dwSeqLen;
	} else {
	 	// dwFileOffset == dwLastMidiIndex
		_vm->_midiMusic->stop();
		_vm->_midiMusic->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop);
	}

	return true;
}
Beispiel #13
0
/**
 * Plays the specified MIDI sequence through the sound driver.
 * @param dwFileOffset		File offset of MIDI sequence data
 * @param bLoop				Whether to loop the sequence
 */
bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) {
	g_currentMidi = dwFileOffset;
	g_currentLoop = bLoop;

	bool mute = false;
	if (ConfMan.hasKey("mute"))
		mute = ConfMan.getBool("mute");

	SetMidiVolume(mute ? 0 : _vm->_config->_musicVolume);

	// the index and length of the last tune loaded
	uint32 dwSeqLen = 0;	// length of the sequence

	// Support for external music from the music enhancement project
	if (_vm->getFeatures() & GF_ENHANCED_AUDIO_SUPPORT) {
		int trackNumber = GetTrackNumber(dwFileOffset);
		// Track 8 has been removed in the German CD re-release "Neon Edition"
		if ((_vm->getFeatures() & GF_ALT_MIDI) && trackNumber >= 8)
			trackNumber++;

		int track = 0;
		if (trackNumber >= 0) {
			if (_vm->getFeatures() & GF_SCNFILES)
				track = enhancedAudioSCNVersion[trackNumber];
			else
				track = enhancedAudioGRAVersion[trackNumber];

			if (track > 0) {
				StopMidi();

				// StopMidi resets these fields, so set them again
				g_currentMidi = dwFileOffset;
				g_currentLoop = bLoop;

				// try to play track, but don't fall back to a true CD
				g_system->getAudioCDManager()->play(track, bLoop ? -1 : 1, 0, 0, true);

				// Check if an enhanced audio track is being played.
				// If it is, stop here and don't load a MIDI track
				if (g_system->getAudioCDManager()->isPlaying()) {
					return true;
				}
			}
		} else {
			warning("Unknown MIDI offset %d", dwFileOffset);
		}
	}

	if (dwFileOffset == 0)
		return true;

	Common::File midiStream;

	// open MIDI sequence file in binary mode
	if (!midiStream.open(MIDI_FILE))
		error(CANNOT_FIND_FILE, MIDI_FILE);

	// move to correct position in the file
	midiStream.seek(dwFileOffset, SEEK_SET);

	if (TinselV1Mac) {
		// The Macintosh version of DW1 uses raw PCM for music
		dwSeqLen = midiStream.readUint32BE();
		_vm->_sound->playDW1MacMusic(midiStream, dwSeqLen);
	} else {
		dwSeqLen = midiStream.readUint32LE();

		// make sure buffer is large enough for this sequence
		assert(dwSeqLen > 0 && dwSeqLen <= g_midiBuffer.size);

		// stop any currently playing tune
		_vm->_midiMusic->stop();

		// read the sequence. This needs to be read again before playSEQ() is
		// called even if the music is restarting, as playSEQ() reads the file
		// name off the buffer itself. However, that function adds SMF headers
		// to the buffer, thus if it's read again, the SMF headers will be read
		// and the filename will always be 'MThd'.
		if (midiStream.read(g_midiBuffer.pDat, dwSeqLen) != dwSeqLen)
			error(FILE_IS_CORRUPT, MIDI_FILE);

		// WORKAROUND for bug #2820054 "DW1: No intro music at first start on Wii",
		// which actually affects all ports, since it's specific to the GRA version.
		//
		// The GRA version does not seem to set the channel volume at all for the first
		// intro track, thus we need to do that here. We only initialize the channels
		// used in that sequence. And we are using 127 as default channel volume.
		//
		// Only in the GRA version dwFileOffset can be "38888", just to be sure, we
		// check for the SCN files feature flag not being set though.
		if (_vm->getGameID() == GID_DW1 && dwFileOffset == 38888 && !(_vm->getFeatures() & GF_SCNFILES)) {
			_vm->_midiMusic->send(0x7F07B0 |  3);
			_vm->_midiMusic->send(0x7F07B0 |  5);
			_vm->_midiMusic->send(0x7F07B0 |  8);
			_vm->_midiMusic->send(0x7F07B0 | 10);
			_vm->_midiMusic->send(0x7F07B0 | 13);
		}

		_vm->_midiMusic->playMIDI(dwSeqLen, bLoop);
	}

	midiStream.close();

	return true;
}
Beispiel #14
0
	virtual void OnClick(Point pt, int widget, int click_count)
	{
		switch (widget) {
			case WID_MTS_LIST_LEFT: { // add to playlist
				int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL);

				if (_settings_client.music.playlist < 4) return;
				if (!IsInsideMM(y, 0, BaseMusic::GetUsedSet()->num_available)) return;

				byte *p = _playlists[_settings_client.music.playlist];
				for (uint i = 0; i != NUM_SONGS_PLAYLIST - 1; i++) {
					if (p[i] == 0) {
						/* Find the actual song number */
						for (uint j = 0; j < NUM_SONGS_AVAILABLE; j++) {
							if (GetTrackNumber(j) == y + 1) {
								p[i] = j + 1;
								break;
							}
						}
						p[i + 1] = 0;
						this->SetDirty();
						ResetPlaylist();
						break;
					}
				}
				break;
			}

			case WID_MTS_LIST_RIGHT: { // remove from playlist
				int y = this->GetRowFromWidget(pt.y, widget, 0, FONT_HEIGHT_SMALL);

				if (_settings_client.music.playlist < 4) return;
				if (!IsInsideMM(y, 0, NUM_SONGS_PLAYLIST)) return;

				byte *p = _playlists[_settings_client.music.playlist];
				for (uint i = y; i != NUM_SONGS_PLAYLIST - 1; i++) {
					p[i] = p[i + 1];
				}

				this->SetDirty();
				ResetPlaylist();
				break;
			}

			case WID_MTS_MUSICSET: {
				int selected = 0;
				DropDownList *dropdown = BuildMusicSetDropDownList(&selected);
				ShowDropDownList(this, dropdown, selected, widget, 0, true, false);
				break;
			}

			case WID_MTS_CLEAR: // clear
				for (uint i = 0; _playlists[_settings_client.music.playlist][i] != 0; i++) _playlists[_settings_client.music.playlist][i] = 0;
				this->SetDirty();
				StopMusic();
				ResetPlaylist();
				break;

			case WID_MTS_ALL: case WID_MTS_OLD: case WID_MTS_NEW:
			case WID_MTS_EZY: case WID_MTS_CUSTOM1: case WID_MTS_CUSTOM2: // set playlist
				SelectPlaylist(widget - WID_MTS_ALL);
				StopMusic();
				ResetPlaylist();
				break;
		}
	}
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;
}