using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using Clifton.Collections.Generic; namespace m3uTool { /// /// Indicates that a problem existed when parsing an mp3 file /// public class Mp3FormatException : Exception { string mFilename; string mFormatExceptionString; public string Filename { get { return mFilename; } } public string FormatExceptionString { get { return mFormatExceptionString; } } /// /// Constructor. /// /// /// public Mp3FormatException(string filename, string formatExceptionString) { mFilename = filename; mFormatExceptionString = formatExceptionString; } } /// /// 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 /// /// path to the mp3 /// private string mMp3Path; /// /// mp3's filename, without path /// private string mMp3Filename; /// /// The mp3 bit rate /// private int mBitRate; /// /// the file size, in bytes /// private long mFileSize; /// /// The sample rate frequency integer /// private int mSampleRateIntegerFrequency; /// /// The sample rate frequency /// private SampleRateFrequency mSampleRateFrequency; /// /// the channel mode /// private ChannelMode mChannelMode; /// /// the mp3 length in seconds /// private int mLengthInSeconds; /// /// True if the id3 tag exists in the mp3 /// private bool mId3TagExists; /// /// The title field /// private string mId3Title; /// /// The artist field /// public string mId3Artist; /// /// The album field /// private string mId3Album; /// /// The year field /// private string mId3Year; /// /// The comment field /// private string mId3Comment; /// /// The track number field /// private byte mId3TrackNumber; /// /// The genre field byte /// private byte mId3Genre; /// /// The genre field name /// private string mId3GenreName; // Private variables used in the process of reading in the MP3 files private ulong bithdr; private bool boolVBitRate; private int intVFrames; #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 readonly static 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 readonly static 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 readonly static 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 readonly static SampleRateFrequency[,] mSampleFrequencyTable = { { SampleRateFrequency.Hz_32000, SampleRateFrequency.Hz_16000, SampleRateFrequency.Hz_8000 // MPEG 2.5 }, { m3uTool.SampleRateFrequency.Hz_0, m3uTool.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) { FileInfo fFileInfo = new FileInfo(name); mMp3Path = fFileInfo.DirectoryName; mMp3Filename = fFileInfo.Name; ReadMP3Information(); ReadMP3Tag(); } #endregion private Mp3FileProperties() { } #region private methods private void ReadMP3Information() { FileStream fs = new FileStream(Mp3PathAndFilename, FileMode.Open, FileAccess.Read); // Set the filename not including the path information char[] chrSeparators = new char[] { '\\', '/' }; 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; byte[] bytHeader = new byte[4]; byte[] 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 = (double)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 m3uTool.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) { ArrayList 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); byte[] 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) byte[] 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 FileStream 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) { Mp3FileProperties 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); Dictionary tCounts = new Dictionary(); foreach (Mp3FileProperties property in properties) { T value = (T) propertyInfo.GetValue(property, null); if (tCounts.ContainsKey(value)) tCounts[value] = tCounts[value] + 1; else tCounts.Add(value, 1); } List 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) { Dictionary stringCounts = new Dictionary(); Dictionary originalStrings = new Dictionary(); foreach (Mp3FileProperties property in properties) { string 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; List 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(); Dictionary 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; List allSubstringsSortedByCount = new List(substringCounts.Keys); List 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); } ); List 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]]; } } }