第21回 レーティングを設定/保存する

今回は曲とアーティストのレーティング設定/保存処理を作成します。

「レーティング」は本来であればファイルのプロパティ情報として、
他のアプリケーションなどからも見れるように保存すべきものです。
しかし今回は独自情報として保持するカタチにしました。

ついでにNGワードのデフォルト値登録が重複登録されるバグがあったためそれの修正もしています。

■Option.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;

namespace MP3Player
{
	[Serializable]
	public class AudioItem
	{
		public string strFile = "";
		public int nEntryIndex = -1;

		public AudioItem()
		{
		}

		public AudioItem(string strFile, int nEntryIndex = -1)
		{
			this.strFile = strFile;
			this.nEntryIndex = nEntryIndex;
		}
	}

	//以下を追加
	[Serializable]
	public class RatingItem : AudioItem
	{
		public int nRating = 0;

		public RatingItem()
		{
		}

		public RatingItem(string strFile, int nEntryIndex, int nRating)
			: base(strFile, nEntryIndex)
		{
			this.nRating = nRating;
		}
	}

	//以下を追加
	[Serializable]
	public class RatingArtistItem
	{
		public string strArtist = "";
		public int nRating = 0;

		public RatingArtistItem()
		{
		}

		public RatingArtistItem(string strArtist, int nRating)
		{
			this.strArtist = strArtist;
			this.nRating = nRating;
		}
	}


	[Serializable]
	public class Option
	{
		void AfterLoaded()		//変更
		{
			//デフォルトでカラオケ曲などをNG指定
			AddNGWord("カラオケ");
			AddNGWord("karaoke");
			AddNGWord("inst.");
			AddNGWord("instrum");
			AddNGWord("トイレの神様");
		}

		public int nVolume
		{
			get { return _nVolume; }
			set { _nVolume = value; }
		}
		int _nVolume = 20;


		public string strFolder
		{
			get { return _strFolder; }
			set { _strFolder = value; }
		}
		string _strFolder = @"G:\Desktop\mp3\";


		//以下を追加
		/// <summary>
		/// 曲のレーティング情報
		/// </summary>
		public List<RatingItem> listRating
		{
			get { return _listRating; }
			set { _listRating = value; }
		}
		List<RatingItem> _listRating = new List<RatingItem>();


		//以下を追加
		/// <summary>
		/// アーティストのレーティング情報
		/// </summary>
		public List<RatingArtistItem> listRatingArtist
		{
			get { return _listRatingArtist; }
			set { _listRatingArtist = value; }
		}
		List<RatingArtistItem> _listRatingArtist = new List<RatingArtistItem>();


		//以下を追加
		/// <summary>
		/// 曲レーティングの設定
		/// </summary>
		public void SetRating(AudioItem item, int nRating)
		{
			string strFile = RemoveMP3FolderName(item.strFile);		//MP3フォルダ部を削除して比較処理に回す

			//すでにレーティングのついている曲ならレーティングを上書きもしくは削除
			for (int i = 0; i < _listRating.Count; i++)
			{
				if (string.Compare(_listRating[i].strFile, strFile, true) != 0)
					continue;

				if (_listRating[i].nEntryIndex != item.nEntryIndex)
					continue;

				if (nRating > 0)
					_listRating[i].nRating = nRating;
				else
					_listRating.RemoveAt(i);
				return;
			}

			if (nRating > 0)
			{
				//新しくレーティングを追加
				_listRating.Add(new RatingItem(strFile, item.nEntryIndex, nRating));
			}
		}


		//以下を追加
		/// <summary>
		/// 曲レーティングの取得
		/// </summary>
		public int GetRating(AudioItem item)
		{
			string strFile = RemoveMP3FolderName(item.strFile);		//MP3フォルダ部を削除して比較処理に回す

			foreach (RatingItem current in _listRating)
			{
				if (string.Compare(current.strFile, strFile, true) != 0)
					continue;

				if (current.nEntryIndex != item.nEntryIndex)
					continue;

				return current.nRating;;
			}
			return 0;
		}




		//以下を追加
		/// <summary>
		/// アーティストレーティングの設定(ignoreCaseで比較)
		/// </summary>
		public void SetRatingArtist(string strArtist, int nRating)
		{
			if (strArtist == "")
				return;

			//すでにレーティングのついているアーティストならレーティングを上書きもしくは削除
			for (int i = 0; i < _listRatingArtist.Count; i++)
			{
				if (string.Compare(_listRatingArtist[i].strArtist, strArtist, true) != 0)
					continue;

				if (nRating > 0)
					_listRatingArtist[i].nRating = nRating;
				else
					_listRatingArtist.RemoveAt(i);
				return;
			}

			if (nRating > 0)
			{
				//新しくレーティングを追加
				_listRatingArtist.Add(new RatingArtistItem(strArtist, nRating));
			}
		}


