第12回 ZIP書庫内の曲を再生する準備
前回までにmp3/m4aファイルに限ればほぼ十分な機能を実装できました。
今回はzip書庫内に含まれるmp3/m4aファイルを再生する準備作業を行います。
これまではファイルの管理はファイル名(string)のみで行っていました。
これだとzip書庫を扱うのに都合が悪いため、int情報を追加したAudioItemクラスを作成してそのクラスで管理することにします。
int情報はzip書庫内のファイル位置(エントリーインデックス)を格納するのに利用します。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using WMPLib; namespace MP3Player { public partial class Form1 : Form { WindowsMediaPlayer _mediaPlayer = new WindowsMediaPlayer(); Timer _timer = new Timer(); //追加 class AudioItem { public string strFile = ""; public int nEntryIndex = -1; public AudioItem(string strFile, int nEntryIndex = -1) { this.strFile = strFile; this.nEntryIndex = nEntryIndex; } } 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(); _listFiles = EnumFiles(@"G:\Desktop\mp3\"); //再生したいmp3保存フォルダを指定 //変更 //フォームが表示されたときの処理 Shown += delegate { //シークバーにフォーカスがあると、再生位置が表示されないのでここで再生ボタンにフォーカスを与える buttonPlay.Focus(); }; //再生位置のシーク trackBarSeek.ValueChanged += delegate { if (trackBarSeek.Focused) { _mediaPlayer.controls.currentPosition = (double)trackBarSeek.Value / 100; } }; //音量の調節 _mediaPlayer.settings.volume = 20; trackBarVolume.Maximum = 100; //音量はゼロから100 trackBarVolume.Value = _mediaPlayer.settings.volume; trackBarVolume.ValueChanged += delegate { _mediaPlayer.settings.volume = (int)trackBarVolume.Value; }; _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 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); //一番最新の履歴曲を削除 } _mediaPlayer.URL = _listFiles[_nCurrentIndex].strFile; //最初の再生曲をセット //変更 _mediaPlayer.controls.play(); //曲名/タイトルの表示 { string strTitle; string strArtist; string strAlbum; GetMp3Info(_listFiles[_nCurrentIndex].strFile, out strTitle, out strArtist, out strAlbum); //変更 labelTitle.Text = strTitle; labelArtist.Text = strArtist; } } /// <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; break; } return nIndex; } /// <summary> /// フォルダ内のファイルを一覧して返す /// </summary> List<AudioItem> EnumFiles(string strFolder) //変更 { List<AudioItem> ret = new List<AudioItem>(); //変更 //指定フォルダ以下の全子フォルダから全ファイルを抜き出す IEnumerable<string> listFiles = Directory.EnumerateFiles(strFolder, "*.*", SearchOption.AllDirectories); foreach (string strFile in listFiles) { //見つかったファイルの拡張子を取り出し string strExt = Path.GetExtension(strFile).ToLower(); if (strExt == "") continue; //mp3/m4a以外なら無視 if (strExt != ".mp3" && strExt != ".m4a") continue; ret.Add(new AudioItem(strFile)); //変更 } return ret; } private void buttonPlay_Click(object sender, EventArgs e) { if (_mediaPlayer.playState == WMPPlayState.wmppsPlaying) _mediaPlayer.controls.pause(); else if (_nCurrentIndex >= 0) _mediaPlayer.controls.play(); } private void buttonStop_Click(object sender, EventArgs e) { if (_mediaPlayer.playState == WMPPlayState.wmppsPlaying || _mediaPlayer.playState == WMPPlayState.wmppsPaused) { _bStopButtonPushed = true; _mediaPlayer.controls.stop(); } } private void buttonNext_Click(object sender, EventArgs e) { Play(); } private void buttonBack_Click(object sender, EventArgs e) { Play(false); //前の曲を再生 } /// <summary> /// MP3ファイルからの曲名情報取得 /// /// TagLib# Portable利用 /// https://github.com/timheuer/taglib-sharp-portable /// </summary> public 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; } } } } }