« 2016年02月 | メイン | 2016年04月 »

前の10件 1  2  3  4

2016年03月 記事一覧

第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);
		}


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

第22回 レーティングを表示する

今回は前回までに設定/保存できるようになったレーティング情報を表示します。

Form1に曲レーティングを表示するためのラベル「labelRating」、
アーティストレーティングを表示するためのラベル「labelRatingArtist」の2つを配置。

そしてForm1のPlay()およびSetRating()、SetRatingArtist()で表示更新をします。

022_1.png

■Form1.cs
		/// <summary>
		/// 曲レーティング設定
		/// </summary>
		void SetRating(int nRating)
		{
			if (_nCurrentIndex < 0)
				return;
			_option.SetRating(_listFiles[_nCurrentIndex], nRating);

			ShowRating();		//追加
		}

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

			ShowRating();		//追加
		}




		/// <summary>
		/// 曲を再生する
		/// </summary>
		void Play(bool bPlayNext = true)
		{
			if (_listFiles.Count == 0)
				return;

			if (bPlayNext || _listPlayHistory.Count == 0)
			{
				//次の曲を再生する
				if (_nCurrentIndex >= 0)
				{
					_listPlayHistory.Add(_nCurrentIndex);					//今の曲を履歴として保存
					if (_listPlayHistory.Count > 100)						//履歴の最大保存数は100
						_listPlayHistory.RemoveAt(0);
				}

				_nCurrentIndex = GetNextIndex();
			}
			else
			{
				//前の曲を再生する
				if (_nCurrentIndex >= 0)
					_listPlayNext.Insert(0, _nCurrentIndex);					//今の曲を次の曲として保存
				_nCurrentIndex = _listPlayHistory[_listPlayHistory.Count - 1];	//一番最新の履歴曲を今の曲とする
				_listPlayHistory.RemoveAt(_listPlayHistory.Count - 1);			//一番最新の履歴曲を削除
			}

			Utility.CleanTempFolder();			//tmpフォルダ内の削除

			string strFile = _listFiles[_nCurrentIndex].strFile;
			if (_listFiles[_nCurrentIndex].nEntryIndex >= 0)
			{
				//zip書庫ならtmpフォルダに解凍する
				bool ret = Utility.ExtractFileFromZip(strFile, _listFiles[_nCurrentIndex].nEntryIndex, Utility.GetTmpFile(), out strFile);
				if (ret == false)
					return;
			}

			_mediaPlayer.URL = strFile;			//再生曲をセット
			_mediaPlayer.controls.play();

			//曲名/タイトルの表示
			{
				string strTitle;
				string strArtist;
				string strAlbum;

				Utility.GetMp3Info(strFile, out strTitle, out strArtist, out strAlbum);

				labelTitle.Text = strTitle;
				labelArtist.Text = strArtist;

				ShowRating();		//追加
			}
		}

		//以下を追加
		/// <summary>
		/// レーティングの表示
		/// </summary>
		void ShowRating()
		{
			if (_nCurrentIndex < 0)
			{
				labelRating.Text = "";
				labelRatingArtist.Text = "";
				return;
			}

			int nRating = _option.GetRating(_listFiles[_nCurrentIndex]);
			int nRatingArtist = _option.GetRatingArtist(labelArtist.Text);

			labelRating.Text = GetRatingString(nRating);
			labelRatingArtist.Text = GetRatingString(nRatingArtist);
		}


		//以下を追加
		/// <summary>
		/// レーティング文字列「★」~「★★★★★」の作成
		/// </summary>
		string GetRatingString(int nRating)
		{
			if (nRating > 5)
				nRating = 5;
			if (nRating < 0)
				nRating = 0;

			string str = "";
			for (int i = 0; i < nRating; i++)
				str += "★";
			for (int i = nRating; i < 5; i++)
				str += " ";

			return str;
		}

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

第23回 再生条件設定画面を作る

今回は再生条件の設定ウインドウを作成します。

設定できる再生条件はおおまかに以下のようにします。
・音楽ファイルを追加した期間指定
・レーティング指定
・再生フォルダ変更

「プロジェクト」メニューから「Windowsフォームの追加」を選択し、「PlayConditionForm.cs」を追加します。

期間指定の開始を示す数値入力欄「numericUpDownPlayDayStart」
期間指定の最後を示す数値入力欄「numericUpDownPlayDayEnd」
をそれぞれMax=10000に設定(1万日=27年相当)。

曲レーティング指定の開始を示す数値入力欄「numericUpDownRatingStart」
曲レーティング指定の最後を示す数値入力欄「numericUpDownRatingEnd」
をそれぞれMax=5に設定(5=★★★★★)。

アーティストレーティング指定の開始を示す数値入力欄「numericUpDownRatingArtistStart」
アーティストレーティング指定の最後を示す数値入力欄「numericUpDownRatingArtistEnd」
をそれぞれMax=5に設定(5=★★★★★)。

再生フォルダを表示するテキストボックス「textBoxFolder」
再生フォルダを指定するボタン「buttonSelectFolder」
を作成。

OKボタン用の「buttonOk」
キャンセルボタン用の「buttonCancel」
を配置し、それぞれフォームのAcceptButtonとCancelButtonに割り当てる。

各ボタンはダブルクリックをしてイベントハンドラーを作成し、コードを追加。
そしてOptionクラスに各値保存用のプロパティを追加し、フォームの右クリックメニューでそのフォームが表示されるようにしたら完成。
各設定値に応じた再生条件設定の適用は次回以降へ持ち越し。

023_1.png

