用树莓派(等)为 USB Midi 键盘增添连接方式

我在去年买了一个 M-Audio 的 Midi 键盘,用来连接电脑或者 iPad 弹琴。但是由于琴摆放的位置没有办法拉充电线,所以我能弹琴多久很大程度上取决于设备还有多少电。前一阵子从朋友手里白嫖了个橘子派,我就考虑用这个板子给我的 Midi 键盘做个无线连接的接口,而且还安装了 FluidSynth 让设备可以直接发出声音让我带耳机练琴。

这篇文章中我首先会介绍 FluidSynth 的配置,再介绍如何通过 Midi over BLE 广播信号,最后介绍如何通过 Midi over Wifi 来广播信号。你可以选择性的看需要的部分。

系统配置

由于我使用的是橘子派而不是树莓派,我遇到了一些只有这边才会遇到的问题,其他开发板可以选择性掠过本节。

橘子派能安装的最新版本 Armbian 内核关闭了 Midi 功能,为了能够使用,我重新编译了内核。(在编译选项中开启:Device Drivers > Sound card support > Advanced Linux Sound Architecture > Sequencer Support )

橘子派还需要在系统中启用声音,使用自带的配置工具 sudo armbian-config,启用 System > Hardware > Toggle hardware configuration > analog-codec 。

由于橘子派会等待网络服务激活后才进入系统,导致开机时间非常长,使用下面的命令关闭它:

sudo systemctl disable NetworkManager-wait-online
复制代码

你还可以使用 systemd-analyze blame 命令查看服务的启动时间,关闭一些其他你不需要的服务。

FluidSynth

安装

Debian 源中自带的 FluidSynth 版本比较低,是 1.x 的。由于旧版本的 FluidSynth 并不能自动连接 Midi 设备,我们需要手动编译新版。

为了安装依赖,我们先要调整 apt 源,在文件中取消几个 deb-src 源前面的注释。

sudo nano /etc/apt/sources.list
sudo apt-get update
复制代码

安装所有需要的依赖包然后编译。

sudo apt-get build-dep fluidsynth --no-install-recommends
git clone https://github.com/FluidSynth/fluidsynth
cd fluidsynth/
mkdir build
cd build
cmake ..
sudo make install
复制代码

接下来在命令行中执行 fluidsynth 确认是否正常。如果出现了找不到库的情况的话,首先应该尝试更新链接库。

sudo ldconfig
复制代码

如果这样不行的话,可以添加环境变量。(下面的代码是临时的,永久修改可以自己 Google 一下。)

export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
复制代码

确认可以正常运行之后就可以进入下一步骤了。

(本节参阅 Github)

获取 Sound Font

如果你尝试过直接用 apt 安装 FluidSynth 的话,你应该会看到它推荐的几个包:fluidr3mono-gm-soundfont、timgm6mb-soundfont、fluid-soundfont-gm。这些都是 Sound Font。但是他们可能不够好听,并不能达到你的要求,这时候你就可以去一些第三方网站下载,比如 SoundFont4U。

FluidSynth 支持 SF2/SF3 格式的音源文件,不支持 SFZ,请注意不要搞错。自己下载的音源可以存到一个叫 sound-fonts 的文件夹下备用,使用 apt 安装的上述几个音源则安装在 /usr/share/sounds/sf2 文件夹下。

测试声音

先运行一次测试是否能够正常出声,这里我使用了 GM 音源。

fluidsynth -is -a alsa -m alsa_seq -g 5 -o midi.autoconnect=1 /usr/share/sounds/sf2/FluidR3_GM.sf2
复制代码

解释一下参数:

  • a,m 输入输出使用的音频驱动。
  • is 作为服务静默运行。
  • g 力度阈值,这里设置的大一点,以防一些设备音量太小。
  • o 细节参数,这里设置了自动连接 Midi 设备 midi.autoconnect。

(本节参阅 Github)

运行之后弹一下连接的键盘,看看有没有声音,比较慢的设备可能需要等一段时间才会显示已连接。按照目前的设置,音量应该会蛮大的,如果声音特别小,可以打开 alsamixer 调整一下音量,并使用 sudo alsactl store 保存状态。确认可以正常演奏之后进行下一步。

启动服务

为了能够让 FluidSynth 开机启动我们需要自己写一个服务:sudo nano /etc/systemd/system/fluidsynth.service。在文件中输入:

[Unit]
Description=FluidSynth Daemon
After=sound.target[Service]
EnvironmentFile=/etc/fluidsynth
ExecStart=/usr/local/bin/fluidsynth -is -a alsa -m alsa_seq -z 64 -c 2 -g $GAIN -o synth.cpu-cores=4 -o midi.autoconnect=1 ${FONT_PATH}[Install]
WantedBy=multi-user.target
复制代码

解释一下这里多出来的几个参数:

  • z 缓存大小,可以使用 64 128 256 等“整数”。
  • c 缓存个数,一般为 2 或 3。
  • o 多了一个设置 CPU 核心数的选项 synth.cpu-cores,你可以根据自己的开发板来设置。可以装一个 htop 数条条看几个核。

文件中还把音源文件名和力度放在了环境文件中方便设置。新建环境文件:sudo nano /etc/fluidsynth,在文件中输入:

FONT_PATH=/home/megabits/sound-fonts/SalC5Light2.sf2
GAIN=1.5
复制代码

这个应该就不用我太多解释了。这里将力度设置为 1.5 是我试出来在橘子派上比较合适的音量,你可以自己调。假如你的 Midi 键盘有音量滑杆那就更方便了。

激活服务:

sudo systemctl enable fluidsynth
sudo systemctl start fluidsynth
复制代码

之后理论上就应该能正常弹琴了。假如听不到声音,可以用 journalctl -u fluidsynth 来查看服务的日志,看看是出了什么问题。

参考文章:

  • Raspberry Pi Zero をMIDI音源ボックス化
  • Orange Pi USB MIDI Host

Midi over Bluetooth Low Energy

安装 BlueZ

首先要下载 BlueZ,由于 BlueZ 默认是没有开启 Midi 功能的,所以需要自己编译。这里使用的 BlueZ 是一个经过修改的版本,提供了 Midi Server 的功能。

git clone https://github.com/oxesoft/bluez
sudo apt-get install -y autotools-dev libtool autoconf
sudo apt-get install -y libasound2-dev
sudo apt-get install -y libusb-dev libdbus-1-dev libglib2.0-dev libudev-dev libical-dev libreadline-dev
cd bluez
./bootstrap
./configure --enable-midi
sudo make install
复制代码

之后测试一下开启服务端并用其他支持 Bluetooth Midi 的软件来搜索,如 iOS 下的 Garageband。

sudo btmidi-server -v -n "Midi over BLE"
复制代码

如果出现了 MGMT_OP_SET_LE failed: Not Supported,就说明设备不支持 BLE。在确认一切正常后打开另外一个终端窗口,连接好 Midi 设备并扫描。注意在这之前要先用别的设备连接上 Bluetooth Midi,BlueZ 只有在有设备连接时才会创建 Midi 通道。

aconnect -l
复制代码

在列表中找到你自己的蓝牙设备和创建的 "Midi over BLE"。比如我的输出结果是:

client 0: 'System' [type=kernel]0 'Timer           '1 'Announce        '
client 14: 'Midi Through' [type=kernel]0 'Midi Through Port-0'
client 20: 'Keystation 88' [type=kernel,card=1]0 'Keystation 88 MIDI 1'1 'Keystation 88 MIDI 2'
client 128: 'Midi over BLE' [type=user,pid=2104]0 'Midi over BLE   '
复制代码

这里可以看到我的键盘 "Keystation 88" 编号是 20,"Midi over BLE" 默认编号是 128。

aconnect 20:0 128:0 
复制代码

这样就可以把两个通道连接起来了。去键盘上按几个键,看看连接的设备会不会发出声音。一切正常就可以进行下一步了。

启动服务

新建一个服务:sudo nano /lib/systemd/system/btmidi.service,在文件中输入:

[Unit]
Description=MIDI Bluetooth connect
After=bluetooth.target
Requires=bluetooth.target[Service] 
ExecStart=/usr/local/bin/btmidi-server -n "Midi over BLE"[Install]
WantedBy=multi-user.target
复制代码

激活服务:

sudo systemctl enable btmidi.service
sudo systemctl start btmidi.service  
复制代码

接下来配置自动连接 Midi 通道,这里我们使用 udev 来在蓝牙设备发生变化时自动连接。首先来写一个连接脚本:

touch linkble.sh
chmod a+x linkble.sh
nano linkble.sh
复制代码