		//以下を追加
		/// <summary>
		/// アーティストレーティングの取得(ignoreCaseで比較)
		/// </summary>
		public int GetRatingArtist(string strArtist)
		{
			if (strArtist == "")
				return 0;

			foreach (RatingArtistItem current in _listRatingArtist)
			{
				if (string.Compare(current.strArtist, strArtist, true) != 0)
					continue;

				return current.nRating;
			}
			return 0;
		}






		public List<string> listNGWords
		{
			get { return _listNGWords; }
			set { _listNGWords = value; }
		}
		List<string> _listNGWords = new List<string>();

		public List<string> listNGArtists
		{
			get { return _listNGArtists; }
			set { _listNGArtists = value; }
		}
		List<string> _listNGArtists = new List<string>();


		public List<AudioItem> listNGFiles
		{
			get { return _listNGFiles; }
			set { _listNGFiles = value; }
		}
		List<AudioItem> _listNGFiles = new List<AudioItem>();


		/// <summary>
		/// NGワードの追加
		/// </summary>
		public void AddNGWord(string strKeyword)
		{
			if (strKeyword == "")
				return;

			//すでにNGリストに含まれるかチェック
			if (IsNGWord(strKeyword))
				return;

			_listNGWords.Add(strKeyword);
		}


		/// <summary>
		/// NGワードかどうかチェック(OrdinalIgnoreCase比較、含むかどうか比較)
		/// </summary>
		public bool IsNGWord(string strTitle)
		{
			strTitle = strTitle.ToLower();
			string find = _listNGWords.Find(cmp =>
				{
					cmp = cmp.ToLower();
					if (strTitle.IndexOf(cmp, StringComparison.OrdinalIgnoreCase) >= 0)
						return true;
					return false;
				});

			return (find != null && find != "") ? true : false;
		}


		/// <summary>
		/// NGアーティストの追加
		/// </summary>
		public void AddNGArtist(string strArtist)
		{
			if (strArtist == "")
				return;

			//すでにNGリストに含まれるかチェック
			if (IsNGArtist(strArtist))
				return;

			_listNGArtists.Add(strArtist);
		}


		/// <summary>
		/// NGアーティストかどうかチェック(ignoreCase比較、スペース無視)
		/// 
		/// アーティストの苗字と名前の間にスペースが入っているケースがあるため、スペースを除去して比較している
		/// これによりまったく別人アーティストが同一と判定される可能性もある
		/// </summary>
		public bool IsNGArtist(string strArtist)
		{
			strArtist = strArtist.Replace(" ", "");		//半角スペース除去
			strArtist = strArtist.Replace(" ", "");	//全角スペース除去

			string find = _listNGArtists.Find(cmp =>
				{
					cmp = cmp.Replace(" ", "");		//半角スペース除去
					cmp = cmp.Replace(" ", "");	//全角スペース除去
					if (string.Compare(cmp, strArtist, true) == 0)
						return true;
					return false;
				});

			return (find != null && find != "") ? true : false;
		}





		/// <summary>
		/// NGファイルの追加
		/// </summary>
		public void AddNGFile(AudioItem item)
		{
			//すでにNGファイルリストに含まれるかチェック
			if (IsNGFile(item))
				return;

			//MP3フォルダ部を削除して追加
			_listNGFiles.Add(new AudioItem(RemoveMP3FolderName(item.strFile), item.nEntryIndex));
		}


		/// <summary>
		/// NGファイルかどうかチェック(ignoreCase比較)
		/// </summary>
		public bool IsNGFile(AudioItem item)
		{
			string strFile = RemoveMP3FolderName(item.strFile);		//MP3フォルダ部を削除して比較処理に回す

			AudioItem find = _listNGFiles.Find(cmp =>
				{
					if (string.Compare(cmp.strFile, strFile, true) != 0)
						return false;			//ファイル名が違うなら無視
					if (cmp.nEntryIndex == item.nEntryIndex)
						return true;			//同じならNG
					if (cmp.nEntryIndex == -1)
						return true;			//-1がNGリストにあった場合はNG(zipファイル自体がNG指定されているケースが該当)
					return false;
				});

			return (find != null) ? true : false;
		}





