一文搞懂:【.NET与树莓派】用MPD制作数字音乐播放器

简介: 一文搞懂:【.NET与树莓派】用MPD制作数字音乐播放器

树莓派的日常家居玩法多多,制作一台属于自己的数字音乐播放机是其中的一种。严格上说,树莓派是没有声卡的,其板载的 3.5 mm 音频孔实际是通过 PWM 来实现音频输出的(通过算法让PWM信号变成模拟信号)。在 Pi 4 上输出的音质还算过得去,至少没有杂音(如果有杂音,俗称电流声,其实电流是没有声音的,只是供电电压的不稳定产生了模拟信号,并不幸地进入了喇叭使它发出莫名的响声),就是低音不够厚高音有点飘,不追求 HiFi 音质只是看看恐怖片的话是没问题的。


正是因为使用 PWM 产生音频信号,所以,如果 GPIO 上要用 PWM ,就不能使用音频了。当然,通过GPIO引脚也能输出音频,因为 PI 有两路 PWM 输出,正好,一路输出左声道,另一路输出右声道。但一般咱们不会这么玩,主要还是音质问题。你也可以直接买个 USB 声卡,也很方便,音质好不好取决于你剁手的能力。不过呢,若真想享受一下自己 DIY 播放机,最好还是买一块 I2S 解码板,树莓派是支持 I2S 协议的。在 /boot/config.txt 文件中,要加上这一句来开启:


dtparam=i2s=on


然后,还要配置 I2S 扩展板的类型,一般使用 HifiBerry DAC 即可,多数扩展板是兼容的。


dtoverlay=hifiberry-dac


最好把板载的音频也禁用(禁用后不仅 3.5 接口不能用,连 HDMI 也不能输出音频的)。


dtparam=audio=off


最后保存 config.txt。


树莓派的官方系统默认自带 ALSA 相关支持的,但是,如果你写 C++ 代码时要使用 asoundlib 的话,需要安装开发者专用的包。


sudo apt install libasound2-dev libasound2-doc


doc 是帮助文档,一起装上也方便。


执行 aplay -L 命令你就会看到 hifiberry DAC 了。


因为板载的音频被禁用,并且没有接入 USB 声卡,所以系统默认只能选择 I2S 扩展板。所以,你不需要再做其他配置,如果在执行一些播放命令时要选用声音设备(如 aplay、gmediarender 等),可以直接用 default 或 sysdefault 来引用。


-------------------------------------------------------------------------------------------------------------------


上面说了那么多,那咱们怎么打造私人播放机呢。其实,有现成的系统的,如 Volumio、MoOdeAudio 等。你要是懒得折腾,可以直接用,但是:


1、Volumio 的新版本 bug 相当地多,而且很不稳定。最可恨的是限制越来越多,你还得充值信仰开会员才能用。果断 PASS;


2、MoodeAudio 倒是做得不错,功能完整,无需充信仰。可是对老周来说感觉它功能太多了。


3、作为码农,老是要犯职业病的——为啥不自己开发一个呢,自己用的话,不用做太多的面子工程(比如界面美化),可以专注于功能,满足自己需求即可。


参考下一些数播系统的源代码,其实他们也是借助现有命令工具的,然后做个 Web 应用,在树莓派上以服务器角色运行,然后,你随便什么设备都能够通过浏览器来控制它。所以咱们用 ASP.NET 当然也可以做。你要是想练练手,不妨把 WPF 版本,Xamarin 版本也做做。


也可以考虑把控制逻辑以 Web API 的方式实现,这样客户端你将来想怎么扩展都行。


后台控制最简单的方法就是调用 aplay 命令,使用 Process 类 Start 一个进程,执行 aplay 命令,参数是要播放的音频文件,这实现起来很简单;缺点是管理功能不强,所以,很多开源的数播系统都选用 MPD。也就是 Music Player Daemon,说通俗一点,它就是一个后台运行的服务,客户端向它传递命令,以控制它的播放行为(可以播放指定曲目,可以暂停,可以停止播放器)。可以认为就是个命令方式驱动的音乐播放器,只是以 C / S 架构来运行。