在文件中输入:

#!/bin/bash

sleep 1
/usr/bin/aconnect 'Keystation 88':0 'Midi over BLE':0
复制代码

注意这里要把键盘的名字改成自己的。之所以要用设备名来连接,是因为设备编号是不稳定的。接下来建立规则 sudo nano /etc/udev/rules.d/44-bt.rules,在文件中输入:

ACTION=="add|remove", SUBSYSTEM=="bluetooth", RUN+="/home/user-name/linkble.sh"
复制代码

刷新规则:

sudo udevadm control --reload-rules
复制代码

这样就可以在其他设备连接到 BLE 的时候自动连接通道了。

参考文章:

  • RASPBERRY PI 3B AS USB/BLUETOOTH MIDI HOST
  • BlueZ with MIDI over BLE Support

Midi over Wifi

安装 raveloxmidi

首先需要激活虚拟 Midi 设备模块,由于我们使用的工具 raveloxmidi 会以独占方式访问 Midi 设备,我们必须用一个虚拟设备来传递信号。

modprobe snd-virmidi
aconnect -l
复制代码

如果输出中出现了虚拟 Midi 设备就说明设置成功了。为了能让模块永久启用,我们来建立一个配置文件 sudo nano /etc/modules-load.d/snd-virmidi.conf,在文件中输入:

snd-virmidi
复制代码

保存并重启。

接下来安装 raveloxmidi:

sudo apt-get install -y git pkg-config libasound2-dev libavahi-client-dev autoconf automake
sudo apt-get install avahi-daemon
git clone -b experimental https://github.com/ravelox/pimidi.git
cd pimidi/raveloxmidi/ && ./autogen.sh && ./configure && make -j2
sudo make install
复制代码

假如你的网络不支持 ipv6,则需要配置一下 avahi-daemon,打开文件:sudo nano /etc/avahi/avahi-daemon.conf,按照下面的值来设置,注意一些行需要取消注释。

use-ipv6=no
publish-addresses=yes
publish-aaaa-on-ipv4=no
复制代码

打开 aconnect -l 来看一下设备在其中的编号,看一下 Midi 键盘的编号和第一个虚拟设备的编号,我的是 28:0 24:0,把它们连接起来。注意这里的顺序,一定是把键盘通道发送给虚拟设备通道。

aconnect 28:0 24:0
复制代码

接下来查看硬件编号:

amidi -l
复制代码

我这里第一个虚拟 Midi 设备的编号是 hw:2,0,记住这个值。为 raveloxmidi 建立一个配置文件:/etc/raveloxmidi.conf,在文件中输入:

alsa.input_device = hw:2,0
network.bind_address = 0.0.0.0
logging.enabled = yes
logging.log_level = normal
复制代码

接下来启动测试:

sudo raveloxmidi -dN -c /etc/raveloxmidi.conf
复制代码

如果程序正常运行了你应该就可以在其他地方连接它。这里以 macOS 上的 Garageband 为例:

打开 “音频 Midi 音频设置” 并在菜单中打开显示 “Midi 音频工作室”。之后在 “Midi 音频工作室” 的菜单或者工具栏上打开 “Midi 网络设置”。启用 “会话1”。在目录中选择 raveloxmidi,点连接。

打开 Garageband 弹几个音试一试,如果正常出声就是成功了。

启动服务

新建一个服务:sudo nano /etc/systemd/system/raveloxmidi.service,在文件中输入:

[Unit]
After=local-fs.target network.target
Description=raveloxmidi RTP-MIDI network server[Install]
WantedBy=multi-user.target[Service]
User=root
ExecStartPre=/usr/bin/aconnect 'Keystation 88':0 24:0
ExecStart=/usr/local/bin/raveloxmidi -dN -c /etc/raveloxmidi.conf
复制代码

注意 Midi 设备的名称要改成自己的。激活服务:

sudo systemctl enable raveloxmidi.service
sudo systemctl start raveloxmidi.service
复制代码

服务没有启动成功也没关系,可能是因为我们之前已经手动连接过一次 Midi 设备了,重启试试就好。

(本节参阅:Github)

参考文章:

  • Using a Raspberry Pi as a RTP-MIDI Gateway for macOS

常见问题

FluidSynth 无法打开默认声卡