		/// <summary>
		/// strFileとして渡されたパスから、mp3保存用フォルダー名部分だけを削除する(完全にファイル名のみにするわけではない)
		/// </summary>
		public string RemoveMP3FolderName(string strFile)
		{
			if (strFile == "" || _strFolder == "" || strFile.Length < _strFolder.Length)
				return strFile;

			if (strFile.Substring(0, _strFolder.Length).ToLower() == _strFolder.ToLower())
				return strFile.Substring(_strFolder.Length);

			return strFile;
		}





		/// <summary>
		/// このクラスをXMLとして保存
		/// </summary>
		public bool Save(string strFilePath)
		{
			try
			{
				XmlSerializer serializer = new XmlSerializer(typeof(Option));
				using (FileStream fs = new FileStream(strFilePath, FileMode.Create))
				{
					serializer.Serialize(fs, this);
					fs.Close();
				}
				return true;
			}
			catch (Exception)
			{
			}
			return false;
		}



		/// <summary>
		/// このクラスをXMLから読み込み
		/// </summary>
		/// <returns>成功時true、失敗時false=new Option()で返す</returns>
		public static bool Load(out Option option, string strFilePath)
		{
			try
			{
				//↓で例外が発生するが、無視してOK 「型 'System.IO.FileNotFoundException' の初回例外が mscorlib.dll で発生しました」
				//http://yk.tea-nifty.com/netdev/2009/03/xmlserializer-a.html
				XmlSerializer serializer = new XmlSerializer(typeof(Option));
#if DEBUG
				Debug.WriteLine("↑'System.IO.FileNotFoundException'の例外が発生しても無視してOK");
#endif
				using (FileStream fs = new FileStream(strFilePath, FileMode.Open, FileAccess.Read))
				{
					option = (Option)serializer.Deserialize(fs);
					fs.Close();
				}

				option.AfterLoaded();		//追加

				return true;
			}
			catch (Exception)
			{
			}
			option = new Option();		//失敗時は新しいのを作る
			return false;
		}

	}
}
■Form1.cs
sing System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Windows.Forms;
using WMPLib;

namespace MP3Player
{
	public partial class Form1 : Form
	{
		WindowsMediaPlayer _mediaPlayer = new WindowsMediaPlayer();

		Timer _timer = new Timer();

		public static Option _option = new Option();


		List<AudioItem> _listFiles;			//音楽ファイル一覧
		int _nCurrentIndex = -1;			//↑へのインデックス


		List<int> _listPlayHistory = new List<int>();		//過去の再生曲
		List<int> _listPlayNext = new List<int>();			//未来の再生曲。なければランダム


		bool _bStopButtonPushed = false;	//「停止」ボタンを押したらtrue