执行下面的命令安装 MPD。


sudo apt install mpd mpc


mpd 是必须的,因为它是服务进程;mpc 是一个简单的客户端程序,以命令行方式使用,用 Socket 通信,可以在本机使用,也可以远程使用。


如果你不要这个简易客户端,那只安装 mpd 即可。


咱们就是自己编程来实现通信的,故而不用 mpc 也行。这个不难的,你会 TCP 编程就行,稍后老周会演示一个例子。


我们现在要做的是配置 mpd ,默认情况下 mpd 是不能正确运行的,咱们必段修改一下配置。配置文件位于 /etc 目录下,名为 mpd.conf。这个文件是面向整个系统配置的,可以跨用户,若要单个用户使用,可以在特定用户的 home 下面建个 .mpd 子目录,再把 mpd.conf 复制进去。但是,咱们是把树莓派做数播机器用的,没必要搞那么多用户配置,直接使用 /etc/mpd.conf 就行。接下来就是修改这个文件。


sudo nano /etc/mpd.conf


选项很多,上面也有注释。咱们只需关注这几个重要的就行。


1、配置音乐文件所存放的目录。


music_directory "/home/pi/music"


我是把音频文件放在 pi 用户下的music目录中,你可以按实际情况修改。


2、播放列表的存放目录。


playlist_directory "/home/pi/music/playlists"


为了省事,我直接在 music 目录下建一个新目录,命名为 playlists。


3、数据库文件的存放路径(完整路径,包括目录和文件名)


db_file "/home/pi/music/tag_cache"


也是图省事,直接放 music 目录下。文件名是 tag_cache。这个数据库用来保存歌曲的信息的,主要从音频文件的 TAG 标记中获取,就是我们平时查看文件属性时看到那些信息,比如曲目、标题、艺术家、专辑名称等。


由于编码问题,显示出来的是 。


4、日志文件的存放路径。


log_file "/home/pi/mpd/mpd.log"


5、存储进程ID的文件(pid 文件)


pid_file "/home/pi/mpd/pid"


这个最好改到 pi 目录下,这样不需要 root 权限就能读写,免去后期各种改权限的麻烦。


6、修改状态数据的文件路径。


state_file "/home/pi/mpd/state"


7、sticker file ,这个注释上说是存放为歌曲附加的动态信息用的,具体是啥玩意儿老周也不清楚。为了统一管理,为了减少后面出错,还是改一下吧。


sticker_file "/home/pi/mpd/sticker.sql"


8、修改运行用户。


user "pi"


这个改为 pi,没必要动不动就 root。


9、配置绑定的本机地址。


bind_to_address "any"


这个选项是必须改的,很重要,用来选定本地绑定的地址,给 TCP 服务器端侦听用的,这个就不必多解释了,折腾过 TCP 通信的话你都懂的。使用 any 表示绑定本机所有地址,如果你只想限制在本机访问,远程不允许连接,可以改为 127.0.0.1;如果你的 Pi 连接网络后有分配固定 IP 的话,可以改为相应的 IP,例如 192.168.0.125。


10、侦听端口。上面配置的是侦听地址,这里是端口,可以不改,默认 6600。


port "8855"


注释掉的话就是用默认值 6600。


11、自动更新数据库。这个还是设置为 yes 较好。


auto_update "yes"


这样一来,如果前面配置的音乐目录下的文件有变动,会自动更改数据库。


12、配置音频输出设备。此处配置 audio-output 节点。前面咱们都把板载音载禁用了,所以 pulse audio 就无法用了,只能选 ALSA 方案。


audio_output {


type "alsa"


name "My ALSA Device"


device "default" # optional


# mixer_type "hardware" # optional


# mixer_device "default" # optional


# mixer_control "PCM" # optional


# mixer_index "0" # optional


}


type 字段要设为 alsa,name 字段你可以随便起个名字;device 字段就是前面用 aplay -L 看到的 hifiberry dac 的设备名,因为现在它已成为系统默认选用的设备,所以用 default 就能引用,或者用 hw:0,1 也行。剩下那几个是可选的,不管它。