■PlayConditionForm.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MP3Player
{
	public partial class PlayConditionForm : Form
	{
		public PlayConditionForm()
		{
			InitializeComponent();

			textBoxFolder.ReadOnly = true;
			textBoxFolder.Text = Form1._option.strFolder;

			numericUpDownPlayDayStart.Value = Form1._option.nPlayDayStart;
			numericUpDownPlayDayEnd.Value = Form1._option.nPlayDayEnd;

			numericUpDownRatingStart.Value = Form1._option.nPlayRatingStart;
			numericUpDownRatingEnd.Value = Form1._option.nPlayRatingEnd;

			numericUpDownRatingArtistStart.Value = Form1._option.nPlayRatingArtistStart;
			numericUpDownRatingArtistEnd.Value = Form1._option.nPlayRatingArtistEnd;
		}


		private void buttonSelectFolder_Click(object sender, EventArgs e)
		{
			FolderBrowserDialog dlg = new FolderBrowserDialog();

			dlg.Description = "再生フォルダの選択";
			dlg.RootFolder = Environment.SpecialFolder.Desktop;
			dlg.SelectedPath = textBoxFolder.Text;
			dlg.ShowNewFolderButton = false;

			DialogResult ret = dlg.ShowDialog(this);
			if (ret == DialogResult.OK)
			{
				textBoxFolder.Text = dlg.SelectedPath;
			}
		}

		private void buttonOk_Click(object sender, EventArgs e)
		{
			Form1._option.strFolder = textBoxFolder.Text;

			Form1._option.nPlayDayStart = (int)numericUpDownPlayDayStart.Value;
			Form1._option.nPlayDayEnd = (int)numericUpDownPlayDayEnd.Value;

			Form1._option.nPlayRatingStart = (int)numericUpDownRatingStart.Value;
			Form1._option.nPlayRatingEnd = (int)numericUpDownRatingEnd.Value;

			Form1._option.nPlayRatingArtistStart = (int)numericUpDownRatingArtistStart.Value;
			Form1._option.nPlayRatingArtistEnd = (int)numericUpDownRatingArtistEnd.Value;

			Close();
		}

		private void buttonCancel_Click(object sender, EventArgs e)
		{
			Close();
		}
	}
}
■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>
		/// 再生するファイル追加開始日
		/// 
		/// 指定した日数よりも前に追加された音楽ファイルは再生しない
		/// ゼロならこの設定は無視
		/// 例えば「180に設定した場合は、最近180日以内に追加したものが再生対象」
		/// </summary>
		public int nPlayDayStart
		{
			get { return _nPlayDayStart; }
			set { _nPlayDayStart = value; }
		}
		int _nPlayDayStart = 0;

		//以下を追加
		/// <summary>
		/// 再生するファイル追加終了日
		/// 
		/// 指定した日数よりも後に追加された音楽ファイルは再生しない
		/// ゼロならこの設定は無視
		/// 例えば「180に設定した場合は、180日以上古いものが再生対象」
		/// </summary>
		public int nPlayDayEnd
		{
			get { return _nPlayDayEnd; }
			set { _nPlayDayEnd = value; }
		}
		int _nPlayDayEnd = 0;

		//以下を追加
		/// <summary>
		/// 再生する最低レーティング
		/// 
		/// 例えば「1ならレーティング1以上の曲を再生」
		/// </summary>
		public int nPlayRatingStart
		{
			get { return _nPlayRatingStart; }
			set { _nPlayRatingStart = value; }
		}
		int _nPlayRatingStart = 0;

		//以下を追加
		/// <summary>
		/// 再生する最高レーティング
		/// 
		/// 例えば「1ならレーティング1以下の曲=レーティング1もしくはゼロの曲を再生」
		/// </summary>
		public int nPlayRatingEnd
		{
			get { return _nPlayRatingEnd; }
			set { _nPlayRatingEnd = value; }
		}
		int _nPlayRatingEnd = 5;

		//以下を追加
		/// <summary>
		/// 再生する最低アーティストレーティング
		/// 
		/// 例えば「1ならレーティング1以上のアーティストの曲を再生」
		/// </summary>
		public int nPlayRatingArtistStart
		{
			get { return _nPlayRatingArtistStart; }
			set { _nPlayRatingArtistStart = value; }
		}
		int _nPlayRatingArtistStart = 0;

		//以下を追加
		/// <summary>
		/// 再生する最高アーティストレーティング
		/// 
		/// 例えば「1ならレーティング1以下のアーティスト曲=レーティング1もしくはゼロの曲を再生」
		/// </summary>
		public int nPlayRatingArtistEnd
		{
			get { return _nPlayRatingArtistEnd; }
			set { _nPlayRatingArtistEnd = value; }
		}
		int _nPlayRatingArtistEnd = 5;
■Form1.cs
using 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
						{
							//以下を追加
							PlayConditionForm dlg = new PlayConditionForm();
							dlg.ShowDialog(this);
						}),
					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;
			}

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

第24回 ファイル追加日時条件で再生スキップする

今回は再生条件のうち、ファイルの追加日時による再生スキップ操作を実装します。
実際には「追加日時」を特定するのは困難なため、ファイル作成日時を利用した簡易的な処理です。

最初に前回、曲もしくはアーティストどちらかのレーティングが指定以上なら再生するというオプションを忘れていたため、それを追加します。
いずれかのレーティング指定の開始を示す数値入力欄「numericUpDownRatingAnyStart」
いずれかのレーティング指定の最後を示す数値入力欄「numericUpDownRatingAnyEnd」
をそれぞれMax=5に設定(5=★★★★★)。

そしてOptionクラス内にファイル追加日時の判定処理を、Form1のGetNextIndex()にそれを呼び出す処理を入れます。

■PlayConditionForm.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MP3Player
{
	public partial class PlayConditionForm : Form
	{
		public PlayConditionForm()
		{
			InitializeComponent();

			textBoxFolder.ReadOnly = true;
			textBoxFolder.Text = Form1._option.strFolder;

			numericUpDownPlayDayStart.Value = Form1._option.nPlayDayStart;
			numericUpDownPlayDayEnd.Value = Form1._option.nPlayDayEnd;

			numericUpDownRatingStart.Value = Form1._option.nPlayRatingStart;
			numericUpDownRatingEnd.Value = Form1._option.nPlayRatingEnd;

			numericUpDownRatingArtistStart.Value = Form1._option.nPlayRatingArtistStart;
			numericUpDownRatingArtistEnd.Value = Form1._option.nPlayRatingArtistEnd;

			//以下を追加
			numericUpDownRatingAnyStart.Value = Form1._option.nPlayRatingAnyStart;
			numericUpDownRatingAnyEnd.Value = Form1._option.nPlayRatingAnyEnd;
		}


		private void buttonSelectFolder_Click(object sender, EventArgs e)
		{
			FolderBrowserDialog dlg = new FolderBrowserDialog();

			dlg.Description = "再生フォルダの選択";
			dlg.RootFolder = Environment.SpecialFolder.Desktop;
			dlg.SelectedPath = textBoxFolder.Text;
			dlg.ShowNewFolderButton = false;

			DialogResult ret = dlg.ShowDialog(this);
			if (ret == DialogResult.OK)
			{
				textBoxFolder.Text = dlg.SelectedPath;
			}
		}

		private void buttonOk_Click(object sender, EventArgs e)
		{
			Form1._option.strFolder = textBoxFolder.Text;

			Form1._option.nPlayDayStart = (int)numericUpDownPlayDayStart.Value;
			Form1._option.nPlayDayEnd = (int)numericUpDownPlayDayEnd.Value;

			Form1._option.nPlayRatingStart = (int)numericUpDownRatingStart.Value;
			Form1._option.nPlayRatingEnd = (int)numericUpDownRatingEnd.Value;

			Form1._option.nPlayRatingArtistStart = (int)numericUpDownRatingArtistStart.Value;
			Form1._option.nPlayRatingArtistEnd = (int)numericUpDownRatingArtistEnd.Value;

			//以下を追加
			Form1._option.nPlayRatingAnyStart = (int)numericUpDownRatingAnyStart.Value;
			Form1._option.nPlayRatingAnyEnd = (int)numericUpDownRatingAnyEnd.Value;

			Close();
		}

		private void buttonCancel_Click(object sender, EventArgs e)
		{
			Close();
		}
	}
}
■Option.csへ以下を追加
		//以下を追加
		/// <summary>
		/// 再生する最低レーティング(曲/アーティストレーティングどちらも対象)
		/// 
		/// 例えば「1ならレーティング1以上のを再生」
		/// </summary>
		public int nPlayRatingAnyStart
		{
			get { return _nPlayRatingAnyStart; }
			set { _nPlayRatingAnyStart = value; }
		}
		int _nPlayRatingAnyStart = 0;


		//以下を追加
		/// <summary>
		/// 再生する最高レーティング(曲/アーティストレーティングどちらも対象)
		/// 
		/// 例えば「1ならレーティング1以下の=レーティング1もしくはゼロのを再生」
		/// </summary>
		public int nPlayRatingAnyEnd
		{
			get { return _nPlayRatingAnyEnd; }
			set { _nPlayRatingAnyEnd = value; }
		}
		int _nPlayRatingAnyEnd = 5;


		//以下を追加
		/// <summary>
		/// 再生期間のチェック
		/// 
		/// ファイルの追加日時が再生条件設定を満たしていればtrue
		/// </summary>
		public bool IsPlayDayTermOK(AudioItem item)
		{
			//条件設定されていないならtrue
			if (_nPlayDayEnd == 0 && _nPlayDayStart == 0)
				return true;
			try
			{
				DateTime dtFile = File.GetCreationTime(item.strFile);		//ファイル作成日時で判断
				TimeSpan span = DateTime.Now - dtFile;

				if (_nPlayDayStart > 0)
				{
					if (span.TotalDays > _nPlayDayStart)
						return false;
				}
				if (_nPlayDayEnd > 0)
				{
					if (span.TotalDays < _nPlayDayEnd)
						return false;
				}

				return true;
			}
			catch (Exception)
			{
			}
			return false;
		}
