重做了一下我的音乐播放器

前些天看到新闻说Windows10自带的Windows Media Player将支持FLAC无损播放,而目前自己的播放器是采用BASS音频库去支持无损的播放的,BASS音频库的用法奇葩不说,文档也不多,遇到问题网上搜半天都找不着答案真是愁死,于是打算将我的音乐播放器重做一番了,这次重新用回WMP内核。

相信做过WPF媒体方面应用开发的同学都知道,new一个MediaPlayer就可以实现对媒体的操作了,而这个MediaPlayer其实是用的当前操作系统中的Windows Media Player,支持播放暂停停止,播放速率调节,音量设置(BASS的音量设置很蛋疼,会连系统音量都给调了),提供媒体打开,媒体结束,播放错误3个事件,因而如果要自己做一个音乐播放器的话,再加上媒体状态的判断,以及添加媒体状态/播放进度改变的事件就差不多了。因为是重做的,所以只实现了一下基础功能(播放控制,收藏,播放模式)以及几个个性功能(音乐统计,定时停止,播放进度的显示),同时BUG还很多,还在继续开发中,如果有兴趣的话可以一起开发哟,项目已经传到TFS了,地址是http://llyn23.visualstudio.com

Debug目录文件下载:http://yun.baidu.com/s/1xYWua

Solution项目源代码下载:http://yun.baidu.com/s/1mguWDCG

1.构建CoreHelper.cs

这个播放器核心类包含媒体的播放,添加取消收藏,切换播放模式,歌曲文件的读取,媒体库/播放列表/播放器配置等的保存等

2.数据的保存和读取

在程序启动(OnStartUp)时加载XML文件反序列化为对象,保存有媒体库,播放列表,播放记录,收藏的歌曲等,退出(OnExit)时则根据对象是否发生改变,将对象序列化到XML文件中

            string repository = System.IO.Path.Combine(new string[] { AppDomain.CurrentDomain.BaseDirectory, "Repository" });
            if (!System.IO.Directory.Exists(repository))
            {
                return;
            }

            //加载播放器设置
            PlayerConfig = SerializeHelper.ToEntity<PlayerConfigModel>(repository + "\\PlayerConfig.xml");

            //加载媒体库
            MediaCollection = SerializeHelper.ToEntity<List<MediaModel>>(repository + "\\MediaCollection.xml");

            //加载播放列表
            Playlists = SerializeHelper.ToEntity<List<PlaylistModel>>(repository + "\\Playlists.xml");

            //加载播放队列
            PlayQueues = SerializeHelper.ToEntity<List<PlayQueueModel>>(repository + "\\PlayQueues.xml");

            //加载播放记录
            PlayRecords = SerializeHelper.ToEntity<List<PlayRecordModel>>(repository + "\\PlayRecords.xml");

            //加载收藏列表
            Favorites = SerializeHelper.ToEntity<List<FavoriteModel>>(repository + "\\Favorites.xml");

            //初始化计数器
            AutoStopCounter = PlayerConfig.AutoStopTimeset;
            string repository = System.IO.Path.Combine(new string[] { AppDomain.CurrentDomain.BaseDirectory, "Repository" });
            if (!System.IO.Directory.Exists(repository))
            {
                try
                {
                    System.IO.Directory.CreateDirectory(repository);
                }
                catch (Exception ex)
                {
                    LogHelper.Error(ex.Message);
                    return;
                }
            }

            //保存播放器设置
            if (IsPlayerConfigChanged)
            {
                SerializeHelper.ToXml<PlayerConfigModel>(PlayerConfig, repository + "\\PlayerConfig.xml");
            }

            //保存媒体库
            if (IsMediaCollectionChanged)
            {
                SerializeHelper.ToXml<List<MediaModel>>(MediaCollection, repository + "\\MediaCollection.xml");
            }

2.样式和资源使用方面

界面元素的背景图片和颜色,字体颜色和字体大小全部存放在Application的资源字典中,可以通过代码动态更新

        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Resources/Color.xaml"></ResourceDictionary>
                <ResourceDictionary Source="/Resources/ImageBrush.xaml"></ResourceDictionary>
                <ResourceDictionary Source="/Resources/LinearGradientBrush.xaml"></ResourceDictionary>
                <ResourceDictionary Source="/Resources/SolidBrush.xaml"></ResourceDictionary>
                <ResourceDictionary Source="/Styles/Border.xaml"></ResourceDictionary>
                <ResourceDictionary Source="/Styles/TextBlock.xaml"></ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>

3.播放控制核心部分