配置完后,按【ctrl + o】写入,【Ctrl + x】退出。


我们还要创建一下目录和文件,就是上面在配置文件中出现的几个目录和文件。


先切换到 home 目录。


cd ~


然后创建 music 目录以及子目录


mkdir -p music/playlists


加个 -p 参数是为了能一次性创建多级目录,毕竟咱们创建了 music 目录和 playlists 子目录。


现在,music 目录已经存在了,所以我们直接在它下面创建 tag_cache 文件,就是上面配置的数据库文件。不需要有数据,只要有这个文件就行了,这时候可以使用 touch 命令,具体用法网上随便一搜就有。这个命令本来是用来更新目录或文件的时间的,但它有个特点——如果文件不存在,会自动新建。


touch music/tag_cache


在 pi 的 home 下再建一个 mpd 目录(也是上面配置文件中提及的)。


mkdir mpd


同样的方法,用 touch 命令创建这些文件。


touch mpd/mpd.log


touch mpd/pid


touch mpd/state


touch mpd/sticker.sql


其实这里面只要创建 pid 和 state 这两个文件就行了,其他的 MPD 会自己创建,除非你运行服务时发现报错。


这一通配置之后,mpd 就能用了,重启一下服务,让它加载新的配置。


sudo systemctl restart mpd


执行一下这条命令,看看状态。


sudo systemctl status mpd


能看到绿油油的 running,那就好了。


要测试,你得有音频文件,mp3、wav、flac、ape 等格式的都行,mp3 和 aac 是有损文件,要 HiFi 的话最好 Pass 掉,WAV 和 Flac 都不错。I2S 扩展板一般都支持硬解码,包括 DTS ,也能直接播放。


准备的测试文件不要太少,起码有七、八个,这样才能感觉到效果。准备好文件后,通过 scp 命令上传到树莓派上,放到你前面配置的音乐文件目录中。


scp *.wav pi@192.168.0.106:/home/pi/music


第一个参数是把当前目录下所有 WAV 文件上传;第二个参数是树莓派上的存放路径,这里可以敲上绝对路径,也可以用 ~ 来代替 home 目录,即 pi@192.168.0.106:~/music。


回到树莓派的终端,cd 到 music 目录下,ls 一下,就会看到音频文件了。


------------------------------------------------------------------------------------------------------


好了,基本的测试条件已满足,下面老周再告诉你怎么通过编程来控制 MPD。


控制方式:通过 TCP 协议直接发送命令文本,每条命令末尾要有换行符(\n)。比如,要让 MPD 播放音乐,先 connect 服务器,然后发送“play\n”。通信方式有点像串口交互。


这样的控制方式是不是很好弄?那具体我们能用啥命令呢?如果你在 apt install 时有安装 mpc 的话,你可以在终端中输入:


mpc help


然后你就会看到所有命令了。比如,要列出音乐目录下的所有文件,可以这样执行:


mpc -h 192.168.0.106 listall


-h 参数指定的是服务器(MPD运行的主机)地址(主机名或IP地址均可),listall 就是命令了。


再例如,要停止播放音乐,执行:


mpc -h localhost stop


如果我们要自己编程呢,那就


1、Connect HOST;


2、发送文本 stop \n;


3、要是没别的事,最好关闭连接,等需要时再连接。


总结起来就是:我们只要以文本格式发送命令部分即可。例如,mpc -h localhost play,那么我们的代码只发送 play 就行了,不要带 mpc -h XXXX。注意最后有换行符。


查看有效命令的另一个方法是查看 MPD 的源代码(C++),在 /src/command/AllCommands.cxx 文件中。