■Form1のGetNextIndex()を変更
		/// <summary>
		/// 次の再生曲インデックスを決める
		/// </summary>
		int GetNextIndex()
		{
			if (_listFiles.Count == 0)
				return -1;

			int nIndex;
			Random rnd = new Random(Environment.TickCount);

			while (true)
			{
				if (_listPlayNext.Count == 0)
				{
					//未来の曲リストが空ならランダムで曲を決定
					nIndex = rnd.Next(0, _listFiles.Count);
				}
				else
				{
					//未来の曲リストから次の曲を決定
					nIndex = _listPlayNext[0];
					_listPlayNext.RemoveAt(0);
				}

				//同じ曲は連続で演奏しない
				if (nIndex == _nCurrentIndex)
					continue;

				//NGファイル指定されていないかチェック
				if (_option.IsNGFile(_listFiles[nIndex]))
					continue;

				//mp3情報を取得
				string strTitle;
				string strArtist;
				string strAlbum;
				Utility.GetMp3Info(_listFiles[nIndex], out strTitle, out strArtist, out strAlbum);

				//アーティストがNG指定されていないかチェック
				if (_option.IsNGArtist(strArtist))
					continue;

				//曲名がNGワードを含まないかチェック
				if (_option.IsNGWord(strTitle))
					continue;

				//以下を追加
				//ファイル追加日時が再生条件を満たすかチェック
				if (_option.IsPlayDayTermOK(_listFiles[nIndex]) == false)
					continue;

				break;
			}

			return nIndex;
		}

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

第25回 曲情報をデータベースに保存する

前回「第24回 ファイル追加日時条件で再生スキップする」は再生条件のうち、ファイル追加日時の判断を追加しました。
このまま今回はアーティストレーティング/曲レーティングの条件判断処理を入れたいところですが、
曲名/アーティスト名の情報を随時ファイルから読み込む方式だと効率が悪いので、
先に曲情報をデータベースへ格納する処理を作成します。

データベースはSqlite3、ライブラリはSystem.Data.SQLitePublic Domain)を利用します。
Microsoft Entity FrameworkMICROSOFT SOFTWARE LICENSE)に依存関係が発生します。

SQLiteを扱うライブラリを取り込むため、
「プロジェクト」メニューにある「NuGetパッケージの管理」を選択して、オンラインで「sqlite」を検索。
ヒットした中から「System.Data.SQLite (x86/x64)」をインストールします。

そして実際にDBへ情報を保存する処理を追加します。
次に「プロジェクト」メニューのある「クラスの追加」を選択して「MP3Info.cs」を作成。
さらにForm1に関連処理を書いておきます。

これで起動時にDBデータがあるかどうかチェックし、DBデータがなければ再生フォルダ内の全音楽ファイルから情報を読み込んでDBへ保存します。
(音楽ファイルが多いと、ものすごく時間がかかります)
2度目以降の起動ではこの処理は行われません。

実際にDBから情報を読みだして利用する処理は次回以降へ持ち越し。