其实就是处理MediaPlayer的4个事件,MediaOpened,MediaFailed,MediaEnded,以及自己加的事件MediaPositionChanging

        private static void _mainPlayer_MediaOpened(object sender, EventArgs e)
        {
            //设置当前媒体
            CurrentMedia = MediaCollection.Where(w => w.FullName == HttpUtility.UrlDecode(MainPlayer.Source.AbsolutePath).Replace("/", "\\")).FirstOrDefault();
        }
        private static void _mainPlayer_MediaFailed(object sender, ExceptionEventArgs e)
        {
            PlayerStatus = PlayerStatusEnum.已停止;
            LogHelper.Error(e.ErrorException.Message);
        }
        private static void _mainPlayer_MediaEnded(object sender, EventArgs e)
        {
            //添加播放记录
            IsPlayRecordsChanged = true;
            PlayRecords.Add(new PlayRecordModel
            {
                CreatedAt = DateTime.Now,
                Media = CurrentMedia.FullName
            });

            //设置播放器状态
            PlayerStatus = PlayerStatusEnum.已停止;

            //根据播放模式来决定接下要播放的歌曲
            switch (PlayerConfig.PlayMode)
            {
                case (int)PlayModeEnum.单曲循环:
                    {
                        PlayByRepeat();
                        break;
                    }
                case (int)PlayModeEnum.列表循环:
                    {
                        PlayByRecycle();
                        break;
                    }
                case (int)PlayModeEnum.顺序播放:
                    {
                        PlayByOrder();
                        break;
                    }
                case (int)PlayModeEnum.随机播放:
                    {
                        PlayByRandom();
                        break;
                    }
            }
        }
        //CoreHelper.cs中触发媒体播放进度改变事件
        private static void _mainTimer1_Tick()
        {
            if (IsAutoStopOpened && AutoStopCounter <= 0)
            {
                //停止播放(跨线程处理)
                MainPlayer.Dispatcher.Invoke(new Action(() =>
                    {
                        Stop();
                    }));

                AutoStopCounter = PlayerConfig.AutoStopTimeset;
                IsAutoStopOpened = false;
                return;
            }

            if (PlayerStatus == PlayerStatusEnum.播放中)
            {
                AutoStopCounter -= 1;
                MediaPositionChanging();
            }
        }

        //在UI线程中处理媒体播放进度事件
        CoreHelper.MediaPositionChanging += CoreHelper_MediaPositionChanging;

        private void CoreHelper_MediaPositionChanging()
        {
            this.Dispatcher.Invoke(new Action(() =>
            {
                DisplayAutoStop();

                TimeSpan duration = TimeSpan.FromSeconds(0);
                if (CoreHelper.MainPlayer.NaturalDuration.HasTimeSpan)
                {
                    duration = CoreHelper.MainPlayer.NaturalDuration.TimeSpan;
                }

                double percent = 0;
                if (duration.TotalSeconds > 0)
                {
                    percent = CoreHelper.MainPlayer.Position.TotalSeconds / duration.TotalSeconds;
                }

                //显示媒体播放进度背景
                gs2.Offset = percent;
                gs3.Offset = percent;

                //显示媒体播放进度和总持续时间文本
                _mediaPositionTextBlock.Text = String.Format("{0}/{1}", StringHelper.GetTimeSpanString(CoreHelper.MainPlayer.Position), StringHelper.GetTimeSpanString(duration));
            }));
        }

5.更新媒体库

