/// <summary> /// Refreshes the metadata of the audio file. /// </summary> public void RefreshMetadata() { // Get file size #if WINDOWSSTORE fileSize = 0; #else var fileInfo = new FileInfo(filePath); fileSize = fileInfo.Length; #endif // Check what is the type of the audio file if (fileType == AudioFileFormat.MP3) { // Declare variables TagLib.Mpeg.AudioFile file = null; try { // Create a more specific type of class for MP3 files file = new TagLib.Mpeg.AudioFile(filePath); // Get the position of the first and last block firstBlockPosition = file.InvariantStartPosition; lastBlockPosition = file.InvariantEndPosition; // Copy tags FillProperties(file.Tag); // Loop through codecs (usually just one) foreach (TagLib.ICodec codec in file.Properties.Codecs) { // Convert codec into a header TagLib.Mpeg.AudioHeader header = (TagLib.Mpeg.AudioHeader)codec; // Copy properties audioChannels = header.AudioChannels; frameLength = header.AudioFrameLength; audioLayer = header.AudioLayer; sampleRate = header.AudioSampleRate; bitsPerSample = 16; // always 16-bit channelMode = header.ChannelMode; bitrate = header.AudioBitrate; length = Conversion.TimeSpanToTimeString(header.Duration); } } catch (Exception ex) { // Throw exception TODO: Check if file exists when catching the exception (to make a better error description) throw new Exception("An error occured while reading the tags and properties of the file (" + filePath + ").", ex); } finally { // Dispose file (if needed) if (file != null) file.Dispose(); } try { // // Check if there's a Xing header // XingInfoHeaderData xingHeader = XingInfoHeaderReader.ReadXingInfoHeader(filePath, firstBlockPosition); // // // Check if the read was successful // if (xingHeader.Status == XingInfoHeaderStatus.Successful) // { // // Set property value // //m_xingInfoHeader = xingHeader; // mp3EncoderDelay = xingHeader.EncoderDelay; // mp3EncoderPadding = xingHeader.EncoderPadding; // mp3EncoderVersion = xingHeader.EncoderVersion; // mp3HeaderType = xingHeader.HeaderType; // } } catch (Exception ex) { throw ex; } } else if (fileType == AudioFileFormat.FLAC) { // Declare variables TagLib.Flac.File file = null; try { // Read VorbisComment in FLAC file file = new TagLib.Flac.File(filePath); // Get the position of the first and last block firstBlockPosition = file.InvariantStartPosition; lastBlockPosition = file.InvariantEndPosition; // Copy tags FillProperties(file.Tag); // Loop through codecs (usually just one) foreach (TagLib.ICodec codec in file.Properties.Codecs) { // Convert codec into a header TagLib.Flac.StreamHeader header = (TagLib.Flac.StreamHeader)codec; // Copy properties bitrate = header.AudioBitrate; audioChannels = header.AudioChannels; sampleRate = header.AudioSampleRate; bitsPerSample = header.BitsPerSample; length = Conversion.TimeSpanToTimeString(header.Duration); } } catch (Exception ex) { // Throw exception throw new Exception("An error occured while reading the tags and properties of the file (" + filePath + ").", ex); } finally { // Dispose file (if needed) if (file != null) file.Dispose(); } } else if (fileType == AudioFileFormat.OGG) { // Declare variables TagLib.Ogg.File file = null; try { // Read VorbisComment in OGG file file = new TagLib.Ogg.File(filePath); // Get the position of the first and last block firstBlockPosition = file.InvariantStartPosition; lastBlockPosition = file.InvariantEndPosition; // Copy tags FillProperties(file.Tag); // Loop through codecs (usually just one) foreach (TagLib.ICodec codec in file.Properties.Codecs) { // Check what kind of codec is used if (codec is TagLib.Ogg.Codecs.Theora) { // Do nothing, this is useless for audio. } else if (codec is TagLib.Ogg.Codecs.Vorbis) { // Convert codec into a header TagLib.Ogg.Codecs.Vorbis header = (TagLib.Ogg.Codecs.Vorbis)codec; // Copy properties bitrate = header.AudioBitrate; audioChannels = header.AudioChannels; sampleRate = header.AudioSampleRate; bitsPerSample = 16; length = Conversion.TimeSpanToTimeString(header.Duration); } } } catch (Exception ex) { // Throw exception throw new Exception("An error occured while reading the tags and properties of the file (" + filePath + ").", ex); } finally { // Dispose file (if needed) if (file != null) file.Dispose(); } } else if (fileType == AudioFileFormat.APE) { // Monkey's Audio (APE) supports APEv2 tags. // http://en.wikipedia.org/wiki/Monkey's_Audio // Declare variables TagLib.Ape.File file = null; try { // Read APE metadata apeTag = APEMetadata.Read(filePath); // Get APEv1/v2 tags from APE file file = new TagLib.Ape.File(filePath); // Get the position of the first and last block firstBlockPosition = file.InvariantStartPosition; lastBlockPosition = file.InvariantEndPosition; // Copy tags FillProperties(file.Tag); // Loop through codecs (usually just one) foreach (TagLib.ICodec codec in file.Properties.Codecs) { // Check what kind of codec is used if (codec is TagLib.Ape.StreamHeader) { // Convert codec into a header TagLib.Ape.StreamHeader header = (TagLib.Ape.StreamHeader)codec; // Copy properties bitrate = header.AudioBitrate; audioChannels = header.AudioChannels; sampleRate = header.AudioSampleRate; bitsPerSample = 16; length = Conversion.TimeSpanToTimeString(header.Duration); } } } catch (Exception ex) { // Throw exception throw new Exception("An error occured while reading the tags and properties of the file (" + filePath + ").", ex); } finally { // Dispose file (if needed) if (file != null) file.Dispose(); } } else if (fileType == AudioFileFormat.MPC) { // MusePack (MPC) supports APEv2 tags. // http://en.wikipedia.org/wiki/Musepack try { // Try to read SV8 header sv8Tag = SV8Metadata.Read(filePath); // Set audio properties audioChannels = sv8Tag.AudioChannels; sampleRate = sv8Tag.SampleRate; bitsPerSample = 16; length = sv8Tag.Length; bitrate = sv8Tag.Bitrate; } catch (SV8TagNotFoundException exSV8) { try { // Try to read the SV7 header sv7Tag = SV7Metadata.Read(filePath); // Set audio properties audioChannels = sv7Tag.AudioChannels; sampleRate = sv7Tag.SampleRate; bitsPerSample = 16; length = sv7Tag.Length; bitrate = sv7Tag.Bitrate; } catch (SV7TagNotFoundException exSV7) { // No headers have been found! SV8TagNotFoundException finalEx = new SV8TagNotFoundException(exSV8.Message, exSV7); throw new Exception("Error: The file is not in SV7/MPC or SV8/MPC format!", finalEx); } } try { // Read APE tag apeTag = APEMetadata.Read(filePath); // Copy tags FillProperties(apeTag); } catch (Exception ex) { // Check exception if (ex is APETagNotFoundException) { // Skip file } else { throw; } } } else if (fileType == AudioFileFormat.OFR) { // TagLib does not support OFR files... // OptimFROG (OFR) supports APEv2 tags. // http://en.wikipedia.org/wiki/OptimFROG } else if (fileType == AudioFileFormat.WV) { // WavPack supports APEv2 and ID3v1 tags. // http://www.wavpack.com/wavpack_doc.html // Declare variables TagLib.WavPack.File file = null; try { // Read WavPack tags file = new TagLib.WavPack.File(filePath); // Get the position of the first and last block firstBlockPosition = file.InvariantStartPosition; lastBlockPosition = file.InvariantEndPosition; // Copy tags FillProperties(file.Tag); // Loop through codecs (usually just one) foreach (TagLib.ICodec codec in file.Properties.Codecs) { // Check what kind of codec is used if (codec is TagLib.WavPack.StreamHeader) { // Convert codec into a header TagLib.WavPack.StreamHeader header = (TagLib.WavPack.StreamHeader)codec; // Copy properties bitrate = header.AudioBitrate; audioChannels = header.AudioChannels; sampleRate = header.AudioSampleRate; bitsPerSample = 16; length = Conversion.TimeSpanToTimeString(header.Duration); } } } catch (Exception ex) { // Throw exception throw new Exception("An error occured while reading the tags and properties of the file (" + filePath + ").", ex); } finally { // Dispose file (if needed) if (file != null) file.Dispose(); } } else if (fileType == AudioFileFormat.TTA) { // The True Audio (TTA) format supports ID3v1, ID3v2 and APEv2 tags. // http://en.wikipedia.org/wiki/TTA_(codec) audioChannels = 2; sampleRate = 44100; bitsPerSample = 16; // TagLib doesn't work. } else if (fileType == AudioFileFormat.WAV) { // Declare variables TagLib.Riff.File file = null; try { // Get WAV file file = new TagLib.Riff.File(filePath); // Get the position of the first and last block firstBlockPosition = file.InvariantStartPosition; lastBlockPosition = file.InvariantEndPosition; // Copy tags FillProperties(file.Tag); // Loop through codecs (usually just one) foreach (TagLib.ICodec codec in file.Properties.Codecs) { // Check what kind of codec is used if (codec is TagLib.Riff.WaveFormatEx) { // Convert codec into a header TagLib.Riff.WaveFormatEx header = (TagLib.Riff.WaveFormatEx)codec; // Copy properties bitrate = header.AudioBitrate; audioChannels = header.AudioChannels; sampleRate = header.AudioSampleRate; bitsPerSample = 16; length = Conversion.TimeSpanToTimeString(header.Duration); } } } catch (Exception ex) { // Throw exception throw new Exception("An error occured while reading the tags and properties of the file (" + filePath + ").", ex); } finally { // Dispose file (if needed) if (file != null) file.Dispose(); } } else if (fileType == AudioFileFormat.WMA) { // Declare variables TagLib.Asf.File file = null; try { // Read ASF/WMA tags file = new TagLib.Asf.File(filePath); // Get the position of the first and last block firstBlockPosition = file.InvariantStartPosition; lastBlockPosition = file.InvariantEndPosition; // Copy tags FillProperties(file.Tag); // The right length is here, not in the codec data structure length = Conversion.TimeSpanToTimeString(file.Properties.Duration); // Loop through codecs (usually just one) foreach (TagLib.ICodec codec in file.Properties.Codecs) { // Check what kind of codec is used if (codec is TagLib.Riff.WaveFormatEx) { // Convert codec into a header TagLib.Riff.WaveFormatEx header = (TagLib.Riff.WaveFormatEx)codec; // Copy properties bitrate = header.AudioBitrate; audioChannels = header.AudioChannels; sampleRate = header.AudioSampleRate; bitsPerSample = 16; } } } catch (Exception ex) { throw ex; } finally { // Dispose file (if needed) if (file != null) file.Dispose(); } } else if (fileType == AudioFileFormat.AAC) { // Declare variables TagLib.Aac.File file = null; try { // Read AAC tags file = new TagLib.Aac.File(filePath); // Doesn't seem to work very well... } catch (Exception ex) { throw ex; } finally { // Dispose file (if needed) if (file != null) file.Dispose(); } } // If the song has no name, give filename as the name if (String.IsNullOrEmpty(Title)) { Title = Path.GetFileNameWithoutExtension(filePath); } // If the artist has no name, give it "Unknown Artist" if (String.IsNullOrEmpty(ArtistName)) { ArtistName = "Unknown Artist"; } // If the song has no album title, give it "Unknown Album" if (String.IsNullOrEmpty(AlbumTitle)) { AlbumTitle = "Unknown Album"; } }
/// <summary> /// Reads the SV7 metadata from a MPC (MusePack) audio file. /// </summary> /// <param name="filePath">Audio file path</param> /// <returns>SV7 data structure</returns> public static SV7Tag Read(string filePath) { if (String.IsNullOrEmpty(filePath)) throw new Exception("The file path cannot be null or empty!"); SV7Tag data = new SV7Tag(); #if WINDOWSSTORE #else BinaryReader reader = null; FileStream stream = null; try { // Open binary reader stream = File.OpenRead(filePath); reader = new BinaryReader(stream); // Get file length long fileLength = reader.BaseStream.Length; // Read signature byte[] bytesSigntaure = reader.ReadBytes(3); string signature = Encoding.UTF8.GetString(bytesSigntaure, 0, bytesSigntaure.Length); // Validate signature if (signature.ToUpper() != "MP+") { throw new SV7TagNotFoundException("The file is not in MPC/SV7 format!", null); } // Read stream major/minor version byte byteStreamMajorMinorVersion = reader.ReadByte(); int streamVersionMinor = ((int)byteStreamMajorMinorVersion >> 4) & 0x0F; int streamVersionMajor = ((int)byteStreamMajorMinorVersion & 0x0F); // Validate version if (streamVersionMajor != 7) { throw new SV7TagNotFoundException("This file header version is not SV7!", null); } // Read frame count (32-bits) byte[] bytesFrameCount = reader.ReadBytes(4); data.FrameCount = BitConverter.ToInt32(bytesFrameCount, 0); // Read Max level byte[] bytesMaxLevel = reader.ReadBytes(2); data.MaxLevel = BitConverter.ToInt16(bytesMaxLevel, 0); // Read Profile / Link / SampleFreq byte byteProfile_Link_SampleFreq = reader.ReadByte(); data.EncoderProfile = ((int)byteProfile_Link_SampleFreq & 0xF0) >> 4; int link = ((int)byteProfile_Link_SampleFreq & 0x0C) >> 2; int sampleFrequency = ((int)byteProfile_Link_SampleFreq & 0x03) >> 0; // Read Intensity stereo / Mid side stereo / Max band byte byteIntensityStereo_MidSideStereo_MaxBand = reader.ReadByte(); int intensityStereo = ((int)byteIntensityStereo_MidSideStereo_MaxBand & 0x80) >> 7; int midSideStereo = ((int)byteIntensityStereo_MidSideStereo_MaxBand & 0x40) >> 6; data.MaxBand = ((int)byteIntensityStereo_MidSideStereo_MaxBand & 0x3F) >> 2; // Read title peak byte[] bytesTitlePeak = reader.ReadBytes(2); data.TitlePeak = BitConverter.ToInt16(bytesTitlePeak, 0); // Read title gain byte[] bytesTitleGain = reader.ReadBytes(2); data.TitleGain = BitConverter.ToInt16(bytesTitleGain, 0); // Read album peak byte[] bytesAlbumPeak = reader.ReadBytes(2); data.AlbumPeak = BitConverter.ToInt16(bytesAlbumPeak, 0); // Read album gain byte[] bytesAlbumGain = reader.ReadBytes(2); data.AlbumGain = BitConverter.ToInt16(bytesAlbumGain, 0); // Read unused bytes reader.ReadBytes(2); // Read true gapless / last frame length / fast seeking byte[] bytesTrueGapless_LastFrameLength_FastSeeking = reader.ReadBytes(2); int trueGapless = ((int)bytesTrueGapless_LastFrameLength_FastSeeking[0] & 0x80) >> 7; data.LastFrameLength = ((int)bytesTrueGapless_LastFrameLength_FastSeeking[0] & 0x80) >> 7; // Get last frame length int lastFrameLength = BitConverter.ToInt16(bytesTrueGapless_LastFrameLength_FastSeeking, 0); data.LastFrameLength = (lastFrameLength & 0x7FF0) >> 4; // Read unused bytes reader.ReadBytes(3); // Read encoder version byte byteEncoderVersion = reader.ReadByte(); string encoderVersion = "mppenc "; // Check for beta if (byteEncoderVersion % 2 == 0) { encoderVersion += "Beta "; } // Check for alpha else if (byteEncoderVersion % 2 == 1) { encoderVersion += "Alpha "; } // Complete string with version encoderVersion += ((double)byteEncoderVersion / 100).ToString("0.00"); // Set metadata data.EncoderVersion = encoderVersion; data.IntensityStereo = (intensityStereo == 1) ? true : false; data.MidSideStereoEnabled = (midSideStereo == 1) ? true : false; data.TrueGapless = (trueGapless == 1) ? true : false; // Sample rate if (sampleFrequency == 0) { data.SampleRate = 44100; } else if (sampleFrequency == 1) { data.SampleRate = 48000; } else if (sampleFrequency == 2) { data.SampleRate = 37800; } else if (sampleFrequency == 3) { data.SampleRate = 32000; } // Calculate length data.LengthSamples = ((((data.FrameCount - 1) * 1152) + data.LastFrameLength) * data.AudioChannels) / 2; // floating point data.LengthMS = ConvertAudio.ToMS(data.LengthSamples, (uint)data.SampleRate); data.Length = Conversion.MillisecondsToTimeString((ulong)data.LengthMS); long audioLengthBytes = fileLength - 28; // SV7 header is always 28 bytes data.Bitrate = (int)(audioLengthBytes / data.LengthMS) * 8; } catch { throw; } finally { // Dispose stream (reader will be automatically disposed too) stream.Close(); } #endif return data; }