■Mp3Info.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MP3Player
{
	class MP3Info
	{
		static SQLiteConnection _conn = null;

		const string _strDBFileName = "mp3info.sql";

		static MP3Info()
		{
			SQL_OpenConnection();
		}



		public static void Close()
		{
			SQL_CloseConnection();
		}


		/// <summary>
		/// DBを開き、テーブルを作成する
		/// </summary>
		static bool SQL_OpenConnection()
		{
			if (_conn != null)
				return true;

			try
			{
				//DBオープン
				_conn = new SQLiteConnection();
				_conn.ConnectionString = "Data Source=" + Utility.GetExeFolder() + _strDBFileName + ";Version=3;";
				_conn.Open();

				//テーブル作成(すでにテーブルがあっても気にせず実行)
				try
				{
					SQLiteCommand cmd = _conn.CreateCommand();
					cmd.CommandText = "CREATE TABLE TagInfo (id INTEGER primary key AUTOINCREMENT, file TEXT, lastwritedate INTEGER, entryindex INTEGER, title TEXT, artist TEXT, album TEXT)";
					cmd.ExecuteNonQuery();
				}
				catch (Exception)
				{
				}

				return true;
			}
			catch (Exception)
			{
			}

			_conn = null;
			return false;
		}


		/// <summary>
		/// DBを閉じる
		/// </summary>
		static void SQL_CloseConnection()
		{
			try
			{
				if (_conn != null)
					_conn.Close();
			}
			catch (Exception)
			{
			}
			_conn = null;
		}


		/// <summary>
		/// データ数の取得
		/// </summary>
		public static long GetCount()
		{
			try
			{
				using (SQLiteCommand cmd = _conn.CreateCommand())
				{
					cmd.CommandText = @"SELECT count(*) FROM TagInfo;";

					using (SQLiteDataReader reader = cmd.ExecuteReader())
					{
						while (reader.Read())
						{
							return reader.GetInt64(0);
						}
					}
				}
			}
			catch (Exception)
			{
			}
			return 0;
		}



		/// <summary>
		/// DBからの情報取得
		/// </summary>
		static bool SQL_FindTagInfo(AudioItem item, out DateTime dtLastWrite, out string strTitle, out string strArtist, out string strAlbum)
		{
			//引数で渡されたstrFileは音楽フォルダーのパスを含んだものかもしれないので、その部分を除外
			string strFile = Form1._option.RemoveMP3FolderName(item.strFile);

			try
			{
				using (SQLiteCommand cmd = _conn.CreateCommand())
				{
					cmd.CommandText = @"SELECT lastwritedate, title, artist, album FROM TagInfo WHERE file=@file and entryindex=@entryindex;";
					cmd.Parameters.Add(new SQLiteParameter("@file", strFile));
					cmd.Parameters.Add(new SQLiteParameter("@entryindex", item.nEntryIndex));
					cmd.Prepare();

					using (SQLiteDataReader reader = cmd.ExecuteReader())
					{
						while (reader.Read())
						{
							try
							{
								dtLastWrite = new DateTime(reader.GetInt64(0));
								strTitle = reader.GetString(1);
								strArtist = reader.GetString(2);
								strAlbum = reader.GetString(3);
								return true;
							}
							catch (Exception)
							{
							}
						}
					}
				}
			}
			catch (Exception)
			{
			}

			dtLastWrite = DateTime.MinValue;
			strTitle = "";
			strArtist = "";
			strAlbum = "";
			return false;
		}



		/// <summary>
		/// DBへのデータ保存
		/// </summary>
		static bool SQL_SetTagInfo(AudioItem item, DateTime dtLastWrite, string strTitle, string strArtist, string strAlbum)
		{
			//引数で渡されたstrFileは音楽フォルダーのパスを含んだものかもしれないので、その部分を除外
			string strFile = Form1._option.RemoveMP3FolderName(item.strFile);

			try
			{
				int nID = -1;

				//DB中にデータがすでにあるかどうかチェック。あればIDを取得
				using (SQLiteCommand cmd = _conn.CreateCommand())
				{
					cmd.CommandText = @"SELECT id FROM TagInfo WHERE file=@file and entryindex=@entryindex;";
					cmd.Parameters.Add(new SQLiteParameter("@file", strFile));
					cmd.Parameters.Add(new SQLiteParameter("@entryindex", item.nEntryIndex));
					cmd.Prepare();

					using (SQLiteDataReader reader = cmd.ExecuteReader())
					{
						while (reader.Read())
						{
							try
							{
								nID = reader.GetInt32(0);
								break;
							}
							catch (Exception)
							{
							}
						}
					}
				}

				//IDがなかった=データがない=新規追加
				if (nID < 0)
					return SQL_AddNewTagInfo(item, dtLastWrite, strTitle, strArtist, strAlbum);

				//IDが見つかった=データがある=更新
				return SQL_UpdateTagInfo(nID, dtLastWrite, strTitle, strArtist, strAlbum);
			}
			catch (Exception)
			{
			}

			return false;
		}



		/// <summary>
		/// DBへのデータ追加
		/// </summary>
		static bool SQL_AddNewTagInfo(AudioItem item, DateTime dtLastWrite, string strTitle, string strArtist, string strAlbum)
		{
			//引数で渡されたstrFileは音楽フォルダーのパスを含んだものかもしれないので、その部分を除外
			string strFile = Form1._option.RemoveMP3FolderName(item.strFile);

			try
			{
				int nRet = -1;
				using (SQLiteTransaction transaction = _conn.BeginTransaction())
				using (SQLiteCommand cmd = _conn.CreateCommand())
				{
					cmd.CommandText = @"INSERT INTO TagInfo (lastwritedate, file, entryindex, title, artist, album) VALUES(@lastwritedate, @file, @entryindex, @title, @artist, @album);";
					cmd.Parameters.Add(new SQLiteParameter("@lastwritedate", dtLastWrite.Ticks));
					cmd.Parameters.Add(new SQLiteParameter("@file", strFile));
					cmd.Parameters.Add(new SQLiteParameter("@entryindex", item.nEntryIndex));
					cmd.Parameters.Add(new SQLiteParameter("@title", strTitle));
					cmd.Parameters.Add(new SQLiteParameter("@artist", strArtist));
					cmd.Parameters.Add(new SQLiteParameter("@album", strAlbum));
					cmd.Prepare();

					nRet = cmd.ExecuteNonQuery();
					transaction.Commit();
				}
				return (nRet > 0) ? true : false;
			}
			catch (Exception)
			{
			}
			return false;
		}


		/// <summary>
		/// DBのデータ更新
		/// </summary>
		static bool SQL_UpdateTagInfo(int nID, DateTime dtLastWrite, string strTitle, string strArtist, string strAlbum)
		{
			if (nID < 0)
				return false;

			try
			{
				int nRet = -1;
				using (SQLiteTransaction transaction = _conn.BeginTransaction())
				using (SQLiteCommand cmd = _conn.CreateCommand())
				{
					cmd.CommandText = @"UPDATE TagInfo SET lastwritedate=@lastwritedate, title=@title, artist=@artist, album=@album WHERE id=@id;";
					cmd.Parameters.Add(new SQLiteParameter("@lastwritedate", dtLastWrite.Ticks));
					cmd.Parameters.Add(new SQLiteParameter("@title", strTitle));
					cmd.Parameters.Add(new SQLiteParameter("@artist", strArtist));
					cmd.Parameters.Add(new SQLiteParameter("@album", strAlbum));
					cmd.Parameters.Add(new SQLiteParameter("@id", nID));
					cmd.Prepare();

					nRet = cmd.ExecuteNonQuery();
					transaction.Commit();
				}
				return (nRet > 0) ? true : false;
			}
			catch (Exception)
			{
			}
			return false;
		}




		/// <summary>
		/// DB情報の構築
		/// </summary>
		public static int RefreshAllInfo(bool bAllClear = false)
		{
			int nCount = 0;

			// サブ・ディレクトも含める
			IEnumerable<string> listFiles = Directory.EnumerateFiles(Form1._option.strFolder, "*.*", SearchOption.AllDirectories);

			bool ret;
			string strArtist;
			string strTitle;
			string strAlbum;

			if (bAllClear)
			{
				//既存のDBデータを全削除
				try
				{
					using (SQLiteCommand cmd = _conn.CreateCommand())
					{
						cmd.CommandText = @"TRUNCATE TABLE TagInfo;";
						cmd.ExecuteNonQuery();
					}
				}
				catch (Exception)
				{
				}
			}


			foreach (string strFile in listFiles)
			{
				string strExt = Path.GetExtension(strFile).ToLower();
				if (strExt == "")
					continue;

				if (strExt == ".mp3" || strExt == ".m4a")
				{
					//タグ情報読み込み
					ret = Utility.GetMp3Info(strFile, out strTitle, out strArtist, out strAlbum);
					if (ret)
					{
						DateTime dtLastWrite = File.GetLastWriteTime(strFile);

						//DBへ追加
						if (bAllClear)
							ret = SQL_AddNewTagInfo(new AudioItem(strFile), dtLastWrite, strTitle, strArtist, strAlbum);
						else
							ret = SQL_SetTagInfo(new AudioItem(strFile), dtLastWrite, strTitle, strArtist, strAlbum);
						if (ret)
							nCount++;
					}
					continue;
				}

				//ZIPファイルなら再生できそうな全エントリーを追加
				if (strExt == ".zip")
				{
					try
					{
						DateTime dtLastWrite = File.GetLastWriteTime(strFile);
						using (ZipArchive archive = ZipFile.OpenRead(strFile))
						{
							for (int i = 0; i < archive.Entries.Count; i++)
							{
								strExt = Path.GetExtension(archive.Entries[i].FullName).ToLower();
								if (strExt == ".mp3" || strExt == ".m4a")
								{
									try
									{
										//解凍
										string strTmpFile = Utility.GetTmpFile() + strExt;
										archive.Entries[i].ExtractToFile(strTmpFile, true);

										//タグ情報読み込み
										ret = Utility.GetMp3Info(strTmpFile, out strTitle, out strArtist, out strAlbum);
										if (ret)
										{
											//DBへ追加
											if (bAllClear)
												ret = SQL_AddNewTagInfo(new AudioItem(strFile, i), dtLastWrite, strTitle, strArtist, strAlbum);
											else
												ret = SQL_SetTagInfo(new AudioItem(strFile, i), dtLastWrite, strTitle, strArtist, strAlbum);
											if (ret)
												nCount++;
										}

										//解凍したファイルの削除
										File.Delete(strTmpFile);
									}
									catch (Exception)
									{
									}
								}
							}
						}
					}
					catch (Exception)
					{
					}
				}

			}
			return nCount;
		}

	}
}
■Form1.cs
using 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フォルダの作成

			//以下を追加
			//DBに情報がなければ再構築する=>時間が非常にかかる!
			if (MP3Info.GetCount() == 0)
			{
				MP3Info.RefreshAllInfo(true);
			}


			//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
						{
							PlayConditionForm dlg = new PlayConditionForm();
							dlg.ShowDialog(this);
						}),
					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");

				MP3Info.Close();		//追加
			};

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

