{"id":20,"date":"2017-06-09T05:06:24","date_gmt":"2017-06-09T12:06:24","guid":{"rendered":"https:\/\/lucidbeaming.com\/blog\/?p=20"},"modified":"2022-06-01T12:41:15","modified_gmt":"2022-06-01T19:41:15","slug":"setting-up-a-raspberry-pi-3-to-run-zynaddsubfx-in-a-headless-configuration","status":"publish","type":"post","link":"https:\/\/lucidbeaming.com\/blog\/setting-up-a-raspberry-pi-3-to-run-zynaddsubfx-in-a-headless-configuration\/","title":{"rendered":"Setting up a Raspberry Pi 3 to run ZynAddSubFX in a headless configuration"},"content":{"rendered":"<p>Most of my music is production oriented and I don&#8217;t have a lot of live performance needs. But, I do want a useful set of evocative instruments to take to strange places. For that, I explored the options available for\u00a0making music with Raspberry Pi minicomputers.<\/p>\n<p>The goal of this particular box was to have the Linux soft-synth <a href=\"http:\/\/zynaddsubfx.sourceforge.net\/\">ZynAddSubFX<\/a> running headless on a battery powered and untethered Raspberry Pi, controllable by a simple MIDI keyboard and an instrument switcher on my phone.<\/p>\n<p>Getting things to run on the desktop version of Raspbian and ZynAddSubFX was pretty easy, but stripping away all the GUI and introducing command line automation with disparate multimedia libraries was a challenge. Then, opening it up to remote control over wifi was a rabbit hole of its own.<\/p>\n<p>But, I got it working and it sounds pretty amazing.<\/p>\n<div data-mode=\"normal\" data-oembed=\"1\" data-provider=\"youtube\" id=\"arve-youtube-t7ha_unqkyi\" class=\"arve\">\n<div class=\"arve-inner\">\n<div class=\"arve-embed arve-embed--has-aspect-ratio\">\n<div class=\"arve-ar\" style=\"padding-top:56.250000%\"><\/div>\n<p>\t\t\t<iframe allow=\"accelerometer &apos;none&apos;;autoplay &apos;none&apos;;bluetooth &apos;none&apos;;browsing-topics &apos;none&apos;;camera &apos;none&apos;;clipboard-read &apos;none&apos;;clipboard-write;display-capture &apos;none&apos;;encrypted-media &apos;none&apos;;gamepad &apos;none&apos;;geolocation &apos;none&apos;;gyroscope &apos;none&apos;;hid &apos;none&apos;;identity-credentials-get &apos;none&apos;;idle-detection &apos;none&apos;;keyboard-map &apos;none&apos;;local-fonts;magnetometer &apos;none&apos;;microphone &apos;none&apos;;midi &apos;none&apos;;otp-credentials &apos;none&apos;;payment &apos;none&apos;;picture-in-picture;publickey-credentials-create &apos;none&apos;;publickey-credentials-get &apos;none&apos;;screen-wake-lock &apos;none&apos;;serial &apos;none&apos;;summarizer &apos;none&apos;;sync-xhr;usb &apos;none&apos;;web-share;window-management &apos;none&apos;;xr-spatial-tracking &apos;none&apos;;\" allowfullscreen=\"\" class=\"arve-iframe fitvidsignore\" credentialless data-arve=\"arve-youtube-t7ha_unqkyi\" data-lenis-prevent=\"\" data-src-no-ap=\"https:\/\/www.youtube-nocookie.com\/embed\/T7Ha_uNQKyI?feature=oembed&amp;iv_load_policy=3&amp;modestbranding=1&amp;rel=0&amp;autohide=1&amp;playsinline=0&amp;autoplay=0\" frameborder=\"0\" height=\"0\" loading=\"lazy\" name=\"\" referrerpolicy=\"strict-origin-when-cross-origin\" sandbox=\"allow-scripts allow-same-origin allow-presentation allow-popups allow-popups-to-escape-sandbox\" scrolling=\"no\" src=\"https:\/\/www.youtube-nocookie.com\/embed\/T7Ha_uNQKyI?feature=oembed&#038;iv_load_policy=3&#038;modestbranding=1&#038;rel=0&#038;autohide=1&#038;playsinline=0&#038;autoplay=0\" title=\"\" width=\"0\"><\/iframe><\/p><\/div>\n<\/p><\/div>\n<p>\t<script type=\"application\/ld+json\">{\"@context\":\"http:\\\/\\\/schema.org\\\/\",\"@id\":\"https:\\\/\\\/lucidbeaming.com\\\/blog\\\/setting-up-a-raspberry-pi-3-to-run-zynaddsubfx-in-a-headless-configuration\\\/#arve-youtube-t7ha_unqkyi\",\"type\":\"VideoObject\",\"embedURL\":\"https:\\\/\\\/www.youtube-nocookie.com\\\/embed\\\/T7Ha_uNQKyI?feature=oembed&iv_load_policy=3&modestbranding=1&rel=0&autohide=1&playsinline=0&autoplay=0\"}<\/script><\/p>\n<\/div>\n<h3>Setting up the Raspberry Pi image<\/h3>\n<p>I use Jessie Lite because I don&#8217;t need the desktop environment. It&#8217;s the same codebase without a few bells and whistles. When <a href=\"https:\/\/www.raspberrypi.org\/downloads\/raspbian\/\">downloading from rasperrypi.org<\/a>, choose the torrent for a much faster transfer than getting the ZIP directly from the site.\u00a0These instructions below are for Mac OS X, using Terminal.<\/p>\n<pre>diskutil list<\/pre>\n<p class=\"p1\"><span class=\"s1\">\/dev\/disk1 (external, physical):<br \/>\n<\/span><span class=\"s1\">#: <span class=\"Apple-converted-space\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <\/span>TYPE NAME<span class=\"Apple-converted-space\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <\/span>SIZE <span class=\"Apple-converted-space\">\u00a0 \u00a0 \u00a0 <\/span>IDENTIFIER<br \/>\n<\/span><span class=\"s1\">0: <span class=\"Apple-converted-space\">\u00a0 \u00a0 \u00a0 \u00a0<\/span>FDisk_partition_scheme<span class=\"Apple-converted-space\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <\/span>*8.0 GB <span class=\"Apple-converted-space\">\u00a0 \u00a0 <\/span>disk1<br \/>\n<\/span><span class=\"s1\">1: <span class=\"Apple-converted-space\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <\/span>DOS_FAT_32 NO NAME <span class=\"Apple-converted-space\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <\/span>8.0 GB <span class=\"Apple-converted-space\">\u00a0 \u00a0 <\/span>disk1s1<\/span><\/p>\n<pre>diskutil unmountDisk \/dev\/disk1\n\nsudo dd bs=1m if=2017-04-10-raspbian-jessie-lite.img of=\/dev\/rdisk1<\/pre>\n<p>After the image gets written, I create an empty file on the boot partition to enable ssh login.<\/p>\n<pre>cd \/Volumes\/boot\ntouch ssh<\/pre>\n<p>Then, I\u00a0set the wifi login so it connects to the network on first boot.<\/p>\n<pre>sudo nano wpa_supplicant.conf\n\nctrl_interface=DIR=\/var\/run\/wpa_supplicant GROUP=netdev\nupdate_config=1\n\nnetwork={\n ssid=\"&lt;your_ssid&gt;\"\n psk=\"&lt;your_password&gt;\"\n }<\/pre>\n<p>The card gets removed from the laptop and inserted into the Pi. Then, after it boots up I go through the standard setup from the command line. The default login is &#8220;pi&#8221; and the default password is &#8220;raspberry&#8221;.<\/p>\n<pre>sudo raspi-config<\/pre>\n<p>[enable ssh,i2c. expand filesystem. set locale and keyboard.]<\/p>\n<p>After setting these, I let it restart when prompted. When it comes back up, I update the codebase.<\/p>\n<pre>sudo apt-get update\nsudo apt-get upgrade<\/pre>\n<h3>Base configuration<\/h3>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"img-responsive alignnone wp-image-38 size-full\" src=\"https:\/\/lucidbeaming.com\/blog\/wp-content\/uploads\/2017\/06\/config.gif\" alt=\"Raspberry config for ZynAddSubFX\" width=\"650\" height=\"388\" \/><\/p>\n<p>ZynAddSubFX is greedy when it comes to processing power and benefits from getting a bump in priority and memory resources. I add the default user (pi) to the group &#8220;audio&#8221; and assign the augmented resources to that group, instead of the user itself.<\/p>\n<pre>sudo usermod -a -G audio pi\n\nsudo nano \/etc\/security\/limits.d\/audio.conf\n\n...\n@audio - rtprio 80\n@audio - memlock unlimited\n...\n<\/pre>\n<p>The Raspbian version of Jessie Lite has CPU throttles, or governors, set to conserve power and reduce heat from the CPU. By default, they are set to &#8220;on demand&#8221;. That means the voltage to the CPU is reduced until general use hits 90% of CPU capacity. Then it triggers a voltage (and speed) increase to handle the load. I change that to &#8220;performance&#8221; so that it has as much horsepower available.<\/p>\n<p>This is done in rc.local:<\/p>\n<pre>sudo nano \/etc\/rc.local\n...\necho \"performance\" &gt; \/sys\/devices\/system\/cpu\/cpu0\/cpufreq\/scaling_governor\necho \"performance\" &gt; \/sys\/devices\/system\/cpu\/cpu1\/cpufreq\/scaling_governor\necho \"performance\" &gt; \/sys\/devices\/system\/cpu\/cpu2\/cpufreq\/scaling_governor\necho \"performance\" &gt; \/sys\/devices\/system\/cpu\/cpu3\/cpufreq\/scaling_governor\n...<\/pre>\n<p>Note that it gets set for all four cores, since the Raspberry Pi is multi-core. For more info about governors and even overclocking, <a href=\"https:\/\/putokaz.wordpress.com\/2015\/03\/01\/overclocking-raspberry-pi-set-scaling_governor-on-raspbian-boot\/\">this is a good resource<\/a>.<\/p>\n<p>Virtual memory also needs to get downgraded so there is little swap activity. Zynaddsubfx is power hungry but doesn&#8217;t use much memory, so it doesn&#8217;t need VM.<\/p>\n<pre>sudo \/sbin\/sysctl -w vm.swappiness=10<\/pre>\n<p>Now, to set up the audio interface. For my ZynAddSubFX box, I use an <a href=\"https:\/\/www.raspberrypi.com\/products\/iqaudio-dac-plus\/\">IQaudio Pi-DAC+<\/a>. I&#8217;ve also used a standard USB audio interface and have instructions for that in my post about the Pi Zero. Raspbian uses Device Tree overlays to load\u00a0I2C, I2S, and SPI interface modules. So, instead of separate drivers to install, I just edit config.txt to include the appropriate modules for the Pi-DAC+. Note that I also\u00a0disabled the crappy built-in audio by commenting out &#8220;dtparam=audio=on&#8221;. This helps later on when setting the default audio device used by the system.<\/p>\n<pre>sudo nano \/boot\/config.txt\n\n...\n\n# Enable audio (loads snd_bcm2835)\n# dtparam=audio=on\n\ndtoverlay=i2s-mmap\ndtoverlay=hifiberry-dacplus\n\n...<\/pre>\n<p>For Jack to grab hold of the Pi-DAC+ for output, the default user (pi) needs a DBus security policy for the audio device.<\/p>\n<pre>sudo nano \/etc\/dbus-1\/system.conf\n\n...\n&lt;!-- Only systemd, which runs as root, may report activation failures. --&gt;\n&lt;policy user=\"root\"&gt;\n&lt;allow send_destination=\"org.freedesktop.DBus\"\n    send_interface=\"org.freedesktop.systemd1.Activator\"\/&gt;\n&lt;\/policy&gt;\n&lt;policy user=\"pi\"&gt;\n    &lt;allow own=\"org.freedesktop.ReserveDevice1.Audio0\"\/&gt;\n&lt;\/policy&gt;\n...<\/pre>\n<p>Next, ALSA gets a default configuration for which sound device to use. Since I disabled the built-in audio earlier, the Pi-DAC+ is now &#8220;0&#8221; in the device stack.<\/p>\n<pre>sudo nano \/etc\/asound.conf\n\npcm.!default {\n type hw card 0\n }\nctl.!default {\n type hw card 0\n }\n\nsudo reboot<\/pre>\n<h3>Software installation<\/h3>\n<p>ZynAddSubFX has thick dependency requirements, so I collected the installers in a bash script. Most of it was lifted from the <a href=\"https:\/\/github.com\/zynthian\/zynthian-sys\/tree\/master\/scripts\">Zynthian repo<\/a>. Download the script from my <a href=\"https:\/\/github.com\/lucidbeaming\/pi-synths\/tree\/master\/ZynAddSubFX\">Github repo<\/a> to install the required packages and run it. The script also includes\u00a0<em>rtirq-init, <\/em>which can improve performance on USB audio devices and give ALSA some room to breath.<\/p>\n<pre>git clone\u00a0https:\/\/raw.githubusercontent.com\/lucidbeaming\/pi-synths\/master\/ZynAddSubFX\/required-packages.sh\n\nsudo chmod a+x required-packages.sh\n\n.\/required-packages.sh<\/pre>\n<p>Now the real meat of it all gets cooked. There are <a href=\"https:\/\/linuxmusicians.com\/viewtopic.php?f=48&amp;t=16894\">some issues<\/a> with build optimizations for SSE and Neon (incompatible with ARM processors), so you&#8217;ll need to disable those in the cmake configuration.<\/p>\n<pre>git clone https:\/\/github.com\/zynaddsubfx\/zynaddsubfx.git\ncd zynaddsubfx\nmkdir build\ncd build\ncmake ..\nccmake .\n<strong>[remove SSE parameters and NoNeonplease=ON]<\/strong>\nsudo make install<\/pre>\n<p>Usually takes 20-40 minutes to compile. Now to test it out and get some basic command line options listed.<\/p>\n<pre>zynaddsubfx -h<\/pre>\n<p><em>Usage: zynaddsubfx [OPTION]<\/em><\/p>\n<p><em>-h , &#8211;help Display command-line help and exit<\/em><br \/>\n<em> -v , &#8211;version Display version and exit<\/em><br \/>\n<em> -l file, &#8211;load=FILE Loads a .xmz file<\/em><br \/>\n<em> -L file, &#8211;load-instrument=FILE Loads a .xiz file<\/em><br \/>\n<em> -r SR, &#8211;sample-rate=SR Set the sample rate SR<\/em><br \/>\n<em> -b BS, &#8211;buffer-size=SR Set the buffer size (granularity)<\/em><br \/>\n<em> -o OS, &#8211;oscil-size=OS Set the ADsynth oscil. size<\/em><br \/>\n<em> -S , &#8211;swap Swap Left &lt;&#8211;&gt; Right<\/em><br \/>\n<em> -U , &#8211;no-gui Run ZynAddSubFX without user interface<\/em><br \/>\n<em> -N , &#8211;named Postfix IO Name when possible<\/em><br \/>\n<em> -a , &#8211;auto-connect AutoConnect when using JACK<\/em><br \/>\n<em> -A , &#8211;auto-save=INTERVAL Automatically save at interval (disabled with 0 interval)<\/em><br \/>\n<em> -p , &#8211;pid-in-client-name Append PID to (JACK) client name<\/em><br \/>\n<em> -P , &#8211;preferred-port Preferred OSC Port<\/em><br \/>\n<em> -O , &#8211;output Set Output Engine<\/em><br \/>\n<em> -I , &#8211;input Set Input Engine<\/em><br \/>\n<em> -e , &#8211;exec-after-init Run post-initialization script<\/em><br \/>\n<em> -d , &#8211;dump-oscdoc=FILE Dump oscdoc xml to file<\/em><br \/>\n<em> -u , &#8211;ui-title=TITLE Extend UI Window Titles<\/em><\/p>\n<h3>The web app<\/h3>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"img-responsive alignnone wp-image-39 size-full\" src=\"https:\/\/lucidbeaming.com\/blog\/wp-content\/uploads\/2017\/06\/webapp.gif\" alt=\"Webapp to switch ZynAddSubFX instruments\" width=\"650\" height=\"389\" \/><\/p>\n<p>I also built a simple web app to switch instruments from a mobile device (or any browser, really). It runs on <a href=\"https:\/\/nodejs.org\/dist\/latest-v6.x\/docs\/api\/\">Node.js<\/a> and leverages <a href=\"http:\/\/expressjs.com\/en\/4x\/api.html\">Express<\/a>, <a href=\"https:\/\/socket.io\/docs\/v4\/\">Socket.io<\/a>, <a href=\"https:\/\/www.npmjs.com\/package\/osc\">OSC<\/a>, and <a href=\"http:\/\/api.jquerymobile.com\/\">Jquery Mobile<\/a>.<\/p>\n<p>First, a specific version of Node is needed and I use NVM to grab it. The script below <a href=\"https:\/\/github.com\/creationix\/nvm#install-script\">installs NVM<\/a>.<\/p>\n<pre>curl https:\/\/raw.githubusercontent.com\/creationix\/nvm\/master\/install.sh | sh<\/pre>\n<p>Logout and log back in to have NVM available to you.<\/p>\n<pre>nvm install v6.10.1<\/pre>\n<p>My Node app is in its <a href=\"https:\/\/github.com\/lucidbeaming\/ZynAddSubFX-WebApp\">own repo<\/a>. The dependencies Express, Socket.io, and OSC will be installed with npm from the included package.json file.<\/p>\n<pre class=\"p1\"><span class=\"s1\">git clone https:\/\/github.com\/lucidbeaming\/ZynAddSubFX-WebApp.git<\/span>\n<span class=\"s1\">cd ZynAddSubFX-WebApp\n<\/span>npm install<\/pre>\n<p>Test the app from the ZynAddSubFX-WebApp directory:<\/p>\n<pre>node index.js<\/pre>\n<p>On a phone\/tablet (or any browser)\u00a0on the same wifi network, go to:<\/p>\n<p>http:\/\/&lt;IP address of the Raspberry Pi&gt;:7000<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"img-responsive alignnone wp-image-48 size-full\" src=\"https:\/\/lucidbeaming.com\/blog\/wp-content\/uploads\/2017\/06\/app-shot.jpg\" alt=\"Image of webapp to switch instruments\" width=\"800\" height=\"530\" \/><\/p>\n<p>You should see a list of instruments to choose from. It won&#8217;t do anything yet, but getting the list to come up is a sign of initial success.<\/p>\n<p>Now, for a little secret sauce. The <a href=\"https:\/\/github.com\/lucidbeaming\/pi-synths\/blob\/master\/ZynAddSubFX\/zynlaunch.sh\">launch script<\/a> I use is from achingly long hours of trial and error. The Raspberry Pi is a very capable machine but has limitations. The command line parameters I use come from the best balance of performance and fidelity I could find. If ZynAddSubFX gets rebuilt with better multimedia processor optimizations for ARM, this could change. I&#8217;ve read that improvements are in the works. Also, this runs Zynaddsubfx <strong>without<\/strong> Jack and just uses ALSA. I was able to get close to RTprio with the installation of\u00a0<em>rtirq-init<\/em>.<\/p>\n<pre class=\"p1\"><span class=\"s1\">#!\/bin\/bash<\/span>\n\n<span class=\"s1\">export DBUS_SESSION_BUS_ADDRESS=unix:path=\/run\/dbus\/system_bus_socket<\/span>\n\n<span class=\"s1\">if pgrep zynaddsubfx\n <\/span><span class=\"s1\">then\n <\/span><span class=\"s1\">echo Zynaddsubfx is already singing\n <\/span><span class=\"s1\">exit 0\n <\/span><span class=\"s1\">else\n <\/span><span class=\"s1\">zynaddsubfx -U -A=0 -o 512 -r 96000 -b 512 -I alsa -O alsa -P 7777 -L \"\/usr\/local\/share\/zynaddsubfx\/banks\/Choir and Voice\/0034-Slow Morph_Choir.xiz\" &amp;\n <\/span><span class=\"s1\">sleep 4<\/span>\n\n<span class=\"s1\">\u00a0 \u00a0if pgrep zynaddsubfx\n <\/span>\u00a0 then\n   echo Zyn is singing\n   else\n   echo Zyn blorked. Epic Fail.\n   fi\n\n<span class=\"s1\">fi<\/span>\n\n<span class=\"s1\">mini=$(aconnect -o | grep \"MINILAB\")\n <\/span><span class=\"s1\">mpk=$(aconnect -o | grep \"MPKmini2\")\n <\/span><span class=\"s1\">mio=$(aconnect -o | grep \"mio\")<\/span>\n\n<span class=\"s1\">if [[ $mini ]]\n <\/span><span class=\"s1\">then\n <\/span><span class=\"s1\">aconnect 'Arturia MINILAB':0 'ZynAddSubFX':0\n <\/span><span class=\"s1\">echo Connected to MINIlab\n <\/span><span class=\"s1\">elif [[ $mpk ]]\n <\/span><span class=\"s1\">then\n <\/span><span class=\"s1\">aconnect 'MPKmini2':0 'ZynAddSubFX':0\n <\/span><span class=\"s1\">echo Connected to MPKmini\n <\/span><span class=\"s1\">elif [[ $mio ]]\n <\/span><span class=\"s1\">then\n <\/span><span class=\"s1\">aconnect 'mio':0 'ZynAddSubFX':0\n <\/span><span class=\"s1\">echo Connected to Mio\n <\/span><span class=\"s1\">else\n <\/span><span class=\"s1\">echo No known midi devices available. Try aconnect -l\n <\/span><span class=\"s1\">fi<\/span>\n\n<span class=\"s1\">exit 0<\/span><\/pre>\n<p>I have 3 MIDI controllers I use for these things and this script is set to check for any of them, in order of priority, and connect them with ZynAddSubFX. Also, I have a few &#8220;sleep&#8221; statements in there that I&#8217;d like to remove when I find a way of including graceful fallback and error reporting from a bash script. For now, this works fine.<\/p>\n<p>I add this line to rc.local to launch Zynaddsubfx automatically on boot and connect MIDI.<\/p>\n<pre>su pi -c '\/home\/pi\/zynlaunch.sh &gt;&gt; \/tmp\/zynaddsubfx.log 2&gt;&amp;1 &amp;'<\/pre>\n<p>Unfortunately, Node won&#8217;t launch the web app from rc.local, so I add some conditionals to \/home\/pi\/.profile to launch the app after the boot sequence.<\/p>\n<pre class=\"p1\"><span class=\"s1\">if pgrep zynaddsubfx\n<\/span><span class=\"s1\">then\n<\/span><span class=\"s1\">echo Zynaddsubfx is singing\n<\/span><span class=\"s1\">fi<\/span>\n\n<span class=\"s1\">if pgrep node\n<\/span><span class=\"s1\">then\n<\/span><span class=\"s1\">echo Zyn app is up\n<\/span><span class=\"s1\">else\n<\/span><span class=\"s1\">node \/home\/pi\/ZynAddSubFX-WebApp\/index.js\n<\/span><span class=\"s1\">fi<\/span><\/pre>\n<h3>Making music<\/h3>\n<p>This ended up being a pad and drone instrument in my tool chest. ZynAddSubFX is really an amazing piece of software and can do much more than I&#8217;m setting up here. The sounds are complex and sonically rich. The GUI version lets you change or create instruments with a deep and precise set of graphic panels.<\/p>\n<p>For my purposes, though, I want something to play live with that has very low resource needs. This little box does just that.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"img-responsive alignnone wp-image-41 size-full\" src=\"https:\/\/lucidbeaming.com\/blog\/wp-content\/uploads\/2017\/06\/unit.jpg\" alt=\"Raspberry Pi 3 with Pi-DAC+\" width=\"800\" height=\"290\" \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The goal of this particular box was to have the Linux soft-synth ZynAddSubFX running headless on a battery powered and untethered Raspberry Pi, controllable by a simple MIDI keyboard and an instrument switcher on my phone.<\/p>\n","protected":false},"author":1,"featured_media":59,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[2],"tags":[4,6,5,7],"class_list":["post-20","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-building","tag-diy","tag-raspberry-pi","tag-synthesizer","tag-tutorial"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/lucidbeaming.com\/blog\/wp-json\/wp\/v2\/posts\/20","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lucidbeaming.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lucidbeaming.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lucidbeaming.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lucidbeaming.com\/blog\/wp-json\/wp\/v2\/comments?post=20"}],"version-history":[{"count":23,"href":"https:\/\/lucidbeaming.com\/blog\/wp-json\/wp\/v2\/posts\/20\/revisions"}],"predecessor-version":[{"id":815,"href":"https:\/\/lucidbeaming.com\/blog\/wp-json\/wp\/v2\/posts\/20\/revisions\/815"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/lucidbeaming.com\/blog\/wp-json\/wp\/v2\/media\/59"}],"wp:attachment":[{"href":"https:\/\/lucidbeaming.com\/blog\/wp-json\/wp\/v2\/media?parent=20"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lucidbeaming.com\/blog\/wp-json\/wp\/v2\/categories?post=20"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lucidbeaming.com\/blog\/wp-json\/wp\/v2\/tags?post=20"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}