这里主要涉及到从文件中读取歌曲信息,所以一个步骤是递归读取文件夹中的文件,第二步骤是读取歌曲信息(通过第三方类库ID3.dll,TagLib.dll),第三步骤是在UI上实时读取状态

        public static void FindMatchedFile(string folder, List<string> extensions, double minFileLength, double maxFileLength, UpdateMediaCollectionEventHandler callback)
        {
            string[] files = System.IO.Directory.GetFiles(folder, "*.*", SearchOption.TopDirectoryOnly);

            FileInfo info = null;
            foreach (string file in files)
            {
                info = new FileInfo(file);
                if (!extensions.Contains(System.IO.Path.GetExtension(file).ToLower()))
                {
                    continue;
                }

                if (info.Length < minFileLength)
                {
                    continue;
                }

                if (info.Length > maxFileLength)
                {
                    continue;
                }

                //通过委托去通知前端UI
                callback(file, AddToMediaCollection(info));
            }

            string[] directories = System.IO.Directory.GetDirectories(folder, "", SearchOption.TopDirectoryOnly);
            foreach (string directory in directories)
            {
                FindMatchedFile(directory, extensions, minFileLength, maxFileLength, callback);
            }
        }
        public static int AddToMediaCollection(FileInfo file)
        {
            ID3Info id3 = null;
            try
            {
                id3 = new ID3Info(file.FullName, true);
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex.Message);
                return -1;
            }

            MediaModel media = new MediaModel();

            //创建的时间
            media.CreatedTime = DateTime.Now;

            //文件长度
            media.FileLength = file.Length;

            //文件全路径
            media.FullName = file.FullName;

            //歌曲的专辑
            media.Album = id3.ID3v2Info.GetTextFrame("TALB") ?? "未知专辑";

            //歌曲的艺术家
            media.Artists = id3.ID3v2Info.GetTextFrame("TPE1") ?? "未知歌手";

            //歌曲的标题
            media.Title = id3.ID3v2Info.GetTextFrame("TIT2") ?? System.IO.Path.GetFileNameWithoutExtension(file.FullName);

            TagLib.File f = null;
            try
            {
                f = TagLib.File.Create(file.FullName);

                //歌曲的持续时间
                media.Duration = f.Properties.Duration.TotalSeconds;

                //保存歌曲封面
                if (f.Tag.Pictures.Length > 0)
                {
                    string folder = System.IO.Path.Combine(new string[] { AppDomain.CurrentDomain.BaseDirectory, "Repository", "Cover" });
                    if (!System.IO.Directory.Exists(folder))
                    {
                        System.IO.Directory.CreateDirectory(folder);
                    }

                    byte[] bytes = f.Tag.Pictures[0].Data.Data;

                    string cover = System.IO.Path.Combine(new string[] { folder, StringHelper.RemoveSpecialCharacters(media.Album) + ".jpg" });
                    using (FileStream fs = new FileStream(cover, FileMode.Create))
                    {
                        fs.Write(bytes, 0, bytes.Length);
                    }
                }
            }
            catch (Exception ex)
            {
                LogHelper.Debug(ex.Message);
            }

            MediaModel model = MediaCollection.Where(w => w.FullName == file.FullName).FirstOrDefault();
            if (model == null || String.IsNullOrEmpty(model.FullName))
            {
                MediaCollection.Add(media);
                LogHelper.Debug("添加了一个媒体:" + file.FullName);
                return 1;
            }

            //更新媒体信息
            model.Album = media.Album;
            model.Artists = media.Artists;
            model.Duration = media.Duration;
            model.FileLength = media.FileLength;
            model.Title = media.Title;

            LogHelper.Debug("更新了一个媒体:" + file.FullName);
            return 0;
        }
        private List<int> _operations = null;

        private void UpdateMediaCollectionTextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            UpdateMediaCollectionTextBlock.IsEnabled = false;
            ProcessingMediaTextBlock.Visibility = Visibility.Visible;
            _operations = new List<int>();

            //开启线程更新媒体库
            BackgroundWorker worker = new BackgroundWorker();
            worker.DoWork += worker_DoWork;
            worker.RunWorkerCompleted += worker_RunWorkerCompleted;
            worker.RunWorkerAsync();
        }

        private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            UpdateMediaCollectionTextBlock.IsEnabled = true;
            ProcessingMediaTextBlock.Visibility = Visibility.Collapsed;

            int succeed = _operations.Count(c => c == 1);
            int updated = _operations.Count(c => c == 0);
            int failed = _operations.Count(c => c < 0);

            if (succeed > 0 || updated > 0)
            {
                CoreHelper.IsMediaCollectionChanged = true;
            }

            UiHelper.ShowPrompt(String.Format("新增:{0}个,更新:{1}个,失败:{2}个", succeed, updated, failed));
            _operations = null;
        }
时间: 2024-11-19 13:37:25

重做了一下我的音乐播放器的相关文章

Android实现音乐播放器(一)

Graphical User Interface 本篇文章记录了我实现Android简单音乐播放器的过程,(一)中介绍了怎么构建音乐播放器的前端页面.首先大家看一下,界面最后是这样的(界面有点粗陋) 音乐文件列表是从SD卡中读取出来的. 首先我们先在Layout里面创建Music Player的GUI,diagram如下图所示: 根据diagram我们在activity_main.xml写出如下代码: <LinearLayout xmlns:android="http://schemas.

手把手教你做音乐播放器(八)桌面小工具(上)