第26回 データベースの曲情報を利用する

今回はデータベースへ保存した曲名/アーティスト名の情報を実際に利用します。

単純に読みだすだけではなくデータベース上の曲情報が古くなっている(更新されていない)場合は
データベースから読み出すのではなく音楽ファイルから読み出し、データベース上の曲情報をアップデートします。

データが更新されているかどうかは、ファイルの更新日時情報を利用して判定しました。

Utility.cs内で定義していた、
GetMp3Info()
GetMp3Info()
class AudioFileAbstraction
の3つを削除して、Mp3Info.csへ移動/変更します。
あとはGetMp3Info()を参照していたForm1内の箇所を修正するだけです。

■Mp3Info.cs
		/// <summary>
		/// DB情報の構築
		/// </summary>
		public static int RefreshAllInfo(bool bAllClear = false)
		{
			int nCount = 0;

			// サブ・ディレクトも含める
			IEnumerable<string> listFiles = Directory.EnumerateFiles(Form1._option.strFolder, "*.*", SearchOption.AllDirectories);

			bool ret;
			string strArtist;
			string strTitle;
			string strAlbum;

			if (bAllClear)
			{
				//既存のDBデータを全削除
				try
				{
					using (SQLiteCommand cmd = _conn.CreateCommand())
					{
						cmd.CommandText = @"TRUNCATE TABLE TagInfo;";
						cmd.ExecuteNonQuery();
					}
				}
				catch (Exception)
				{
				}
			}


			foreach (string strFile in listFiles)
			{
				string strExt = Path.GetExtension(strFile).ToLower();
				if (strExt == "")
					continue;

				if (strExt == ".mp3" || strExt == ".m4a")
				{
					//タグ情報読み込み
					ret = GetMp3Info(strFile, out strTitle, out strArtist, out strAlbum);		//変更
					if (ret)
					{
						DateTime dtLastWrite = File.GetLastWriteTime(strFile);

						//DBへ追加
						if (bAllClear)
							ret = SQL_AddNewTagInfo(new AudioItem(strFile), dtLastWrite, strTitle, strArtist, strAlbum);
						else
							ret = SQL_SetTagInfo(new AudioItem(strFile), dtLastWrite, strTitle, strArtist, strAlbum);
						if (ret)
							nCount++;
					}
					continue;
				}

				//ZIPファイルなら再生できそうな全エントリーを追加
				if (strExt == ".zip")
				{
					try
					{
						DateTime dtLastWrite = File.GetLastWriteTime(strFile);
						using (ZipArchive archive = ZipFile.OpenRead(strFile))
						{
							for (int i = 0; i < archive.Entries.Count; i++)
							{
								strExt = Path.GetExtension(archive.Entries[i].FullName).ToLower();
								if (strExt == ".mp3" || strExt == ".m4a")
								{
									try
									{
										//解凍
										string strTmpFile = Utility.GetTmpFile() + strExt;
										archive.Entries[i].ExtractToFile(strTmpFile, true);

										//タグ情報読み込み
										ret = GetMp3Info(strTmpFile, out strTitle, out strArtist, out strAlbum);		//変更
										if (ret)
										{
											//DBへ追加
											if (bAllClear)
												ret = SQL_AddNewTagInfo(new AudioItem(strFile, i), dtLastWrite, strTitle, strArtist, strAlbum);
											else
												ret = SQL_SetTagInfo(new AudioItem(strFile, i), dtLastWrite, strTitle, strArtist, strAlbum);
											if (ret)
												nCount++;
										}

										//解凍したファイルの削除
										File.Delete(strTmpFile);
									}
									catch (Exception)
									{
									}
								}
							}
						}
					}
					catch (Exception)
					{
					}
				}

			}
			return nCount;
		}



		//Utilityから移動/変更
		/// <summary>
		/// MP3ファイルからの曲名情報取得(DB/zip対応)
		/// </summary>
		public static bool GetMp3Info(AudioItem item, out string strTitle, out string strArtist, out string strAlbum)
		{
			bool ret;

			DateTime dtLastWriteDB;
			DateTime dtLastWriteCurrent = File.GetLastWriteTime(item.strFile);

			//DBから取得を試みる
			ret = SQL_FindTagInfo(item, out dtLastWriteDB, out strTitle, out strArtist, out strAlbum);
			if (ret)
			{
				//mp3ファイルが更新されていなければDBの情報をそのまま返す
				//(更新されていれば、新しい情報を読み込む)
				if (dtLastWriteCurrent <= dtLastWriteDB)
					return true;
			}

			if (item.nEntryIndex < 0)
			{
				//zipファイル以外はそのまま情報取得
				ret = GetMp3Info(item.strFile, out strTitle, out strArtist, out strAlbum);
			}
			else
			{
				//zipファイルは解凍して取得
				try
				{
					string strFile;

					ret = Utility.ExtractFileFromZip(item.strFile, item.nEntryIndex, Utility.GetTmpFile(), out strFile);
					if (ret)
						ret = GetMp3Info(strFile, out strTitle, out strArtist, out strAlbum);

					File.Delete(strFile);
				}
				catch (Exception)
				{
					ret = false;
				}
			}

			if (ret)
			{
				//DBに情報を入れておく
				SQL_SetTagInfo(item, dtLastWriteCurrent, strTitle, strArtist, strAlbum);
				return true;
			}

			strArtist = "";
			strTitle = "";
			strAlbum = "";
			return false;
		}


		//Utilityから移動/変更
		/// <summary>
		/// MP3ファイルからの曲名情報取得(ファイルから直接取得)
		/// 
		/// TagLib# Portable利用
		/// https://github.com/timheuer/taglib-sharp-portable
		/// </summary>
		static bool GetMp3Info(string strFile, out string strTitle, out string strArtist, out string strAlbum)
		{
			strArtist = "";
			strTitle = "";
			strAlbum = "";
			try
			{
				using (FileStream fs = new FileStream(strFile, FileMode.Open, FileAccess.Read))
				{
					AudioFileAbstraction abstraction = new AudioFileAbstraction(strFile, fs);

					using (TagLib.File file = TagLib.File.Create(abstraction))
					{
						strTitle = file.Tag.Title;
						strAlbum = file.Tag.Album;
						strArtist = file.Tag.FirstPerformer;

						if (strArtist == null || strArtist == "")
						{
							if (file.Tag.AlbumArtists.Length > 0)
								strArtist = file.Tag.AlbumArtists[0];
						}

						if (strArtist == null || strArtist == "")
							strArtist = file.Tag.FirstArtist;		//古いのも使う
						if (strArtist == null || strArtist == "")
							strArtist = file.Tag.FirstComposer;
						if (strArtist == null || strArtist == "")
							strArtist = file.Tag.JoinedAlbumArtists;

						file.Dispose();
					}
				}

				if (strArtist == null)
					strArtist = "";
				if (strTitle == null)
					strTitle = "";
				if (strAlbum == null)
					strAlbum = "";

				return true;
			}
			catch (Exception)
			{
			}
			strArtist = "";
			strTitle = "";
			strAlbum = "";
			return false;
		}



		/// <summary>
		/// TagLibからの情報取得用
		/// </summary>
		class AudioFileAbstraction : TagLib.File.IFileAbstraction
		{
			string _strName = "";
			Stream _stream = null;

			public AudioFileAbstraction(string Name, Stream Stream)
			{
				_strName = Name;
				_stream = Stream;
			}

			void TagLib.File.IFileAbstraction.CloseStream(Stream stream)
			{
				stream.Close();
			}

			string TagLib.File.IFileAbstraction.Name
			{
				get { return _strName; }
			}

			Stream TagLib.File.IFileAbstraction.ReadStream
			{
				get { return _stream; }
			}

			Stream TagLib.File.IFileAbstraction.WriteStream
			{
				get { return _stream; }
			}
		}
	}
}
■Form1.cs
		/// <summary>
		/// 曲を再生する
		/// </summary>
		void Play(bool bPlayNext = true)
		{
			if (_listFiles.Count == 0)
				return;

			if (bPlayNext || _listPlayHistory.Count == 0)
			{
				//次の曲を再生する
				if (_nCurrentIndex >= 0)
				{
					_listPlayHistory.Add(_nCurrentIndex);					//今の曲を履歴として保存
					if (_listPlayHistory.Count > 100)						//履歴の最大保存数は100
						_listPlayHistory.RemoveAt(0);
				}

				_nCurrentIndex = GetNextIndex();
			}
			else
			{
				//前の曲を再生する
				if (_nCurrentIndex >= 0)
					_listPlayNext.Insert(0, _nCurrentIndex);					//今の曲を次の曲として保存
				_nCurrentIndex = _listPlayHistory[_listPlayHistory.Count - 1];	//一番最新の履歴曲を今の曲とする
				_listPlayHistory.RemoveAt(_listPlayHistory.Count - 1);			//一番最新の履歴曲を削除
			}

			Utility.CleanTempFolder();			//tmpフォルダ内の削除

			string strFile = _listFiles[_nCurrentIndex].strFile;
			if (_listFiles[_nCurrentIndex].nEntryIndex >= 0)
			{
				//zip書庫ならtmpフォルダに解凍する
				bool ret = Utility.ExtractFileFromZip(strFile, _listFiles[_nCurrentIndex].nEntryIndex, Utility.GetTmpFile(), out strFile);
				if (ret == false)
					return;
			}

			_mediaPlayer.URL = strFile;			//再生曲をセット
			_mediaPlayer.controls.play();

			//曲名/タイトルの表示
			{
				string strTitle;
				string strArtist;
				string strAlbum;

				MP3Info.GetMp3Info(_listFiles[_nCurrentIndex], out strTitle, out strArtist, out strAlbum);		//変更

				labelTitle.Text = strTitle;
				labelArtist.Text = strArtist;

				ShowRating();		//追加
			}
		}

		//以下を追加
		/// <summary>
		/// レーティングの表示
		/// </summary>
		void ShowRating()
		{
			if (_nCurrentIndex < 0)
			{
				labelRating.Text = "";
				labelRatingArtist.Text = "";
				return;
			}

			int nRating = _option.GetRating(_listFiles[_nCurrentIndex]);
			int nRatingArtist = _option.GetRatingArtist(labelArtist.Text);

			labelRating.Text = GetRatingString(nRating);
			labelRatingArtist.Text = GetRatingString(nRatingArtist);
		}


		//以下を追加
		/// <summary>
		/// レーティング文字列「★」~「★★★★★」の作成
		/// </summary>
		string GetRatingString(int nRating)
		{
			if (nRating > 5)
				nRating = 5;
			if (nRating < 0)
				nRating = 0;

			string str = "";
			for (int i = 0; i < nRating; i++)
				str += "★";
			for (int i = nRating; i < 5; i++)
				str += " ";

			return str;
		}




		/// <summary>
		/// 次の再生曲インデックスを決める
		/// </summary>
		int GetNextIndex()
		{
			if (_listFiles.Count == 0)
				return -1;

			int nIndex;
			Random rnd = new Random(Environment.TickCount);

			while (true)
			{
				if (_listPlayNext.Count == 0)
				{
					//未来の曲リストが空ならランダムで曲を決定
					nIndex = rnd.Next(0, _listFiles.Count);
				}
				else
				{
					//未来の曲リストから次の曲を決定
					nIndex = _listPlayNext[0];
					_listPlayNext.RemoveAt(0);
				}

				//同じ曲は連続で演奏しない
				if (nIndex == _nCurrentIndex)
					continue;

				//NGファイル指定されていないかチェック
				if (_option.IsNGFile(_listFiles[nIndex]))
					continue;

				//mp3情報を取得
				string strTitle;
				string strArtist;
				string strAlbum;
				MP3Info.GetMp3Info(_listFiles[nIndex], out strTitle, out strArtist, out strAlbum);		//変更

				//アーティストがNG指定されていないかチェック
				if (_option.IsNGArtist(strArtist))
					continue;

				//曲名がNGワードを含まないかチェック
				if (_option.IsNGWord(strTitle))
					continue;

				//ファイル追加日時が再生条件を満たすかチェック
				if (_option.IsPlayDayTermOK(_listFiles[nIndex]) == false)
					continue;

				break;
			}

			return nIndex;
		}

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