遇到这种情况可能是你开机的时候 Alsa 没有把第一个输出设备分给你的声卡,而是分给了虚拟 Midi 或 USB 设备。首先我们来看一下你的音频设备都有什么:

aplay -l
复制代码

列出的内容可能是这样的:

 0 snd_virmidi1 snd_usb_audio2 (null)
复制代码

这其中的 0 和 1 号卡都不是输出声音的设备,我们可以降低他们的优先级。打开 Alsa 的配置文件,为每一个设备设置 -2 的优先级:

sudo nano /etc/modprobe.d/alsa-base.conf
复制代码
options snd_virmidi index=-2
options snd_usb_audio index=-2
复制代码

重启之后应该就可以了,另外你还需要修改 Midi over Wifi 的配置文件,更新声卡编号。

参考文章:

  • ALSA - ArchWiki

破音

如果 FluidSynth 出现了破音的现象,你需要考虑两方面原因。

假如破音时和负载有关的,你可能需要增加使用多 CPU 核心选项。

假如是定时的,大概一秒一次这种的,可能是由于 schedutil 导致的,这个调整 CPU 频率的工具对音频应用非常不友好。你需要配置它锁住 CPU 频率或者采用比较保守的方案来调整频率才不会出问题。

后记

弄 Midi 这档子事坑还是挺多的,我折腾了一周左右遇到了各种各样的问题,现在给大家总结一下,假如后来有人要做类似的事情,就可以省点心了。

转载于:https://juejin.im/post/5d52e3aa5188255b0743ffe5

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423648.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python ftp 上传文档出现 553 Could not create file

