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

前の10件 1  2  3  4

2016年03月 記事一覧

第31回 タイトルバーを消す

今回はメインウインドウのタイトルバーを非表示にします。
ただ消すだけだとウインドウの移動ができなくなり不便なため、併せてウインドウのドラッグで移動ができるようにもします。

■Form1.cs
		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);
			}


			//以下を追加
			ControlBox = false;		//タイトルバーを消す
			FormBorderStyle = FormBorderStyle.None;


			//以下を追加
			//フォームのドラッグでウインドウを移動する
			{
				bool _bMoving = false;
				Point _ptMouseDown = new Point(-1, -1);
				Point _ptMouseDownWindowPos = new Point(-1, -1);

				MouseDown += (sender, e) =>
				{
					if (e.Button != MouseButtons.Left)
						return;

					_ptMouseDown.X = e.X;
					_ptMouseDown.Y = e.Y;
					_ptMouseDown = PointToScreen(_ptMouseDown);
					_ptMouseDownWindowPos.X = this.Left;
					_ptMouseDownWindowPos.Y = this.Top;
					_bMoving = true;
				};

				MouseMove += (sender, e) =>
				{
					if (_bMoving)
					{
						Point tmp = new Point(e.X, e.Y);
						tmp = PointToScreen(tmp);

						this.Left = _ptMouseDownWindowPos.X - _ptMouseDown.X + tmp.X;
						this.Top = _ptMouseDownWindowPos.Y - _ptMouseDown.Y + tmp.Y;
					}
				};

				MouseUp += (sender, e) =>
				{
					_bMoving = false;
					_ptMouseDown.X = -1;
					_ptMouseDown.Y = -1;
				};
			}


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

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

第32回 タスクトレイに常駐させる

今回はタスクバーの右側、通知領域となるタスクトレイにアイコンを追加し、そのダブルクリックで最小化/元に戻すができるようにする。

まずタスクトレイに表示するアイコンをプロジェクトに追加する。
「プロジェクト」メニューから「既存項目の追加」を開き、適当なアイコンファイルを追加する。
今回はVisual Studio 2008付属の再配布可能アイコンのAudioFile.icoを利用した。

次にフォームへ通知アイコンコントロールを追加する。
Form1のデザインを開き、「NotifyIcon」を配置する。名前はデフォルト「notifyIcon1」のまま。
そしてプロパティを開き、Iconプロパティにプロジェクトフォルダ内のアイコンを指定する。
さらにTextプロパティは「MP3Player」とした。

■Form1.cs
		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);
			}


			ControlBox = false;		//タイトルバーを消す
			FormBorderStyle = FormBorderStyle.None;


			//フォームのドラッグでウインドウを移動する
			{
				bool _bMoving = false;
				Point _ptMouseDown = new Point(-1, -1);
				Point _ptMouseDownWindowPos = new Point(-1, -1);

				MouseDown += (sender, e) =>
				{
					if (e.Button != MouseButtons.Left)
						return;

					_ptMouseDown.X = e.X;
					_ptMouseDown.Y = e.Y;
					_ptMouseDown = PointToScreen(_ptMouseDown);
					_ptMouseDownWindowPos.X = this.Left;
					_ptMouseDownWindowPos.Y = this.Top;
					_bMoving = true;
				};

				MouseMove += (sender, e) =>
				{
					if (_bMoving)
					{
						Point tmp = new Point(e.X, e.Y);
						tmp = PointToScreen(tmp);

						this.Left = _ptMouseDownWindowPos.X - _ptMouseDown.X + tmp.X;
						this.Top = _ptMouseDownWindowPos.Y - _ptMouseDown.Y + tmp.Y;
					}
				};

				MouseUp += (sender, e) =>
				{
					_bMoving = false;
					_ptMouseDown.X = -1;
					_ptMouseDown.Y = -1;
				};
			}


			ShowInTaskbar = false;		//タスクバーから消す		//追加


			//以下を追加
			//タスクトレイアイコン右クリックメニューの設定
			{
				ContextMenuStrip menu = new ContextMenuStrip();

				ToolStripItem[] stripItem = new ToolStripMenuItem[]
				{
					new ToolStripMenuItem("表示/最小化", null, delegate
						{
							if (WindowState == FormWindowState.Normal)
								WindowState = FormWindowState.Minimized;
							else if (WindowState == FormWindowState.Minimized)
								WindowState = FormWindowState.Normal;
						}),
					new ToolStripMenuItem("終了", null, delegate
						{
							Close();
						}),
				};
				menu.Items.AddRange(stripItem);

				notifyIcon1.ContextMenuStrip = menu;
			}

			//以下を追加
			//タスクトレイアイコンのダブルクリック処理
			notifyIcon1.DoubleClick += delegate
			{
				if (WindowState == FormWindowState.Normal)
					WindowState = FormWindowState.Minimized;
				else if (WindowState == FormWindowState.Minimized)
					WindowState = FormWindowState.Normal;
			};


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

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