2016年03月11日

第27回 レーティングに応じて再生する

今回は再生条件として設定できるレーティングを利用した再生スキップ処理を入れます。

Optionクラスに判定処理、Form1のGetNextIndex()にそれを呼び出す処理を追加するだけです。
再生条件が増えると条件にマッチした曲を見つけるのに時間がかかったり、見つからず無限ループになる可能性がありました。
そのためループ試行回数を最大200に変更しています。

■Option.csへ以下を追加
		//以下を追加
		/// <summary>
		/// 再生条件のチェック(内部用途)
		/// </summary>
		bool IsPlay(int nRating, int nPlayRatingStart, int nPlayRatingEnd)
		{
			//条件設定されていないならtrue
			if (nPlayRatingStart == 0 && nPlayRatingEnd == 5)
				return true;

			if (nPlayRatingStart > 0)
			{
				if (nRating < nPlayRatingStart)
					return false;
			}
			if (nPlayRatingEnd < 5)
			{
				if (nRating > nPlayRatingEnd)
					return false;
			}

			return true;
		}


		//以下を追加
		/// <summary>
		/// 曲レーティングのチェック
		/// 
		/// 曲レーティングが再生条件設定を満たしていればtrue
		/// </summary>
		public bool IsPlayRatingOK(AudioItem item)
		{
			//条件設定されていないならtrue
			if (_nPlayRatingStart == 0 && _nPlayRatingEnd == 5)
				return true;

			int nRating = GetRating(item);
			return IsPlay(nRating, _nPlayRatingStart, _nPlayRatingEnd);
		}

		//以下を追加
		/// <summary>
		/// アーティストレーティングのチェック
		/// 
		/// アーティストレーティングが再生条件設定を満たしていればtrue
		/// </summary>
		public bool IsPlayRatingArtistOK(string strArtist)
		{
			//条件設定されていないならtrue
			if (_nPlayRatingArtistStart == 0 && _nPlayRatingArtistEnd == 5)
				return true;

			int nRating = GetRatingArtist(strArtist);
			return IsPlay(nRating, _nPlayRatingArtistStart, _nPlayRatingArtistEnd);
		}

		//以下を追加
		/// <summary>
		/// 曲/アーティストレーティングのチェック
		/// 
		/// 曲レーティングもしくはアーティストレーティングのいずれかが再生条件設定を満たしていればtrue
		/// </summary>
		public bool IsPlayRatingAnyOK(AudioItem item, string strArtist)
		{
			//条件設定されていないならtrue
			if (_nPlayRatingAnyStart == 0 && _nPlayRatingAnyEnd == 5)
				return true;

			int nRating;
			bool ret;

			nRating = GetRating(item);
			ret = IsPlay(nRating, _nPlayRatingAnyStart, _nPlayRatingAnyEnd);
			if (ret)
				return true;

			nRating = GetRatingArtist(strArtist);
			ret = IsPlay(nRating, _nPlayRatingAnyStart, _nPlayRatingAnyEnd);

			return ret;
		}