报错内容: *cmd* TYPE I*put* TYPE I\r\n*get* 200 Switching to Binary mode.\r\n*resp* 200 Switching to Binary mode.*cmd* PASV*put* PASV\r\n*get* 227 Entering Passive Mode (10,65,252,38,254,125).\r\n*resp* 227 Entering Passive Mode (10,65,252,38,25…

shiro学习(3):用户权限

工具idea 首先创建maven项目 配置文件 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http…

WPF学习笔记(三)

1.1 事件概括 第一节中我们给窗体添加了一个按钮&#xff0c;不过好像Button点个几下也只有些发光样式的变化&#xff0c;什么你还把系统皮肤去掉了&#xff1f;算了承认下确实够寒碜&#xff0c;那让我们再动动手。 1.1.1 路由事件简述 public HelloWorld() { Button button …

结构体的赋值和初始化与取出结构体变量中的成员

1 /*结构体的赋值和初始化*/2 3 # include <stdio.h>4 5 struct Student 6 {7 int age;8 float score;9 char sex;10 };11 12 int main(void)13 {14 struct Student st { 80, 66.6, F};//定义同时就赋值15 struct Student st2;//下一行不能写一句类似于…

shiro学习(4):shiro认证流程

Shiro登录校验流程实现与分析 什么是Shiro Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。 三个核心组件 Subject, Se…

python之Map函数

转载 https://www.cnblogs.com/gongxr/p/7247855.html# map()函数使用举例 # 功能&#xff1a;map()接受一个函数f和一个或多个list&#xff0c;将f依次作用在list的每个元素&#xff0c;得到一个新的列表 # 语法&#xff1a;map(方法名&#xff0c;列表&#xff0c;[列表2]) #…

Windows Mobile开发应该选择哪种开发语言?

Windows Mobile开发应该选择哪种开发语言&#xff1f;这个问题曾经被问了很多很多次&#xff0c;特别是打算开始学习Windows Mobile开发的朋友。Native Code or Managed Code? C/C or C#/VB.NET? 简单的说&#xff0c;Native Code的代码执行效率高&#xff0c;开发效率低&…

在ASP.NET MVC中实现Select多选

我们知道&#xff0c;在ASP.NET MVC中实现多选Select的话&#xff0c;使用Html.ListBoxFor或Html.ListBox方法就可以。在实际应用中&#xff0c;到底该如何设计View Model&#xff0c; 控制器如何接收多选Select的选中项呢&#xff1f; 实现效果如下&#xff1a; 初始状态某些选…

shiro学习(5):ini文件和自定义realm

工具idea 首先创建maven项目 配置文件 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http…

关于python中带下划线的变量和函数 的意义

转载:https://www.cnblogs.com/wangshuyi/p/6096362.html总结:变量:1. 前带_的变量: 标明是一个私有变量, 只用于标明, 外部类还是可以访问到这个变量2. 前带两个_ ,后带两个_ 的变量: 标明是内置变量,3. 大写加下划线的变量: 标明是 不会发生改变的全局变量函数:1. 前带…

几个简单的正则小例子

1 ^[\w][\w\.-]*[\w][\w-]*(?<val>\.\w){0,2}$ 电子邮件2 ^http://([\w-]*\.)([\w-]*)(/\w)*([\w-\.?%&]*)$ URL3 ^1[3,5]{1}\d{9}$ 手机号码4 ^(0\d{2,3})[-]?\d{7,8}(-\d{4})*$|\(0\d{2,3}\)[-]?\d{7,8}(-\d{4})*$ 电话号码5 Js替换字符串两边非空字符6 String…

康拓展开学习笔记

康拓展开 给出一个全排列&#xff0c;求他是第几个全排列称为康拓展开。 暴力康拓展开 对于一个全排列来说&#xff0c;从左往右第i位&#xff0c;有 n 1 - i 种选择。如果用变进制数表示的话&#xff0c;这一位就是 n 1 - i 进制的数&#xff0c;如果这一位选择了第k种情况&…

歌谣对自己的“自勉“

前言 为什么突然想写一篇文章呢&#xff0c;可能是源于个人吧&#xff0c;每天睡觉前都会思考一下是否达到了以后想要的生活。很显然答案是没有&#xff0c;不然我现在也不和大家在一起侃侃而谈。讲真&#xff08;这是我室友经常说的一句话&#xff09;&#xff0c;我是参与安…

网页中的按钮无法显示问题解决

今天有朋友反映她的电脑有问题&#xff0c;打开的网页中按纽无法显示&#xff0c;例如&#xff1a;163.com的邮箱中&#xff0c;发送邮件的按钮无法显示。 问了她一下是不是修改过背景色&#xff0c;她说是。就知道问题所在了。 解决方法&#xff1a;IE-工具-Internet选项-辅助…

python之Map函数 reduce 函数

转载&#xff1a;https://www.cnblogs.com/gongxr/p/7247855.htmlpython之Map函数# map()函数使用举例 # 功能&#xff1a;map()接受一个函数f和一个或多个list&#xff0c;将f依次作用在list的每个元素&#xff0c;得到一个新的列表 # 语法&#xff1a;map(方法名&#xff0c;…

你的心事我全知晓——心情日记小程序丨实战

闲暇之余&#xff0c;听媳妇嘀咕说要给她做一个能表达她每日心情的小程序&#xff0c;只能她在上面发东西。既然媳妇发话了&#xff0c;就花点心思做一个吧&#xff0c;因为没有UI图&#xff0c;所有布局全靠自己瞎编&#xff0c;下面结合图片和代码跟大家讲解下实现过程&#…

java面试题28 牛客 下面有关java classloader说法错误的是?

java面试题28 牛客 下面有关java classloader说法错误的是? A Java默认提供的三个ClassLoader是BootStrap ClassLoader&#xff0c;Extension ClassLoader&#xff0c;App ClassLoader B ClassLoader使用的是双亲委托模型来搜索类的 C JVM在判定两个class是否相同时&#x…

css居中无效的解决办法

前几天自己用css做居中的效果 margin: 0 auto,平时我都事这样来写的&#xff0c;不过这次就是不能实现居中的效果&#xff0c;检查代码&#xff0c;原来是自己没有添加DTD语句&#xff0c;即&#xff1a;&#xff1c;!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitiona…

spark on yarn 部署问题

spark on yarn 部署报:java.io.IOException: Resource file:/usr/local/spark-1.6.3-bin-hadoop2.6/lib/spark-assembly-1.6.3-hadoop2.6.0.jar changed on src filesystem (expected 1530607524000, was 1478125561000 解决&#xff1a;spark-env.sh 之前配置 export HADOOP_C…

java面试题29 牛客 以下关于集合类ArrayList、LinkedList、HashMap描述

java面试题29 牛客 以下关于集合类ArrayList、LinkedList、HashMap描述错误的是&#xff08;&#xff09; A HashMap实现Map接口&#xff0c;它允许任何类型的键和值对象&#xff0c;并允许将null用作键或值 B ArrayList和LinkedList均实现了List接口 C 添加和删除元素时&am…