2016年03月12日

第33回 プログレスウインドウの不都合を解消する

以前「第29回 再生フォルダ読み込みをキャンセル可能にする」でプログレスウインドウを用意した。
今回はこのウインドウで不都合が生じる部分を修正する。

例えばキャンセルボタンではなくタイトルバーの「☓」ボタンからプログレスウインドウを閉じたり、
プログレスウインドウが開いているときにメインウインドウを終了したり、
プログレスウインドウが開いているときにさらに再生フォルダの変更を行ったり
した場合など、意図しない操作をするとすぐにフリーズする。
そのためそれらの操作を禁止する。

■ProgressDlg.cs
using System;
using System.Drawing;			//追加
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;

			//追加
			ControlBox = false;		//タイトルバーを消す
			FormBorderStyle = FormBorderStyle.FixedSingle;

			//追加
			//フォームのドラッグでウインドウを移動する
			{
				bool _bMoving = false;
				Point _ptMouseDown = new Point(-1, -1);
				Point _ptMouseDownWindowPos = new Point(-1, -1);

				MouseDown += (sender, e) =>
				{
					if (e.Button != MouseButtons.Left)
						return;

					_ptMouseDown.X = e.X;
					_ptMouseDown.Y = e.Y;
					_ptMouseDown = PointToScreen(_ptMouseDown);
					_ptMouseDownWindowPos.X = this.Left;
					_ptMouseDownWindowPos.Y = this.Top;
					_bMoving = true;
				};

				MouseMove += (sender, e) =>
				{
					if (_bMoving)
					{
						Point tmp = new Point(e.X, e.Y);
						tmp = PointToScreen(tmp);

						this.Left = _ptMouseDownWindowPos.X - _ptMouseDown.X + tmp.X;
						this.Top = _ptMouseDownWindowPos.Y - _ptMouseDown.Y + tmp.Y;
					}
				};

				MouseUp += (sender, e) =>
				{
					_bMoving = false;
					_ptMouseDown.X = -1;
					_ptMouseDown.Y = -1;
				};
			}
		}

		//以下を追加
		public void Init()
		{
			_bCancel = false;
		}
■Form1.cs
			//フォーム右クリックメニューの設定
			{
				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
						{
							//追加
							//読み込み中に再生フォルダ変更されるとやっかいだから読込中は設定不可に
							if (_dlgCreateFileList.Visible)
								return;

							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;
			}
			_option.FolderChanged += delegate
			{
				CreateFileListAndPlay(true);		//フォルダを読み込み、再生開始
			};
			_option.FolderChanged(null, null);		//フォルダを読み込み、再生開始


			//以下を追加
			FormClosing += (sender, e) =>
			{
				//ファイルリスト読み込み中なら終了キャンセル
				if (_dlgCreateFileList.Visible)
					e.Cancel = true;
			};
		}


		//移動/名前変更
		ProgressForm _dlgCreateFileList = new ProgressForm("再生フォルダの読み込み中");


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

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

			//移動
			//ProgressForm dlg = new ProgressForm("再生フォルダの読み込み中");
			_dlgCreateFileList.Init();		//追加
			_dlgCreateFileList.StartPosition = FormStartPosition.Manual;
			_dlgCreateFileList.Location = new Point(Location.X + 50, Location.Y + 50);
			_dlgCreateFileList.Show(this);

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

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

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

			thread.Start();
		}

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

第34回 データベース再構築をキャンセル可能にする

今回は曲情報を保管するためのデータベース再構築処理をスレッドに載せてキャンセル可能にします。

変更作業は「第29回 再生フォルダ読み込みをキャンセル可能にする」と同様です。
ついでに少しだけデータベースの処理を効率化させました。まだまだ無駄が多いですがその辺は放置。