■Form1.csのGetNextIndex()を変更
		/// <summary>
		/// 次の再生曲インデックスを決める
		/// </summary>
		int GetNextIndex()
		{
			if (_listFiles.Count == 0)
				return -1;

			int nIndex = 0;				//変更
			Random rnd = new Random(Environment.TickCount);

			bool bFound = false;		//追加

			for (int i = 0; i < 200; i++)		//200回曲検索試行		//変更
			{
				if (_listPlayNext.Count == 0)
				{
					//未来の曲リストが空ならランダムで曲を決定
					nIndex = rnd.Next(0, _listFiles.Count);
				}
				else
				{
					//未来の曲リストから次の曲を決定
					nIndex = _listPlayNext[0];
					_listPlayNext.RemoveAt(0);
				}

				//同じ曲は連続で演奏しない
				if (nIndex == _nCurrentIndex)
					continue;

				//NGファイル指定されていないかチェック
				if (_option.IsNGFile(_listFiles[nIndex]))
					continue;

				//mp3情報を取得
				string strTitle;
				string strArtist;
				string strAlbum;
				MP3Info.GetMp3Info(_listFiles[nIndex], out strTitle, out strArtist, out strAlbum);

				//アーティストがNG指定されていないかチェック
				if (_option.IsNGArtist(strArtist))
					continue;

				//曲名がNGワードを含まないかチェック
				if (_option.IsNGWord(strTitle))
					continue;

				//ファイル追加日時が再生条件を満たすかチェック
				if (_option.IsPlayDayTermOK(_listFiles[nIndex]) == false)
					continue;

				//追加
				//曲レーティングが再生条件を満たすかチェック
				if (_option.IsPlayRatingOK(_listFiles[nIndex]) == false)
					continue;

				//追加
				//アーティストレーティングが再生条件を満たすかチェック
				if (_option.IsPlayRatingArtistOK(strArtist) == false)
					continue;

				//追加
				//いずれかのレーティングが再生条件を満たすかチェック
				if (_option.IsPlayRatingAnyOK(_listFiles[nIndex], strArtist) == false)
					continue;

				bFound = true;
				break;
			}

			//追加
			if (bFound == false)
			{
				//次の再生曲を決定できなかった
				//過去の曲もしくはインデックスゼロを再生
				if (_listPlayHistory.Count > 0)
				{
					//過去の曲からランダムに決定
					nIndex = rnd.Next(0, _listPlayHistory.Count);
				}
				else
				{
					nIndex = 0;
				}
			}

			return nIndex;
		}

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

第28回 再生フォルダを変更可能にする

だいぶ前「第23回 再生条件設定画面を作る」で再生フォルダの選択/設定部分は作りました。
しかしフォルダを変更してもプログラムを再起動しないと変更が適用されていませんでした。
今回は再生フォルダの変更適用処理を追加します。
データベース関連の適用処理については次回以降へ持ち越しです。

EventHandlerを使いたい気分になってきたので、今回はそれを利用した実装にします。
Option.strFolderが変更されたらFolderChangedイベントが発生するようにしました。

■Option.cs
	[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
			{
				if (value != _strFolder)
				{
					_strFolder = value;

					if (FolderChanged != null)
						FolderChanged(this, null);
				}
			}
		}
		string _strFolder = @"G:\Desktop\mp3\";

		[XmlIgnore]
		public EventHandler<EventArgs> FolderChanged = null;		//追加
■Form1.cs
		public Form1()
		{
			InitializeComponent();

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

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


			//DBに情報がなければ再構築する=>時間が非常にかかる!
			if (MP3Info.GetCount() == 0)
			{
				MP3Info.RefreshAllInfo(true);
			}

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


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


		.
		.
		.

			_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;
				}
			};

			//以下を追加
			_option.FolderChanged += delegate
			{
				CreateFileListAndPlay(true);		//フォルダを読み込み、再生開始
			};
			_option.FolderChanged(null, null);		//フォルダを読み込み、再生開始

			//Play();		//削除
		}


		//以下を追加
		/// <summary>
		/// 設定フォルダから音楽ファイルリストを読み込む
		/// </summary>
		void CreateFileListAndPlay(bool bPlay)
		{
			buttonStop_Click(null, null);		//再生停止

			//再生リストのクリア
			_listPlayHistory.Clear();
			_listPlayNext.Clear();
			_nCurrentIndex = -1;

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

			if (bPlay)
				Play();		//再生開始
		}

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

第29回 再生フォルダ読み込みをキャンセル可能にする


