using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Text; namespace m3uTool { /// /// Indicates that a problem existed when parsing an mp3 file /// public class Mp3FormatException : Exception { private readonly string mFilename; private readonly string mFormatExceptionString; /// /// Constructor. /// /// /// public Mp3FormatException(string filename, string formatExceptionString) { mFilename = filename; mFormatExceptionString = formatExceptionString; } public string Filename { get { return mFilename; } } public string FormatExceptionString { get { return mFormatExceptionString; } } } /// /// Tool for accessing the SampleRateFreqency Enum /// public class ChannelModeUtility { /// /// Converts a ChannelMode into an Int value /// /// /// public static int GetChannelModeInt(ChannelMode s) { switch (s) { case ChannelMode.Stereo: return 2; case ChannelMode.JointStereo: return 2; case ChannelMode.DualChannel: return 2; case ChannelMode.SingleChannel: return 1; default: return 2; } } /// /// (j)oint, (s)imple, (f)orce, (d)dual-mono, (m)ono (-m) /// /// public static char GetChannelModeChar(ChannelMode s) { switch (s) { case ChannelMode.Stereo: return 's'; case ChannelMode.JointStereo: return 'j'; case ChannelMode.DualChannel: return 'd'; case ChannelMode.SingleChannel: return 'm'; default: return 'j'; } } } /// /// The channel mode an mp3 is encoded in /// public enum ChannelMode { /// /// /// Stereo, /// /// Merge a given frequency range of multiple sound channels together so that the /// resulting encoding will perceive the sound information of that range not as a /// bundle of separate channels but as one homogenous lump /// JointStereo, /// /// made of two independant mono channel. Each one uses exactly half the bitrate of the file /// DualChannel, /// /// Mono /// SingleChannel } /// /// Tool for accessing the SampleRateFreqency Enum /// public class SampleRateFrequencyUtility { /// /// Converts a SampleRateFrequency into an Int value /// /// /// public static int GetSampleRateFrequencyInt(SampleRateFrequency s) { return Convert.ToInt32(s.ToString().Substring(s.ToString().IndexOf("_") + 1)); } } /// /// Sample rate frequency of an audio file /// public enum SampleRateFrequency { /// /// reserved /// Hz_0, /// /// 8000 /// Hz_8000, /// /// 11025 /// Hz_11025, /// /// 12000 /// Hz_12000, /// /// 16000 /// Hz_16000, /// /// 22050 /// Hz_22050, /// /// 24000 /// Hz_24000, /// /// 32000 /// Hz_32000, /// /// 41000 /// Hz_44100, /// /// 48000 /// Hz_48000 } /// /// Bitrates /// 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256 and 320 /// public enum BitRate { /// /// reserved /// KBPS_0, /// /// 32 /// KBPS_32, /// /// 40 /// KBPS_40, /// /// 48 /// KBPS_48, /// /// 48 /// KBPS_56, /// /// 64 /// KBPS_64, /// /// 80 /// KBPS_80, /// /// 96 /// KBPS_96, /// /// 112 /// KBPS_112, /// /// 128 /// KBPS_128, /// /// 160 /// KBPS_160, /// /// 192 /// KBPS_192, /// /// 224 /// KBPS_224, /// /// 256 /// KBPS_256, /// /// 320 /// KBPS_320, } /// /// Tool for accessing the BitRate Enum /// public class BitRateFrequencyUtility { /// /// Converts a BitRate into an Int value /// /// /// public static int GetBitRateInt(BitRate b) { return Convert.ToInt32(b.ToString().Substring(b.ToString().IndexOf("_") + 1)); } /// /// Converts a BitRate into an Int value /// /// /// public static int GetBitRateBitsInt(BitRate b) { return GetBitRateInt(b)*1024; } } /// /// Given an mp3 file, this extracts all the encoding details and id3 fields /// public class Mp3FileProperties { #region private variables /// /// mp3's filename, without path /// private readonly string mMp3Filename; /// /// path to the mp3 /// private readonly string mMp3Path; private ulong bithdr; private bool boolVBitRate; private int intVFrames; /// /// The mp3 bit rate /// private int mBitRate; /// /// the channel mode /// private ChannelMode mChannelMode; /// /// the file size, in bytes /// private long mFileSize; /// /// The album field /// private string mId3Album; /// /// The artist field /// public string mId3Artist; /// /// The comment field /// private string mId3Comment; /// /// The genre field byte /// private byte mId3Genre; /// /// The genre field name /// private string mId3GenreName; /// /// True if the id3 tag exists in the mp3 /// private bool mId3TagExists; /// /// The title field /// private string mId3Title; /// /// The track number field /// private byte mId3TrackNumber; /// /// The year field /// private string mId3Year; /// /// the mp3 length in seconds /// private int mLengthInSeconds; /// /// The sample rate frequency /// private SampleRateFrequency mSampleRateFrequency; /// /// The sample rate frequency integer /// private int mSampleRateIntegerFrequency; // Private variables used in the process of reading in the MP3 files #endregion #region Public Properties /// /// path to the mp3 /// public string Mp3Path { get { return mMp3Path; } } /// /// mp3's filename, without path /// public string Mp3Filename { get { return mMp3Filename; } } /// /// mp3's path and filename /// public string Mp3PathAndFilename { get { return mMp3Path + (mMp3Path.EndsWith("\\") ? "" : "\\") + mMp3Filename; } } /// /// The mp3 bit rate /// public int BitRate { get { return mBitRate; } } /// /// the file size, in bytes /// public long FileSize { get { return mFileSize; } } /// /// The sample rate frequency integer /// public int SampleRateIntegerFrequency { get { return mSampleRateIntegerFrequency; } } /// /// The sample rate frequency /// public SampleRateFrequency SampleRateFrequency { get { return mSampleRateFrequency; } } /// /// the channel mode /// public ChannelMode ChannelMode { get { return mChannelMode; } } /// /// the mp3 length in seconds /// public int LengthInSeconds { get { return mLengthInSeconds; } } /// /// the length formatted /// public string LengthFormatted { get { // Complete number of seconds int s = GetLengthInSeconds(); // Seconds to display int ss = s%60; // Complete number of minutes int m = (s - ss)/60; // Minutes to display int mm = m%60; // Complete number of hours int h = (m - mm)/60; // Make "hh:mm:ss" return h.ToString("D2") + ":" + mm.ToString("D2") + ":" + ss.ToString("D2"); } } /// /// the mp3 length in timespan /// public TimeSpan FileLength { get { return new TimeSpan(0, 0, 0, 0, mLengthInSeconds); } } /// /// True if the id3 tag exists in the mp3 /// public bool Id3TagExists { get { return mId3TagExists; } } /// /// The title field /// public string Id3Title { get { return mId3Title; } } /// /// The artist field /// public string Id3Artist { get { return mId3Artist; } } /// /// The album field /// public string Id3Album { get { return mId3Album; } } /// /// The year field /// public string Id3Year { get { return mId3Year; } } /// /// The comment field /// public string Id3Comment { get { return mId3Comment; } } /// /// The track number field /// public byte Id3TrackNumber { get { return mId3TrackNumber; } } /// /// The genre field byte /// public byte Id3Genre { get { return mId3Genre; } } /// /// The genre field name /// public string Id3GenreName { get { return mId3GenreName; } } #endregion #region Private Readonly Static Lookup /// /// list of genres defined by id3 1.1 /// private static readonly string[] mGenres = { "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alternative Rock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass" , "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhytmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "Acapella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Trash Metal", "Anime", "JPop", "SynthPop" }; /// /// bit rates available to mp3 /// private static readonly int[,,] mBitrateTable = { { // MPEG 2 & 2.5 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144 , 160, 0 }, // Layer III { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144 , 160, 0 }, // Layer II { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192 , 224, 256, 0 } // Layer I }, { // MPEG 1 { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 }, // Layer III { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256 , 320, 384, 0 }, // Layer II { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 } // Layer I } }; /// /// integer sample rates avaiable for mp3 /// private static readonly int[,] mSampleFrequencyIntegerTable = { {32000, 16000, 8000}, // MPEG 2.5 {0, 0, 0}, // reserved {22050, 24000, 16000}, // MPEG 2 {44100, 48000, 32000} // MPEG 1 }; /// /// Lookup for /// private static readonly SampleRateFrequency[,] mSampleFrequencyTable = { { SampleRateFrequency.Hz_32000, SampleRateFrequency.Hz_16000, SampleRateFrequency.Hz_8000 // MPEG 2.5 }, { SampleRateFrequency.Hz_0, SampleRateFrequency.Hz_0, SampleRateFrequency.Hz_0 // reserved }, { SampleRateFrequency.Hz_22050, SampleRateFrequency.Hz_24000, SampleRateFrequency.Hz_16000 // MPEG 2 }, { SampleRateFrequency.Hz_44100, SampleRateFrequency.Hz_48000, SampleRateFrequency.Hz_32000 // MPEG 1 } }; #endregion #region Constructor // Required struct constructor public Mp3FileProperties(string name) { var fFileInfo = new FileInfo(name); mMp3Path = fFileInfo.DirectoryName; mMp3Filename = fFileInfo.Name; ReadMP3Information(); ReadMP3Tag(); } #endregion private Mp3FileProperties() { } #region private methods private void ReadMP3Information() { var fs = new FileStream(Mp3PathAndFilename, FileMode.Open, FileAccess.Read); // Set the filename not including the path information var chrSeparators = new[] {'\\', '/'}; string[] strSeparator = Mp3PathAndFilename.Split(chrSeparators); int intUpper = strSeparator.GetUpperBound(0); //mMp3PathAndFilename = strSeparator[intUpper]; //// Replace ' with '' for the SQL INSERT statement //mMp3PathAndFilename = mMp3PathAndFilename.Replace("'", "''"); // Set the file size mFileSize = fs.Length; var bytHeader = new byte[4]; var bytVBitRate = new byte[12]; int intPos = 0; // Keep reading 4 bytes from the header until we know for sure that in // fact it's an MP3 do { fs.Position = intPos; fs.Read(bytHeader, 0, 4); intPos++; LoadMP3Header(bytHeader); } while (!IsValidHeader() && (fs.Position != fs.Length)); // If the current file stream position is equal to the length, // that means that we've read the entire file and it's not a valid MP3 file if (fs.Position == fs.Length) throw new Mp3FormatException(Mp3PathAndFilename, "Invalid mp3 file"); intPos += 3; if (getVersionIndex() == 3) // MPEG Version 1 { if (getModeIndex() == 3) // Single Channel { intPos += 17; } else { intPos += 32; } } else // MPEG Version 2.0 or 2.5 { if (getModeIndex() == 3) // Single Channel { intPos += 9; } else { intPos += 17; } } // Check to see if the MP3 has a variable bitrate fs.Position = intPos; fs.Read(bytVBitRate, 0, 12); boolVBitRate = LoadVBRHeader(bytVBitRate); // Once the file's read in, then assign the properties of the file to the public variables mBitRate = GetBitrate(); mSampleRateIntegerFrequency = GetSampleRateFrequencyInt(); mSampleRateFrequency = GetSampleRateFrequency(); mChannelMode = GetChannelMode(); mLengthInSeconds = GetLengthInSeconds(); fs.Close(); } private void LoadMP3Header(byte[] c) { // this thing is quite interesting, it works like the following // c[0] = 00000011 // c[1] = 00001100 // c[2] = 00110000 // c[3] = 11000000 // the operator << means that we'll move the bits in that direction // 00000011 << 24 = 00000011000000000000000000000000 // 00001100 << 16 = 000011000000000000000000 // 00110000 << 24 = 0011000000000000 // 11000000 = 11000000 // +_________________________________ // 00000011000011000011000011000000 bithdr = (ulong) (((c[0] & 255) << 24) | ((c[1] & 255) << 16) | ((c[2] & 255) << 8) | ((c[3] & 255))); } private bool LoadVBRHeader(byte[] inputheader) { // If it's a variable bitrate MP3, the first 4 bytes will read 'Xing' // since they're the ones who added variable bitrate-edness to MP3s if (inputheader[0] == 88 && inputheader[1] == 105 && inputheader[2] == 110 && inputheader[3] == 103) { int flags = (((inputheader[4] & 255) << 24) | ((inputheader[5] & 255) << 16) | ((inputheader[6] & 255) << 8) | ((inputheader[7] & 255))); if ((flags & 0x0001) == 1) { intVFrames = (((inputheader[8] & 255) << 24) | ((inputheader[9] & 255) << 16) | ((inputheader[10] & 255) << 8) | ((inputheader[11] & 255))); return true; } else { intVFrames = -1; return true; } } return false; } private bool IsValidHeader() { return (((getFrameSync() & 2047) == 2047) && ((getVersionIndex() & 3) != 1) && ((getLayerIndex() & 3) != 0) && ((getBitrateIndex() & 15) != 0) && ((getBitrateIndex() & 15) != 15) && ((getFrequencyIndex() & 3) != 3) && ((getEmphasisIndex() & 3) != 2)); } private int getFrameSync() { return (int) ((bithdr >> 21) & 2047); } private int getVersionIndex() { return (int) ((bithdr >> 19) & 3); } private int getLayerIndex() { return (int) ((bithdr >> 17) & 3); } //private int getProtectionBit() //{ // return (int)((bithdr>>16) & 1); //} private int getBitrateIndex() { return (int) ((bithdr >> 12) & 15); } private int getFrequencyIndex() { return (int) ((bithdr >> 10) & 3); } //private int getPaddingBit() //{ // return (int)((bithdr>>9) & 1); //} //private int getPrivateBit() //{ // return (int)((bithdr>>8) & 1); //} private int getModeIndex() { return (int) ((bithdr >> 6) & 3); } //private int getModeExtIndex() //{ // return (int)((bithdr>>4) & 3); //} //private int getCoprightBit() //{ // return (int)((bithdr>>3) & 1); //} //private int getOrginalBit() //{ // return (int)((bithdr>>2) & 1); //} private int getEmphasisIndex() { return (int) (bithdr & 3); } //private double getVersion() //{ // double[] table = {2.5, 0.0, 2.0, 1.0}; // return table[getVersionIndex()]; //} //private int getLayer() //{ // return (int)(4 - getLayerIndex()); //} private int GetBitrate() { // If the file has a variable bitrate, then we return an integer average bitrate, // otherwise, we use a lookup table to return the bitrate int layerIndex = getLayerIndex(); if (!boolVBitRate) { try { int versionIndex = getVersionIndex(); int bitrateIndex = getBitrateIndex(); return mBitrateTable[versionIndex & 1, layerIndex - 1, bitrateIndex]; } catch (Exception) // must have been variable afterall { boolVBitRate = true; } } double medFrameSize = mFileSize/(double) getNumberOfFrames(); return (int) ((medFrameSize*GetSampleRateFrequencyInt())/(1000.0*((layerIndex == 3) ? 12.0 : 144.0))); } private int GetSampleRateFrequencyInt() { return mSampleFrequencyIntegerTable[getVersionIndex(), getFrequencyIndex()]; } private SampleRateFrequency GetSampleRateFrequency() { return mSampleFrequencyTable[getVersionIndex(), getFrequencyIndex()]; } private ChannelMode GetChannelMode() { switch (getModeIndex()) { default: return ChannelMode.Stereo; case 1: return ChannelMode.JointStereo; case 2: return ChannelMode.DualChannel; case 3: return ChannelMode.SingleChannel; } } private int GetLengthInSeconds() { // "intKilBitFileSize" made by dividing by 1000 in order to match the "Kilobits/second" int intKiloBitFileSize = ((8*(int) mFileSize)/1000); return (intKiloBitFileSize/GetBitrate()); } private int getNumberOfFrames() { // Again, the number of MPEG frames is dependant on whether it's a variable bitrate MP3 or not if (!boolVBitRate) { double medFrameSize = (((getLayerIndex() == 3) ? 12 : 144)* ((1000.0*GetBitrate())/GetSampleRateFrequencyInt())); return (int) (mFileSize/medFrameSize); } else return intVFrames; } private static string TruncateStringAtNull(string sourceString) { var targetCharList = new ArrayList(sourceString.Length); for (int i = 0; i < sourceString.Length && sourceString[i] != 0; i++) { // Debug.WriteLine("c : " + (int)sourceString[i] + " " + sourceString[i]); targetCharList.Add(sourceString[i] < 32 ? ' ' : sourceString[i]); } return new string((char[]) targetCharList.ToArray(typeof (char))); // Regex.Replace(new string((char [])targetCharList.ToArray(typeof (char))), @"\W*", ""); } private void ReadMP3Tag() { // Read the 128 byte ID3 tag into a byte array FileStream oFileStream; oFileStream = new FileStream(Mp3PathAndFilename, FileMode.Open); var bBuffer = new byte[128]; oFileStream.Seek(-128, SeekOrigin.End); oFileStream.Read(bBuffer, 0, 128); oFileStream.Close(); // Convert the Byte Array to a String Encoding instEncoding = new ASCIIEncoding(); // NB: Encoding is an Abstract class string id3Tag = instEncoding.GetString(bBuffer); // If there is an attched ID3 v1.x TAG then read it if (id3Tag.Substring(0, 3) == "TAG") { mId3Title = TruncateStringAtNull(id3Tag.Substring(3, 30)); mId3Artist = TruncateStringAtNull(id3Tag.Substring(33, 30)); mId3Album = TruncateStringAtNull(id3Tag.Substring(63, 30)); mId3Year = TruncateStringAtNull(id3Tag.Substring(93, 4)); mId3Comment = TruncateStringAtNull(id3Tag.Substring(97, 28)); // Get the track number if TAG conforms to ID3 v1.1 if (id3Tag[125] == 0) mId3TrackNumber = bBuffer[126]; else mId3TrackNumber = 0; mId3Genre = bBuffer[127]; if (mGenres.Length > mId3Genre) mId3GenreName = mGenres[mId3Genre]; mId3TagExists = true; // ********* IF USED IN ANGER: ENSURE to test for non-numeric year } else { // ID3 Tag not found so create an empty TAG in case the user saces later mId3Title = ""; mId3Artist = ""; mId3Album = ""; mId3Year = ""; mId3Comment = ""; mId3TrackNumber = 0; mId3Genre = 0; mId3GenreName = ""; mId3TagExists = false; } } private static void UpdateMP3ID3Tag(ref Mp3FileProperties paramMP3) { // Trim any whitespace paramMP3.mId3Title = paramMP3.mId3Title.Trim(); paramMP3.mId3Artist = paramMP3.mId3Artist.Trim(); paramMP3.mId3Album = paramMP3.mId3Album.Trim(); paramMP3.mId3Year = paramMP3.mId3Year.Trim(); paramMP3.mId3Comment = paramMP3.mId3Comment.Trim(); // Ensure all properties are correct size if (paramMP3.mId3Title.Length > 30) paramMP3.mId3Title = paramMP3.mId3Title.Substring(0, 30); if (paramMP3.mId3Artist.Length > 30) paramMP3.mId3Artist = paramMP3.mId3Artist.Substring(0, 30); if (paramMP3.mId3Album.Length > 30) paramMP3.mId3Album = paramMP3.mId3Album.Substring(0, 30); if (paramMP3.mId3Year.Length > 4) paramMP3.mId3Year = paramMP3.mId3Year.Substring(0, 4); if (paramMP3.mId3Comment.Length > 28) paramMP3.mId3Comment = paramMP3.mId3Comment.Substring(0, 28); // Build a new ID3 Tag (128 Bytes) var tagByteArray = new byte[128]; for (int i = 0; i < tagByteArray.Length; i++) tagByteArray[i] = 0; // Initialise array to nulls // Convert the Byte Array to a String Encoding instEncoding = new ASCIIEncoding(); // NB: Encoding is an Abstract class // ************ To DO: Make a shared instance of ASCIIEncoding so we don't keep creating/destroying it // Copy "TAG" to Array byte[] workingByteArray = instEncoding.GetBytes("TAG"); Array.Copy(workingByteArray, 0, tagByteArray, 0, workingByteArray.Length); // Copy Title to Array workingByteArray = instEncoding.GetBytes(paramMP3.mId3Title); Array.Copy(workingByteArray, 0, tagByteArray, 3, workingByteArray.Length); // Copy Artist to Array workingByteArray = instEncoding.GetBytes(paramMP3.mId3Artist); Array.Copy(workingByteArray, 0, tagByteArray, 33, workingByteArray.Length); // Copy Album to Array workingByteArray = instEncoding.GetBytes(paramMP3.mId3Album); Array.Copy(workingByteArray, 0, tagByteArray, 63, workingByteArray.Length); // Copy Year to Array workingByteArray = instEncoding.GetBytes(paramMP3.mId3Year); Array.Copy(workingByteArray, 0, tagByteArray, 93, workingByteArray.Length); // Copy Comment to Array workingByteArray = instEncoding.GetBytes(paramMP3.mId3Comment); Array.Copy(workingByteArray, 0, tagByteArray, 97, workingByteArray.Length); // Copy Track and Genre to Array tagByteArray[126] = paramMP3.mId3TrackNumber; tagByteArray[127] = paramMP3.mId3Genre; // SAVE TO DISK: Replace the final 128 Bytes with our new ID3 tag var oFileStream = new FileStream(paramMP3.Mp3PathAndFilename, FileMode.Open); if (paramMP3.mId3TagExists) oFileStream.Seek(-128, SeekOrigin.End); else oFileStream.Seek(0, SeekOrigin.End); oFileStream.Write(tagByteArray, 0, 128); oFileStream.Close(); paramMP3.mId3TagExists = true; } #endregion /// /// Returns a that represents the current . /// /// /// A that represents the current . /// public override string ToString() { return string.Format("{0}, BR:{1}, SR:{2}, CH:{3}", mMp3Filename, mBitRate, mSampleRateFrequency, mChannelMode); } public static Mp3FileProperties GetCommonProperties(List properties) { var targetProperty = new Mp3FileProperties(); Type mp3FilePropertiesType = typeof (Mp3FileProperties); foreach (PropertyInfo propertyInfo in mp3FilePropertiesType.GetProperties()) { FieldInfo field = mp3FilePropertiesType.GetField("m" + propertyInfo.Name, BindingFlags.NonPublic | BindingFlags.Instance); if (field == null) { // Debug.WriteLine("Couldn't find " + "m" + propertyInfo.Name); continue; } if (propertyInfo.PropertyType == typeof (string)) { string commonString = GetCommonString(properties, propertyInfo); if (commonString != null) field.SetValue(targetProperty, commonString); Debug.WriteLine(propertyInfo.Name + " " + commonString); } else if (propertyInfo.PropertyType == typeof (int)) { field.SetValue(targetProperty, GetCommonThing(properties, propertyInfo)); } else if (propertyInfo.PropertyType == typeof (long)) { field.SetValue(targetProperty, GetCommonThing(properties, propertyInfo)); } else if (propertyInfo.PropertyType == typeof (ChannelMode)) { field.SetValue(targetProperty, GetCommonThing(properties, propertyInfo)); } else if (propertyInfo.PropertyType == typeof (SampleRateFrequency)) { field.SetValue(targetProperty, GetCommonThing(properties, propertyInfo)); } else if (propertyInfo.PropertyType == typeof (byte)) { field.SetValue(targetProperty, GetCommonThing(properties, propertyInfo)); } else if (propertyInfo.PropertyType == typeof (bool)) { field.SetValue(targetProperty, GetCommonThing(properties, propertyInfo)); } else throw new ApplicationException("Unknown type " + propertyInfo.PropertyType); // Debug.WriteLine(propertyInfo); } return targetProperty; } /// /// Gets the common thing. /// /// The properties. /// The property info. /// public static T GetCommonThing(List properties, PropertyInfo propertyInfo) where T : new() { if (typeof (T) != propertyInfo.PropertyType) throw new ArgumentException("Incorrect type. Expecting " + typeof (T) + ", got " + propertyInfo.PropertyType); var tCounts = new Dictionary(); foreach (Mp3FileProperties property in properties) { var value = (T) propertyInfo.GetValue(property, null); if (tCounts.ContainsKey(value)) tCounts[value] = tCounts[value] + 1; else tCounts.Add(value, 1); } var keys = new List(tCounts.Keys); keys.Sort(delegate(T x, T y) { return tCounts[x].CompareTo(tCounts[y]); } ); if (keys.Count > 0) return keys[0]; return new T(); } /// /// Gets the common string. /// /// The properties. /// The property info. /// public static string GetCommonString(List properties, PropertyInfo propertyInfo) { var stringCounts = new Dictionary(); var originalStrings = new Dictionary(); foreach (Mp3FileProperties property in properties) { var originalString = (string) propertyInfo.GetValue(property, null); if (originalString == null) continue; // remove whitespace while (originalString.Contains(" ")) originalString.Replace(" ", " "); while (originalString.Contains("\t")) originalString.Replace("\t", " "); string value = originalString.ToLower(); if (!originalStrings.ContainsKey(value)) originalStrings.Add(value, originalString); if (stringCounts.ContainsKey(value)) stringCounts[value] = stringCounts[value] + 1; else stringCounts[value] = 1; } if (stringCounts.Count == 0) return null; var keys = new List(originalStrings.Keys); keys.Sort(delegate(string x, string y) { return stringCounts[x].CompareTo(stringCounts[y]); } ); if (stringCounts.Count > 1 && stringCounts.Count > (properties.Count/2)) { // Dictionary substringLengths = new Dictionary(); var substringCounts = new Dictionary(); foreach (string keyLHS in keys) foreach (string keyRHS in keys) foreach (string substring in LongestCommonSubstrings.GetLongestSubstring(keyLHS, keyRHS)) substringCounts[substring] = (substringCounts.ContainsKey(substring) ? substringCounts[substring] : 0) + 1; var allSubstringsSortedByCount = new List(substringCounts.Keys); var allSubstringsSortedByLength = new List(substringCounts.Keys); allSubstringsSortedByCount.Sort( delegate(string x, string y) { return substringCounts[x].CompareTo(substringCounts[y]); } ); allSubstringsSortedByLength.Sort(delegate(string x, string y) { return x.Length.CompareTo(y.Length); } ); var allSubstringsSortedRankCombination = new List(allSubstringsSortedByLength); allSubstringsSortedRankCombination.Sort(delegate(string x, string y) { return (allSubstringsSortedByCount.IndexOf(x) + allSubstringsSortedByLength.IndexOf(x)).CompareTo( allSubstringsSortedByCount.IndexOf(y) + allSubstringsSortedByLength.IndexOf(y) ); } ); int startIndex = keys[0].IndexOf(allSubstringsSortedRankCombination[0]); return originalStrings[keys[0]].Substring(startIndex, allSubstringsSortedRankCombination[0].Length); } return originalStrings[keys[0]]; } } }