■MP3Info.cs
		//追加
		/// <summary>
		/// DBにある情報の更新日時を取得
		/// 
		/// ZIPファイル向けにエントリーインデックスを指定せずに使えるようにしたもの
		/// </summary>
		static bool SQL_FindDateInfo(AudioItem item, out DateTime dtLastWrite)
		{
			//引数で渡された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;";
					cmd.Parameters.Add(new SQLiteParameter("@file", strFile));
					cmd.Prepare();

					using (SQLiteDataReader reader = cmd.ExecuteReader())
					{
						while (reader.Read())
						{
							try
							{
								dtLastWrite = new DateTime(reader.GetInt64(0));
								return true;
							}
							catch (Exception)
							{
							}
						}
					}
				}
			}
			catch (Exception)
			{
			}

			dtLastWrite = DateTime.MinValue;
			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;
				DateTime dtLastWriteDB = DateTime.MinValue;		//追加

				//DB中にデータがすでにあるかどうかチェック。あればIDと日時を取得
				using (SQLiteCommand cmd = _conn.CreateCommand())
				{
					cmd.CommandText = @"SELECT id, lastwritedate 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);
								dtLastWriteDB = new DateTime(reader.GetInt64(1));		//追加
								break;
							}
							catch (Exception)
							{
							}
						}
					}
				}

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

				//変更
				//IDが見つかった=データがある=古いなら更新
				if (dtLastWriteDB < dtLastWrite)
					return SQL_UpdateTagInfo(nID, dtLastWrite, strTitle, strArtist, strAlbum);
				else
					return true;		//更新の必要なし
			}
			catch (Exception)
			{
			}

			return false;
		}



		//変更
		/// <summary>
		/// DB情報の構築
		/// </summary>
		public static int RefreshAllInfo(bool bAllClear, ProgressForm dlg)		//変更
		{
			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)
			{
				//追加
				if (dlg != null)
				{
					if (dlg.bCancel)
						break;
					dlg.strProgress = "" + nCount;
				}

				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 dtLastWriteDB;		//追加
						DateTime dtLastWrite = File.GetLastWriteTime(strFile);

						//追加
						if (bAllClear == false)
						{
							SQL_FindDateInfo(new AudioItem(strFile), out dtLastWriteDB);
							if (dtLastWriteDB >= dtLastWrite)
								continue;		//情報更新の必要なし
						}

						using (ZipArchive archive = ZipFile.OpenRead(strFile))
						{
							for (int i = 0; i < archive.Entries.Count; i++)
							{
								//追加
								if (dlg != null)
								{
									if (dlg.bCancel)
										break;
									dlg.strProgress = "" + nCount;
								}

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

■Form1.cs
sing 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);
			//}
		ProgressForm _dlgCreateFileList = new ProgressForm("再生フォルダの読み込み中");


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

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

			_dlgCreateFileList.Init();
			_dlgCreateFileList.StartPosition = FormStartPosition.Manual;
			_dlgCreateFileList.Location = new Point(Location.X + 50, Location.Y + 50);
			_dlgCreateFileList.Show(this);

			Thread thread = new Thread(new ThreadStart(delegate
				{
					//追加
					{
						DialogResult ret = MessageBox.Show("ファイル読み込みだけでなく、DB更新もしますか?\n(時間がかかります)", "ファイル読み込み", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
						if (ret == DialogResult.OK)
						{
							//DB更新
							MP3Info.RefreshAllInfo(false, _dlgCreateFileList);
						}
					}

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

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

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

			thread.Start();
		}

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

第35回 ソースコードを整理する

今回はごちゃごちゃしているソースコードを一部整理。

Option.nPlayRatingStart/Endのようなネーミングが頭悪いプロパティやメンバ変数の名前を変更、
Form1のコンストラクタ内にあった右クリックメニュー、タスクトレイ処理などをInit_~()という形の関数化、
Form1のFormClosed、FormClosing、Shownなどの関数化
を行いました。
変更はForm1.csとOption.cs。

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


第36回 ツールチップを表示する

今回はウインドウのサイズを少し小さくする。
すると長いタイトルだと表示しきれないため、マウスポインターを運べばツールチップで表示されるようにする。

ついでにトラックバーはクリックしてもクリック位置に移動してくれず、使い勝手が悪いためその部分も修正した。

■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

		ToolTip _wndToolTip = new ToolTip();		//追加

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


			Init_FormDragMove();			//タイトルバーを消しフォームのドラッグで移動可能にする
			Init_TasktrayIcon();			//タスクトレイへの常駐化
			Init_ContextMenu();				//フォーム右クリックメニューの設定

			Init_MediaPlayer();				//WindowsMediaPlayer関連の初期化


			//以下を追加
			_wndToolTip.AutoPopDelay = 5000;		//表示される時間
			_wndToolTip.InitialDelay = 200;			//表示されるまでの時間
			_wndToolTip.ReshowDelay = 200;
			_wndToolTip.ShowAlways = true;


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



		/// <summary>
		/// WindowsMediaPlayer関連の初期化
		/// </summary>
		void Init_MediaPlayer()
		{
			//再生位置の手動シーク処理(シークバーの操作でシーク)
			trackBarSeek.ValueChanged += delegate
			{
				if (trackBarSeek.Focused)
				{
					_mediaPlayer.controls.currentPosition = (double)trackBarSeek.Value / 100;
				}
			};

			//以下を追加
			//トラックバーのクリック位置に応じてシークさせる
			trackBarSeek.MouseDown += (sender, e) =>
			{
				try
				{
					int nValue = trackBarSeek.Maximum * e.X / (trackBarSeek.Width - trackBarSeek.Margin.Horizontal);
					if (nValue < trackBarSeek.Minimum)
						nValue = trackBarSeek.Minimum;
					if (nValue > trackBarSeek.Maximum)
						nValue = trackBarSeek.Maximum;
					trackBarSeek.Value = nValue;
				}
				catch (Exception)
				{
				}
			};


			//音量の調節(トラックバーの操作で音量変更)
			_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;
			};


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

				//以下を追加
				//ラベルへツールチップス設定
				_wndToolTip.SetToolTip(labelTitle, strTitle);
				_wndToolTip.SetToolTip(labelArtist, strArtist);

				ShowRating();
			}
		}

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

2016年03月16日

第37回 レーティングに応じた重み付け再生する

今回はレーティングがついた曲の再生頻度を高める処理を追加する。

曲のレーティングだけなら再生する曲を決めるときに重み付けすればいいが、
アーティストのレーティングもあるとリアルタイムの重み付けは大変になりそうなので、
再生フォルダを読み込むときに重み付けデータ_listnRatingWeightItemを作成する形にした。
再生時にはGetNextIndex()でそれを加味して重み付けしている。

"重み"はGetRatingWeightPlayCount()で決め、今回は曲レーティングの5倍、アーティストレーティングの3倍で重み付けした。
(曲レーティング1なら5倍、曲レーティング5&アーティストレーティング5なら40倍の頻度で再生される)

■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> _listnRatingWeightItem = new List<int>();	//↑↑へのインデックス。レイティングで重み付けした数だけ格納		//追加


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


		bool _bStopButtonPushed = false;	//「停止」ボタンを押したらtrue
		/// <summary>
		/// 設定フォルダから音楽ファイルリストを読み込む
		/// </summary>
		void CreateFileListAndPlay(bool bPlay)
		{
			buttonStop_Click(null, null);		//再生停止

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

			_dlgCreateFileList.Init();
			_dlgCreateFileList.StartPosition = FormStartPosition.Manual;
			_dlgCreateFileList.Location = new Point(Location.X + 50, Location.Y + 50);
			_dlgCreateFileList.Show(this);

			Thread thread = new Thread(new ThreadStart(delegate
				{
					{
						DialogResult ret = MessageBox.Show("ファイル読み込みだけでなく、DB更新もしますか?\n(時間がかかります)", "ファイル読み込み", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
						if (ret == DialogResult.OK)
						{
							//DB更新
							MP3Info.RefreshAllInfo(false, _dlgCreateFileList);
						}
					}

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

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

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

			thread.Start();
		}
		/// <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 + _listnRatingWeightItem.Count);

					if (nIndex >= _listFiles.Count)
						nIndex = _listnRatingWeightItem[nIndex - _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;
		}



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

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

			//追加
			AudioItem item;
			string strTitle;
			string strArtist;
			string strAlbum;
			int nCount;

			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")
				{
					//追加
					item = new AudioItem(strFile);

					MP3Info.GetMp3Info(item, out strTitle, out strArtist, out strAlbum);
					if (_option.IsNGFile(item) || _option.IsNGArtist(strArtist) || _option.IsNGWord(strTitle))
						continue;			//NGなら処理しない

					//レーティングで重みづけた再生回数リスト化
					if (listnRatingWeightItem != null)
					{
						nCount = GetRatingWeightPlayCount(item, strTitle, strArtist, strAlbum);
						for (int j = 0; j < nCount; j++)
						{
							listnRatingWeightItem.Add(ret.Count);		//ret.Count == この曲へのインデックス
						}
					}

					ret.Add(item);		//変更
					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")
								{
									//追加
									item = new AudioItem(strFile, i);

									MP3Info.GetMp3Info(item, out strTitle, out strArtist, out strAlbum);
									if (_option.IsNGFile(item) || _option.IsNGArtist(strArtist) || _option.IsNGWord(strTitle))
										continue;			//NGなら処理しない

									//レーティングで重みづけた再生回数リスト化
									if (listnRatingWeightItem != null)
									{
										nCount = GetRatingWeightPlayCount(item, strTitle, strArtist, strAlbum);
										for (int j = 0; j < nCount; j++)
										{
											listnRatingWeightItem.Add(ret.Count);		//ret.Count == この曲へのインデックス
										}
									}

									ret.Add(item);		//変更
								}
							}
						}
					}
					catch (Exception)
					{
					}
				}

			}

			return ret;
		}



		//追加
		/// <summary>
		/// レーティングで重みづけた再生回数を決める
		/// </summary>
		int GetRatingWeightPlayCount(AudioItem item, string strTitle, string strArtist, string strAlbum)
		{
			int nRating = _option.GetRating(item);
			int nRatingArtist = _option.GetRatingArtist(strArtist);

			return (nRating * 5 + nRatingArtist * 3);
		}

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

前の10件 1  2  3  4





usefullcode@gmail.com

About 2016年03月

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

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

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

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