「プロジェクト」メニューにある「Windowsフォームの追加」から「ProgressForm.cs」を追加する。
そしてそこに
状況表示用のラベル「labelProgress」
キャンセルボタン「buttonCancel」
を配置し、ボタンをダブルクリックしてイベントハンドラーを自動生成。
そこにキャンセルボタンを押したらtrueとするbCancelプロパティを用意する。

あとはForm1.CreateFileListAndPlay()でProgressFormを開き、スレッド上を作ってそこで処理をさせる。
時間のかかるForm1.EnumFiles()でbCancelプロパティを随時参照してtrueになったら中断する。

■ProgressForm.cs
using System;
using System.Windows.Forms;

namespace MP3Player
{
	public partial class ProgressForm : Form
	{
		public bool bCancel
		{
			get { return _bCancel; }
		}
		bool _bCancel = false;

		public EventHandler<EventArgs> Canceled = null;

		public string strProgress
		{
			set
			{
				try
				{
					Invoke((MethodInvoker)delegate
					{
						labelProgress.Text = value;
					});
				}
				catch (Exception)
				{
				}
			}
		}

		public ProgressForm(string strTitle)
		{
			InitializeComponent();
			Text = strTitle;
		}

		private void buttonCancel_Click(object sender, EventArgs e)
		{
			try
			{
				_bCancel = true;
				if (Canceled != null)
					Canceled(null, null);
			}
			catch (Exception)
			{
			}

			try
			{
				Close();
			}
			catch (Exception)
			{
			}
		}
	}
}
■Form1.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Threading;			//追加
using System.Windows.Forms;
using WMPLib;

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

		System.Windows.Forms.Timer _timer = new System.Windows.Forms.Timer();		//変更

		public static Option _option = new Option();
		/// <summary>
		/// 設定フォルダから音楽ファイルリストを読み込む
		/// </summary>
		void CreateFileListAndPlay(bool bPlay)
		{
			buttonStop_Click(null, null);		//再生停止

			//再生リストのクリア
			_listPlayHistory.Clear();
			_listPlayNext.Clear();
			_nCurrentIndex = -1;

			ProgressForm dlg = new ProgressForm("再生フォルダの読み込み中");

			dlg.Show(this);

			Thread thread = new Thread(new ThreadStart(delegate
				{
					//mp3フォルダからの音楽ファイルリスト読み込み
					_listFiles = EnumFiles(_option.strFolder, dlg);

					try
					{
						Invoke((MethodInvoker)delegate
						{
							dlg.Close();
						});
					}
					catch (Exception)
					{
					}

					if (bPlay)
					{
						Invoke((MethodInvoker)delegate
						{
							Play();		//再生開始
						});
					}
				}));

			thread.Start();
		}



		/// <summary>
		/// フォルダ内のファイルを一覧して返す
		/// 
		/// dlg.bCancel==trueになったら処理を中断して抜ける
		/// </summary>
		List<AudioItem> EnumFiles(string strFolder, ProgressForm dlg = null)		//変更
		{
			List<AudioItem> ret = new List<AudioItem>();

			//指定フォルダ以下の全子フォルダから全ファイルを抜き出す
			IEnumerable<string> listFiles = Directory.EnumerateFiles(strFolder, "*.*", SearchOption.AllDirectories);
			//↑の処理は遅いが途中で中断できない

			foreach (string strFile in listFiles)
			{
				//追加
				if (dlg != null)
				{
					try
					{
						dlg.strProgress = "" + ret.Count;
						if (dlg.bCancel)
							break;
					}
					catch (Exception)
					{
					}
				}

				//見つかったファイルの拡張子を取り出し
				string strExt = Path.GetExtension(strFile).ToLower();
				if (strExt == "")
					continue;

				//mp3/m4aならそのまま追加
				if (strExt == ".mp3" || strExt == ".m4a")
				{
					ret.Add(new AudioItem(strFile));
					continue;
				}

				//ZIPファイルなら書庫に含まれるmp3/m4aファイルのエントリーを追加
				if (strExt == ".zip")
				{
					try
					{
						using (ZipArchive archive = ZipFile.OpenRead(strFile))
						{
							for (int i = 0; i < archive.Entries.Count; i++)
							{
								//追加
								if (dlg != null)
								{
									try
									{
										dlg.strProgress = "" + ret.Count;
										if (dlg.bCancel)
											break;
									}
									catch (Exception)
									{
									}
								}

								string strExtension = Path.GetExtension(archive.Entries[i].FullName).ToLower();
								if (strExtension == ".mp3" || strExtension == ".m4a")
								{
									ret.Add(new AudioItem(strFile, i));
								}
							}
						}
					}
					catch (Exception)
					{
					}
				}

			}

			return ret;
		}

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

第30回 ウインドウ表示位置を設定する

今回はメインウインドウの表示位置を前回終了時の位置へ復元する処理と、NG登録画面などの表示位置の設定をします。

基本的にはOptionクラスに位置保存用のptWindowPosプロパティを追加、
Form1で表示する際に位置を設定、終了の際に位置を保存するだけです。

■Option.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;			//追加
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("トイレの神様");
		}

		//以下を追加
		/// <summary>
		/// ウインドウの表示位置
		/// </summary>
		public Point ptWindowPos
		{
			get { return _ptWindowPos; }
			set { _ptWindowPos = value; }
		}
		Point _ptWindowPos = new Point(0, 0);
■Form1.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;			//追加
using System.IO;
using System.IO.Compression;
using System.Threading;
using System.Windows.Forms;
using WMPLib;

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

		System.Windows.Forms.Timer _timer = new System.Windows.Forms.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フォルダの作成


			//以下を追加
			//ウインドウ表示位置設定
			//[Ctrl]キーが押されていないときのみ設定=表示位置を復元したくないときは[Ctrl]キーを押して起動する
			if ((Control.ModifierKeys & Keys.Control) != Keys.Control)
			{
				StartPosition = FormStartPosition.Manual;
				Location = _option.ptWindowPos;
			}

			//DBに情報がなければ再構築する=>時間が非常にかかる!
			if (MP3Info.GetCount() == 0)
			{
				MP3Info.RefreshAllInfo(true);
			}


			//フォームが表示されたときの処理
			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.StartPosition = FormStartPosition.Manual;					//追加
								dlg.Location = new Point(Location.X + 50, Location.Y + 50);		//追加
								dlg.ShowDialog();
							}
						}),
					new ToolStripMenuItem("再生設定", null, delegate
						{
							PlayConditionForm dlg = new PlayConditionForm();
							dlg.ShowDialog(this);
						}),
					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
			{
				//以下を追加
				//ウインドウ位置を保存
				//最小化されてたときは保存しない
				if (WindowState == FormWindowState.Normal)
					_option.ptWindowPos = Location;


				Utility.CleanTempFolder();			//tmpフォルダ内の削除

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

				MP3Info.Close();
			};

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

前の10件 1  2  3  4





usefullcode@gmail.com

About 2016年03月

2016年03月にブログ「UsefullCode.net」に投稿されたすべてのエントリーです。

前の記事は2016年02月です。

次の記事は2016年04月です。

他にも多くのエントリーがあります。メインページ記事一覧も見てください。