第8节 桌面小工具 桌面小工具是可以放置在主界面的.快速控制应用的小助手.例如我们的音乐小工具,它可以帮助用户在桌面上就完成音乐的暂停.播放.切换等操作,而不需要启动应用本身. 在安卓系统中,我们也常常叫它App widget. 实现一个App widget要经过以下几个步骤, 创建一个App widget类,让它继承自AppWidgetProvider,例如AnddleMusicAppWidget类: 放在res\layout目录下,为App widget的界面定义一个布局,例如anddle_

毕业设计——基于STM32的音乐播放器设计(一)

基于STM32的音乐播放器设计, 源代码下载地址:http://download.csdn.net/detail/cxp2205455256/8334021      SD卡文件下载地址:http://download.csdn.net/detail/cxp2205455256/8334089 电路图下载地址:文件太大了,上传不了....... 以下是截图: 1.硬件电路 2.软件主界面 3.音乐播放器界面 4.音乐定时播放界面 5.音乐列表界面 6.日历功能界面 9.温度功能界面 10.计算器

Xamarin.Android开发音乐播放器

最近.Net开源着实让C#火了一把,好久就听说Mono for Android一直没静下心来看,上周末找来看看,确实不错,前台界面axml编写跟Java安卓开发毫无区别,后台用C#其实很多windows下的方法都可以用,做一个安卓音乐播放器,主要实现音乐播放,上一曲下一曲,音乐播放列表,随机播放,扫描SD卡内所有音乐. 先show一组界面啊,话说有图有真相啊: 项目结构如图: Lrc是歌词处理方法 MusicService是安卓服务播放音乐,服务Broadcast,以及界面接收Receiver

html网页音乐播放器自带播放列表

基于网页的音乐播放器demo  http://pan.baidu.com/s/1dDgm7HR 自己diy了一个手机端在线音乐播放器演示地址http://shanxi2014.com/zhuandizhi/dom.php 全面支持手机端浏览器. 主要修改引用路径(不要修改文件相对存放地址) 第二就是正则拼接参数了 <script src="/city/js/libs/jquery-1.10.2.min.js"></script> <script src=&

【源码分享】mui实现简单的手机音乐播放器

mui实现简单的手机音乐播放器 最近先来无事,我用mui写了一个可以跨页面控制的音乐播放器.主要功能有上一曲,下一曲,播放,暂停,感兴趣的可以继续看下去. 说的总是不实在,直接上源码,有兴趣的可以读下注释. 1首页代码 ①首页的html代码 1 <a> 2 <i id="bofang" class="iconfont icon-play-o"></i> <!--就是一个播放按钮没啥特殊的--> 3 </a>

HTML5项目笔记4:使用Audio API设计绚丽的HTML5音乐播放器

HTML5 有两个很炫的元素,就是Audio和 Video,可以用他们在页面上创建音频播放器和视频播放器,制作一些效果很不错的应用. 无论是视屏还是音频,都是一个容器文件,包含了一些音频轨道,视频轨道和一些元数据,这些是和你的视频或者音频控件绑定到一块的,这样才形成了一个完整的播放组件. 浏览器支持情况: 浏览器 支持情况 编解码器 Chrome 3.0 Theora . Vorbis .Ogg H.264 . AAC .MPEG4 FireFox 3.5 Theora . Vorbis .Og

web音乐播放器

今天闲暇时间,花了2小时,写了个简单音乐播放器.欢迎大家来吐糟 先看下界面截图 大体实现:播放,停止,上一曲,下一曲,循环播放功能. 知识点:1.html 中audio 2.css 位置fixed 其中audio用到的方法:1.play 2.pause ,属性:1.src 2.loop 对于audio不熟悉的移步到http://www.w3school.com.cn/jsref/dom_obj_audio.asp 其中数据部分是在网上找了的保存在listrings.js中 下边关键时刻代码来了

用PHP+H5+Boostrap做简单的音乐播放器(进阶版)

前言:之前做了一个音乐播放器(纯前端),意外的受欢迎,然后有人建议我把后台一起做了,正好也想学习后台,所以学了两天php(不要吐槽我的速度,慢工出细活嘛~)然后在之前的基础上也又完善了一些功能,所以这个Demo比之前的可以算是进阶呢~v2.0哈哈哈~感觉截图体验很不好呢,所以在美图秀秀上面做了简易的动图,大家感受感受 正文: 老规矩,先上图~感觉有点卡,愿意等的就等等嘛,不愿意等的,往下看,有图片讲解 功能实现: 1.点击音乐列表播放音乐 2.拖动或点击进度条,调节音乐播放进度 3.浮动到音量控