static constexpr struct command commands【】 = {


{ "add", PERMISSION_ADD, 1, 2, handle_add },


{ "addid", PERMISSION_ADD, 1, 2, handle_addid },


{ "addtagid", PERMISSION_ADD, 3, 3, handle_addtagid },


{ "albumart", PERMISSION_READ, 2, 2, handle_album_art },


{ "binarylimit", PERMISSION_NONE, 1, 1, handle_binary_limit },


{ "channels", PERMISSION_READ, 0, 0, handle_channels },


{ "clear", PERMISSION_PLAYER, 0, 0, handle_clear },


{ "clearerror", PERMISSION_PLAYER, 0, 0, handle_clearerror },


{ "cleartagid", PERMISSION_ADD, 1, 2, handle_cleartagid },


{ "close", PERMISSION_NONE, -1, -1, handle_close },


{ "commands", PERMISSION_NONE, 0, 0, handle_commands },


{ "config", PERMISSION_ADMIN, 0, 0, handle_config },


{ "consume", PERMISSION_PLAYER, 1, 1, handle_consume },


#ifdef ENABLE_DATABASE


{ "count", PERMISSION_READ, 1, -1, handle_count },


#endif


{ "crossfade", PERMISSION_PLAYER, 1, 1, handle_crossfade },


{ "currentsong", PERMISSION_READ, 0, 0, handle_currentsong },


{ "decoders", PERMISSION_READ, 0, 0, handle_decoders },


{ "delete", PERMISSION_PLAYER, 1, 1, handle_delete },


{ "deleteid", PERMISSION_PLAYER, 1, 1, handle_deleteid },


{ "delpartition", PERMISSION_ADMIN, 1, 1, handle_delpartition },


{ "disableoutput", PERMISSION_ADMIN, 1, 1, handle_disableoutput },


{ "enableoutput", PERMISSION_ADMIN, 1, 1, handle_enableoutput },


#ifdef ENABLE_DATABASE


{ "find", PERMISSION_READ, 1, -1, handle_find },


{ "findadd", PERMISSION_ADD, 1, -1, handle_findadd},


#endif


#ifdef ENABLE_CHROMAPRINT


{ "getfingerprint", PERMISSION_READ, 1, 1, handle_getfingerprint },


#endif


{ "getvol", PERMISSION_READ, 0, 0, handle_getvol },


{ "idle", PERMISSION_READ, 0, -1, handle_idle },


{ "kill", PERMISSION_ADMIN, -1, -1, handle_kill },


#ifdef ENABLE_DATABASE


{ "list", PERMISSION_READ, 1, -1, handle_list },


{ "listall", PERMISSION_READ, 0, 1, handle_listall },


{ "listallinfo", PERMISSION_READ, 0, 1, handle_listallinfo },


#endif


{ "listfiles", PERMISSION_READ, 0, 1, handle_listfiles },


#ifdef ENABLE_DATABASE


{ "listmounts", PERMISSION_READ, 0, 0, handle_listmounts },


#endif


#ifdef ENABLE_NEIGHBOR_PLUGINS


{ "listneighbors", PERMISSION_READ, 0, 0, handle_listneighbors },


#endif


{ "listpartitions", PERMISSION_READ, 0, 0, handle_listpartitions },


{ "listplaylist", PERMISSION_READ, 1, 1, handle_listplaylist },


{ "listplaylistinfo", PERMISSION_READ, 1, 1, handle_listplaylistinfo },


{ "listplaylists", PERMISSION_READ, 0, 0, handle_listplaylists },


{ "load", PERMISSION_ADD, 1, 3, handle_load },


{ "lsinfo", PERMISSION_READ, 0, 1, handle_lsinfo },


{ "mixrampdb", PERMISSION_PLAYER, 1, 1, handle_mixrampdb },


{ "mixrampdelay", PERMISSION_PLAYER, 1, 1, handle_mixrampdelay },


#ifdef ENABLE_DATABASE


{ "mount", PERMISSION_ADMIN, 2, 2, handle_mount },


#endif


{ "move", PERMISSION_PLAYER, 2, 2, handle_move },


{ "moveid", PERMISSION_PLAYER, 2, 2, handle_moveid },


{ "moveoutput", PERMISSION_ADMIN, 1, 1, handle_moveoutput },


{ "newpartition", PERMISSION_ADMIN, 1, 1, handle_newpartition },


{ "next", PERMISSION_PLAYER, 0, 0, handle_next },


{ "notcommands", PERMISSION_NONE, 0, 0, handle_not_commands },


{ "outputs", PERMISSION_READ, 0, 0, handle_devices },


{ "outputset", PERMISSION_ADMIN, 3, 3, handle_outputset },


//代码效果参考:http://hnjlyzjd.com/xl/wz_25494.html

{ "partition", PERMISSION_READ, 1, 1, handle_partition },

{ "password", PERMISSION_NONE, 1, 1, handle_password },


{ "pause", PERMISSION_PLAYER, 0, 1, handle_pause },


{ <span style="color: rg

相关文章
一起谈.NET技术,Silverlight实例教程 - Out of Browser音乐播放器
  Silverlight 实例教程索引 Silverlight 实例教程 - Out of Browser开篇 Silverlight 实例教程 - Out of Browser配置,安装和卸载 Silverlight 实例教程 - Out of Browser的自定义应用 Silverligh...
1057 0
|
XML 监控 安全
一起谈.NET技术,使用 Silverlight Media Framework 构建自定义播放器
流媒体已在网络上无处不在。似乎所有人(从新闻站点到社交网络再到隔壁的邻居)都在享受在线视频体验。由于用户群的不断攀升,大多数站点需要以一种值得信赖、用户友好的方式为其客户提供高品质视频,通常为高品质带宽感知 视频。
1121 0
VB.net_音乐播放器
2015年11月25日21:51:50网名:浩秦; 邮箱:root#landv.pw; 只要我能控制一個國家的貨幣發行,我不在乎誰制定法律。金錢一旦作響,壞話隨之戛然而止。
947 0
|
API 开发工具 C++
.NET中使用APlayer组件自制播放器
原文:.NET中使用APlayer组件自制播放器 目录 说明 APlayer介绍 APlayer具备功能 APlayer使用 自制播放器Demo 未完成工作 源码下载   说明 由于需求原因,需要在项目中(桌面程序)集成一个在线播放视频的功能。
1308 0
|
安全 前端开发 .NET
ASP.NET MVC 音乐商店 - 7.成员管理和授权
转自http://www.cnblogs.com/haogj/archive/2011/11/18/2253140.html   目前,我们的 Store Manager 可以被任何人访问,让我们限制一下对站点管理的访问。
1086 0
|
JSON 前端开发 JavaScript
ASP.NET MVC 音乐商店 - 8. 使用 Ajax 更新的购物车
转自http://www.cnblogs.com/haogj/archive/2011/11/20/2255515.html   在这个项目中,我们将允许用户在没有注册登录的情况下将专辑加入购物车,但是,在完成结账的时候必须完成注册工作。
1133 0
|
前端开发 JavaScript .NET
ASP.NET MVC 音乐商店 - 9. 注册和结账
转自http://www.cnblogs.com/haogj/archive/2011/11/20/2255675.html   在这一节,我们将创建结账的控制器 CheckoutController 来收集用户的地址和付款信息,我们需要用户在结账前注册账户,因为这个控制器需要授权。
1096 0
|
前端开发 .NET 开发框架
ASP.NET MVC 音乐商店 - 10. 完成导航和站点的设计
转自 http://www.cnblogs.com/haogj/archive/2011/11/20/2255680.html 我们已经完成了网站的大部分工作,但是,还有一些添加到站点的导航功能,主页,以及商店的浏览页面。
1036 0
|
前端开发 .NET
ASP.NET MVC 音乐商店 - 0 概览
转自 http://www.cnblogs.com/haogj/archive/2011/11/08/2241710.html 这是一个系列文章,原文内容出自微软的 MusicStore。 首先对原文内容进行了简单的翻译,以方便大家参考,另外对于其中的部分内容,也进行了简单的分析,使用的 Visual Studio 也换成了中文版,这样大家看起来也更亲切一些。
1016 0
|
Web App开发 前端开发 .NET
ASP.NET MVC 音乐商店 - 1 创建项目
转自http://www.cnblogs.com/haogj/archive/2011/11/09/2241817.html 我们的项目从在 Visual Studio 中的文件菜单中选择“新建”,选择“项目”开始。
1009 0