		public Form1()
		{
			InitializeComponent();

			//設定の読み込み
			Option.Load(out _option, Utility.GetExeFolder() + "option.txt");

			Utility.CreateTmpFolder();			//tmpフォルダの作成


			//mp3フォルダからの音楽ファイルリスト読み込み
			_listFiles = EnumFiles(_option.strFolder);


			//フォームが表示されたときの処理
			Shown += delegate
			{
				//シークバーにフォーカスがあると、再生位置が表示されないのでここで再生ボタンにフォーカスを与える
				buttonPlay.Focus();
			};


			//フォーム右クリックメニューの設定
			{
				ContextMenuStrip menu = new ContextMenuStrip();

				ToolStripItem[] stripItem = new ToolStripMenuItem[]
				{
					new ToolStripMenuItem("曲レーティング",null,new ToolStripMenuItem[]
					{
						new ToolStripMenuItem("なし", null, delegate
							{
								SetRating(0);		//追加
							}),
						new ToolStripMenuItem("★", null, delegate
							{
								SetRating(1);		//追加
							}),
						new ToolStripMenuItem("★★", null, delegate
							{
								SetRating(2);		//追加
							}),
						new ToolStripMenuItem("★★★", null, delegate
							{
								SetRating(3);		//追加
							}),
						new ToolStripMenuItem("★★★★", null, delegate
							{
								SetRating(4);		//追加
							}),
						new ToolStripMenuItem("★★★★★", null, delegate
							{
								SetRating(5);		//追加
							}),
					}),
					new ToolStripMenuItem("アーティストレーティング",null,new ToolStripMenuItem[]
					{
						new ToolStripMenuItem("なし", null, delegate
							{
								SetRatingArtist(0);	//追加
							}),
						new ToolStripMenuItem("★", null, delegate
							{
								SetRatingArtist(1);	//追加
							}),
						new ToolStripMenuItem("★★", null, delegate
							{
								SetRatingArtist(2);	//追加
							}),
						new ToolStripMenuItem("★★★", null, delegate
							{
								SetRatingArtist(3);	//追加
							}),
						new ToolStripMenuItem("★★★★", null, delegate
							{
								SetRatingArtist(4);	//追加
							}),
						new ToolStripMenuItem("★★★★★", null, delegate
							{
								SetRatingArtist(5);	//追加
							}),
					}),
					new ToolStripMenuItem("NG登録", null, delegate
						{
							if (_nCurrentIndex >= 0)
							{
								AddNGForm dlg = new AddNGForm(_listFiles[_nCurrentIndex], labelTitle.Text, labelArtist.Text);
								dlg.ShowDialog();
							}
						}),
					new ToolStripMenuItem("再生設定", null, delegate
						{
						}),
					new ToolStripMenuItem("エクスプローラー", null, delegate
						{
							if (_nCurrentIndex >= 0)
							{
								Process.Start("explorer.exe","/select,\"" + _listFiles[_nCurrentIndex].strFile + "\"");
							}
						}),
					new ToolStripMenuItem("最小化", null, delegate
						{
							if (WindowState == FormWindowState.Normal)
								WindowState = FormWindowState.Minimized;
						}),
					new ToolStripMenuItem("終了", null, delegate
						{
							Close();
						}),
				};
				menu.Items.AddRange(stripItem);

				ContextMenuStrip = menu;
			}



			//終了時処理
			FormClosed += delegate
			{
				Utility.CleanTempFolder();			//tmpフォルダ内の削除

				//設定の保存
				_option.Save(Utility.GetExeFolder() + "option.txt");
			};


			//再生位置のシーク
			trackBarSeek.ValueChanged += delegate
			{
				if (trackBarSeek.Focused)
				{
					_mediaPlayer.controls.currentPosition = (double)trackBarSeek.Value / 100;
				}
			};


			//音量の調節
			_mediaPlayer.settings.volume = _option.nVolume;
			trackBarVolume.Maximum = 100;			//音量はゼロから100
			trackBarVolume.Value = _mediaPlayer.settings.volume;
			trackBarVolume.ValueChanged += delegate
			{
				_option.nVolume = (int)trackBarVolume.Value;
				_mediaPlayer.settings.volume = _option.nVolume;
			};


			_timer.Interval = 300;			//300msecごとに処理を実行
			_timer.Tick += delegate
			{
				if (_mediaPlayer.playState == WMPPlayState.wmppsPlaying || _mediaPlayer.playState == WMPPlayState.wmppsPaused)
				{
					trackBarSeek.Maximum = (int)(_mediaPlayer.controls.currentItem.duration * 100);
					//シークバーにフォーカスがないときだけ再生位置を表示する
					if (trackBarSeek.Focused == false)
					{
						try
						{
							//シークバーに再生位置を表示
							//曲変更タイミングによっては例外出る可能性ある
							trackBarSeek.Value = (int)(_mediaPlayer.controls.currentPosition * 100);
						}
						catch (Exception)
						{
						}
					}

					//再生時間の表示
					labelTime.Text = _mediaPlayer.controls.currentPositionString;
				}

				//止まっていたら再び再生。ただし停止ボタンが押されている場合は無視
				if (_mediaPlayer.playState == WMPPlayState.wmppsStopped)
				{
					if (_bStopButtonPushed == false)
						Play();
				}
			};
			_timer.Start();


			_mediaPlayer.PlayStateChange += delegate(int nNewState)
			{
				WMPPlayState state = (WMPPlayState)nNewState;

				switch (state)
				{
					case WMPPlayState.wmppsStopped:
						buttonPlay.Text = ">";
						trackBarSeek.Value = 0;
						break;

					case WMPPlayState.wmppsPaused:
						_timer.Stop();
						buttonPlay.Text = ">";
						break;

					case WMPPlayState.wmppsPlaying:
						_bStopButtonPushed = false;
						buttonPlay.Text = "||";
						_timer.Start();
						break;

					case WMPPlayState.wmppsTransitioning:
						break;
				}
			};

			Play();
		}


		//以下を追加
		/// <summary>
		/// 曲レーティング設定
		/// </summary>
		void SetRating(int nRating)
		{
			if (_nCurrentIndex < 0)
				return;
			_option.SetRating(_listFiles[_nCurrentIndex], nRating);
		}

		//以下を追加
		/// <summary>
		/// アーティストレーティング設定
		/// </summary>
		void SetRatingArtist(int nRating)
		{
			if (_nCurrentIndex < 0)
				return;
			_option.SetRatingArtist(labelArtist.Text, nRating);
		}


プロジェクトファイルをダウンロード


カテゴリー「MP3プレーヤーを作る(C#)」 のエントリー