<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matthew D. Miller</title>
    <description>The latest articles on DEV Community by Matthew D. Miller (@goober99).</description>
    <link>https://dev.to/goober99</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F144773%2F7421fe27-c303-4e63-86e9-96bf28865878.jpeg</url>
      <title>DEV Community: Matthew D. Miller</title>
      <link>https://dev.to/goober99</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/goober99"/>
    <language>en</language>
    <item>
      <title>Encrypt an Existing Linux Installation Online with the Magic of LVM</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Tue, 14 Jun 2022 22:02:28 +0000</pubDate>
      <link>https://dev.to/goober99/encrypt-an-existing-linux-installation-online-with-the-magic-of-lvm-1mjc</link>
      <guid>https://dev.to/goober99/encrypt-an-existing-linux-installation-online-with-the-magic-of-lvm-1mjc</guid>
      <description>&lt;p&gt;So you have an existing Linux installation and your disks aren’t encrypted? You
want to encrypt the entire disk including your root partition, and you’d like
to do it without taking the server offline. If the server already uses LVM,
which is the default for many popular enterprise distros, then you’re in luck.&lt;/p&gt;

&lt;p&gt;Because of the awesomeness of LVM, an existing installation can be encrypted
with LUKS completely online. This does require a spare disk of equal or greater
size to the existing disk. In a virtual environment, this can be achieved by
adding an extra virtual disk to the VM. For a desktop or workstation, this can
be achieved with an external drive.&lt;/p&gt;

&lt;p&gt;What we want to do is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Add the spare disk to the volume group.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Move the data over to the spare disk.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remove the original disk from the volume group.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Encrypt the original disk with LUKS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add it back to the volume group and move the data back over.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remove the spare disk from the volume group.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Get the name of the existing physical volume with &lt;code&gt;sudo pvdisplay&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Get the name of the existing volume group with &lt;code&gt;sudo vgdisplay&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Determine the name of the spare disk with &lt;code&gt;sudo fdisk -l&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m encrypting a VM with a virtual disk (&lt;code&gt;/dev/sda&lt;/code&gt;). It has two
partitions: a small &lt;code&gt;/boot&lt;/code&gt; partition (&lt;code&gt;/dev/sda1&lt;/code&gt;) and an LVM physical
volume (&lt;code&gt;/dev/sda2&lt;/code&gt;). The name of the volume group used in this example
is vgpool. I added a second virtual disk. It is unpartitioned and called
&lt;code&gt;/dev/sdb&lt;/code&gt;. Substitute the values for your environment.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;

&lt;/td&gt;
&lt;td&gt;
I highly recommend taking a snapshot or backup before continuing. Any
time you are messing with storage, there is the possibility for things to go
very wrong.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add the spare disk to the volume group.&lt;/p&gt;


&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo pvcreate /dev/sdb
&lt;span class="tok-go"&gt;  Physical volume "/dev/sdb" successfully created.&lt;/span&gt;
&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo vgextend vgpool /dev/sdb
&lt;span class="tok-go"&gt;  Volume group "vgpool" successfully extended&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Move the physical extents from the current physical volume to the spare
physical volume. This will take a long time. It will periodically print
progress to the screen.&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo pvmove /dev/sda2 /dev/sdb&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Remove the original physical volume from the volume group.&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo vgreduce vgpool /dev/sda2
&lt;span class="tok-go"&gt;  Removed "/dev/sda2" from volume group "vgpool"&lt;/span&gt;
&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo pvremove /dev/sda2
&lt;span class="tok-go"&gt; Labels on physical volume "/dev/sda2" successfully wiped.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;a href="https://wiki.archlinux.org/title/Dm-crypt/Drive_preparation#dm-crypt_specific_methods"&gt;Wipe&lt;/a&gt;
the original disk. Because the disk already had data on it, it is important to
securely erase the disk to prevent file recovery of unencrypted data.&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo yum install cryptsetup
&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo cryptsetup open --type plain -d /dev/urandom /dev/sda2 to_be_wiped
&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo dd &lt;span class="tok-k"&gt;if&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="tok-nv"&gt;of&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;/dev/mapper/to_be_wiped &lt;span class="tok-nv"&gt;bs&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;1M &lt;span class="tok-nv"&gt;status&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;progress
&lt;span class="tok-go"&gt;dd: error writing '/dev/mapper/to_be_wiped': No space left on device&lt;/span&gt;
&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo cryptsetup close to_be_wiped&lt;/code&gt;&lt;/pre&gt;





&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;

&lt;/td&gt;
&lt;td&gt;

&lt;p&gt;The default cipher for plain dm-crypt encryption is aes-cbc. It might be worth
changing the cipher to aes-xts, as it might be significantly faster (verify
with &lt;code&gt;cryptsetup benchmark&lt;/code&gt;).&lt;/p&gt;




&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo cryptsetup open --type plain -d /dev/urandom --cipher aes-xts-plain64 /dev/sda2 to_be_wiped&lt;/code&gt;&lt;/pre&gt;



&lt;br&gt;
&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;

&lt;/td&gt;
&lt;td&gt;
Use of &lt;code&gt;/dev/urandom&lt;/code&gt; with &lt;code&gt;dd&lt;/code&gt; is not required as the encryption cipher
is used for randomness.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Encrypt the original disk with LUKS using a detached LUKS header stored on
the &lt;code&gt;/boot&lt;/code&gt; partition.&lt;/p&gt;


&lt;p&gt;The &lt;code&gt;cryptsetup&lt;/code&gt; default is to store the LUKS header at the beginning of the
partition or disk. If the LUKS header is stored on the original, the volume
group will no longer fit when we go to move it back to the original disk from
the spare disk, and you’ll get an error like this:&lt;/p&gt;




&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo pvmove /dev/sdb /dev/mapper/lvmcrypt
&lt;span class="tok-go"&gt;  Insufficient free space: 261887 extents needed, but only 261883 available&lt;/span&gt;
&lt;span class="tok-go"&gt;  Unable to allocate mirror extents for ol_ods/pvmove0.&lt;/span&gt;
&lt;span class="tok-go"&gt;  Failed to convert pvmove LV to mirrored.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





&lt;p&gt;LUKS has the ability to use a
&lt;a href="https://linuxconfig.org/how-to-use-luks-with-a-detached-header"&gt;detached
header&lt;/a&gt;. Since you probably already have an unencrypted &lt;code&gt;/boot&lt;/code&gt;, the header can
be stored there.&lt;/p&gt;




&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo cryptsetup -y luksFormat /dev/sda2 --header /boot/luksheader.img

&lt;span class="tok-go"&gt;WARNING!&lt;/span&gt;
&lt;span class="tok-go"&gt;========&lt;/span&gt;
&lt;span class="tok-go"&gt;Header file does not exist, do you want to create it?&lt;/span&gt;

&lt;span class="tok-go"&gt;Are you sure? (Type 'yes' in capital letters): YES&lt;/span&gt;
&lt;span class="tok-go"&gt;Enter passphrase for /boot/luksheader.img:&lt;/span&gt;
&lt;span class="tok-go"&gt;Verify passphrase:&lt;/span&gt;
&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo cryptsetup luksOpen /dev/sda2 lvmcrypt --header /boot/luksheader.img
&lt;span class="tok-go"&gt;Enter passphrase for /dev/sda2:&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Add the now encrypted disk back to the volume group.&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo pvcreate /dev/mapper/lvmcrypt
&lt;span class="tok-go"&gt;  Physical volume "/dev/mapper/lvmcrypt" successfully created.&lt;/span&gt;
&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo vgextend vgpool /dev/mapper/lvmcrypt
&lt;span class="tok-go"&gt;  Volume group "vgpool" successfully extended&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Move the physical extents from the spare physical volume to the encrypted
physical volume. Like before, this will take a long time.&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo pvmove /dev/sdb /dev/mapper/lvmcrypt&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Remove the spare physical volume from the volume group.&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo vgreduce vgpool /dev/sdb
&lt;span class="tok-go"&gt;  Removed "/dev/sdb" from volume group "vgpool"&lt;/span&gt;
&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo pvremove /dev/sdb
&lt;span class="tok-go"&gt;  Labels on physical volume "/dev/sdb" successfully wiped.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





&lt;p&gt;The data is now entirely on the original physical volume but this time
encrypted. You can now delete the spare virtual disk in the case of a VM or
remove the external drive.&lt;/p&gt;



&lt;/li&gt;

&lt;/ol&gt;

&lt;h2 id="_configure_initramfs"&gt;Configure Initramfs&lt;/h2&gt;

&lt;p&gt;We’ve accomplished what we set out to do: we’ve encrypted a live system without
requiring a reboot. But whenever you do reboot, you want your system to come
back up. Since we encrypted the root partition, the decryption needs to happen
in the initramfs. There are various tools to regenerate the initramfs depending
on your distro. Red Hat-derived distros like Oracle Linux usually use Dracut.&lt;/p&gt;

&lt;p&gt;Since we used a detached header, configuring the initramfs is a little more
&lt;a href="https://medium.com/@privb0x23/lose-your-head-attempting-to-boot-from-luks-without-a-header-2d61174df360"&gt;complicated&lt;/a&gt;.
“A LUKS volume with a detached header, at least using &lt;code&gt;udev&lt;/code&gt;/&lt;code&gt;blkid&lt;/code&gt;, does not
have a UUID.” However, unlike in the linked article, there is a PARTUUID since
partitioning is used on the disk (assuming &lt;code&gt;/dev/sda1&lt;/code&gt; is &lt;code&gt;/boot&lt;/code&gt; and
&lt;code&gt;/dev/sda2&lt;/code&gt; is LVM because the Red Hat installer requires &lt;code&gt;/boot&lt;/code&gt; on a separate
partition when using LVM).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Get the PARTUUID of the LUKS device with (&lt;code&gt;sudo blkid /dev/sda2&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add it to &lt;code&gt;/etc/crypttab&lt;/code&gt; in the form:&lt;/p&gt;


&lt;pre&gt;volume-name encrypted-device key-file options&lt;/pre&gt;







&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;volume-name&lt;/code&gt; is the device mapper name (e.g. if the &lt;code&gt;volume-name&lt;/code&gt; is
&lt;code&gt;lvmcrypt&lt;/code&gt;, it will mount the LUKS device as &lt;code&gt;/dev/mapper/lvmcrypt&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;encrypted-device&lt;/code&gt; is the
&lt;a href="https://github.com/dracutdevs/dracut/pull/290"&gt;specification&lt;/a&gt; of the encrypted
device via &lt;code&gt;UUID=&lt;/code&gt;, &lt;code&gt;PARTUUID=&lt;/code&gt;, or &lt;code&gt;ID=&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;key-file&lt;/code&gt; is the absolute path to a file with the encryption key. If there
is no key file, use &lt;code&gt;none&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;options&lt;/code&gt; is a comma-delimited list of options.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;







&lt;p&gt;Since we used a detached header, we need to include the &lt;code&gt;header&lt;/code&gt; option and
&lt;code&gt;force&lt;/code&gt; to force the inclusion of the entry in the initramfs (the only place I
found where this option is documented is in the
&lt;a href="https://github.com/dracutdevs/dracut/pull/290"&gt;pull request&lt;/a&gt; that added it), so
our &lt;code&gt;crypttab&lt;/code&gt; will look something like:&lt;/p&gt;




&lt;pre&gt;lvmcrypt PARTUUID=&amp;lt;PARTUUID-identifier&amp;gt; none header=/boot/luksheader.img,force&lt;/pre&gt;





&lt;p&gt;where &lt;code&gt;&amp;lt;PARTUUID-identifer&amp;gt;&lt;/code&gt; is the PARTUUID of the LUKS device.&lt;/p&gt;



&lt;p&gt;If this is for a laptop or workstation where you are OK with being prompted for
a password during boot, you can skip to the last step and regenerate initramfs.
The following steps will set up Network-Bound Disk Encryption (NBDE) with
Clevis.&lt;/p&gt;



&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Bind LUKS volume to &lt;a href="https://github.com/latchset/clevis#binding-luks-volumes"&gt;Clevis&lt;/a&gt;.&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo yum install clevis clevis-luks clevis-dracut
&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo clevis luks &lt;span class="tok-nb"&gt;bind&lt;/span&gt; -d /dev/sda2 tang &lt;span class="tok-s1"&gt;'{"url": "http://tang.example.com"}'&lt;/span&gt;
&lt;span class="tok-go"&gt;/dev/sda2 is not a LUKS device!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





&lt;p&gt;Unfortunately, Clevis does not currently support detached headers. Clevis
mostly consists of shell scripts, so this can be easily hacked. These
modifications will have to be remerged whenever updating Clevis, so it would be
ideal to get the modifications necessary to support detached headers merged
upstream like the author of this
&lt;a href="https://medium.com/@privb0x23/lose-your-head-attempting-to-boot-from-luks-without-a-header-2d61174df360"&gt;post&lt;/a&gt;
did to get detached headers supported by
&lt;a href="https://github.com/dracutdevs/dracut/pull/290"&gt;Dracut&lt;/a&gt;. I’m going to start by
opening an issue on the Clevis GitHub to see if they are open to a pull request
adding this and update this with the results.&lt;/p&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Navigate to &lt;code&gt;/usr/bin&lt;/code&gt; and make a backup copy of &lt;code&gt;clevis-luks-bind&lt;/code&gt;.&lt;/p&gt;


&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;&lt;span class="tok-nb"&gt;cd&lt;/span&gt; /usr/bin
&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo cp clevis-luks-bind clevis-luks-bind.original&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Edit &lt;code&gt;clevis-luks-bind&lt;/code&gt;. Locate where the command line arguments are parsed
with &lt;code&gt;getopts&lt;/code&gt; and make the following additions:&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="sh"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-k"&gt;while&lt;/span&gt; &lt;span class="tok-nb"&gt;getopts&lt;/span&gt; &lt;span class="tok-s2"&gt;":hfyd:s:k:t:o:"&lt;/span&gt; o&lt;span class="tok-p"&gt;;&lt;/span&gt; &lt;span class="tok-k"&gt;do&lt;/span&gt; &lt;b&gt;(1)&lt;/b&gt;
    &lt;span class="tok-k"&gt;case&lt;/span&gt; &lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$o&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt; &lt;span class="tok-k"&gt;in&lt;/span&gt;
    f&lt;span class="tok-o"&gt;)&lt;/span&gt; &lt;span class="tok-nv"&gt;FRC&lt;/span&gt;&lt;span class="tok-o"&gt;+=(&lt;/span&gt;-f&lt;span class="tok-o"&gt;)&lt;/span&gt;&lt;span class="tok-p"&gt;;;&lt;/span&gt;
    d&lt;span class="tok-o"&gt;)&lt;/span&gt; &lt;span class="tok-nv"&gt;DEV&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$OPTARG&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-p"&gt;;;&lt;/span&gt;
    s&lt;span class="tok-o"&gt;)&lt;/span&gt; &lt;span class="tok-nv"&gt;SLT&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$OPTARG&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-p"&gt;;;&lt;/span&gt;
    k&lt;span class="tok-o"&gt;)&lt;/span&gt; &lt;span class="tok-nv"&gt;KEY&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$OPTARG&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-p"&gt;;;&lt;/span&gt;
    t&lt;span class="tok-o"&gt;)&lt;/span&gt; &lt;span class="tok-nv"&gt;TOKEN_ID&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$OPTARG&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-p"&gt;;;&lt;/span&gt;
    o&lt;span class="tok-o"&gt;)&lt;/span&gt; &lt;span class="tok-nv"&gt;HEADER&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$OPTARG&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-p"&gt;;;&lt;/span&gt; &lt;b&gt;(2)&lt;/b&gt;
    y&lt;span class="tok-o"&gt;)&lt;/span&gt; &lt;span class="tok-nv"&gt;FRC&lt;/span&gt;&lt;span class="tok-o"&gt;+=(&lt;/span&gt;-f&lt;span class="tok-o"&gt;)&lt;/span&gt;
       &lt;span class="tok-nv"&gt;YES&lt;/span&gt;&lt;span class="tok-o"&gt;+=(&lt;/span&gt;-y&lt;span class="tok-o"&gt;)&lt;/span&gt;&lt;span class="tok-p"&gt;;;&lt;/span&gt;
    *&lt;span class="tok-o"&gt;)&lt;/span&gt; usage&lt;span class="tok-p"&gt;;;&lt;/span&gt;
    &lt;span class="tok-k"&gt;esac&lt;/span&gt;
&lt;span class="tok-k"&gt;done&lt;/span&gt;

&lt;span class="tok-k"&gt;if&lt;/span&gt; &lt;span class="tok-o"&gt;[&lt;/span&gt; ! -z &lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$HEADER&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt; &lt;span class="tok-o"&gt;]&lt;/span&gt;&lt;span class="tok-p"&gt;;&lt;/span&gt; &lt;span class="tok-k"&gt;then&lt;/span&gt; &lt;b&gt;(3)&lt;/b&gt;
    &lt;span class="tok-nv"&gt;LUKSOPTS&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;&lt;span class="tok-s2"&gt;"--header=&lt;/span&gt;&lt;span class="tok-nv"&gt;$HEADER&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;
&lt;span class="tok-k"&gt;fi&lt;/span&gt;

&lt;span class="tok-c1"&gt;# Alias cryptsetup to insert options:&lt;/span&gt;
cryptsetup&lt;span class="tok-o"&gt;()&lt;/span&gt; &lt;span class="tok-o"&gt;{&lt;/span&gt;
    /usr/sbin/cryptsetup &lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$LUKSOPTS&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt; &lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$@&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;
&lt;span class="tok-o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Modify the &lt;code&gt;getopts&lt;/code&gt; spec adding &lt;code&gt;o:&lt;/code&gt; to the end. Since &lt;code&gt;h&lt;/code&gt; for header
wasn’t available, and &lt;code&gt;d&lt;/code&gt; for detached is already used for device, I chose &lt;code&gt;o&lt;/code&gt;
for option.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Add an entry to the &lt;code&gt;case&lt;/code&gt; statement to set a &lt;code&gt;$HEADER&lt;/code&gt; variable to the
value of the &lt;code&gt;o&lt;/code&gt; argument.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Add this and the following lines. The syntax for the &lt;code&gt;cryptsetup&lt;/code&gt; utility
is &lt;code&gt;cryptsetup &amp;lt;options&amp;gt; &amp;lt;action&amp;gt; &amp;lt;action args&amp;gt;&lt;/code&gt;. Clevis uses &lt;code&gt;cryptsetup&lt;/code&gt; to
bind to a LUKS volume. What we want to do is insert the &lt;code&gt;header&lt;/code&gt; option into
&lt;code&gt;&amp;lt;options&amp;gt;&lt;/code&gt; whenever Clevis calls &lt;code&gt;cryptsetup&lt;/code&gt;. A quick-and-dirty hack to
accomplish this with minimal changes to the code (especially since we’ll have
to remerge our changes whenever updating Clevis) is to alias &lt;code&gt;cryptsetup&lt;/code&gt; with
a function that calls the absolute path to the &lt;code&gt;cryptsetup&lt;/code&gt; executable with the
&lt;code&gt;header&lt;/code&gt; option and then appends the rest of the arguments Clevis originally
called &lt;code&gt;cryptsetup&lt;/code&gt; with.&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Now you can include the &lt;code&gt;-o&lt;/code&gt; argument we just added to Clevis to bind it to
the LUKS volume.&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo clevis luks &lt;span class="tok-nb"&gt;bind&lt;/span&gt; -o /boot/luksheader.img -d /dev/sda2 tang &lt;span class="tok-s1"&gt;'{"url": "http://tang.example.com"}'&lt;/span&gt;
&lt;span class="tok-go"&gt;The advertisement contains the following signing keys:&lt;/span&gt;

&lt;span class="tok-go"&gt;-Y7MtTJlw8rGGVgXRBhFtumJIqk&lt;/span&gt;

&lt;span class="tok-go"&gt;Do you wish to trust these keys? [ynYN] y&lt;/span&gt;
&lt;span class="tok-go"&gt;You are about to initialize a LUKS device for metadata storage.&lt;/span&gt;
&lt;span class="tok-go"&gt;Attempting to initialize it may result in data loss if data was&lt;/span&gt;
&lt;span class="tok-go"&gt;already written into the LUKS header gap in a different format.&lt;/span&gt;
&lt;span class="tok-go"&gt;A backup is advised before initialization is performed.&lt;/span&gt;

&lt;span class="tok-go"&gt;Do you wish to initialize /dev/sda2? [yn] y&lt;/span&gt;
&lt;span class="tok-go"&gt;Enter existing LUKS password:&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Modify &lt;code&gt;/usr/libexec/clevis-luks-askpass&lt;/code&gt; to support detached headers.
Modifying Clevis to bind to a LUKS volume with a deatched header is only
one   half of the equation. The Clevis Dracut module also needs to be able to
unlock the volume at boot.&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="sh"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-k"&gt;while&lt;/span&gt; &lt;span class="tok-nb"&gt;read&lt;/span&gt; -r line&lt;span class="tok-p"&gt;;&lt;/span&gt; &lt;span class="tok-k"&gt;do&lt;/span&gt; &lt;b&gt;(1)&lt;/b&gt;
  &lt;span class="tok-k"&gt;case&lt;/span&gt; &lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$line&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt; &lt;span class="tok-k"&gt;in&lt;/span&gt;
      &lt;span class="tok-nv"&gt;Id&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;cryptsetup:*&lt;span class="tok-o"&gt;)&lt;/span&gt; &lt;span class="tok-nv"&gt;d&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-si"&gt;${&lt;/span&gt;&lt;span class="tok-nv"&gt;line&lt;/span&gt;&lt;span class="tok-p"&gt;##Id=cryptsetup:&lt;/span&gt;&lt;span class="tok-si"&gt;}&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-p"&gt;;;&lt;/span&gt;
      &lt;span class="tok-nv"&gt;Socket&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;*&lt;span class="tok-o"&gt;)&lt;/span&gt; &lt;span class="tok-nv"&gt;s&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-si"&gt;${&lt;/span&gt;&lt;span class="tok-nv"&gt;line&lt;/span&gt;&lt;span class="tok-p"&gt;##Socket=&lt;/span&gt;&lt;span class="tok-si"&gt;}&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-p"&gt;;;&lt;/span&gt;
  &lt;span class="tok-k"&gt;esac&lt;/span&gt;
&lt;span class="tok-k"&gt;done&lt;/span&gt; &amp;lt; &lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$question&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt;

&lt;span class="tok-o"&gt;[&lt;/span&gt; -b &lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-si"&gt;${&lt;/span&gt;&lt;span class="tok-nv"&gt;d&lt;/span&gt;&lt;span class="tok-si"&gt;}&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt; &lt;span class="tok-o"&gt;]&lt;/span&gt; &lt;span class="tok-o"&gt;||&lt;/span&gt; &lt;span class="tok-k"&gt;continue&lt;/span&gt;
&lt;span class="tok-o"&gt;[&lt;/span&gt; -S &lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-si"&gt;${&lt;/span&gt;&lt;span class="tok-nv"&gt;s&lt;/span&gt;&lt;span class="tok-si"&gt;}&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt; &lt;span class="tok-o"&gt;]&lt;/span&gt; &lt;span class="tok-o"&gt;||&lt;/span&gt; &lt;span class="tok-k"&gt;continue&lt;/span&gt;

&lt;span class="tok-c1"&gt;# Detached header hack &lt;/span&gt;&lt;b&gt;(2)&lt;/b&gt;
&lt;span class="tok-k"&gt;if&lt;/span&gt; &lt;span class="tok-o"&gt;[&lt;/span&gt; &lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-si"&gt;${&lt;/span&gt;&lt;span class="tok-nv"&gt;d&lt;/span&gt;&lt;span class="tok-si"&gt;}&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt; &lt;span class="tok-o"&gt;=&lt;/span&gt; &lt;span class="tok-s2"&gt;"/dev/disk/by-partuuid/&amp;lt;PARTUUID-identifier&amp;gt;"&lt;/span&gt; &lt;span class="tok-o"&gt;]&lt;/span&gt;&lt;span class="tok-p"&gt;;&lt;/span&gt; &lt;span class="tok-k"&gt;then&lt;/span&gt; &lt;b&gt;(3)&lt;/b&gt;
  cryptsetup&lt;span class="tok-o"&gt;()&lt;/span&gt; &lt;span class="tok-o"&gt;{&lt;/span&gt;
      /usr/sbin/cryptsetup --header&lt;span class="tok-o"&gt;=&lt;/span&gt;/boot/luksheader.img &lt;span class="tok-s2"&gt;"&lt;/span&gt;&lt;span class="tok-nv"&gt;$@&lt;/span&gt;&lt;span class="tok-s2"&gt;"&lt;/span&gt; &lt;b&gt;(4)&lt;/b&gt;
  &lt;span class="tok-o"&gt;}&lt;/span&gt;
&lt;span class="tok-k"&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Locate the &lt;code&gt;while&lt;/code&gt; loop that reads the &lt;code&gt;ask.*&lt;/code&gt; files in &lt;code&gt;/run/systemd/ask-password&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Add this and the following lines.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;This is an ugly hack to get something that works with minimal changes
to the code. All the &lt;code&gt;ask.*&lt;/code&gt; file contains is the device, not any of the
options. If upstream Clevis supports detached headers in the future, ideally it
would do something like look up the device in &lt;code&gt;crypttab&lt;/code&gt; to get the header, but
I’m not familiar enough with the inner workings of Clevis and Dracut to try and
implement that in a quick hack. For now since we know which device we want to
unlock and where the header is located for that device, we’ll hardcode it into
&lt;code&gt;clevis-luks-askpass&lt;/code&gt;. Replace &lt;code&gt;&amp;lt;PARTUUID-identifier&amp;gt;&lt;/code&gt; with the PARTUUID of the
LUKS volume obtained above.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;Like we did to get Clevis to bind to a LUKS volume with a detached
header, alias &lt;code&gt;cryptsetup&lt;/code&gt; with a function that calls the absolute path to the
&lt;code&gt;cryptsetup&lt;/code&gt; executable with the &lt;code&gt;header&lt;/code&gt; option and then appends the rest of
the arguments Clevis originally called &lt;code&gt;cryptsetup&lt;/code&gt; with.&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;/li&gt;

&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Set the &lt;code&gt;rd.neednet&lt;/code&gt; Dracut kernel command line parameter to make sure
network is available in the initramfs for Clevis to be able to contact the Tang
server. Create &lt;code&gt;/etc/dracut.conf.d/clevis-nbde.conf&lt;/code&gt;:&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="cfg"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-na"&gt;kernel_cmdline&lt;/span&gt;&lt;span class="tok-o"&gt;=&lt;/span&gt;&lt;span class="tok-s"&gt;"rd.neednet=1"&lt;/span&gt;&lt;span class="tok-w"&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;





&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;

&lt;/td&gt;
&lt;td&gt;

&lt;p&gt;This step might not be necessary depending on your distro, version of Clevis,
and/or version of Dracut. Without it, Clevis failed to unlock the LUKS volume,
and I had to type the passphrase in at the prompt. After boot, I checked what
the issue was with &lt;code&gt;sudo journalctl | grep clevis&lt;/code&gt; and saw a whole bunch of
messages like this:&lt;/p&gt;




&lt;pre&gt;Jun 14 10:31:45 example clevis-luks-askpass[869]: Error communicating with the server http://tang.example.com&lt;/pre&gt;





&lt;p&gt;Adding the above Dracut configuration parameter
&lt;a href="https://github.com/latchset/clevis/issues/232"&gt;resolved the issue&lt;/a&gt;. It appears
this issue may not exist in versions of Clevis included with recent versions of
Fedora, but at least for the version of Dracut included with Oracle Linux 8.5,
the above workaround was still needed.&lt;/p&gt;



&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Regenerate initramfs. Include the LUKS detached header in the initramfs.&lt;/p&gt;



&lt;pre class="pygments highlight"&gt;&lt;code data-lang="console"&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="tok-gp"&gt;$ &lt;/span&gt;sudo dracut --install /boot/luksheader.img -f&lt;/code&gt;&lt;/pre&gt;





&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt;

&lt;/td&gt;
&lt;td&gt;
If it fails to automatically unlock at boot, you can troubleshoot the
issue with &lt;code&gt;sudo journalctl | grep clevis&lt;/code&gt;. If it fails to boot at all, you can
manually unlock the LUKS volume from the Dracut Emergency Shell, and then it
should proceed to boot. Then you can troubleshoot the issue by &lt;code&gt;grep&lt;/code&gt;ing the
output of &lt;code&gt;journalctl&lt;/code&gt;.
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>linux</category>
      <category>sysadmin</category>
      <category>encryption</category>
      <category>security</category>
    </item>
    <item>
      <title>Running Tomcat on Privileged Ports</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Mon, 06 Jun 2022 20:11:12 +0000</pubDate>
      <link>https://dev.to/goober99/running-tomcat-on-privileged-ports-j8n</link>
      <guid>https://dev.to/goober99/running-tomcat-on-privileged-ports-j8n</guid>
      <description>&lt;p&gt;By default, Tomcat listens on port 8080 for HTTP and 8443 for HTTPS. If you want Tomcat to listen on the standard HTTP (80) and HTTPS (443) ports, it’s not easy, because ports below 1024 are considered privileged ports on Linux and only available to processes running as root. It’s a very (very!) bad idea to run Tomcat as root.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse Proxy
&lt;/h2&gt;

&lt;p&gt;You can configure a reverse proxy or load balancer like HAProxy to proxy port 443 to 8443.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install HAProxy (&lt;code&gt;sudo yum install haproxy&lt;/code&gt; on Red Hat-derived distros and &lt;code&gt;sudo apt install haproxy&lt;/code&gt; on Debian-derived distros).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start HAProxy and configure it to restart at boot.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start haproxy
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;haproxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Edit &lt;code&gt;/etc/haproxy/haproxy.cfg&lt;/code&gt; and add:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enable Access on 443 (SSL Passthrough)
&lt;/span&gt;&lt;span class="n"&gt;frontend&lt;/span&gt; &lt;span class="n"&gt;tomcat&lt;/span&gt;-&lt;span class="n"&gt;proxy&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt;            &lt;span class="n"&gt;tcp&lt;/span&gt;
    &lt;span class="n"&gt;bind&lt;/span&gt;            :&lt;span class="m"&gt;443&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;  &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
    &lt;span class="n"&gt;default_backend&lt;/span&gt; &lt;span class="n"&gt;tomcat&lt;/span&gt;

&lt;span class="n"&gt;backend&lt;/span&gt; &lt;span class="n"&gt;tomcat&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt;            &lt;span class="n"&gt;tcp&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="n"&gt;connect&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;  &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt;          &lt;span class="n"&gt;tomcat&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;.&lt;span class="n"&gt;example&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;:&lt;span class="m"&gt;8443&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modify the Tomcat &lt;code&gt;server.xml&lt;/code&gt; and add &lt;code&gt;proxyPort=443&lt;/code&gt; to the &lt;code&gt;&amp;lt;Connector&amp;gt;&lt;/code&gt;. The &lt;code&gt;proxyPort&lt;/code&gt; attribute is used when Tomcat is run behind a proxy server. This attribute modifies the value returned to web applications that call the &lt;code&gt;request.getServerPort()&lt;/code&gt; method, which is often used to construct absolute URLs for redirects. Without configuring this attribute, the value returned would reflect the port on which the connection from the proxy server was received, rather than the port to whom the client directed the original request. See &lt;a href="https://tomcat.apache.org/tomcat-9.0-doc/config/http.html#Proxy_Support"&gt;Proxy Support&lt;/a&gt; in the Tomcat documentation for more info.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Authbind
&lt;/h2&gt;

&lt;p&gt;The authbind utility allows a program that would normally require superuser privileges to access privileged ports to run as a non-privileged user. It allows a system administrator to permit specific users and groups access to bind to TCP and UDP ports below 1024.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install authbind. On Debian-derived distros, you can do this with &lt;code&gt;sudo apt install authbind&lt;/code&gt;. It's not available in the repos of Red Hat-derived distros, but there is a &lt;a href="https://aaronsilber.me/2016/04/24/install-authbind-on-centos-7-x86_64-download-the-rpm/"&gt;pre-rolled RPM&lt;/a&gt; available. Download it and install with &lt;code&gt;sudo yum *.rpm&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Authorize the non-privileged user to access port 443. If you installed Tomcat from your distro's repos, it is probably run by the &lt;em&gt;tomcat&lt;/em&gt; user.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo touch&lt;/span&gt; /etc/authbind/byport/443
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;500 /etc/authbind/byport/443
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;tomcat /etc/authbind/byport/443
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modify the Tomcat &lt;code&gt;server.xml&lt;/code&gt; and configure Tomcat to listen to port 443.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prefix the command to start Tomcat with &lt;code&gt;/usr/bin/authbind --deep&lt;/code&gt;. If you are using systemd to start Tomcat, your &lt;code&gt;ExecStart&lt;/code&gt; would look something like this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ExecStart=/usr/bin/authbind --deep /opt/apache-tomcat-8.5.30/bin/startup.sh
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Normally authbind arranges for only the program which it directly invokes to be affected by its special version of &lt;code&gt;bind&lt;/code&gt;. If you specify &lt;code&gt;--deep&lt;/code&gt;, then all programs which that program invokes directly or indirectly will be affected, so long as they do not unset the environment variables set up by authbind.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Jsvc
&lt;/h2&gt;

&lt;p&gt;Jsvc is a set of libraries and applications for making Java applications run on Unix more easily. According to the &lt;a href="https://commons.apache.org/proper/commons-daemon/jsvc.html"&gt;documentation&lt;/a&gt;, "Jsvc allows the application (e.g. Tomcat) to perform some privileged operations as root (e.g. bind to a port &amp;lt; 1024), and then switch identity to a non-privileged user." I tried it but was never able to get it to work, and documentation and examples online seem sparse, so I abandoned it.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>security</category>
      <category>sysadmin</category>
      <category>tomcat</category>
    </item>
    <item>
      <title>Learn Scheme by Example: IUP GUI with Chicken Scheme</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Mon, 02 May 2022 04:41:04 +0000</pubDate>
      <link>https://dev.to/goober99/learn-scheme-by-example-iup-gui-with-chicken-scheme-4292</link>
      <guid>https://dev.to/goober99/learn-scheme-by-example-iup-gui-with-chicken-scheme-4292</guid>
      <description>&lt;p&gt;IUP is a cross-platform GUI toolkit that uses native controls. It uses GTK on Linux and, well, GTK on macOS too. According to the roadmap, there is a macOS native driver in the works, but it looks like one of those perpetually "coming soon" features. There are bindings for various programming languages including a Chicken egg. Instead of building yet another calculator, let's build a GUI for generating a tone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fiup.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fiup.png%3Fraw%3Dtrue" title="Example screenshot" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll need Chicken Scheme installed. It's available in the repositories of most Linux distros. You'll also need the IUP libraries installed before you can install the IUP egg. Unfortunately, IUP doesn't appear to be packaged for many Linux distros. Fortunately, there are pre-compiled binaries available for download on the IUP website. They are packaged as a tarball. Each tarball includes shell scripts for installing the libraries (moving them to system-wide locations for which &lt;code&gt;sudo&lt;/code&gt; may be required depending on your distro). In addition to &lt;a href="https://www.tecgraf.puc-rio.br/iup/en/download.html" rel="noopener noreferrer"&gt;IUP&lt;/a&gt;, you also need to download two other libraries also developed by Tecgraf Institute at PUC-Rio called &lt;a href="https://www.tecgraf.puc-rio.br/im/en/download.html" rel="noopener noreferrer"&gt;IM&lt;/a&gt; and &lt;a href="https://www.tecgraf.puc-rio.br/cd/en/download.html" rel="noopener noreferrer"&gt;CD&lt;/a&gt;. For Linux, there are different downloads for different kernel versions. These appear to correspond to versions of the Linux kernel that came with different Ubuntu releases. I use Debian Sid, which has a newer kernel than any Ubuntu release, so I just downloaded the package for the most recent kernel available, and it worked fine. Position yourself in the directory where you downloaded the tarballs and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;iup
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xpvf&lt;/span&gt; iup-&lt;span class="k"&gt;*&lt;/span&gt;_lib.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; iup
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iup/install
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;iup/install_dev
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo cp &lt;/span&gt;iup/ftgl/lib/Linux&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt; /usr/lib64
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;im
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xpvf&lt;/span&gt; im-&lt;span class="k"&gt;*&lt;/span&gt;_lib.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; im
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;im/install
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;im/install_dev
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;mkdir cd&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xpvf&lt;/span&gt; cd-&lt;span class="k"&gt;*&lt;/span&gt;_lib.tar.gz &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt;
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo cd&lt;/span&gt;/install
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo cd&lt;/span&gt;/install_dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above scripts install the libraries in non-standard paths on Debian, and &lt;code&gt;chicken-install&lt;/code&gt; is unable to find them. You can use the &lt;code&gt;CSC_OPTIONS&lt;/code&gt; environment variable to tell &lt;code&gt;chicken-install&lt;/code&gt; where to find these files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CSC_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'-I/usr/include/iup -I/usr/include/im -I/usr/include/cd -L/usr/lib64'&lt;/span&gt; chicken-install &lt;span class="nt"&gt;-sudo&lt;/span&gt; iup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll also need to specify the dynamic library path when executing the program with Chicken.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LD_LIBRARY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/lib64 csi bleep.scm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If using IUP from its native C, attribute setting is a separate step from object construction. The Scheme bindings add a little syntactic sugar that allow specifying your entire UI as an S-expression. Attribute names are keywords which can be passed directly to widget constructors along with child widgets. The attributes have a trailing colon. You can look up what attributes a widget accepts in the &lt;a href="https://www.tecgraf.puc-rio.br/iup/" rel="noopener noreferrer"&gt;original C documentation&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt; &lt;span class="nv"&gt;iup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;; Main window&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;dlg&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dialog&lt;/span&gt;
    &lt;span class="nv"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Bleep"&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;vbox&lt;/span&gt;
      &lt;span class="nv"&gt;alignment:&lt;/span&gt; &lt;span class="ss"&gt;'ACENTER&lt;/span&gt;
      &lt;span class="nv"&gt;gap:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
      &lt;span class="nv"&gt;margin:&lt;/span&gt; &lt;span class="ss"&gt;'25x25&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt; &lt;span class="nv"&gt;dlg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main-loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;destroy!&lt;/span&gt; &lt;span class="nv"&gt;dlg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;vbox&lt;/code&gt; is an invisible widget that helps with layout. Let's start by placing a slider within this &lt;code&gt;vbox&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;valuator&lt;/span&gt;
    &lt;span class="nv"&gt;min:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
    &lt;span class="nv"&gt;max:&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;
    &lt;span class="nv"&gt;value:&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;
    &lt;span class="nv"&gt;expand:&lt;/span&gt; &lt;span class="ss"&gt;'Yes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;; Main window&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;dlg&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dialog&lt;/span&gt;
    &lt;span class="nv"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Bleep"&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;vbox&lt;/span&gt;
      &lt;span class="nv"&gt;alignment:&lt;/span&gt; &lt;span class="ss"&gt;'ACENTER&lt;/span&gt;
      &lt;span class="nv"&gt;gap:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
      &lt;span class="nv"&gt;margin:&lt;/span&gt; &lt;span class="ss"&gt;'25x25&lt;/span&gt;
      &lt;span class="nv"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In IUP parlance, a slider is a &lt;code&gt;valuator&lt;/code&gt;. IUP convention is to use "Yes" and "No" as boolean values. In Scheme you can use the 'Yes and 'No symbols. To help with organization, you can store the widget in a variable. You can add child widgets to the &lt;code&gt;vbox&lt;/code&gt; by passing them to the &lt;code&gt;vbox&lt;/code&gt; constructor after any other attributes (actually, the child widgets can be passed before other attributes or even mixed in with the other attributes, but I find it clearest to keep them grouped at the end).&lt;/p&gt;

&lt;p&gt;The range of frequencies audible by humans is typically between 20 Hz and 20 KHz (we lose the ability to hear some of those higher frequencies as we age). The &lt;a href="https://en.wikipedia.org/wiki/A440_(pitch_standard)" rel="noopener noreferrer"&gt;musical note A above middle C&lt;/a&gt; is 440 Hz. Since A4 serves as a general tuning standard, it seems like a sensible default, but if you run the above in Chicken, this is what you'll see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fiup-linearslider.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fiup-linearslider.png%3Fraw%3Dtrue" title="Slider showing 440 using a linear scale" alt="Slider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scale of 20 to 20,000 is so large that 440 doesn't appear to move the slider at all. Ideally, 440 would fall about the middle of the slider. To achieve this, let's use a logarithmic scale.&lt;/p&gt;

&lt;p&gt;I found a &lt;a href="https://stackoverflow.com/questions/846221/logarithmic-slider/846249#846249" rel="noopener noreferrer"&gt;Stack Overflow answer&lt;/a&gt; on how to map a slider to a logarithmic scale. The code given in the answer is JavaScript, but it was easy enough to port to Scheme.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Scale used by slider&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*max-position*&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Range of frequencies&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;; Logarithmic scale for frequency (so middle A [440] falls about in the middle)&lt;/span&gt;
&lt;span class="c1"&gt;; Adapted from https://stackoverflow.com/questions/846221/logarithmic-slider&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;max-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;max-freq&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*max-position*&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="c1"&gt;; Convert slider position to frequency&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;position-&amp;gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;exp&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;position&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;span class="c1"&gt;; Convert frequency to slider position&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added some global parameters to the top of the script. The variable name &lt;code&gt;*min-position*&lt;/code&gt; is just a Lisp naming convention for global parameters. I came up with the range of 0-2,000 by trial and error. It seemed to strike the best balance between each step of the slider making a noticeable change to the frequency while still allowing the user to narrow in on a specific frequency with just the slider.&lt;/p&gt;

&lt;p&gt;Then we create two functions: one that takes the position on the slider and returns the frequency (&lt;code&gt;position-&amp;gt;frequency&lt;/code&gt;) and another that takes a frequency and returns the position on the slider (&lt;code&gt;frequency-position&lt;/code&gt;). Now let's set the initial position of our slider with the &lt;code&gt;frequency-&amp;gt;position&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;valuator&lt;/span&gt;
    &lt;span class="nv"&gt;min:&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt;
    &lt;span class="nv"&gt;max:&lt;/span&gt; &lt;span class="nv"&gt;*max-position*&lt;/span&gt;
    &lt;span class="nv"&gt;value:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;expand:&lt;/span&gt; &lt;span class="ss"&gt;'Yes&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Underneath the slider is a spin box showing the current frequency and buttons to increase/decrease the frequency by one octave.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;textbox&lt;/span&gt;
    &lt;span class="nv"&gt;spin:&lt;/span&gt; &lt;span class="ss"&gt;'Yes&lt;/span&gt;
    &lt;span class="nv"&gt;spinmin:&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt;
    &lt;span class="nv"&gt;spinmax:&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt;
    &lt;span class="nv"&gt;spinvalue:&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;frequency-controls&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hbox&lt;/span&gt;
    &lt;span class="nv"&gt;alignment:&lt;/span&gt; &lt;span class="ss"&gt;'ACENTER&lt;/span&gt;
    &lt;span class="nv"&gt;gap:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
    &lt;span class="nv"&gt;margin:&lt;/span&gt; &lt;span class="ss"&gt;'0x0&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt; &lt;span class="nv"&gt;title:&lt;/span&gt; &lt;span class="ss"&gt;'&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hbox&lt;/span&gt;
      &lt;span class="nv"&gt;alignment:&lt;/span&gt; &lt;span class="ss"&gt;'ACENTER&lt;/span&gt;
      &lt;span class="nv"&gt;gap:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
      &lt;span class="nv"&gt;frequency-field&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt; &lt;span class="s"&gt;"Hz"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt; &lt;span class="nv"&gt;title:&lt;/span&gt; &lt;span class="ss"&gt;'&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="c1"&gt;; Main window&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;dlg&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dialog&lt;/span&gt;
    &lt;span class="nv"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Bleep"&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;vbox&lt;/span&gt;
      &lt;span class="nv"&gt;alignment:&lt;/span&gt; &lt;span class="ss"&gt;'ACENTER&lt;/span&gt;
      &lt;span class="nv"&gt;gap:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
      &lt;span class="nv"&gt;margin:&lt;/span&gt; &lt;span class="ss"&gt;'25x25&lt;/span&gt;
      &lt;span class="nv"&gt;slider&lt;/span&gt;
      &lt;span class="nv"&gt;frequency-controls&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In IUP a spin box is just a text box that has the &lt;code&gt;spin&lt;/code&gt; attribute set to "Yes." At this point, we are starting to have a nice looking interface, but it doesn't do anything. If you click the buttons or slide the slider, nothing happens. Widgets also take callbacks. Callbacks are specified just like attributes and are functions that take one or more arguments, usually self, which is the widget to which the callback belongs, and return a symbol: usually &lt;code&gt;'default&lt;/code&gt; but also &lt;code&gt;'close&lt;/code&gt; to close a dialog or others such as &lt;code&gt;'ignore&lt;/code&gt; and &lt;code&gt;'continue&lt;/code&gt;. The names of callbacks that a widget accepts and their arguments can be looked up in the &lt;a href="https://www.tecgraf.puc-rio.br/iup/" rel="noopener noreferrer"&gt;original C documentation&lt;/a&gt;. Both the slider/valuator and the spin box have a &lt;code&gt;valuechanged-cb&lt;/code&gt; callback (converted from &lt;code&gt;valuechanged_cb&lt;/code&gt; in the original C API).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;valuator&lt;/span&gt;
    &lt;span class="nv"&gt;min:&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt;
    &lt;span class="nv"&gt;max:&lt;/span&gt; &lt;span class="nv"&gt;*max-position*&lt;/span&gt;
    &lt;span class="nv"&gt;value:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nv"&gt;expand:&lt;/span&gt; &lt;span class="ss"&gt;'Yes&lt;/span&gt;
    &lt;span class="nv"&gt;valuechanged-cb:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;attribute-set!&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="nv"&gt;spinvalue:&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;position-&amp;gt;frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;attribute&lt;/span&gt; &lt;span class="nv"&gt;self&lt;/span&gt; &lt;span class="nv"&gt;value:&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
      &lt;span class="ss"&gt;'default&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;textbox&lt;/span&gt;
    &lt;span class="nv"&gt;spin:&lt;/span&gt; &lt;span class="ss"&gt;'Yes&lt;/span&gt;
    &lt;span class="nv"&gt;spinmin:&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt;
    &lt;span class="nv"&gt;spinmax:&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt;
    &lt;span class="nv"&gt;spinvalue:&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;
    &lt;span class="nv"&gt;valuechanged-cb:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;attribute-set!&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="nv"&gt;value:&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;attribute&lt;/span&gt; &lt;span class="nv"&gt;self&lt;/span&gt; &lt;span class="nv"&gt;spinvalue:&lt;/span&gt;&lt;span class="p"&gt;)))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;attribute&lt;/code&gt; and &lt;code&gt;attribute-set&lt;/code&gt; functions get the value of an attribute and set the value of an attribute, respectively. The first argument to each function is a widget, the second an attribute. When setting an attribute, you also provide an additional argument with the new value for the attribute. Attribute values are always returned as strings. Our logarithmic conversion functions take numbers, so we have to convert the attribute string to a number with &lt;code&gt;string-&amp;gt;number&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Wire the &lt;code&gt;action&lt;/code&gt; callbacks of the buttons up to functions called &lt;code&gt;decrease-octave&lt;/code&gt; and &lt;code&gt;increase-octave&lt;/code&gt;. An &lt;a href="https://en.wikipedia.org/wiki/Octave" rel="noopener noreferrer"&gt;octave&lt;/a&gt; is "the interval between one musical pitch and another with double its frequency."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Set frequency slider and display&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;attribute-set!&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="nv"&gt;value:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;attribute-set!&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="nv"&gt;value:&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="c1"&gt;; Buttons increase and decrease frequency by one octave&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-octave&lt;/span&gt; &lt;span class="nv"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;attribute&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="nv"&gt;spinvalue:&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nv"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decrease-octave&lt;/span&gt; &lt;span class="nv"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-octave&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;increase-octave&lt;/span&gt; &lt;span class="nv"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-octave&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's use another spin box to specify the duration of the beep in milliseconds.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;textbox&lt;/span&gt;
    &lt;span class="nv"&gt;spin:&lt;/span&gt; &lt;span class="ss"&gt;'Yes&lt;/span&gt;
    &lt;span class="nv"&gt;spinmin:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nv"&gt;spinmax:&lt;/span&gt; &lt;span class="mi"&gt;600000&lt;/span&gt; &lt;span class="c1"&gt;; 10 minutes&lt;/span&gt;
    &lt;span class="nv"&gt;spinvalue:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;general-controls&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hbox&lt;/span&gt;
    &lt;span class="nv"&gt;alignment:&lt;/span&gt; &lt;span class="ss"&gt;'ACENTER&lt;/span&gt;
    &lt;span class="nv"&gt;gap:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
    &lt;span class="nv"&gt;margin:&lt;/span&gt; &lt;span class="ss"&gt;'0x0&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hbox&lt;/span&gt;
      &lt;span class="nv"&gt;alignment:&lt;/span&gt; &lt;span class="ss"&gt;'ACENTER&lt;/span&gt;
      &lt;span class="nv"&gt;gap:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
      &lt;span class="nv"&gt;duration-field&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt; &lt;span class="s"&gt;"ms"&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;span class="c1"&gt;; Main window&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;dlg&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dialog&lt;/span&gt;
    &lt;span class="nv"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Bleep"&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;vbox&lt;/span&gt;
      &lt;span class="nv"&gt;alignment:&lt;/span&gt; &lt;span class="ss"&gt;'ACENTER&lt;/span&gt;
      &lt;span class="nv"&gt;gap:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
      &lt;span class="nv"&gt;margin:&lt;/span&gt; &lt;span class="ss"&gt;'25x25&lt;/span&gt;
      &lt;span class="nv"&gt;slider&lt;/span&gt;
      &lt;span class="nv"&gt;frequency-controls&lt;/span&gt;
      &lt;span class="nv"&gt;general-controls&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frequency is rather abstract. Let's also give the user the ability to select a musical note. We can store the corresponding frequencies for A4-G4 in an association list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Notes -&amp;gt; frequency (middle A-G [A4-G4])&lt;/span&gt;
&lt;span class="c1"&gt;; http://pages.mtu.edu/~suits/notefreqs.html&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;notes&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt; &lt;span class="mf"&gt;440.00&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"B"&lt;/span&gt; &lt;span class="mf"&gt;493.88&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="mf"&gt;261.63&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"D"&lt;/span&gt; &lt;span class="mf"&gt;293.66&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"E"&lt;/span&gt; &lt;span class="mf"&gt;329.63&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"F"&lt;/span&gt; &lt;span class="mf"&gt;349.23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"G"&lt;/span&gt; &lt;span class="mf"&gt;292.00&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll give the user a drop-down menu. Whenever a note is selected from the drop-down menu, we'll look up the frequency in the association list and set it using the &lt;code&gt;set-frequency&lt;/code&gt; helper function we created for the octave buttons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;general-controls&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hbox&lt;/span&gt;
    &lt;span class="nv"&gt;alignment:&lt;/span&gt; &lt;span class="ss"&gt;'ACENTER&lt;/span&gt;
    &lt;span class="nv"&gt;gap:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
    &lt;span class="nv"&gt;margin:&lt;/span&gt; &lt;span class="ss"&gt;'0x0&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hbox&lt;/span&gt;
      &lt;span class="nv"&gt;alignment:&lt;/span&gt; &lt;span class="ss"&gt;'ACENTER&lt;/span&gt;
      &lt;span class="nv"&gt;gap:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
      &lt;span class="nv"&gt;duration-field&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt; &lt;span class="s"&gt;"ms"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hbox&lt;/span&gt;
      &lt;span class="nv"&gt;alignment:&lt;/span&gt; &lt;span class="ss"&gt;'ACENTER&lt;/span&gt;
      &lt;span class="nv"&gt;gap:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt; &lt;span class="s"&gt;"♪"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;listbox&lt;/span&gt;
        &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;:1&lt;/span&gt; &lt;span class="s"&gt;"A"&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;:2&lt;/span&gt; &lt;span class="s"&gt;"B"&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;:3&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;:4&lt;/span&gt; &lt;span class="s"&gt;"D"&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;:5&lt;/span&gt; &lt;span class="s"&gt;"E"&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;:6&lt;/span&gt; &lt;span class="s"&gt;"F"&lt;/span&gt; &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;:7&lt;/span&gt; &lt;span class="s"&gt;"G"&lt;/span&gt;
        &lt;span class="nv"&gt;value:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="nv"&gt;dropdown:&lt;/span&gt; &lt;span class="ss"&gt;'Yes&lt;/span&gt;
        &lt;span class="nv"&gt;action:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;self&lt;/span&gt; &lt;span class="nv"&gt;text&lt;/span&gt; &lt;span class="nv"&gt;item&lt;/span&gt; &lt;span class="nv"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cadr&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;assoc&lt;/span&gt; &lt;span class="nv"&gt;text&lt;/span&gt; &lt;span class="nv"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;))))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's make some noise. There are Chicken Scheme &lt;a href="http://wiki.call-cc.org/eggref/5/allegro" rel="noopener noreferrer"&gt;bindings&lt;/a&gt; to the &lt;a href="https://en.wikipedia.org/wiki/Allegro_(software_library)" rel="noopener noreferrer"&gt;Allegro&lt;/a&gt; library. Allegro is a library primarily used by games for cross-platform graphics, input devices, and more. What we're interested in is the audio addon that can be used to generate a tone with a sine wave. You'll need to install the Allegro library. Make sure you also install the header files. In some Linux distros, these are split into a separate package (e.g. &lt;code&gt;liballegro5-dev&lt;/code&gt; on Debian). Also, install the Allegro egg (&lt;code&gt;chicken-install -sudo allegro&lt;/code&gt;). I added the following lines near the top to import the Allegro bindings (and the chicken memory module, which we'll also use).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prefix&lt;/span&gt; &lt;span class="nv"&gt;allegro&lt;/span&gt; &lt;span class="s"&gt;"al:"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;chicken&lt;/span&gt; &lt;span class="nv"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;+pi+&lt;/span&gt; &lt;span class="mf"&gt;3.141592&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You must place the code to initialize Allegro between the function to &lt;code&gt;show&lt;/code&gt; the main IUP dialog and starting the IUP &lt;code&gt;main-loop&lt;/code&gt;. If it comes before &lt;code&gt;show&lt;/code&gt;, the program will die with a &lt;code&gt;segmentation violation&lt;/code&gt;, and it can't come after the &lt;code&gt;main-loop&lt;/code&gt;, because it would never get executed until the window closed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Display GUI&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;show&lt;/span&gt; &lt;span class="nv"&gt;dlg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;; Initialize Allegro and audio addon&lt;/span&gt;
&lt;span class="c1"&gt;; Must be initialized afer showing the main dialog but before starting the main&lt;/span&gt;
&lt;span class="c1"&gt;; loop or else program will die with a segmentation violation.&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="s"&gt;"Could not initialize Allegro."&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-addon-install&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="s"&gt;"Could not initialize sound."&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:reserve-samples&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;; Start IUP main loop&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main-loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;destroy!&lt;/span&gt; &lt;span class="nv"&gt;dlg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Allegro egg is accompanied by a couple of examples but no examples showing the use of the audio addon. The Allegro library itself comes with an &lt;a href="https://github.com/liballeg/allegro5/blob/master/examples/ex_saw.c" rel="noopener noreferrer"&gt;example showing how to generate a saw wave&lt;/a&gt;, but being a C library, the example is, of course, in C. I &lt;a href="https://github.com/goober99/lisp-gui-examples/blob/master/examples/pstk/saw.scm" rel="noopener noreferrer"&gt;ported that example to Scheme&lt;/a&gt;. I would have contributed the example back to the Allegro egg, but the repo is marked as "archived by the owner" and read-only on GitHub. I've included the example in the repo alongside the rest of the code for this tutorial in case someone finds it useful.&lt;/p&gt;

&lt;p&gt;Allegro is very low-level. You create an audio &lt;code&gt;stream&lt;/code&gt;. In this case, the stream buffers eight fragments of 1,024 samples each at a frequency (often called sampling rate) of 44,100 Hz (the sampling rate of an audio CD), which means there are 44,100 samples per second. Each sample is a 32-bit float (what is called the bit depth of the audio), and we only have one channel to keep things as simple as possible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Generate a tone using Allegro&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate-tone&lt;/span&gt; &lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;samples-per-buffer&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;stream-frequency&lt;/span&gt; &lt;span class="mi"&gt;44100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;amplitude&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:make-audio-stream&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="nv"&gt;samples-per-buffer&lt;/span&gt; &lt;span class="nv"&gt;stream-frequency&lt;/span&gt; &lt;span class="ss"&gt;'float32&lt;/span&gt; &lt;span class="ss"&gt;'one&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;queue&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:make-event-queue&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;event&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:make-event&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-stream-attach-to-mixer!&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:default-mixer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="s"&gt;"Could not attach stream to mixer."&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:event-queue-register-source!&lt;/span&gt; &lt;span class="nv"&gt;queue&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-stream-event-source&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;event-loop&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="c1"&gt;; Grab and handle events&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="nv"&gt;duration&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;stream-frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;samples-per-buffer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:event-queue-wait!&lt;/span&gt; &lt;span class="nv"&gt;queue&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:event-type&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;'audio-stream-fragment&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;buffer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-stream-fragment&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
            &lt;span class="c1"&gt;; If the stream is not ready for new data, buffer will be null.&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;not&lt;/span&gt; &lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;event-loop&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;begin&lt;/span&gt;
              &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fill-buffer&lt;/span&gt; &lt;span class="nv"&gt;buffer&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;; Placeholder&lt;/span&gt;
              &lt;span class="c1"&gt;; Repeat&lt;/span&gt;
              &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;event-loop&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))))))))&lt;/span&gt;

    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-stream-drain&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An event loop waits for the audio stream to ask for another buffer. Our job is to fill that buffer with 1,024 32-bit floats at a time. In the code listing above, this is done by &lt;code&gt;fill-buffer&lt;/code&gt;. That was just a placeholder, so I could break the code up into shorter, more easily explainable chunks. This is what goes in the place of &lt;code&gt;(fill-buffer buffer n)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;adr&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pointer-&amp;gt;address&lt;/span&gt; &lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;i&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="nv"&gt;samples-per-buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;samples-per-buffer&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;stream-frequency&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="c1"&gt;; al:audio-stream-fragment returns a C pointer. Use (chicken&lt;/span&gt;
        &lt;span class="c1"&gt;; memory) module to operate on foreign pointer objects.&lt;/span&gt;
        &lt;span class="c1"&gt;; Iterate over array four bytes at a time since 32-bit depth.&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pointer-f32-set!&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;address-&amp;gt;pointer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;adr&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;amplitude&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;+pi+&lt;/span&gt; &lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;time&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-stream-fragment-set!&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt; &lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="s"&gt;"Error setting stream fragment"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Allegro egg is a pretty thin wrapper of the Allegro library. The &lt;code&gt;audio-stream-fragment&lt;/code&gt; procedure in the egg just passes along the C pointer that the corresponding &lt;code&gt;al_get_audio_stream_fragment&lt;/code&gt; function from the C library returns. It would have been nice if the egg had offered some Scheme conveniences atop Allegro like allowing us to pass a Scheme list or array to Allegro to provide the buffer of samples. Since it doesn't, we'll use the chicken memory module to fill the C array starting at the C pointer returned by &lt;code&gt;audio-stream-fragment&lt;/code&gt;. We use &lt;code&gt;pointer-&amp;gt;address&lt;/code&gt; to get the address of the pointer. A pointer references a byte of memory. We can reference the preceding or following byte by subtracting or adding 1 to the address. Since we are filling the array with 32-bit floats, and 32 bits is 4 bytes, we want to increment the address by 4 each time. Then we can set the value of the current location with &lt;code&gt;pointer-f32-set!&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then you just need to feed Allegro buffers of 1,024 samples at a time. The &lt;a href="http://pld.cs.luc.edu/telecom/mnotes/digitized_sound.html" rel="noopener noreferrer"&gt;basic formula for a sine wave&lt;/a&gt; is A sin(2πft) where &lt;em&gt;A&lt;/em&gt; is amplitude, &lt;em&gt;f&lt;/em&gt; is frequency, and &lt;em&gt;t&lt;/em&gt; is time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;amplitude&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;+pi+&lt;/span&gt; &lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;time&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire this up to a play button, and you're ready to make some noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt;
  &lt;span class="nv"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;"Play"&lt;/span&gt;
  &lt;span class="nv"&gt;action:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate-tone&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;attribute&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="nv"&gt;spinvalue:&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;attribute&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt; &lt;span class="nv"&gt;spinvalue:&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://wiki.call-cc.org/eggref/5/iup" rel="noopener noreferrer"&gt;documentation for the iup egg&lt;/a&gt; is pretty sparse, but &lt;a href="https://wiki.call-cc.org/iup-tutor" rel="noopener noreferrer"&gt;this tutorial&lt;/a&gt; on using the iup egg was indispensable in writing the above tutorial.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can check out the entire example on &lt;a href="https://github.com/goober99/lisp-gui-examples" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. This started as a personal learning project to explore the state of GUI programming in Lisp and has become a series of tutorials on building GUIs with various dialects of Lisp.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>lisp</category>
      <category>scheme</category>
      <category>gui</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Introduction to the Sam Text Editor</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Fri, 24 Dec 2021 18:42:37 +0000</pubDate>
      <link>https://dev.to/goober99/introduction-to-the-sam-text-editor-5hi9</link>
      <guid>https://dev.to/goober99/introduction-to-the-sam-text-editor-5hi9</guid>
      <description>&lt;p&gt;Programmers are passionate about their tools, and if there is one tool they are especially passionate about, it is their text editor. An &lt;a href="https://en.wikipedia.org/wiki/Editor_war" rel="noopener noreferrer"&gt;editor war&lt;/a&gt; has raged between Emacs and vi for decades, and new challengers such as VS Code and Sublime Text vie for superiority. I decided to try out an editor that isn't near as popular as these others but has no less a distinguished pedigree.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Sam_(text_editor)" rel="noopener noreferrer"&gt;Sam&lt;/a&gt; was one of two text editors included in the Plan 9 operating system. It was originally designed by Rob Pike for the Blit windowing terminal in the early 1980s.&lt;/p&gt;

&lt;p&gt;Sam for modern Linux and BSD can be obtained from two different sources. One is &lt;a href="https://9fans.github.io/plan9port/" rel="noopener noreferrer"&gt;Plan 9 from User Space&lt;/a&gt; that ports Plan 9 programs to other Unix-like operating systems, and the other is &lt;a href="https://github.com/deadpixi/sam" rel="noopener noreferrer"&gt;Deadpixi Sam&lt;/a&gt; that has continued development of the 1990s Unix port of Sam. I installed and reviewed Deadpixi Sam. If you install a different version of Sam, some of the configuration details might differ, but most of the information here should apply to all versions of Sam.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Brief Tutorial
&lt;/h2&gt;

&lt;p&gt;Vi is notorious for trapping novice programmers who don't even know how to exit it. I plead guilty here: The first time I was dropped into a vi session on an HP-UX server, I couldn't get it to do anything and was too afraid to ask someone thinking I had broken something. The learning curve for Sam isn't quite as steep, but it also isn't obvious what to do when you first launch Sam and are presented with this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxxpgydqn6av3tz0ys89.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxxpgydqn6av3tz0ys89.png" alt="Sam on first launch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You could, of course, read the man page, but the man page being a reference source isn't really laid out in a way that introduces you to Sam. It starts out by detailing a whole bunch of editing commands you can't even use yet, because it's not clear how to even load a text file to edit. You have to read through over half of the man page before you get to that. Despite Sam being around since the 1980s, I couldn't find any quick tutorial to get you up and running in a couple minutes to experiment with Sam, so I'll try to do that here.&lt;/p&gt;

&lt;p&gt;When you first launch Sam, it is divided into top and bottom areas or "windows." The top window is the command window. This is where you type commands to manipulate text and control Sam. The bottom is a text window. You start out with one text window, but you can open as many text windows as you want. The text windows display the contents of the files being edited.&lt;/p&gt;

&lt;p&gt;Deadpixi Sam can be configured with a &lt;code&gt;.samrc&lt;/code&gt; file in your home directory. I recommend copying the example that comes packaged with Deadpixi Sam in &lt;code&gt;doc/samrc&lt;/code&gt; to &lt;code&gt;~/.samrc&lt;/code&gt;, because it comes with some helpful defaults. To edit this file with Sam, you would load it by typing &lt;code&gt;B .samrc&lt;/code&gt; in the command window followed by Enter. The cursor will change. Point at the text window. Select it to display the file by right clicking anywhere inside it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h9f2tg6sldojjiytsuk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h9f2tg6sldojjiytsuk.png" alt="Selecting a text window for current file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can left click within the text window to move the text cursor around. You can left click and drag to select text or double click to select a word. If you double click just inside brackets, parentheses, quotes, etc., it will select all the text within the brackets. By default, you can navigate with the keyboard using Ctrl+e,x,d,s to move the text cursor up, down, right, and left. The reason I highly recommended copying over the &lt;code&gt;samrc&lt;/code&gt; that comes with Deadpixi Sam is because it enables moving the text cursor with the arrow keys and the more familiar (at least to those coming from Vim) Ctrl+k,j,l,h. You can edit files directly in the text window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyl5rk38jw505cv7djn8e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyl5rk38jw505cv7djn8e.png" alt="Menu of commands to manipulate windows"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Right click and hold (with most modern operating systems, we are used to right clicking and releasing to access context menus, but you have to keep the right mouse button held down to access the menu in Sam) to access a menu with commands to manipulate windows. Some of the commands give you a crosshairs cursor to select the size and position of windows. Below the window operators is a list of available files starting with &lt;code&gt;~~sam~~&lt;/code&gt;, the command window. Selecting a file from the list makes the most recently used window on that file current. If no windows are open on the file, you are prompted to open one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6kov76la0kvlz7oigue.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft6kov76la0kvlz7oigue.png" alt="Menu of editing commands"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Middle click and hold (just like above but hold down on your mouse wheel) to access a menu with editing commands. With this menu you can do things like copy (called snarf in Sam for some reason), cut, and paste. If you copied the &lt;code&gt;samrc&lt;/code&gt; over that comes with Deadpixi Sam, it binds the familiar Ctrl+x,c,v,q with cut, snarf, paste, and exchange.&lt;/p&gt;

&lt;p&gt;Sam doesn't use the system clipboard, but you can swap Sam's snarf with the system with &lt;code&gt;&amp;lt;exch&amp;gt;&lt;/code&gt; from the editing menu. This allows you to copy and paste into other apps and vice versa. By default, it swaps snarf with the system selection. In Linux, this is what pastes when you middle click. This didn't integrate with the clipboard manager I use, so I added a line to &lt;code&gt;.samrc&lt;/code&gt; to tell Sam to use the system clipboard instead of the system selection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$
a/\n# Make work with system clipboard manager\n/
a/snarfselection clipboard/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dollar sign tells Sam to jump to the end of the file. The &lt;code&gt;a/text/&lt;/code&gt; command appends text at the current location (there is also &lt;code&gt;i&lt;/code&gt; to insert and &lt;code&gt;c&lt;/code&gt; to replace). I did this to demonstrate some of the commands you can use in Sam, but for the above example, it probably would have been easier to type the above directly into the text window. You can also type regular expressions like &lt;code&gt;/font/&lt;/code&gt; into the command window to locate strings in the text window. Consult the man file that comes with Sam for a complete list of available commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzs31jqqrnci99z9ania.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzs31jqqrnci99z9ania.png" alt="Appending option to Sam configuration file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we're ready to save our changes and exit Sam. Type &lt;code&gt;w&lt;/code&gt; in the command window to write the changes to the file. Then type &lt;code&gt;q&lt;/code&gt; to quit. If you type &lt;code&gt;q&lt;/code&gt; when there are unsaved changes, instead of closing, Sam will print &lt;code&gt;?changed files&lt;/code&gt;. To force it to close with unsaved changes, just type &lt;code&gt;q&lt;/code&gt; a second time. If you copied the &lt;code&gt;samrc&lt;/code&gt; over that comes with Deadpixi Sam, it binds the familiar Ctrl+s with write (and also Ctrl+z to undo).&lt;/p&gt;

&lt;h2&gt;
  
  
  Review of Sam
&lt;/h2&gt;

&lt;p&gt;Sam does a lot of things different than the conventions used by most modern operating systems. For example, the scrollbar is on the left instead of the right. You can grab the scrollbar by left clicking and drag to scroll up and down, but moving your mouse up scrolls down, and moving your mouse down scrolls up. As mentioned in my review, you must hold down the right and middle mouse button to select from the menus (you can't just click and release). These would be fine if they followed the conventions of the operating system, but they will be the opposite of what most new users expect. This is because Sam is almost 40 years old and was created before many of these conventions were established.&lt;/p&gt;

&lt;p&gt;It's obvious from projects like Plan 9 from User Space and posts on Hacker News and Reddit that Plan 9 still has a number of ardent followers. Sam is also reportedly the preferred editor of Ken Thompson, Bjarne Stroustrup, and Brian Kernighan. A project like Deadpixi has to tread a fine line between preserving Sam as it was to satisfy users who have built up muscle memory and modernizing it to attract new users. Copying the &lt;code&gt;samrc&lt;/code&gt; over that comes with Deadpixi goes a long way toward configuring key bindings that make Sam mostly behave like other text entry in modern operating systems. What I still miss is being able to hold Shift while using the arrow keys to select text, and I don't think there's any way to do this just using &lt;code&gt;.samrc&lt;/code&gt;. I made a couple additional tweaks to my &lt;code&gt;.samrc&lt;/code&gt; during the couple of days I played around with Sam.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bind * Home command bol
bind * End command eol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the Home and End key behave as you would expect. The maintainer of Deadpixi sets 12 pt. Go Regular as the font in the example, &lt;code&gt;samrc&lt;/code&gt;; I changed this to 13 pt. Inconsolata. The Deadpixi example &lt;code&gt;samrc&lt;/code&gt; also adds a list of background colors for new windows, but I decided to stick with just white. I set &lt;code&gt;tabs&lt;/code&gt; to 2 spaces and the aforementioned &lt;code&gt;snarfselection&lt;/code&gt; to &lt;code&gt;clipboard&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Sam was created before desktop environments and window managers were common. The original Sam was designed to run on a terminal (not a terminal emulator like you have on your computer but an actual terminal). Instead of using more modern metaphors such as tabs, Sam includes its own rudimentary window management. You can have multiple text windows open and make Sam look something like this (the screenshot from the Deadpixi REAMDE):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fui0fm67jpsi1gdjy9a04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fui0fm67jpsi1gdjy9a04.png" alt="Sam with multiple text windows open"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only way to create and position windows is using the mouse to sweep an area for the window. This is awkward, and during the couple of days I experimented with Sam, I primarily used the right-click menu to switch between which file occupied the single text window that was created upon launch. Key bindings for window tiling or even Acme-like tiling is listed as a development goal in the Deadpixi README but not yet implemented.&lt;/p&gt;

&lt;p&gt;The Sam GUI is built with &lt;a href="https://en.wikipedia.org/wiki/X_Toolkit_Intrinsics" rel="noopener noreferrer"&gt;Xt&lt;/a&gt;, a library that provides primitives to create widgets for the X Window System used by widget toolkits like Motif (whereas GTK and Qt draw their own widgets). This gives it a decidedly dated look and feel. It's telling the the screenshots in Rob Pike's &lt;a href="http://doc.cat-v.org/plan_9/4th_edition/papers/sam/" rel="noopener noreferrer"&gt;original 1987 paper&lt;/a&gt; describing Sam look exactly like Sam does today. Removing the Xt dependency is listed as a stretch goal in the Deadpixi README, but continued development seems slow. At the time of this writing, it has been over a year since the last commit.&lt;/p&gt;

&lt;p&gt;It also doesn't support other features most user's probably expect from a modern text editor. It doesn't do syntax highlighting, and that is listed under "Things That Won't Ever Happen (Sorry)" in the README. In a &lt;a href="https://github.com/deadpixi/sam/issues/89" rel="noopener noreferrer"&gt;GitHub issue&lt;/a&gt;, the maintainer clarifies it's not that he'll never implement syntax highlighting but just that it would be almost impossible in the current 30-year-old codebase. If he rewrites the GUI in Tcl to get rid of the Xt dependency, syntax highlighting might become a possibility.&lt;/p&gt;

&lt;p&gt;While I enjoyed playing around with Sam for a couple of days, I don't see it becoming my main text editor. It was a nice peek at computing history, and I might keep it around to edit a configuration file now and again, but I'll continue using Geany when programming. The structural regular expressions and being able to pipe the current selection to a shell command and replace the selection with the result are neat but not compelling enough to do without things like syntax highlighting (plus most modern text editors can do those other things too).&lt;/p&gt;

&lt;p&gt;Are you a Sam fan? Do you use it as your daily driver? I'd love to hear from you. How long have you used it? What is it about Sam that makes you prefer it to other text editors? Is there something I missed? Any tips or tricks to share?&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>texteditor</category>
      <category>tools</category>
    </item>
    <item>
      <title>Learn Common Lisp by Example: GTK GUI with SBCL</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Sat, 20 Nov 2021 05:38:52 +0000</pubDate>
      <link>https://dev.to/goober99/learn-common-lisp-by-example-gtk-gui-with-sbcl-5e5c</link>
      <guid>https://dev.to/goober99/learn-common-lisp-by-example-gtk-gui-with-sbcl-5e5c</guid>
      <description>&lt;p&gt;The cl-cffi-gtk library provides Common Lisp bindings to GTK. The library is developed with SBCL but should also work with Clozure CL and CLISP. For this tutorial, I'll be using SBCL. Instead of building yet another calculator, let's build a GUI for generating a tone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fclcffigtk.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fclcffigtk.png%3Fraw%3Dtrue" title="Example screenshot" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll need SBCL installed. It's available in the repositories of most Linux distros, so just install it from your distro's repo. Depending on your destkop environment, you probably already have GTK installed (even if you use KDE, it's highly likely you already have GTK installed). If you're on Windows or macOS, you'll probably have to install GTK in addition to SBCL.&lt;/p&gt;

&lt;p&gt;The Common Lisp bindings to GTK can be installed with &lt;a href="https://www.quicklisp.org/" rel="noopener noreferrer"&gt;Quicklisp&lt;/a&gt;. If you don't already have Quicklisp installed, it's painless. See the Quicklisp website for more details, but here's an example of installing Quicklisp on Debian and configuring SBCL. The steps should be the same for any Linux distro and macOS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://beta.quicklisp.org/quicklisp.lisp
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sbcl &lt;span class="nt"&gt;--load&lt;/span&gt; quicklisp.lisp
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;quicklisp-quickstart:install&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ql:add-to-init-file&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "proper" way to include a dependency would be to use &lt;a href="https://common-lisp.net/project/asdf/" rel="noopener noreferrer"&gt;ASDF&lt;/a&gt; and create a &lt;code&gt;.asd&lt;/code&gt; file for the project. Since this is a quick tutorial, I'll use with &lt;code&gt;ql:quickload&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ql:quickload&lt;/span&gt; &lt;span class="ss"&gt;:cl-cffi-gtk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time you run &lt;code&gt;bleep.lisp&lt;/code&gt;, it will take awhile as Quicklisp downloads cl-cffi-gtk (by default it will be downloaded to &lt;code&gt;~/quicklisp&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Main window&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;window&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-window&lt;/span&gt; &lt;span class="ss"&gt;:type&lt;/span&gt; &lt;span class="ss"&gt;:toplevel&lt;/span&gt; &lt;span class="ss"&gt;:title&lt;/span&gt; &lt;span class="s"&gt;"Bleep"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;vbox&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-box&lt;/span&gt; &lt;span class="ss"&gt;:orientation&lt;/span&gt; &lt;span class="ss"&gt;:vertical&lt;/span&gt;
                                                      &lt;span class="ss"&gt;:spacing&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
                                                      &lt;span class="ss"&gt;:margin&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:within-main-loop&lt;/span&gt;
  &lt;span class="c1"&gt;; Quit program when window closed&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gobject:g-signal-connect&lt;/span&gt; &lt;span class="nv"&gt;window&lt;/span&gt; &lt;span class="s"&gt;"destroy"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;declare&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ignore&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:leave-gtk-main&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
  &lt;span class="c1"&gt;; Display GUI&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-container-add&lt;/span&gt; &lt;span class="nv"&gt;window&lt;/span&gt; &lt;span class="nv"&gt;vbox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-widget-show-all&lt;/span&gt; &lt;span class="nv"&gt;window&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although written in C, GTK is object oriented. It uses a portable object system called GObject. GObject classes are wrapped with corresponding Lisp classes. You create a window by instantiating the &lt;code&gt;gtk:gtk-window&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;gtk:gtk-box&lt;/code&gt; is a packing widget. A &lt;code&gt;gtk:gtk-window&lt;/code&gt; can only contain one widget at a time. Packing widgets are you used to pack widgets together and arrange them.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;gtk:within-main-loop&lt;/code&gt; macro does the work of setting up a GTK main loop. The macro does some additional bookkeeping to run the GUI in a separate thread. This is cool, because even after the window appears, you can still type commands into the REPL to interact with the program (e.g. query the properties of a widget). With most of the Lisp GUI libraries I've tried out, the GUI takes over completely once it is launched, and you have to close the window before being able to type commands into the REPL again.&lt;/p&gt;

&lt;p&gt;Then add our packing widget to the window, and we're ready to launch our window. Now let's add some more widgets to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-scale&lt;/span&gt;
                              &lt;span class="ss"&gt;:orientation&lt;/span&gt; &lt;span class="ss"&gt;:horizontal&lt;/span&gt;
                              &lt;span class="ss"&gt;:draw-value&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
                              &lt;span class="ss"&gt;:width-request&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
                              &lt;span class="ss"&gt;:adjustment&lt;/span&gt;
                              &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-adjustment&lt;/span&gt;
                                             &lt;span class="ss"&gt;:value&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;
                                             &lt;span class="ss"&gt;:lower&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
                                             &lt;span class="ss"&gt;:upper&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;
                                             &lt;span class="ss"&gt;:step-increment&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;vbox&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The range of frequencies audible by humans is typically between 20 Hz and 20 KHz (we lose the ability to hear some of those higher frequencies as we age). The &lt;a href="https://en.wikipedia.org/wiki/A440_(pitch_standard)" rel="noopener noreferrer"&gt;musical note A above middle C&lt;/a&gt; is 440 Hz. Since A4 serves as a general tuning standard, it seems like a sensible default, but if you run the above in SBCL, this is what you'll see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fclcffigtk-linearslider.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fclcffigtk-linearslider.png%3Fraw%3Dtrue" title="Slider showing 440 using a linear scale" alt="Slider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scale of 20 to 20,000 is so large that 440 doesn't appear to move the slider at all. Ideally, 440 would fall about the middle of the slider. To achieve this, let's use a logarithmic scale.&lt;/p&gt;

&lt;p&gt;I found a &lt;a href="https://stackoverflow.com/questions/846221/logarithmic-slider/846249#846249" rel="noopener noreferrer"&gt;Stack Overflow answer&lt;/a&gt; on how to map a slider to a logarithmic scale. The code given in the answer is JavaScript, but it was easy enough to port to Common Lisp.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Scale used by slider&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defparameter&lt;/span&gt; &lt;span class="vg"&gt;*min-position*&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defparameter&lt;/span&gt; &lt;span class="vg"&gt;*max-position*&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Range of frequencies&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defparameter&lt;/span&gt; &lt;span class="vg"&gt;*min-frequency*&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defparameter&lt;/span&gt; &lt;span class="vg"&gt;*max-frequency*&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;; Logarithmic scale for frequency (so middle A [440] falls about in the middle)&lt;/span&gt;
&lt;span class="c1"&gt;; Adapted from https://stackoverflow.com/questions/846221/logarithmic-slider&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="vg"&gt;*min-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;max-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="vg"&gt;*max-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;max-freq&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="vg"&gt;*max-position*&lt;/span&gt; &lt;span class="vg"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="c1"&gt;; Convert slider position to frequency&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;position-&amp;gt;frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;exp&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;position&lt;/span&gt; &lt;span class="vg"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;span class="c1"&gt;; Convert frequency to slider position&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="vg"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added some global parameters to the top of the script. The variable name &lt;code&gt;*min-position*&lt;/code&gt; is just a Lisp naming convention for global parameters. I came up with the range of 0-2,000 by trial and error. It seemed to strike the best balance between each step of the slider making a noticeable change to the frequency while still allowing the user to narrow in on a specific frequency with just the slider.&lt;/p&gt;

&lt;p&gt;Then we create two functions: one that takes the position on the slider and returns the frequency (&lt;code&gt;position-&amp;gt;frequency&lt;/code&gt;) and another that takes a frequency and returns the position on the slider (&lt;code&gt;frequency-position&lt;/code&gt;). Now let's modify our slider to use &lt;code&gt;frequency-&amp;gt;position&lt;/code&gt; to convert the initial &lt;code&gt;value&lt;/code&gt; to a slider position using our logarithmic scale.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-scale&lt;/span&gt;
                              &lt;span class="ss"&gt;:orientation&lt;/span&gt; &lt;span class="ss"&gt;:horizontal&lt;/span&gt;
                              &lt;span class="ss"&gt;:draw-value&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
                              &lt;span class="ss"&gt;:adjustment&lt;/span&gt;
                              &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-adjustment&lt;/span&gt;
                                             &lt;span class="ss"&gt;:value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                             &lt;span class="ss"&gt;:lower&lt;/span&gt; &lt;span class="vg"&gt;*min-position*&lt;/span&gt;
                                             &lt;span class="ss"&gt;:upper&lt;/span&gt; &lt;span class="vg"&gt;*max-position*&lt;/span&gt;
                                             &lt;span class="ss"&gt;:step-increment&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Underneath the slider is a spin button showing the current frequency and buttons to increase/decrease the frequency by one octave.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Create a spin button with a units label&lt;/span&gt;
&lt;span class="c1"&gt;; Arguments: from - number, lower bound of range),&lt;/span&gt;
&lt;span class="c1"&gt;;            to - number, upper bound of range&lt;/span&gt;
&lt;span class="c1"&gt;;            initial - number, initial value of spin button&lt;/span&gt;
&lt;span class="c1"&gt;;            units - string, label after spin button&lt;/span&gt;
&lt;span class="c1"&gt;; Optional Arguments: digits - number &amp;lt;= 20, number of decimal places to show&lt;/span&gt;
&lt;span class="c1"&gt;; Return value: The gtk-spin-button instance inside the container&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;units-spin-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;from&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;initial&lt;/span&gt; &lt;span class="nv"&gt;units&lt;/span&gt; &lt;span class="k"&gt;&amp;amp;key&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;digits&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;container&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-box&lt;/span&gt; &lt;span class="ss"&gt;:orientation&lt;/span&gt; &lt;span class="ss"&gt;:horizontal&lt;/span&gt; &lt;span class="ss"&gt;:spacing&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;spin-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-spin-button&lt;/span&gt;
                                    &lt;span class="ss"&gt;:digits&lt;/span&gt; &lt;span class="nv"&gt;digits&lt;/span&gt;
                                    &lt;span class="ss"&gt;:adjustment&lt;/span&gt;
                                    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-adjustment&lt;/span&gt;
                                                   &lt;span class="ss"&gt;:value&lt;/span&gt; &lt;span class="nv"&gt;initial&lt;/span&gt;
                                                   &lt;span class="ss"&gt;:lower&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt;
                                                   &lt;span class="ss"&gt;:upper&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt;
                                                   &lt;span class="ss"&gt;:step-increment&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;label&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-label&lt;/span&gt; &lt;span class="ss"&gt;:label&lt;/span&gt; &lt;span class="nv"&gt;units&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;container&lt;/span&gt; &lt;span class="nv"&gt;spin-button&lt;/span&gt; &lt;span class="ss"&gt;:fill&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;container&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt; &lt;span class="ss"&gt;:fill&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;; Return the container holding the spin button and label&lt;/span&gt;
    &lt;span class="nv"&gt;spin-button&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;units-spin-button&lt;/span&gt; &lt;span class="vg"&gt;*min-frequency*&lt;/span&gt; &lt;span class="vg"&gt;*max-frequency*&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt; &lt;span class="s"&gt;"Hz"&lt;/span&gt; &lt;span class="ss"&gt;:digits&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;lower-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-button&lt;/span&gt; &lt;span class="ss"&gt;:label&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;higher-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-button&lt;/span&gt; &lt;span class="ss"&gt;:label&lt;/span&gt; &lt;span class="s"&gt;"&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;frequency-box&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-box&lt;/span&gt; &lt;span class="ss"&gt;:orientation&lt;/span&gt; &lt;span class="ss"&gt;:horizontal&lt;/span&gt; &lt;span class="ss"&gt;:spacing&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;frequency-box&lt;/span&gt; &lt;span class="nv"&gt;lower-button&lt;/span&gt; &lt;span class="ss"&gt;:fill&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;frequency-box&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-widget-parent&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ss"&gt;:fill&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;frequency-box&lt;/span&gt; &lt;span class="nv"&gt;higher-button&lt;/span&gt; &lt;span class="ss"&gt;:fill&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;vbox&lt;/span&gt; &lt;span class="nv"&gt;frequency-box&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also use boxes to help with layout. I created a function that I can reuse later to generate the spin button and label and pack them together in a box.&lt;/p&gt;

&lt;p&gt;At this point, we are starting to have a nice looking interface, but it doesn't do anything. If you click the buttons or slide the slider, nothing happens. Widgets emit signals, and callback functions can be connected to these signals. If we connect a callback function to the &lt;code&gt;change-value&lt;/code&gt; signal of the slider, that function will be called whenever the slider is moved. The arguments a callback function takes are dependent on the signal being handled. The adjustment object of the spin button has a &lt;code&gt;value-changed&lt;/code&gt; signal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Link slider to text field display of frequency&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gobject:g-signal-connect&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="s"&gt;"change-value"&lt;/span&gt;
  &lt;span class="c1"&gt;; Connect to change-value signal of slider instead of value-changed signal of&lt;/span&gt;
  &lt;span class="c1"&gt;; its corresponding adjustment object so that frequency will only be updated&lt;/span&gt;
  &lt;span class="c1"&gt;; when interactively moved avoiding rounding differences between slider and&lt;/span&gt;
  &lt;span class="c1"&gt;; sping button.&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;range&lt;/span&gt; &lt;span class="nv"&gt;scroll&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;declare&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ignore&lt;/span&gt; &lt;span class="nv"&gt;range&lt;/span&gt; &lt;span class="nv"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setf&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-adjustment-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-spin-button-adjustment&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;position-&amp;gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gobject:g-signal-connect&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-spin-button-adjustment&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;"value-changed"&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;adjustment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setf&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-adjustment-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-range-adjustment&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-adjustment-value&lt;/span&gt; &lt;span class="nv"&gt;adjustment&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire the buttons up to callback functions called &lt;code&gt;decrease-octave&lt;/code&gt; and &lt;code&gt;increase-octave&lt;/code&gt;. An &lt;a href="https://en.wikipedia.org/wiki/Octave" rel="noopener noreferrer"&gt;octave&lt;/a&gt; is "the interval between one musical pitch and another with double its frequency."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Buttons increase and decrease frequency by one octave&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;set-frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setf&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-adjustment-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-spin-button-adjustment&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;adjust-octave&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;set-frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-adjustment-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-spin-button-adjustment&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nv"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;decrease-octave&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;declare&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ignore&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;adjust-octave&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;increase-octave&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;declare&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ignore&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;adjust-octave&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gobject:g-signal-connect&lt;/span&gt; &lt;span class="nv"&gt;lower-button&lt;/span&gt; &lt;span class="s"&gt;"clicked"&lt;/span&gt; &lt;span class="nf"&gt;#'&lt;/span&gt;&lt;span class="nv"&gt;decrease-octave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gobject:g-signal-connect&lt;/span&gt; &lt;span class="nv"&gt;higher-button&lt;/span&gt; &lt;span class="s"&gt;"clicked"&lt;/span&gt; &lt;span class="nf"&gt;#'&lt;/span&gt;&lt;span class="nv"&gt;increase-octave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll reuse the &lt;code&gt;units-spin-button&lt;/code&gt; function we created to create a field to specify the duration of the beep in millseconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;control-box&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-box&lt;/span&gt; &lt;span class="ss"&gt;:orientation&lt;/span&gt; &lt;span class="ss"&gt;:horizontal&lt;/span&gt; &lt;span class="ss"&gt;:spacing&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;units-spin-button&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="mi"&gt;600000&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="s"&gt;"ms"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;control-box&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-widget-parent&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ss"&gt;:fill&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;vbox&lt;/span&gt; &lt;span class="nv"&gt;control-box&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frequency is rather abstract. Let's also give the user the ability to select a musical note. We can store the corresponding frequencies for A4-G4 in an association list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Notes -&amp;gt; frequency (middle A-G [A4-G4])&lt;/span&gt;
&lt;span class="c1"&gt;; http://pages.mtu.edu/~suits/notefreqs.html&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;notes&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;440.00&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"B"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;493.88&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;261.63&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"D"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;293.66&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"E"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;329.63&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"F"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;349.23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"G"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;292.00&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll give the user a drop-down menu. Whenever a note is selected from the drop-down menu, we'll look up the frequency in the association list and set it using the &lt;code&gt;set-frequency&lt;/code&gt; helper function we created for the octave buttons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Create combo box and label&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;note-box&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-box&lt;/span&gt; &lt;span class="ss"&gt;:orientation&lt;/span&gt; &lt;span class="ss"&gt;:horizontal&lt;/span&gt; &lt;span class="ss"&gt;:spacing&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;note-label&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-label&lt;/span&gt; &lt;span class="ss"&gt;:label&lt;/span&gt; &lt;span class="s"&gt;"♪"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;note-box&lt;/span&gt; &lt;span class="nv"&gt;note-label&lt;/span&gt; &lt;span class="ss"&gt;:fill&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-combo-box-text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;note-box&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="ss"&gt;:fill&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Populate combo box&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-combo-box-text-append-text&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-combo-box-text-append-text&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-combo-box-text-append-text&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-combo-box-text-append-text&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="s"&gt;"D"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-combo-box-text-append-text&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="s"&gt;"E"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-combo-box-text-append-text&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="s"&gt;"F"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-combo-box-text-append-text&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="s"&gt;"G"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Set frequency to specific note&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gobject:g-signal-connect&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="s"&gt;"changed"&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-combo-box-text-get-active-text&lt;/span&gt; &lt;span class="nv"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;set-frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cdr&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;assoc&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt; &lt;span class="nv"&gt;notes&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="ss"&gt;'equal&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;span class="c1"&gt;; Pack the combo box&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;control-box&lt;/span&gt; &lt;span class="nv"&gt;note-box&lt;/span&gt; &lt;span class="ss"&gt;:fill&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;vbox&lt;/span&gt; &lt;span class="nv"&gt;control-box&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let's make some noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ql:quickload&lt;/span&gt; &lt;span class="ss"&gt;:cl-portaudio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;; Generate a tone using CL-PortAudio&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;generate-tone&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;sample-rate&lt;/span&gt; &lt;span class="mf"&gt;44100d0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;amplitude&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;; Initialize PortAudio environment&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;portaudio:with-audio&lt;/span&gt;
      &lt;span class="c1"&gt;; Open and start audio stream&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;portaudio:with-default-audio-stream&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;astream&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                                            &lt;span class="ss"&gt;:sample-format&lt;/span&gt; &lt;span class="ss"&gt;:float&lt;/span&gt;
                                            &lt;span class="ss"&gt;:sample-rate&lt;/span&gt; &lt;span class="nv"&gt;sample-rate&lt;/span&gt;
                                            &lt;span class="ss"&gt;:frames-per-buffer&lt;/span&gt; &lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dotimes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="nv"&gt;duration&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;sample-rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
          &lt;span class="c1"&gt;; Write buffer to output stream&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;portaudio:write-stream&lt;/span&gt; &lt;span class="nv"&gt;astream&lt;/span&gt;
            &lt;span class="c1"&gt;; portaudio:write-stream requires an array as input, not a list&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-array&lt;/span&gt; &lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt; &lt;span class="ss"&gt;:element-type&lt;/span&gt; &lt;span class="ss"&gt;'single-float&lt;/span&gt; &lt;span class="ss"&gt;:initial-contents&lt;/span&gt;
              &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;loop&lt;/span&gt; &lt;span class="nv"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;j&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nv"&gt;collect&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="nv"&gt;j&lt;/span&gt; &lt;span class="nv"&gt;sample-rate&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
                  &lt;span class="c1"&gt;; Since sample-rate and pi are double-float, they make result&lt;/span&gt;
                  &lt;span class="c1"&gt;; double-float. PortAudio expects single-float, and will warn&lt;/span&gt;
                  &lt;span class="c1"&gt;; when run with SBCL if not given single-float.&lt;/span&gt;
                  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;coerce&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;amplitude&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;pi&lt;/span&gt; &lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="ss"&gt;'single-float&lt;/span&gt;&lt;span class="p"&gt;))))))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll use &lt;a href="https://github.com/filonenko-mikhail/cl-portaudio" rel="noopener noreferrer"&gt;Common Lisp bindings to PortAudio&lt;/a&gt; to generate the tone. This can be loaded with &lt;a href="https://www.quicklisp.org/" rel="noopener noreferrer"&gt;Quicklisp&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;CL-PortAudio comes with a couple of helpful macros that makes initializing PortAudio and starting a stream simple. The &lt;code&gt;with-audio&lt;/code&gt; macro executes body within a PortAudio initialize/terminate environment. The &lt;code&gt;with-default-audio-stream&lt;/code&gt; macro executes body with an opened and started stream and shuts down the stream after it is done.&lt;/p&gt;

&lt;p&gt;Then you just feed PortAudio arrays of samples, &lt;code&gt;:frames-per-buffer&lt;/code&gt; at a time. I initiated &lt;code&gt;with-default-audio-stream&lt;/code&gt; with one channel, so the array is just a single-dimensional array of floating point numbers. If you were producing stereo sound, you would generate a two-dimensional array. The &lt;a href="http://pld.cs.luc.edu/telecom/mnotes/digitized_sound.html" rel="noopener noreferrer"&gt;basic formula for a sine wave&lt;/a&gt; is A sin(2πft) where &lt;em&gt;A&lt;/em&gt; is amplitude, &lt;em&gt;f&lt;/em&gt; is frequency, and &lt;em&gt;t&lt;/em&gt; is time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;amplitude&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;pi&lt;/span&gt; &lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire this up to a button between the duration and note selector, and you're ready to make some noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-instance&lt;/span&gt; &lt;span class="ss"&gt;'gtk:gtk-button&lt;/span&gt; &lt;span class="ss"&gt;:label&lt;/span&gt; &lt;span class="s"&gt;"Play"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gobject:g-signal-connect&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt; &lt;span class="s"&gt;"clicked"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;declare&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ignore&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;generate-tone&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-adjustment-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-spin-button-adjustment&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-adjustment-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-spin-button-adjustment&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;gtk:gtk-box-pack-start&lt;/span&gt; &lt;span class="nv"&gt;control-box&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt; &lt;span class="ss"&gt;:fill&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;You can check out the entire example on &lt;a href="https://github.com/goober99/lisp-gui-examples" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. This started as a personal learning project to explore the state of GUI programming in Lisp and has become a series of tutorials on building GUIs with various dialects of Lisp.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>lisp</category>
      <category>gtk</category>
      <category>gui</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Learn Scheme by Example: Tk GUI with Chicken Scheme</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Fri, 12 Nov 2021 21:51:55 +0000</pubDate>
      <link>https://dev.to/goober99/learn-scheme-by-example-tk-gui-with-chicken-scheme-3cn9</link>
      <guid>https://dev.to/goober99/learn-scheme-by-example-tk-gui-with-chicken-scheme-3cn9</guid>
      <description>&lt;p&gt;PS/Tk stands for a portable Scheme interface to the Tk GUI toolkit. It has a rich history going all the way back to Scheme_wish by Sven Hartrumpf in 1997. Wolf-Dieter Busch created a Chicken port called Chicken/Tk in 2004. It took on its current name when Nils M Holm stripped it of Chicken-isms to make it portable amongst Scheme implementations in 2006.&lt;/p&gt;

&lt;p&gt;If you've ever tried to write portable Scheme, you know that, except for the most trivial of programs, it is much easier said than done. Holm's &lt;code&gt;pstk.scm&lt;/code&gt; had a configurable section titled &lt;code&gt;NON-PORTABLE&lt;/code&gt; that you had to configure for your chosen implementation. It came full circle and was repackaged as a Chicken egg.&lt;/p&gt;

&lt;p&gt;Chicken is a popular Scheme implementation that compiles Scheme to C. Eggs are Chicken-specific extenstion libraries that are stored in a centralized repository (like CPAN but for Chicken Scheme). Instead of building yet another calculator, let's build a GUI for generating a tone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fpstk.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fpstk.png%3Fraw%3Dtrue" title="Example screenshot" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll need Chicken installed. It's available in the repositories of most Linux distros. PS/Tk interfaces with Tk, not with C library bindings, but with a named pipe to &lt;code&gt;tclsh8.6&lt;/code&gt;. The TCL package in most Linux distros will provide this. For Debian, I did &lt;code&gt;sudo apt install chicken-bin tcl tk&lt;/code&gt;. Once Chicken is installed, you can use the &lt;code&gt;chicken-install&lt;/code&gt; utility that comes with it to install the PS/Tk egg.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;chicken-install &lt;span class="nt"&gt;-sudo&lt;/span&gt; pstk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you think of Tk, you may think of something that looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Ftk-old.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Ftk-old.png%3Fraw%3Dtrue" title="Legacy Tk open dialog" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tk has come a long way in recent years. Tcl/Tk 8.5 and later comes with a new set of widgets built in called Tile or Ttk that can be themed. These widgets are available alongside the classic widgets, so you have to explicitly tell your app to use Ttk or else it will end up looking like it was designed for a 1980s Unix workstation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt; &lt;span class="nv"&gt;pstk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk-start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ttk-map-widgets&lt;/span&gt; &lt;span class="ss"&gt;'all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;; Use the Ttk widget set&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/wm&lt;/span&gt; &lt;span class="ss"&gt;'title&lt;/span&gt; &lt;span class="nv"&gt;tk&lt;/span&gt; &lt;span class="s"&gt;"Bleep"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk-event-loop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All PS/Tk function names begin with &lt;code&gt;tk/&lt;/code&gt; or &lt;code&gt;tk-&lt;/code&gt; (or &lt;code&gt;ttk/&lt;/code&gt; and &lt;code&gt;ttk-&lt;/code&gt; for the few Ttk-specific functions). The &lt;a href="https://github.com/utz82/pstk/tree/master/doc" rel="noopener noreferrer"&gt;doc directory&lt;/a&gt; in the PS/Tk GitHub repo unfortunately has not been updated since this convention was adopted. One example from the docs is &lt;code&gt;start-tk&lt;/code&gt; which is now &lt;code&gt;tk-start&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ttk-map-widgets&lt;/code&gt; function is what tells Tk to use the Ttk widgets instead of the classic widgets. Tk comes with a few built-in themes. The default themes on Windows and macOS supposedly do a decent job of approximating the look of native widgets on those platforms. I don't use either of those platforms, so I can't verify this first hand. For some reason, the default theme on Linux is vaguely Windows 95ish. It comes with a built-in theme called &lt;a href="https://wiki.tcl-lang.org/page/ttk%3A%3Atheme%3A%3Aclam" rel="noopener noreferrer"&gt;clam&lt;/a&gt; that is supposed to provide "a look somewhat like a Linux application". You can set this theme with &lt;code&gt;(ttk/set-theme "clam")&lt;/code&gt;, but it's really not that much of an improvement.&lt;/p&gt;

&lt;p&gt;Ideally, something like &lt;a href="https://github.com/Geballin/gtkTtk" rel="noopener noreferrer"&gt;gtkTtk&lt;/a&gt; that has GTK do the actual drawing would be integrated into Tcl/Tk and become the default on Linux. In the meantime, there are &lt;a href="https://ttkthemes.readthedocs.io/en/latest/themes.html" rel="noopener noreferrer"&gt;third party themes&lt;/a&gt; that imitate the look and feel of the most popular GTK and Qt themes. I use MATE with the Arc GTK theme, so I went with the Arc theme. There was even a Debian package for it (&lt;code&gt;sudo apt install tcl-ttkthemes&lt;/code&gt;). We can then &lt;a href="https://blog.serindu.com/2019/03/07/applying-tk-themes-to-git-gui/" rel="noopener noreferrer"&gt;apply the theme system wide&lt;/a&gt; (&lt;code&gt;echo '*TkTheme: arc' | xrdb -merge -&lt;/code&gt;), so that all Tk apps such as git-gui also inherit the theme. It is probably better to give your Linux users instructions on how to install their own theme instead of hard coding one with &lt;code&gt;ttk/set-theme&lt;/code&gt;, so they can choose one that matches their system theme (KDE users might pick Breeze while Ubuntu users might opt for Yaru). The screenshots in this tutorial use the Arc theme.&lt;/p&gt;

&lt;p&gt;We set the window title with &lt;code&gt;tk/wm&lt;/code&gt; and start the event loop with &lt;code&gt;tk-event-loop&lt;/code&gt;. We now have an empty window. Now let's add some widgets to this window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'scale&lt;/span&gt; &lt;span class="ss"&gt;'from:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="ss"&gt;'to:&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'set&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/grid&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'row:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ss"&gt;'columnspan:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="ss"&gt;'sticky:&lt;/span&gt; &lt;span class="ss"&gt;'ew&lt;/span&gt; &lt;span class="ss"&gt;'padx:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="ss"&gt;'pady:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Widgets are organized hierarchically. This is done by invoking a parent widget with the sub-command &lt;code&gt;create-widget&lt;/code&gt;. PS/Tk associates a widget named &lt;code&gt;tk&lt;/code&gt; with the top-level window, so most widgets will start as a call to &lt;code&gt;tk&lt;/code&gt; (e.g. &lt;code&gt;(tk 'create-widget 'label 'text: "Hello, World!")&lt;/code&gt;). Options are quoted and get a trailing colon (e.g. &lt;code&gt;'text: "Hello, World!"&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Creating a widget returns a Scheme function. If you give this function a name, you can call it with sub-commands such as &lt;code&gt;configure&lt;/code&gt;, &lt;code&gt;get&lt;/code&gt;, and &lt;code&gt;set&lt;/code&gt;. Just creating a widget doesn't make it appear on screen. For that you need a geometry manager, of which Tk has three: the packer, the gridder, and the placer (&lt;code&gt;tk/pack&lt;/code&gt;, &lt;code&gt;tk/grid&lt;/code&gt;, and &lt;code&gt;tk/place&lt;/code&gt; in Scheme, respectively).&lt;/p&gt;

&lt;p&gt;The range of frequencies audible by humans is typically between 20 Hz and 20 KHz (we lose the ability to hear some of those higher frequencies as we age). The &lt;a href="https://en.wikipedia.org/wiki/A440_(pitch_standard)" rel="noopener noreferrer"&gt;musical note A above middle C&lt;/a&gt; is 440 Hz. Since A4 serves as a general tuning standard, it seems like a sensible default, but if you run the above in Chicken, this is what you'll see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fpstk-linearslider.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fpstk-linearslider.png%3Fraw%3Dtrue" title="Slider showing 440 using a linear scale" alt="Slider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scale of 20 to 20,000 is so large that 440 doesn't appear to move the slider at all. Ideally, 440 would fall about the middle of the slider. To achieve this, let's use a logarithmic scale.&lt;/p&gt;

&lt;p&gt;I found a &lt;a href="https://stackoverflow.com/questions/846221/logarithmic-slider/846249#846249" rel="noopener noreferrer"&gt;Stack Overflow answer&lt;/a&gt; on how to map a slider to a logarithmic scale. The code given in the answer is JavaScript, but it was easy enough to port to Scheme.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Scale used by slider&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*max-position*&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Range of frequencies&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;; Logarithmic scale for frequency (so middle A [440] falls about in the middle)&lt;/span&gt;
&lt;span class="c1"&gt;; Adapted from https://stackoverflow.com/questions/846221/logarithmic-slider&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;max-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;max-freq&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*max-position*&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="c1"&gt;; Convert slider position to frequency&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;position-&amp;gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;exp&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;position&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;span class="c1"&gt;; Convert frequency to slider position&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added some global parameters to the top of the script. The variable name &lt;code&gt;*min-position*&lt;/code&gt; is just a Lisp naming convention for global parameters. I came up with the range of 0-2,000 by trial and error. It seemed to strike the best balance between each step of the slider making a noticeable change to the frequency while still allowing the user to narrow in on a specific frequency with just the slider.&lt;/p&gt;

&lt;p&gt;Then we create two functions: one that takes the position on the slider and returns the frequency (&lt;code&gt;position-&amp;gt;frequency&lt;/code&gt;) and another that takes a frequency and returns the position on the slider (&lt;code&gt;frequency-position&lt;/code&gt;). Now let's set the initial position of our slider with the &lt;code&gt;frequency-&amp;gt;position&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'scale&lt;/span&gt; &lt;span class="ss"&gt;'from:&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt; &lt;span class="ss"&gt;'to:&lt;/span&gt; &lt;span class="nv"&gt;*max-position*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'configure&lt;/span&gt; &lt;span class="ss"&gt;'value:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Underneath the slider is a spin box showing the current frequency and buttons to increase/decrease the frequency by one octave.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Create a spin box with a units label&lt;/span&gt;
&lt;span class="c1"&gt;; Returns frame widget encompassing both spin box and label and the spin box&lt;/span&gt;
&lt;span class="c1"&gt;; widget itself. This way you can access the value of the spin box.&lt;/span&gt;
&lt;span class="c1"&gt;; e.g. (define-values (box-with-label just-box) (units-spinbox 1 12 6 "inches"))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;units-spinbox&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;initial&lt;/span&gt; &lt;span class="nv"&gt;units&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;container&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'frame&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;spinbox&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;container&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'spinbox&lt;/span&gt; &lt;span class="ss"&gt;'from:&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt; &lt;span class="ss"&gt;'to:&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt;
                             &lt;span class="ss"&gt;'width:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-length&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;number-&amp;gt;string&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;container&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt; &lt;span class="ss"&gt;'text:&lt;/span&gt; &lt;span class="nv"&gt;units&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;spinbox&lt;/span&gt; &lt;span class="ss"&gt;'set&lt;/span&gt; &lt;span class="nv"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/pack&lt;/span&gt; &lt;span class="nv"&gt;spinbox&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt; &lt;span class="ss"&gt;'side:&lt;/span&gt; &lt;span class="ss"&gt;'left&lt;/span&gt; &lt;span class="ss"&gt;'padx:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;values&lt;/span&gt; &lt;span class="nv"&gt;container&lt;/span&gt; &lt;span class="nv"&gt;spinbox&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;lower-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'button&lt;/span&gt; &lt;span class="ss"&gt;'text:&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;define-values&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-ext&lt;/span&gt; &lt;span class="nv"&gt;frequency-int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;units-spinbox&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt; &lt;span class="s"&gt;"Hz"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;higher-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'button&lt;/span&gt; &lt;span class="ss"&gt;'text:&lt;/span&gt; &lt;span class="s"&gt;"&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/grid&lt;/span&gt; &lt;span class="nv"&gt;lower-button&lt;/span&gt; &lt;span class="ss"&gt;'row:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ss"&gt;'column:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ss"&gt;'padx:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="ss"&gt;'pady:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/grid&lt;/span&gt; &lt;span class="nv"&gt;frequency-ext&lt;/span&gt; &lt;span class="ss"&gt;'row:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ss"&gt;'column:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ss"&gt;'padx:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="ss"&gt;'pady:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/grid&lt;/span&gt; &lt;span class="nv"&gt;higher-button&lt;/span&gt; &lt;span class="ss"&gt;'row:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ss"&gt;'column:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ss"&gt;'padx:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="ss"&gt;'pady:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frame widget is an invisible widget that helps with layout. Since all I need to arrange within the frame is a spin box and a label, I used &lt;code&gt;tk/pack&lt;/code&gt; to &lt;code&gt;pack&lt;/code&gt; them side by side. The frame is then organized in a &lt;code&gt;grid&lt;/code&gt; with the rest of the widgets. I created a function that I can reuse later to generate the spin box, label, and frame all together. At this point, we are starting to have a nice looking interface, but it doesn't do anything. If you click the buttons or slide the slider, nothing happens. The widgets have a &lt;code&gt;command&lt;/code&gt; option that wires the widget up to a function. If we add a command to the slider, that command will be called each time the slider is moved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'scale&lt;/span&gt; &lt;span class="ss"&gt;'from:&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt; &lt;span class="ss"&gt;'to:&lt;/span&gt; &lt;span class="nv"&gt;*max-position*&lt;/span&gt;
                   &lt;span class="ss"&gt;'command:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-int&lt;/span&gt; &lt;span class="ss"&gt;'set&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;position-&amp;gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command for the slider takes one argument that indicates the new value of the slider. The spin box does have a &lt;code&gt;command&lt;/code&gt; option, but the command is only called when the value is changed by clicking the up or down arrow, not when the value is changed by other means such as typing a frequency into the field. Tk has a &lt;code&gt;bind&lt;/code&gt; command (the Scheme &lt;code&gt;tk/bind&lt;/code&gt; function) that allows binding functions to an event on a widget. We'll bind our callback to the &lt;code&gt;KeyRelase&lt;/code&gt; event. The &lt;code&gt;tk/bind&lt;/code&gt; function takes up to three arguments. The first is the widget to bind to (or a tag created with &lt;code&gt;tk/bindtags&lt;/code&gt; to apply the binding to multiple widgets). The second is the event pattern. The event pattern is surrounded by angle brackets and can specify modifiers, event types, and more. You can find detailed documentation on the event pattern in the &lt;a href="https://www.tcl.tk/man/tcl8.6/TkCmd/bind.htm" rel="noopener noreferrer"&gt;Tcl/Tk documentation&lt;/a&gt;. The third is a lambda expression to associate with the event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/bind&lt;/span&gt; &lt;span class="nv"&gt;frequency-int&lt;/span&gt; &lt;span class="ss"&gt;'&amp;lt;KeyRelease&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;; If frequency value is a valid number, set slider to current value&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;numified&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-int&lt;/span&gt; &lt;span class="ss"&gt;'get&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt; &lt;span class="nv"&gt;numified&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'configure&lt;/span&gt; &lt;span class="ss"&gt;'value:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="nv"&gt;numified&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire the buttons up to callback functions called &lt;code&gt;decrease-octave&lt;/code&gt; and &lt;code&gt;increase-octave&lt;/code&gt;. An &lt;a href="https://en.wikipedia.org/wiki/Octave" rel="noopener noreferrer"&gt;octave&lt;/a&gt; is "the interval between one musical pitch and another with double its frequency."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Set frequency slider and display&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'configure&lt;/span&gt; &lt;span class="ss"&gt;'value:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-int&lt;/span&gt; &lt;span class="ss"&gt;'set&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="c1"&gt;; Buttons increase and decrease frequency by one octave&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-octave&lt;/span&gt; &lt;span class="nv"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-int&lt;/span&gt; &lt;span class="ss"&gt;'get&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nv"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decrease-octave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-octave&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;increase-octave&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-octave&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you slide the slider, the text field updates accordingly. If you type a number in the text field, the slider updates accordingly. All good, right? What if a user (and you know they will) enters a number higher than 20,000 or a letter?&lt;/p&gt;

&lt;p&gt;Let's extend the function that returns our labeled spin box to bind a validation function to the &lt;code&gt;FocusOut&lt;/code&gt; event on the spin box. The spin box does have a &lt;code&gt;validatecommand&lt;/code&gt; option, but I wasn't able to get it working. I looked through the examples that have come with the various variations of PS/Tk and couldn't find a single example of a spin box with a &lt;code&gt;validatecommand&lt;/code&gt;. I even looked at the source code for &lt;a href="https://github.com/bintracker/bintracker/" rel="noopener noreferrer"&gt;Bintracker&lt;/a&gt;, a chiptune audio workstation written in Chicken Scheme with a PS/Tk GUI and developed by the current maintainer of the PS/Tk egg. Even it binds a &lt;code&gt;validate-new-value&lt;/code&gt; function to the &lt;code&gt;Return&lt;/code&gt; and &lt;code&gt;FocusOut&lt;/code&gt; events of the spin box rather than using &lt;code&gt;validatecommand&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Create a spin box with a units label&lt;/span&gt;
&lt;span class="c1"&gt;; Returns frame widget encompassing both spin box and label and the spin box&lt;/span&gt;
&lt;span class="c1"&gt;; widget itself. This way you can access the value of the spin box.&lt;/span&gt;
&lt;span class="c1"&gt;; e.g. (define-values (box-with-label just-box) (units-spinbox 1 12 6 "inches"))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;units-spinbox&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt; &lt;span class="nv"&gt;initial&lt;/span&gt; &lt;span class="nv"&gt;units&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;container&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'frame&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;spinbox&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;container&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'spinbox&lt;/span&gt; &lt;span class="ss"&gt;'from:&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt; &lt;span class="ss"&gt;'to:&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt;
                             &lt;span class="ss"&gt;'width:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-length&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;number-&amp;gt;string&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;label&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;container&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt; &lt;span class="ss"&gt;'text:&lt;/span&gt; &lt;span class="nv"&gt;units&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;spinbox&lt;/span&gt; &lt;span class="ss"&gt;'set&lt;/span&gt; &lt;span class="nv"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/bind&lt;/span&gt; &lt;span class="nv"&gt;spinbox&lt;/span&gt; &lt;span class="ss"&gt;'&amp;lt;FocusOut&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;current-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;spinbox&lt;/span&gt; &lt;span class="ss"&gt;'get&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="nv"&gt;current-value&lt;/span&gt;
                     &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;current-value&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                     &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;current-value&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;spinbox&lt;/span&gt; &lt;span class="ss"&gt;'set&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="c1"&gt;; Also reset slider position to make sure it still matches display&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'configure&lt;/span&gt; &lt;span class="ss"&gt;'value:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-int&lt;/span&gt; &lt;span class="ss"&gt;'get&lt;/span&gt;&lt;span class="p"&gt;))))))))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/pack&lt;/span&gt; &lt;span class="nv"&gt;spinbox&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt; &lt;span class="ss"&gt;'side:&lt;/span&gt; &lt;span class="ss"&gt;'left&lt;/span&gt; &lt;span class="ss"&gt;'padx:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;values&lt;/span&gt; &lt;span class="nv"&gt;container&lt;/span&gt; &lt;span class="nv"&gt;spinbox&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also use this function to create a field to specify the duration of the beep in milliseconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;define-values&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;duration-ext&lt;/span&gt; &lt;span class="nv"&gt;duration-int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;units-spinbox&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="mi"&gt;600000&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="s"&gt;"ms"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/grid&lt;/span&gt; &lt;span class="nv"&gt;duration-ext&lt;/span&gt; &lt;span class="ss"&gt;'row:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ss"&gt;'column:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ss"&gt;'padx:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="ss"&gt;'pady:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frequency is rather abstract. Let's also give the user the ability to select a musical note. We can store the corresponding frequencies for A4-G4 in an association list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Notes -&amp;gt; frequency (middle A-G [A4-G4])&lt;/span&gt;
&lt;span class="c1"&gt;; http://pages.mtu.edu/~suits/notefreqs.html&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;notes&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt; &lt;span class="mf"&gt;440.00&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"B"&lt;/span&gt; &lt;span class="mf"&gt;493.88&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="mf"&gt;261.63&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"D"&lt;/span&gt; &lt;span class="mf"&gt;293.66&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"E"&lt;/span&gt; &lt;span class="mf"&gt;329.63&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"F"&lt;/span&gt; &lt;span class="mf"&gt;349.23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"G"&lt;/span&gt; &lt;span class="mf"&gt;292.00&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll give the user a drop-down menu. Whenever a note is selected from the drop-down menu, we'll look up the frequency in the association list and set it using the &lt;code&gt;set-frequency&lt;/code&gt; helper function we created for the octave buttons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;note-frame&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'frame&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;note-frame&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'combobox&lt;/span&gt; &lt;span class="ss"&gt;'width:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ss"&gt;'values:&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt; &lt;span class="s"&gt;"B"&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="s"&gt;"D"&lt;/span&gt; &lt;span class="s"&gt;"E"&lt;/span&gt; &lt;span class="s"&gt;"F"&lt;/span&gt; &lt;span class="s"&gt;"G"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/bind&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="ss"&gt;'&amp;lt;&amp;lt;ComboboxSelected&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cadr&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;assoc&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;note&lt;/span&gt; &lt;span class="ss"&gt;'get&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;note-label&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;note-frame&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt; &lt;span class="ss"&gt;'text:&lt;/span&gt; &lt;span class="s"&gt;"♪"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/pack&lt;/span&gt; &lt;span class="nv"&gt;note-label&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="ss"&gt;'side:&lt;/span&gt; &lt;span class="ss"&gt;'left&lt;/span&gt; &lt;span class="ss"&gt;'padx:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/grid&lt;/span&gt; &lt;span class="nv"&gt;note-frame&lt;/span&gt; &lt;span class="ss"&gt;'row:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ss"&gt;'column:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ss"&gt;'padx:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="ss"&gt;'pady:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's make some noise. There are Chicken Scheme &lt;a href="http://wiki.call-cc.org/eggref/5/allegro" rel="noopener noreferrer"&gt;bindings&lt;/a&gt; to the &lt;a href="https://en.wikipedia.org/wiki/Allegro_(software_library)" rel="noopener noreferrer"&gt;Allegro&lt;/a&gt; library. Allegro is a library primarily used by games for cross-platform graphics, input devices, and more. What we're interested in is the audio addon that can be used to generate a tone with a sine wave. You'll need to install the Allegro library. Make sure you also install the header files. In some Linux distros, these are split into a separate package (e.g. &lt;code&gt;liballegro5-dev&lt;/code&gt; on Debian). Also, install the Allegro egg (&lt;code&gt;chicken-install -sudo allegro&lt;/code&gt;). I added the following lines near the top to import the Allegro bindings (and the chicken memory module, which we'll also use) and initialize Allegro.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prefix&lt;/span&gt; &lt;span class="nv"&gt;allegro&lt;/span&gt; &lt;span class="s"&gt;"al:"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;chicken&lt;/span&gt; &lt;span class="nv"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;+pi+&lt;/span&gt; &lt;span class="mf"&gt;3.141592&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;; Initialize Allegro and audio addon&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:init&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="s"&gt;"Could not initialize Allegro."&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-addon-install&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="s"&gt;"Could not initialize sound."&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:reserve-samples&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Allegro egg is accompanied by a couple of examples but no examples showing the use of the audio addon. The Allegro library itself comes with an &lt;a href="https://github.com/liballeg/allegro5/blob/master/examples/ex_saw.c" rel="noopener noreferrer"&gt;example showing how to generate a saw wave&lt;/a&gt;, but being a C library, the example is, of course, in C. I &lt;a href="https://github.com/goober99/lisp-gui-examples/blob/master/examples/pstk/saw.scm" rel="noopener noreferrer"&gt;ported that example to Scheme&lt;/a&gt;. I would have contributed the example back to the Allegro egg, but the repo is marked as "archived by the owner" and read-only on GitHub. I've included the example in the repo alongside the rest of the code for this tutorial in case someone finds it useful.&lt;/p&gt;

&lt;p&gt;Allegro is very low-level. You create an audio &lt;code&gt;stream&lt;/code&gt;. In this case, the stream buffers eight fragments of 1,024 samples each at a frequency (often called sampling rate) of 44,100 Hz (the sampling rate of an audio CD), which means there are 44,100 samples per second. Each sample is a 32-bit float (what is called the bit depth of the audio), and we only have one channel to keep things as simple as possible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Generate a tone using Allegro&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate-tone&lt;/span&gt; &lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;samples-per-buffer&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;stream-frequency&lt;/span&gt; &lt;span class="mi"&gt;44100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;amplitude&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:make-audio-stream&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="nv"&gt;samples-per-buffer&lt;/span&gt; &lt;span class="nv"&gt;stream-frequency&lt;/span&gt; &lt;span class="ss"&gt;'float32&lt;/span&gt; &lt;span class="ss"&gt;'one&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;queue&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:make-event-queue&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;event&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:make-event&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-stream-attach-to-mixer!&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:default-mixer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="s"&gt;"Could not attach stream to mixer."&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:event-queue-register-source!&lt;/span&gt; &lt;span class="nv"&gt;queue&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-stream-event-source&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;event-loop&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="c1"&gt;; Grab and handle events&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="nv"&gt;duration&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;stream-frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;samples-per-buffer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:event-queue-wait!&lt;/span&gt; &lt;span class="nv"&gt;queue&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:event-type&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;'audio-stream-fragment&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;buffer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-stream-fragment&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
            &lt;span class="c1"&gt;; If the stream is not ready for new data, buffer will be null.&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;not&lt;/span&gt; &lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;event-loop&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;begin&lt;/span&gt;
              &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fill-buffer&lt;/span&gt; &lt;span class="nv"&gt;buffer&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;; Placeholder&lt;/span&gt;
              &lt;span class="c1"&gt;; Repeat&lt;/span&gt;
              &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;event-loop&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))))))))&lt;/span&gt;

    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-stream-drain&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An event loop waits for the audio stream to ask for another buffer. Our job is to fill that buffer with 1,024 32-bit floats at a time. In the code listing above, this is done by &lt;code&gt;fill-buffer&lt;/code&gt;. That was just a placeholder, so I could break the code up into shorter, more easily explainable chunks. This is what goes in the place of &lt;code&gt;(fill-buffer buffer n)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;adr&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pointer-&amp;gt;address&lt;/span&gt; &lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;i&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="nv"&gt;samples-per-buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;samples-per-buffer&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;stream-frequency&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
        &lt;span class="c1"&gt;; al:audio-stream-fragment returns a C pointer. Use (chicken&lt;/span&gt;
        &lt;span class="c1"&gt;; memory) module to operate on foreign pointer objects.&lt;/span&gt;
        &lt;span class="c1"&gt;; Iterate over array four bytes at a time since 32-bit depth.&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pointer-f32-set!&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;address-&amp;gt;pointer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;adr&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;amplitude&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;+pi+&lt;/span&gt; &lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;time&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;al:audio-stream-fragment-set!&lt;/span&gt; &lt;span class="nv"&gt;stream&lt;/span&gt; &lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt; &lt;span class="s"&gt;"Error setting stream fragment"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Allegro egg is a pretty thin wrapper of the Allegro library. The &lt;code&gt;audio-stream-fragment&lt;/code&gt; procedure in the egg just passes along the C pointer that the corresponding &lt;code&gt;al_get_audio_stream_fragment&lt;/code&gt; function from the C library returns. It would have been nice if the egg had offered some Scheme conveniences atop Allegro like allowing us to pass a Scheme list or array to Allegro to provide the buffer of samples. Since it doesn't, we'll use the chicken memory module to fill the C array starting at the C pointer returned by &lt;code&gt;audio-stream-fragment&lt;/code&gt;. We use &lt;code&gt;pointer-&amp;gt;address&lt;/code&gt; to get the address of the pointer. A pointer refrences a byte of memory. We can reference the preceding or following byte by subtracting or adding 1 to the address. Since we are filling the array with 32-bit floats, and 32 bits is 4 bytes, we want to increment the address by 4 each time. Then we can set the value of the current location with pointer-f32-set!.&lt;/p&gt;

&lt;p&gt;Then you just need to feed Allegro buffers of 1,024 samples at a time. The &lt;a href="http://pld.cs.luc.edu/telecom/mnotes/digitized_sound.html" rel="noopener noreferrer"&gt;basic formula for a sine wave&lt;/a&gt; is A sin(2πft) where &lt;em&gt;A&lt;/em&gt; is amplitude, &lt;em&gt;f&lt;/em&gt; is frequency, and &lt;em&gt;t&lt;/em&gt; is time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;amplitude&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;+pi+&lt;/span&gt; &lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;time&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire this up to a play button, and you're ready to make some noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk&lt;/span&gt; &lt;span class="ss"&gt;'create-widget&lt;/span&gt; &lt;span class="ss"&gt;'button&lt;/span&gt; &lt;span class="ss"&gt;'text:&lt;/span&gt; &lt;span class="s"&gt;"Play"&lt;/span&gt; &lt;span class="ss"&gt;'command:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate-tone&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-int&lt;/span&gt; &lt;span class="ss"&gt;'get&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;duration-int&lt;/span&gt; &lt;span class="ss"&gt;'get&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tk/grid&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt; &lt;span class="ss"&gt;'row:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="ss"&gt;'column:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="ss"&gt;'padx:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="ss"&gt;'pady:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tk has been around a long time, and it shows. While it is stable and highly portable, even with recent improvements, it just looks a little dated. At least on Linux, none of the themes I tried really fit in. There were always differences that made the Tk GUI stick out like a sore thumb. If you're building an internal tool where it doesn't really matter how pretty it is, you can get Tk to work with a variety of Schemes in a variety of places.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can check out the entire example on &lt;a href="https://github.com/goober99/lisp-gui-examples" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. This started as a personal learning project to explore the state of GUI programming in Lisp and has become a series of tutorials on building GUIs with various dialects of Lisp.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>lisp</category>
      <category>scheme</category>
      <category>gui</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Learn Common Lisp by Example: Qt GUI with EQL5</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Fri, 19 Feb 2021 20:36:48 +0000</pubDate>
      <link>https://dev.to/goober99/learn-common-lisp-by-example-qt-gui-with-eql5-1lmn</link>
      <guid>https://dev.to/goober99/learn-common-lisp-by-example-qt-gui-with-eql5-1lmn</guid>
      <description>&lt;p&gt;Embedded Qt5 Lisp (EQL5) is a Qt5 binding for Embedded Common Lisp (ECL). EQL5 is a bit different than most bindings. Instead of executing your Lisp source with &lt;code&gt;ecl&lt;/code&gt; and importing the bindings, you compile a new interpreter (&lt;code&gt;eql5&lt;/code&gt;) that combines both ECL and Qt5 bindings. Instead of building yet another calculator, let's build a GUI for generating a tone.&lt;/p&gt;

&lt;p&gt;There are ports of EQL5 to Android and iOS, and a work-in-progress port to Sailfish OS, so EQL5 can also be used to develop mobile apps. The "Embedded" part of the name (of both ECL and EQL5) refers to the fact that it can be embedded in existing C++ projects. For this tutorial, we'll be building a standalone desktop app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Feql5.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Feql5.png%3Fraw%3Dtrue" title="Example screenshot" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiling EQL5
&lt;/h2&gt;

&lt;p&gt;EQL5 doesn't appear to be in the repos of any major Linux distro. It is available from AUR on Arch, but since I use Debian, I compiled it from source. Don't worry. It's not as scary as it sounds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone the EQL5 Git repo. EQL5 doesn't appear to have releases or tags, so I guess you just clone &lt;code&gt;master&lt;/code&gt; and hope that it's in a workable state.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git clone https://gitlab.com/eql/EQL5.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install dependencies. This was actually the hardest part. Not the installing. That's just &lt;code&gt;apt install whatever&lt;/code&gt; (or the package manager of your preferred distro). There are hundreds of Qt packages on Debian, and it's not always clear what Qt module corresponds to what Debian package (here's a &lt;a href="https://askubuntu.com/a/577334" rel="noopener noreferrer"&gt;list&lt;/a&gt; I found useful). If I got an unknown module error during the &lt;code&gt;make&lt;/code&gt; step, I would look the Qt module up in the list on Ask Ubuntu and install the corresponding dev package. I ended up needing to install libqt5svg5-dev, qml-module-qtquick2, qtbase5-dev, qtdeclarative5-dev, qtmultimedia5-dev, qttools5-dev, and qtwebengine5-dev. This will get EQL5 compiled, but if you choose to build your GUI with QML, depending on what &lt;code&gt;import&lt;/code&gt; statements you use in your QML file, you'll need some additional packages. To follow this tutorial, you'll also need qml-module-qtquick-controls2, qml-module-qtquick-layouts, qml-module-qtquick-window2. The other dependency you'll need is ECL, and I also recommend installing &lt;code&gt;qml&lt;/code&gt; (it is helpful if building your GUI with QML). You can install all the dependencies needed on Debian/Ubuntu with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;ecl libqt5svg5-dev qml-module-qtquick2 qtbase5-dev qtdeclarative5-dev qtmultimedia5-dev qttools5-dev qtwebengine5-dev qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qtquick-window2 qml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Navigate into the &lt;code&gt;src&lt;/code&gt; subdirectory of the cloned EQL5 Git repo, compile, and install. The &lt;code&gt;-qt5&lt;/code&gt; flag was necessary on Debian, because &lt;code&gt;qmake&lt;/code&gt; is a symlink to &lt;code&gt;qtchooser&lt;/code&gt;. On other distros, this flag may not be required.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;EQL5/src
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ecl &lt;span class="nt"&gt;-shell&lt;/span&gt; make
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;qmake &lt;span class="nt"&gt;-qt5&lt;/span&gt; eql5.pro
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;make
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Now &lt;code&gt;eql5&lt;/code&gt; should be available to run. Verify it with &lt;code&gt;eql5 -qgui&lt;/code&gt;, which will launch a GUI REPL. This also gives you access to some documentation on the EQL5 functions. Go the the &lt;em&gt;Help&lt;/em&gt; tab for a searchable list of functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Building the GUI with QML
&lt;/h2&gt;

&lt;p&gt;EQL5 supports both Qt Widgets and Qt Quick. They primarily differ in how you define your UI. With Qt Widgets, you declare it procedurally. With Qt Quick, you use a declarative language called QML to define your UI. The underlying implementation of the widgets also differ. Qt Widgets has been around a lot longer and has a broader range of available widgets, but many of these widgets are more desktop-oriented without touch components and not easily adapted for touch. Qt Quick, on the other hand, has been developed from the beginning to work well on touch screens (tablets, smartphones, etc.).&lt;/p&gt;

&lt;p&gt;Qt is essentially maintaining two sets of widgets. It would make more sense to me if QML was just an alternative way of specifying which widgets to use instead of a completely new set of widgets, but I'm not a Qt maintainer or even a C++ developer. I guess creating new widgets was easier than adapting the old widgets to be touch friendly.&lt;/p&gt;

&lt;p&gt;Qt Quick is sometimes criticized for having a foreign look on the desktop (neither Qt Quick or Qt Widgets use native widgets, but Qt Widgets look more native on the desktop). It also has a smaller set of available widgets. Native look and feel is not important to me, and Qt Quick has plenty of widgets to implement the example in this tutorial. Given a choice between defining a GUI procedurally and declaratively, I'm going to choose the declarative option.&lt;/p&gt;

&lt;p&gt;The syntax of QML looks like a cross between JSON and CSS. Objects are specified followed by a pair of braces. Properties of the object are specified with key-value colon-separated pairs. You can create white text on a blue background like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;

&lt;span class="kt"&gt;Rectangle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="kt"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;anchors.centerIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can build custom GUI elements out of rectangles and other drawing primitives, but it would be nice to have some pre-defined widgets to use. Let's also import Qt Quick Controls and Qt Quick Layouts. You will need to have both modules installed on your system. They are probably available in your distro's package manager (see above for installing them on Debian).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Controls&lt;/span&gt; &lt;span class="mf"&gt;2.2&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Layouts&lt;/span&gt; &lt;span class="mf"&gt;1.2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For maximum compatibility, I recommend specifying the minimum version of modules. You might have Qt Quick Controls 2.15 on the machine your developing on, but one of your users might have an earlier version. Only bump the version if a later version has features you need to use. For example, &lt;a href="https://doc.qt.io/qt-5/qml-qtquick-layouts-layout.html#margins-attached-prop" rel="noopener noreferrer"&gt;&lt;code&gt;Layout.margins&lt;/code&gt;&lt;/a&gt; wasn't introduced until Qt Quick Layouts 1.2. We'll be setting margins in the example we're building with this tutorial, so I imported version 1.2 of Qt Quick Layouts.&lt;/p&gt;

&lt;p&gt;We arrange items in our UI using &lt;code&gt;ColumnLayout&lt;/code&gt; and &lt;code&gt;RowLayout&lt;/code&gt; from Qt Quick Layouts. &lt;code&gt;ColumnLayout&lt;/code&gt; arranges its children vertically, and &lt;code&gt;RowLayout&lt;/code&gt; arranged its children horizontally. At the top, there will be a slider, so we'll put a &lt;code&gt;Slider&lt;/code&gt; within a &lt;code&gt;ColumnLayout&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="kt"&gt;ColumnLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;root&lt;/span&gt;

  &lt;span class="kt"&gt;Slider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;frequencySlider&lt;/span&gt;
    &lt;span class="nl"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
    &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;
    &lt;span class="nl"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;
    &lt;span class="nl"&gt;Layout.fillWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nl"&gt;Layout.margins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The range of frequencies audible by humans is typically between 20 Hz and 20 KHz (we lose the ability to hear some of those higher frequencies as we age). The &lt;a href="https://en.wikipedia.org/wiki/A440_(pitch_standard)" rel="noopener noreferrer"&gt;musical note A above middle C&lt;/a&gt; is 440 Hz. Since A4 serves as a general tuning standard, it seems like a sensible default. &lt;code&gt;Layout.fillWidth&lt;/code&gt; makes the slider take up the full width of the window.&lt;/p&gt;

&lt;p&gt;Under the slider, there will be a row (a great use for &lt;code&gt;RowLayout&lt;/code&gt;) with two buttons and a spin box (a widget for entering a number within a fixed range).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="kt"&gt;RowLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
  &lt;span class="nl"&gt;Layout.alignment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Qt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AlignHCenter&lt;/span&gt;
  &lt;span class="nl"&gt;Layout.margins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;

  &lt;span class="kt"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;RowLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;SpinBox&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;frequencyField&lt;/span&gt;
      &lt;span class="nl"&gt;editable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="nl"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
      &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;frequencySlider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
      &lt;span class="nl"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kt"&gt;Label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under that is another row of controls: another spin box, a button, and a drop-down menu to select notes from. Frequency is rather abstract. The drop-down gives the user the ability to select a musical note.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="kt"&gt;RowLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
  &lt;span class="nl"&gt;Layout.margins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;

  &lt;span class="kt"&gt;RowLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;SpinBox&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;durationField&lt;/span&gt;
      &lt;span class="nl"&gt;editable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="nl"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
      &lt;span class="nl"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;600000&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kt"&gt;Label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ms&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Play&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kt"&gt;RowLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;Label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;♪&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kt"&gt;ComboBox&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;C&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;D&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;E&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;F&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I started this project with zero knowledge of QML. I went from Googling "qml" to the complete mockup above in about 30 minutes. I haven't written any of the application logic (in fact, we haven't even touched Common Lisp yet), but you can see how powerful a declarative syntax like QML is for defining GUIs. On big teams, you could even have a designer write the QML while a backend programmer wrote the logic. If you installed the QML viewer above (the qml package on Debian), you can preview your GUI with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;qml bleep.qml
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Feql5-mockup.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Feql5-mockup.png%3Fraw%3Dtrue" title="QML preview" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the Logic in Common Lisp
&lt;/h2&gt;

&lt;p&gt;Copy &lt;code&gt;qml-lisp.lisp&lt;/code&gt; from the EQL5 example directory (&lt;code&gt;EQL5/examples/M-modules/quick/qml-lisp/qml-lisp.lisp&lt;/code&gt;) to your working directory. This enables QML to call Lisp functions and vice versa. I'm not sure why this isn't built into EQL5, but it's easy enough to copy it from the Qt Quick examples that come with EQL5. Also, create a &lt;code&gt;bleep.lisp&lt;/code&gt; for the program logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;qrequire&lt;/span&gt; &lt;span class="ss"&gt;:quick&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;; Have EQL5 use Qt Quick module from Qt&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="ss"&gt;:qml-lisp&lt;/span&gt; &lt;span class="s"&gt;"qml-lisp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;; Load qml-lisp.lisp package copied from example&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;use-package&lt;/span&gt; &lt;span class="ss"&gt;:qml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;; Import all external symbols from above package&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;qrequire&lt;/code&gt; function is part of the EQL5 bindings. It loads a specified Qt module. In our case, we want to load the Qt Quick module. Then we load the Lisp package we copied from the EQL5 examples that enables communication between Lisp and QML. We'll use these two imports to run the QML UI we created above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;run&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c1"&gt;; QQuickView provides a window for displaying a Qt Quick user interface:&lt;/span&gt;
  &lt;span class="c1"&gt;; https://doc.qt.io/qt-5/qquickview.html&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;setf&lt;/span&gt; &lt;span class="nv"&gt;qml:*quick-view*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;qnew&lt;/span&gt; &lt;span class="s"&gt;"QQuickView"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x:do-with&lt;/span&gt; &lt;span class="nv"&gt;qml:*quick-view*&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;|setSource|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;|fromLocalFile.QUrl|&lt;/span&gt; &lt;span class="s"&gt;"bleep.qml"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;|setTitle|&lt;/span&gt; &lt;span class="s"&gt;"Bleep"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;|show|&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;run&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;*quick-view*&lt;/code&gt; variable is supplied by the &lt;code&gt;qml-lisp.lisp&lt;/code&gt; file we copied over (hence the &lt;code&gt;qml&lt;/code&gt; namespace). It can be either a QQuickView or QQuickWidget Qt object. We use &lt;code&gt;qnew&lt;/code&gt; from EQL5 to create a &lt;a href="https://doc.qt.io/qt-5/qquickview.html" rel="noopener noreferrer"&gt;QQuickView&lt;/a&gt; object. The &lt;code&gt;qnew&lt;/code&gt; function creates a Qt object of a given class.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;x&lt;/code&gt; namespace includes utility functions and macros that come with EQL5. Since this namespace is built into EQL5, I would have thought the &lt;code&gt;qml&lt;/code&gt; namespace could have been too. The &lt;code&gt;x&lt;/code&gt; namespace does not appear to be documented anywhere. I copied the usage of &lt;code&gt;do-with&lt;/code&gt; from the Qt Quick examples that come with EQL5. It basically let's us chain together a bunch of Qt methods on the &lt;code&gt;*quick-view*&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;EQL5 comes with &lt;code&gt;qfun&lt;/code&gt; for calling Qt methods on objects. For example, &lt;code&gt;(qfun qml:*quick-view* "show")&lt;/code&gt; would call the &lt;code&gt;show&lt;/code&gt; method on the &lt;code&gt;qml:*quick-view*&lt;/code&gt; object. EQL5 also comes with a shorthand for this: &lt;code&gt;(|show| qml:*quick-view*)&lt;/code&gt;. We can use this shorthand to set the source of the QQuickView to the QML file we created above, set a title for the window, and finally call the &lt;code&gt;show&lt;/code&gt; method to display the window. Execute this with &lt;code&gt;eql5&lt;/code&gt;, and you should get a window that looks just like the window you previewed above with &lt;code&gt;qml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;eql5 bleep.lisp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can tell that the default style has been designed to be touch friendly. The handle on the slider is extra big so that it can be dragged with a finger. All the buttons have plenty of padding to make them easy targets. Qt Quick also comes with a platform-agnostic style called Fusion that offers a desktop-oriented look and feel.&lt;/p&gt;

&lt;p&gt;There are &lt;a href="https://doc.qt.io/QT-5/qtquickcontrols2-styles.html#using-styles-in-qt-quick-controls" rel="noopener noreferrer"&gt;four ways&lt;/a&gt; to set the style. The first is using the &lt;code&gt;QQuickStyle&lt;/code&gt; class, but this class is not wrapped by EQL5. The second is to pass a &lt;code&gt;-style&lt;/code&gt; command line argument. Asking our user to include a command line argument to get the desired styling seems a bit much. The next option is to set a &lt;code&gt;QT_QUICK_CONTROLS_STYLE&lt;/code&gt; environment variable. ECL has &lt;a href="https://common-lisp.net/project/ecl/static/manual/Operating-System-Interface.html" rel="noopener noreferrer"&gt;&lt;code&gt;ext:setenv&lt;/code&gt;&lt;/a&gt; for setting environment variables. We'll set the environment variable before defining &lt;code&gt;run&lt;/code&gt; and then execute our code again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Use the desktop-oriented style Fusion instead of the default&lt;/span&gt;
&lt;span class="c1"&gt;; https://doc.qt.io/QT-5/qtquickcontrols2-styles.html&lt;/span&gt;
&lt;span class="c1"&gt;; EQL5 doesn't wrap the QQuickStyle class so using environment variable&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ext:setenv&lt;/span&gt; &lt;span class="s"&gt;"QT_QUICK_CONTROLS_STYLE"&lt;/span&gt; &lt;span class="s"&gt;"fusion"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Feql5-fusion.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Feql5-fusion.png%3Fraw%3Dtrue" title="Fusion style" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scale of 20 to 20,000 is so large that 440 barely appears to move the slider at all. Ideally, 440 would fall about the middle of the slider. To achieve this, let's use a logarithmic scale.&lt;/p&gt;

&lt;p&gt;I found a &lt;a href="https://stackoverflow.com/questions/846221/logarithmic-slider/846249#846249" rel="noopener noreferrer"&gt;Stack Overflow answer&lt;/a&gt; on how to map a slider to a logarithmic scale. The code given in the answer is JavaScript. We can embed JavaScript directly into our QML, so we could use the JavaScript example. This being a Lisp tutorial, we are going to port it to Common Lisp.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Scale used by slider&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defparameter&lt;/span&gt; &lt;span class="vg"&gt;*min-position*&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defparameter&lt;/span&gt; &lt;span class="vg"&gt;*max-position*&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;; Range of frequencies&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defparameter&lt;/span&gt; &lt;span class="vg"&gt;*min-frequency*&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defparameter&lt;/span&gt; &lt;span class="vg"&gt;*max-frequency*&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;; Logarithmic scale for frequency (so middle A [440] falls about in the middle)&lt;/span&gt;
&lt;span class="c1"&gt;; Adapted from https://stackoverflow.com/questions/846221/logarithmic-slider&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="vg"&gt;*min-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;max-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="vg"&gt;*max-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defvar&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;max-freq&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="vg"&gt;*max-position*&lt;/span&gt; &lt;span class="vg"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="c1"&gt;; Convert slider position to frequency&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;position-&amp;gt;frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;exp&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nb"&gt;position&lt;/span&gt; &lt;span class="vg"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;span class="c1"&gt;; Convert frequency to slider position&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="vg"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added some global parameters to the top of the script. The variable name &lt;code&gt;*min-position*&lt;/code&gt; is just a Lisp naming convention for global parameters. I came up with the range of 0-2,000 by trial and error. It seemed to strike the best balance between each step of the slider making a noticeable change to the frequency while still allowing the user to narrow in on a specific frequency with just the slider. Then we create two functions: one that takes the position on the slider and returns the frequency (&lt;code&gt;position-&amp;gt;frequency&lt;/code&gt;) and another that takes a frequency and returns the position on the slider (&lt;code&gt;frequency-position&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;It would be useful if these global parameters were available both to Lisp and QML. Qt has &lt;a href="https://doc.qt.io/qt-5/qtqml-cppintegration-contextproperties.html" rel="noopener noreferrer"&gt;a way to embed C++ Objects into QML&lt;/a&gt; by setting a context property on the root context. We could pass the string "Bugs Bunny" from C++ to QML this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// C++&lt;/span&gt;
&lt;span class="n"&gt;QString&lt;/span&gt; &lt;span class="n"&gt;fictionalRabbit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bugs Bunny"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rootContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setContextProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fictionalRabbit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fictionalRabbit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="c1"&gt;// QML&lt;/span&gt;
&lt;span class="kt"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fictionalRabbit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using EQL5 and Lisp, it would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;|setContextProperty|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;|rootContext|&lt;/span&gt; &lt;span class="nv"&gt;qml:*quick-view*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="s"&gt;"fictionalRabbit"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;qvariant-from-value&lt;/span&gt; &lt;span class="s"&gt;"Bugs Bunny"&lt;/span&gt; &lt;span class="s"&gt;"QString"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order is reversed in Lisp compared to C++. Whereas in C++ you have &lt;code&gt;view.rootContext()&lt;/code&gt;, in Lisp you do &lt;code&gt;(|rootContext| qml:*quick-view*)&lt;/code&gt;. The method to call comes first. Then the object to call that method on followed by any additional arguments. The value passed must be a C++ object. EQL5 comes with &lt;code&gt;qvariant-from-value&lt;/code&gt; that makes it easy to construct a &lt;code&gt;QVariant&lt;/code&gt; of a specified type. That's more than I want to type for every variable I want to export from Lisp into QML, so I whipped up a little helper function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Helper function to make Lisp data available in QML&lt;/span&gt;
&lt;span class="c1"&gt;; Allows writing (set-context-property variable-in-lisp "variableInLisp" "QString")&lt;/span&gt;
&lt;span class="c1"&gt;; instead of (|setContextProperty| (|rootContext| qml:*quick-view*)&lt;/span&gt;
&lt;span class="c1"&gt;;   "variableInLisp" (qvariant-from-value variable-in-lisp "QString"))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;set-context-property&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;lisp-var&lt;/span&gt; &lt;span class="nv"&gt;qml-name&lt;/span&gt; &lt;span class="nv"&gt;type-name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;root-context&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;|rootContext|&lt;/span&gt; &lt;span class="nv"&gt;qml:*quick-view*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;objectified&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;qvariant-from-value&lt;/span&gt; &lt;span class="nv"&gt;lisp-var&lt;/span&gt; &lt;span class="nv"&gt;type-name&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;|setContextProperty|&lt;/span&gt; &lt;span class="nv"&gt;root-context&lt;/span&gt; &lt;span class="nv"&gt;qml-name&lt;/span&gt; &lt;span class="nv"&gt;objectified&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I added this to my &lt;code&gt;run&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Make data available in QML&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;set-context-property&lt;/span&gt; &lt;span class="vg"&gt;*min-position*&lt;/span&gt; &lt;span class="s"&gt;"minPosition"&lt;/span&gt; &lt;span class="s"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;set-context-property&lt;/span&gt; &lt;span class="vg"&gt;*max-position*&lt;/span&gt; &lt;span class="s"&gt;"maxPosition"&lt;/span&gt; &lt;span class="s"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;set-context-property&lt;/span&gt; &lt;span class="vg"&gt;*min-frequency*&lt;/span&gt; &lt;span class="s"&gt;"minFrequency"&lt;/span&gt; &lt;span class="s"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;set-context-property&lt;/span&gt; &lt;span class="vg"&gt;*max-frequency*&lt;/span&gt; &lt;span class="s"&gt;"maxFrequency"&lt;/span&gt; &lt;span class="s"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now need to modify our QML to use these Lisp functions and variables. The first thing we need to do is add another &lt;code&gt;import&lt;/code&gt; statement at the top.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Controls&lt;/span&gt; &lt;span class="mf"&gt;2.2&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;QtQuick&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Layouts&lt;/span&gt; &lt;span class="mf"&gt;1.2&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;EQL5&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This imports everything we need to call Lisp functions from QML.&lt;/p&gt;

&lt;p&gt;If you played around with the QML mockup above, you may have noticed moving the slider automatically updated the spin box underneath it. This was done with a property binding. The value of the spin box was set to &lt;code&gt;frequencySlider.value&lt;/code&gt;. Whenever the slider value changes, the spin box value automatically updates. This was fine for a quick mockup, but we'd like for it to work both ways. When you change the value of the spin box, the slider should also automatically update. Unfortunately, QML doesn't support &lt;a href="http://imaginativethinking.ca/bi-directional-data-binding-qt-quick/" rel="noopener noreferrer"&gt;bi-directional property bindings&lt;/a&gt;. What we'll do is create a property that we'll bind both the slider and spin box to. Then we'll create handlers in both to update this property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="kt"&gt;ColumnLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;root&lt;/span&gt;

  &lt;span class="kd"&gt;property&lt;/span&gt; &lt;span class="kt"&gt;real&lt;/span&gt; &lt;span class="nl"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;

  &lt;span class="kt"&gt;Slider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;frequencySlider&lt;/span&gt;
    &lt;span class="nl"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;minPosition&lt;/span&gt;
    &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Lisp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;frequency-&amp;gt;position&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nl"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;maxPosition&lt;/span&gt;
    &lt;span class="nl"&gt;onValueChanged&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Lisp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;position-&amp;gt;frequency&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nl"&gt;Layout.fillWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nl"&gt;Layout.margins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

    &lt;span class="kt"&gt;RowLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kt"&gt;SpinBox&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;frequencyField&lt;/span&gt;
        &lt;span class="nl"&gt;editable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="nl"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;minFrequency&lt;/span&gt;
        &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt;
        &lt;span class="nl"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;maxFrequency&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="kt"&gt;Label&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The range for the slider and spin box is now defined using the context properties set in Lisp. To call Lisp functions, use &lt;code&gt;Lisp.call()&lt;/code&gt;. It's not pretty (the function name must be quoted and the list comma separated), but it gets the job done. If you run the code with EQL5 from a terminal, you can move the slider, and the spin box will update accordingly, but you'll get a whole bunch of these warnings in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;QML Slider: Binding loop detected for property "value"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whenever you move the slider, &lt;code&gt;onValueChanged&lt;/code&gt; updates the &lt;code&gt;frequency&lt;/code&gt; property. When &lt;code&gt;frequency&lt;/code&gt; changes this triggers another update of the slider, which in turn, updates &lt;code&gt;frequency&lt;/code&gt; again, creating a loop. Qt Quick Controls 2.2 and later have &lt;code&gt;onMoved&lt;/code&gt; in addition to &lt;code&gt;onValueChanged&lt;/code&gt;. If you swap &lt;code&gt;onValueChanged&lt;/code&gt; out for &lt;code&gt;onMoved&lt;/code&gt;, it breaks the loop. With &lt;code&gt;onMoved&lt;/code&gt;, the slider only updates the &lt;code&gt;frequency&lt;/code&gt; property when interactively moved. When a change to &lt;code&gt;frequency&lt;/code&gt; triggers a value change of the slider, it will not be propogated back to &lt;code&gt;frequency&lt;/code&gt; avoiding the binding loop.&lt;/p&gt;

&lt;p&gt;The other problem with our quick QML mockup upon closer inspection is that the QML spin box only works on integers. We want more precision than that to represent notes. For example, middle C is 261.63. Qt Widgets has &lt;a href="https://doc.qt.io/qt-5/qdoublespinbox.html" rel="noopener noreferrer"&gt;&lt;code&gt;QDoubleSpinBox&lt;/code&gt;&lt;/a&gt;, but Qt Quick doesn't have an &lt;a href="https://bugreports.qt.io/browse/QTBUG-67349" rel="noopener noreferrer"&gt;equivalent&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There is an &lt;a href="https://doc.qt.io/qt-5/qml-qtquick-controls2-spinbox.html#custom-values" rel="noopener noreferrer"&gt;example&lt;/a&gt; in the Qt documentation of the QML SpinBox on how it can be customized to accept floating point numbers. The example as-is truncates digits after 2 decimal places, thus 500.157 would become 500.15. This is probably contrary to what users expect, so I added &lt;code&gt;Math.round&lt;/code&gt; to &lt;code&gt;value&lt;/code&gt; and &lt;code&gt;valueFromText&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="kt"&gt;SpinBox&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;frequencyField&lt;/span&gt;
  &lt;span class="nl"&gt;editable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nl"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;minFrequency&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="nl"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nl"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;maxFrequency&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="nl"&gt;stepSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="nl"&gt;onValueChanged&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

  &lt;span class="kd"&gt;property&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nl"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="kd"&gt;property&lt;/span&gt; &lt;span class="kt"&gt;real&lt;/span&gt; &lt;span class="nl"&gt;realValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;

  &lt;span class="nl"&gt;validator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;DoubleValidator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frequencyField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;frequencyField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frequencyField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;frequencyField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nl"&gt;textFromValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;decimals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nl"&gt;valueFromText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromLocaleString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On either side of the spin box underneath the slider are buttons to increase/decrease the frequency by one octave. This could be done with a little JavaScript right in the QML, but since this is a Lisp tutorial, we're going to do it with Common Lisp. You can access QML properties from Lisp, but it needs &lt;code&gt;objectName&lt;/code&gt; to be set. To have uniform access to QML items from both QML and Lisp, it is convenient to set both &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;objectName&lt;/code&gt; to the same name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="kt"&gt;ColumnLayout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;root&lt;/span&gt;
  &lt;span class="nl"&gt;objectName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="kd"&gt;property&lt;/span&gt; &lt;span class="kt"&gt;real&lt;/span&gt; &lt;span class="nl"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the double quotes around the &lt;code&gt;objectName&lt;/code&gt;. It can't be a bare keyword. It must be a string. We can then access properties of this item from Lisp with &lt;code&gt;q&amp;lt;&lt;/code&gt; and &lt;code&gt;q&amp;gt;&lt;/code&gt; (both from &lt;code&gt;qml-lisp.lisp&lt;/code&gt; that we copied over.) They are shorthands for &lt;code&gt;qml-get&lt;/code&gt; and &lt;code&gt;qml-set&lt;/code&gt;, respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Buttons increase and decrease frequency by one octave&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;adjust-octave&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;q&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;|frequency|&lt;/span&gt; &lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;new-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt; &lt;span class="nv"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;unless&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;new-freq&lt;/span&gt; &lt;span class="vg"&gt;*min-frequency*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;new-freq&lt;/span&gt; &lt;span class="vg"&gt;*max-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;q&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;|frequency|&lt;/span&gt; &lt;span class="s"&gt;"root"&lt;/span&gt; &lt;span class="nv"&gt;new-freq&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;decrease-octave&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;adjust-octave&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;increase-octave&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;adjust-octave&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire the QML buttons up to these functions. An &lt;a href="https://en.wikipedia.org/wiki/Octave" rel="noopener noreferrer"&gt;octave&lt;/a&gt; is "the interval between one musical pitch and another with double its frequency."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="kt"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nl"&gt;onClicked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Lisp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;decrease-octave&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="kt"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nl"&gt;onClicked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Lisp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;increase-octave&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We gave the user a drop-down menu (combo box) in our QML mockup. Whenever a note is selected from the drop-down menu, we'll look up the frequency in a model and update the &lt;code&gt;frequency&lt;/code&gt; property.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="kt"&gt;ComboBox&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;textRole&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;note&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="c1"&gt;// Notes -&amp;gt; frequency (middle A-G [A4-G4])&lt;/span&gt;
  &lt;span class="c1"&gt;// http://pages.mtu.edu/~suits/notefreqs.html&lt;/span&gt;
  &lt;span class="nl"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;440.00&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;B&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;493.88&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;C&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;261.63&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;D&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;293.66&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;E&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;329.63&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;F&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;349.23&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;G&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;392.00&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nl"&gt;onActivated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;frequency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;freq&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also had to add &lt;code&gt;(si::trap-fpe t nil)&lt;/code&gt; to the top of my Lisp file. If not, I got a &lt;code&gt;Condition of type: DIVISION-BY-ZERO&lt;/code&gt; error whenever trying to expand the combo box. I'm not sure exactly why. I just copied it from one of the Qt Quick examples bundled with EQL5. There were no comments or documentation explaining its purpose.&lt;/p&gt;

&lt;p&gt;Now, let's make some noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;ql:quickload&lt;/span&gt; &lt;span class="s"&gt;"cl-portaudio"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;; Use Quicklisp to load CL-PortAudio&lt;/span&gt;

&lt;span class="c1"&gt;; Generate a tone using CL-PortAudio&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;generate-tone&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;sample-rate&lt;/span&gt; &lt;span class="mf"&gt;44100d0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;amplitude&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;; Initialize PortAudio environment&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;portaudio:with-audio&lt;/span&gt;
      &lt;span class="c1"&gt;; Open and start audio stream&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;portaudio:with-default-audio-stream&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;astream&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                                            &lt;span class="ss"&gt;:sample-format&lt;/span&gt; &lt;span class="ss"&gt;:float&lt;/span&gt;
                                            &lt;span class="ss"&gt;:sample-rate&lt;/span&gt; &lt;span class="nv"&gt;sample-rate&lt;/span&gt;
                                            &lt;span class="ss"&gt;:frames-per-buffer&lt;/span&gt; &lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;dotimes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="nv"&gt;duration&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;sample-rate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
          &lt;span class="c1"&gt;; Write buffer to output stream&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;portaudio:write-stream&lt;/span&gt; &lt;span class="nv"&gt;astream&lt;/span&gt;
            &lt;span class="c1"&gt;; portaudio:write-stream requires an array as input, not a list&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;make-array&lt;/span&gt; &lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt; &lt;span class="ss"&gt;:initial-contents&lt;/span&gt;
              &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;loop&lt;/span&gt; &lt;span class="nv"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;j&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;to&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;frames-per-buffer&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nv"&gt;collect&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="nv"&gt;j&lt;/span&gt; &lt;span class="nv"&gt;sample-rate&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
                  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;amplitude&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;pi&lt;/span&gt; &lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;))))))))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll use &lt;a href="https://github.com/filonenko-mikhail/cl-portaudio" rel="noopener noreferrer"&gt;Common Lisp bindings to PortAudio&lt;/a&gt; to generate the tone. This can be installed with &lt;a href="https://www.quicklisp.org/" rel="noopener noreferrer"&gt;Quicklisp&lt;/a&gt;. If you don't already have Quicklisp installed, it's painless. See the Quicklisp website for more details, but here's an example of installing Quicklisp on Debian and configuring ECL. The steps should be the same for any Linux distro and macOS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://beta.quicklisp.org/quicklisp.lisp
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ecl &lt;span class="nt"&gt;-load&lt;/span&gt; quicklisp.lisp
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;quicklisp-quickstart:install&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ql:add-to-init-file&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first time you run &lt;code&gt;bleep.lisp&lt;/code&gt;, it will take awhile as Quicklisp downloads CL-PortAudio (by default it will be downloaded to &lt;code&gt;~/quicklisp&lt;/code&gt;). The "proper" way to include this dependency would be to use &lt;a href="https://common-lisp.net/project/asdf/" rel="noopener noreferrer"&gt;ASDF&lt;/a&gt; and create a &lt;code&gt;.asd&lt;/code&gt; file for the project. EQL5 includes &lt;a href="https://gitlab.com/eql/EQL5/-/tree/master/my_app" rel="noopener noreferrer"&gt;an example&lt;/a&gt; of how to package an EQL5 app with ASDF. Since this is a quick tutorial, I'll stick with &lt;code&gt;ql:quickload&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;CL-PortAudio comes with a couple of helpful macros that makes initializing PortAudio and starting a stream simple. The &lt;code&gt;with-audio&lt;/code&gt; macro executes body within a PortAudio initialize/terminate environment. The &lt;code&gt;with-default-audio-stream&lt;/code&gt; macro executes body with an opened and started stream and shuts down the stream after it is done.&lt;/p&gt;

&lt;p&gt;Then you just feed PortAudio arrays of samples, &lt;code&gt;:frames-per-buffer&lt;/code&gt; at a time. I initiated &lt;code&gt;with-default-audio-stream&lt;/code&gt; with one channel, so the array is just a single-dimensional array of floating point numbers. If you were producing stereo sound, you would generate a two-dimensional array. The &lt;a href="http://pld.cs.luc.edu/telecom/mnotes/digitized_sound.html" rel="noopener noreferrer"&gt;basic formula for a sine wave&lt;/a&gt; is A sin(2πft) where &lt;em&gt;A&lt;/em&gt; is amplitude, &lt;em&gt;f&lt;/em&gt; is frequency, and &lt;em&gt;t&lt;/em&gt; is time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight common_lisp"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;amplitude&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sin&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;pi&lt;/span&gt; &lt;span class="nv"&gt;frequency&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire this up to the play button in the QML, and you're ready to make some noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight qml"&gt;&lt;code&gt;&lt;span class="kt"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Play&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="nl"&gt;onClicked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Lisp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;generate-tone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;durationField&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;You can check out the entire example on &lt;a href="https://github.com/goober99/lisp-gui-examples" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. This started as a personal learning project to explore the state of GUI programming in Lisp and has become a series of tutorials on building GUIs with various dialects of Lisp.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>lisp</category>
      <category>gui</category>
      <category>qt</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Learn Clojure by Example: JavaFX GUI with Cljfx</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Mon, 11 Jan 2021 01:11:21 +0000</pubDate>
      <link>https://dev.to/goober99/learn-clojure-by-example-javafx-gui-with-cljfx-2f3b</link>
      <guid>https://dev.to/goober99/learn-clojure-by-example-javafx-gui-with-cljfx-2f3b</guid>
      <description>&lt;p&gt;Clojure is a dialect of Lisp that runs on the JVM. JavaFX is a modern GUI toolkit for the JVM. You could use JavaFX directly with Clojure's Java interop, but Cljfx provides a declarative and functional wrapper for JavaFX. Instead of building yet another calculator, we're going to use Cljfx to build a GUI for generating a tone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fcljfx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fcljfx.png" title="Example screenshot" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll need Clojure installed. We also need a way to pull the Cljfx dependency into our app. It seems &lt;a href="https://clojure.org/guides/deps_and_cli" rel="noopener noreferrer"&gt;deps.edn&lt;/a&gt; would be the easiest way to accomplish this for a short tutorial example such as this, but I develop on Debian and found that the new &lt;a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=891141" rel="noopener noreferrer"&gt;command line tools&lt;/a&gt; for Clojure that enable this haven't made it into Debian yet. I could install Clojure from the Clojure website, but the point of this tutorial is to be easy to follow by someone not familiar with Clojure. It's a lot easier to direct a person to &lt;code&gt;apt install clojure&lt;/code&gt; (or whatever package manager your preferred distro uses). This tutorial is a living document maintained on GitHub along with the example code. Whenever the Clojure command line tools become available on Debian, it's possible I'll revisit this tutorial and update it.&lt;/p&gt;

&lt;p&gt;The Clojure build tool Leiningen is capable of managing dependencies (and a whole lot more). There is also a build tool for Clojure called Boot. I found more plentiful tutorials and beginner resources for Leiningen, plus Leiningen is available as a Debian package, so for this tutorial, Leiningen it is. Install Clojure and Leiningen from your distro's repo. Debian also has an OpenJFX package, but Leiningen was able to pull OpenJFX in as a dependency of Cljfx. The version pulled in by Leiningen is probably more up to date than whatever might be sitting in your distro's repos.&lt;/p&gt;

&lt;p&gt;After that, we need to create a Leiningen project directory. This can be accomplished with &lt;code&gt;lein new app bleep&lt;/code&gt;, but that created a directory full of subdirectories and files that were unnecessary for a short little example like this. I created the directory structure (&lt;code&gt;mkdir -p cljfx/src/bleep&lt;/code&gt;) and created &lt;code&gt;cljfx/project.clj&lt;/code&gt; and &lt;code&gt;cljfx/src/bleep/core.clj&lt;/code&gt; with the minimum needed to create a window with Cljfx.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;project.clj&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;defproject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bleep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"0.1.0-SNAPSHOT"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:dependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;org.clojure/clojure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"1.10.0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cljfx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"1.7.12"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;com.jsyn/jsyn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"20170815"&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bleep.core&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;core.clj&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;ns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bleep.core&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;:gen-class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;:require&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;cljfx.api&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fx&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;:import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;javafx.application&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;com.jsyn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSyn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;com.jsyn.unitgen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LineOut&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SineOscillator&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hello-world&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hello, World!"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:stage&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:showing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:title&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Bleep"&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:scene&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:scene&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="no"&gt;:root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:v-box&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="no"&gt;:padding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="no"&gt;:spacing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="no"&gt;:children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hello-world&lt;/span&gt;&lt;span class="p"&gt;}]}}})&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fx/create-renderer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;-main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Platform/setImplicitExit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;renderer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;root&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project file (&lt;code&gt;project.clj&lt;/code&gt;) is the file that tells Leiningen about your project (such as its dependencies). Typically with Leiningen, your code will live in the &lt;code&gt;src&lt;/code&gt; subdirectory in a subdirectory that matches the top-level namespace of your project. All this can be changed with your &lt;code&gt;project.clj&lt;/code&gt;, but we'll be sticking to convention for this example. In this subdirectory is &lt;code&gt;core.clj&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Begin the file with a namespace declaration. The namespace should have a &lt;code&gt;(:gen-class)&lt;/code&gt; declaration in the &lt;code&gt;ns&lt;/code&gt; form at the top for Leiningen. Since we'll be using Cljfx to create our GUI, we need to &lt;code&gt;require&lt;/code&gt; it. We'll mostly be using JavaFX via Cljfx, but you can also access JavaFX classes directly. Let's &lt;code&gt;import&lt;/code&gt; (a way to get Java code into Clojure) the JavaFX Platform class. We'll also import the Java JSyn library for use later in generating the tone.&lt;/p&gt;

&lt;p&gt;Leiningen needs a &lt;code&gt;-main&lt;/code&gt; function (note the leading dash is part of the function name). This is the function that will get called when you run your app with Leiningen or when you compile it to a standalone JAR. We use the &lt;code&gt;setImplicitExit&lt;/code&gt; method from the JavaFX Platform class to make sure the JavaFX runtime shuts down when the window is closed.&lt;/p&gt;

&lt;p&gt;Cljfx is declarative instead of imperative or object oriented like many GUI libraries. You describe the layout with a map of key-value pairs instead of creating controls with methods or functions. The &lt;code&gt;:fx/type&lt;/code&gt; key has a value of a kebab-cased keyword derived from a JavaFX class name. The other keys of the map are the kebab-cased properties of that class. You can refer to the &lt;a href="https://openjfx.io/" rel="noopener noreferrer"&gt;JavaFX documentation&lt;/a&gt; for a list of classes and their properties. JavaFX is well documented and has a large number of controls available.&lt;/p&gt;

&lt;p&gt;JavaFX defines the user interface by means of a stage and a scene. The stage is the top-level JavaFX container (the window). The scene is the container for all content. To add a JavaFX class to the map, you convert the class name to kebab-case. For example, JavaFX has a &lt;code&gt;VBox&lt;/code&gt; pane used to lay out controls vertically. This would be written &lt;code&gt;:v-box&lt;/code&gt; in the Cljfx map. The &lt;code&gt;VBox&lt;/code&gt; class has a &lt;code&gt;padding&lt;/code&gt; property. We can set this to 25 by adding a &lt;code&gt;:padding&lt;/code&gt; key to the map with a value of 25.&lt;/p&gt;

&lt;p&gt;Cljfx provides a renderer function created with &lt;code&gt;fx/create-renderer&lt;/code&gt; that you pass the map describing your user interface to. You can call &lt;code&gt;renderer&lt;/code&gt; multiple times to dynamically change the GUI. Now let's replace the Hello World label with a slider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-slider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:slider&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:min&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:max&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of placing the definition of your entire UI into one big map, you can compose the UI from reusable functions. This also makes your code more readable, because you don't have a deeply nested map. These functions can be used just like any other JavaFX class. The &lt;code&gt;:fx/type&lt;/code&gt; key is the function name, and the other keys of the map are the arguments to the function. We can add the &lt;code&gt;frequency-slider&lt;/code&gt; we created above to the &lt;code&gt;:children&lt;/code&gt; vector of the &lt;code&gt;:v-box&lt;/code&gt; in the &lt;code&gt;root&lt;/code&gt; map like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-slider&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;440&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The range of frequencies audible by humans is typically between 20 Hz and 20 KHz (we lose the ability to hear some of those higher frequencies as we age). The &lt;a href="https://en.wikipedia.org/wiki/A440_(pitch_standard)" rel="noopener noreferrer"&gt;musical note A above middle C&lt;/a&gt; is 440 Hz. Since A4 serves as a general tuning standard, it seems like a sensible default, but if you run the above with &lt;code&gt;lein run&lt;/code&gt;, this is what you'll see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fcljfx-linearslider.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fgoober99%2Flisp-gui-examples%2Fmaster%2Fscreenshots%2Fcljfx-linearslider.png" title="Slider showing 440 using a linear scale" alt="Slider"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The scale of 20 to 20,000 is so large that 440 doesn't appear to move the slider at all. Ideally, 440 would fall about the middle of the slider. To achieve this, let's use a logarithmic scale.&lt;/p&gt;

&lt;p&gt;I found a &lt;a href="https://stackoverflow.com/questions/846221/logarithmic-slider/846249#846249" rel="noopener noreferrer"&gt;Stack Overflow answer&lt;/a&gt; on how to map a slider to a logarithmic scale. The code given in the answer is JavaScript, but it was easy enough to port to Clojure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Scale used by slider&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;; Range of frequencies&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c1"&gt;; Logarithmic scale for frequency (so middle A [440] falls about in the middle)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;; Adapted from https://stackoverflow.com/questions/846221/logarithmic-slider&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-freq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Math/log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-frequency&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-freq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Math/log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-frequency&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-scale&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-freq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-position&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;; Convert slider position to frequency&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;position-&amp;gt;frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Math/round&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Math/exp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-freq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-scale&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-position&lt;/span&gt;&lt;span class="p"&gt;)))))))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;; Convert frequency to slider position&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-&amp;gt;position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Math/round&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Math/log&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-scale&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-position&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added some global parameters to the top of the script. I came up with the range of 0-2,000 by trial and error. It seemed to strike the best balance between each step of the slider making a noticeable change to the frequency while still allowing the user to narrow in on a specific frequency with just the slider.&lt;/p&gt;

&lt;p&gt;Then we create two functions: one that takes the position on the slider and returns the frequency (&lt;code&gt;position-&amp;gt;frequency&lt;/code&gt;) and another that takes a frequency and returns the position on the slider (&lt;code&gt;frequency-position&lt;/code&gt;). Now let's set the initial position of our slider with the &lt;code&gt;frequency-&amp;gt;position&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-slider&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:slider&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:min&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-position&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:max&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-position&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Underneath the slider is a text field showing the current frequency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-controls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:h-box&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:alignment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:center&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:spacing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:h-box&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:alignment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:center&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:spacing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:text-field&lt;/span&gt;&lt;span class="w"&gt;
                           &lt;/span&gt;&lt;span class="no"&gt;:text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="w"&gt;
                          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt;
                           &lt;/span&gt;&lt;span class="no"&gt;:text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hz"&lt;/span&gt;&lt;span class="p"&gt;}]}]})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this to the &lt;code&gt;:children&lt;/code&gt; vector of the &lt;code&gt;:v-box&lt;/code&gt; in the &lt;code&gt;root&lt;/code&gt; map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-slider&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-controls&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, we are starting to have a nice looking interface, but it doesn't do anything. If you slide the slider, nothing happens. If your experience is mostly with object-oriented GUI libraries, the way Cljfx does things will be a little unfamiliar. If you've used the JavaScript library React, it employs a similar model. Global state is stored in an atom. When you update that atom, all the relevant controls are updated accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Cljfx global state&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;*state&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;atom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;440&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add middleware to the &lt;code&gt;renderer&lt;/code&gt; that maps incoming data from the global state atom to the component description to be rendered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fx/create-renderer&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="no"&gt;:middleware&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fx/wrap-map-desc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;assoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then modify &lt;code&gt;-main&lt;/code&gt; to use &lt;code&gt;fx/mount-renderer&lt;/code&gt; instead of calling &lt;code&gt;renderer&lt;/code&gt; directly. This will watch the global state atom for changes and rerender the GUI accordingly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;-main&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Platform/setImplicitExit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fx/mount-renderer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;*state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In most of the other examples, I would now write a function for each control that updates the other controls when its value changes. For example, the slider must update the text field, and the text field must update the slider. Imagine in the future I decide to add a meter to the UI that also needs to be updated whenever the frequency changes. I would have to remember to add code to update the meter to both the slider and the text field. With Cljfx, the slider just has to update the value in the global state atom. Then the text field and any controls we add in the future that depend on the frequency will automatically update. Here's a function that will update the global state atom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Update frequency in global state if it's a valid frequency&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;set-frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;when&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-frequency&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;swap!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;*state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;assoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can add an &lt;code&gt;:on-value-changed&lt;/code&gt; property to the &lt;code&gt;:slider&lt;/code&gt; that calls this function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="no"&gt;:on-value-changed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;position-&amp;gt;frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and add an &lt;code&gt;:on-text-changed&lt;/code&gt; property to the &lt;code&gt;:text-field&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="no"&gt;:on-text-changed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;read-string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it. When one changes, the other does too. Once you get your head around it, it's a really elegant way to handle UI state.&lt;/p&gt;

&lt;p&gt;It might be helpful to have a couple buttons to increase or decrease the frequency by an octave. An &lt;a href="https://en.wikipedia.org/wiki/Octave" rel="noopener noreferrer"&gt;octave&lt;/a&gt; is "the interval between one musical pitch and another with double its frequency."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;octave-button&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:button&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:on-action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;)))})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now just add these buttons to the &lt;code&gt;:children&lt;/code&gt; vector of the &lt;code&gt;:h-box&lt;/code&gt; in the &lt;code&gt;frequency-controls&lt;/code&gt; map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;octave-button&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"&amp;lt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:modifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:h-box&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:alignment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:center&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:spacing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:padding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:top&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:bottom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:text-field&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="no"&gt;:text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="no"&gt;:on-text-changed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;read-string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="p"&gt;))}&lt;/span&gt;&lt;span class="w"&gt;
             &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="no"&gt;:text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hz"&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;octave-button&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:modifier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you slide the slider, the text field updates accordingly. If you type a number in the text field, the slider updates accordingly. All good, right? What if a user (and you know they will) enters a number higher than 20,000 or a letter?&lt;/p&gt;

&lt;p&gt;JavaFX has a &lt;code&gt;TextFormatter&lt;/code&gt; class for this purpose. The &lt;code&gt;TextFormatter&lt;/code&gt; uses a filter that can intercept and modify user input and a value converter that converts the value of the text field to a specified type whenever the field loses focus or the user hits Enter. The filter should be a function that accepts a Java &lt;code&gt;TextFormatter.Change&lt;/code&gt; object and either returns that object or returns null (&lt;code&gt;nil&lt;/code&gt; in Clojurese) to reject the change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Text field limited to entering numbers within range that updates specified&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;; key in global state atom (state-key)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num-filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.getControlNewText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;numified&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;read-string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;number?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;numified&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number-field&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;min-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;init-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state-key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:h-box&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:alignment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:center&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:spacing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:padding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:top&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:bottom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:left&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:right&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:text-field&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:pref-column-count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-value&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:text-formatter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:text-formatter&lt;/span&gt;&lt;span class="w"&gt;
                                &lt;/span&gt;&lt;span class="no"&gt;:value-converter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:number&lt;/span&gt;&lt;span class="w"&gt;
                                &lt;/span&gt;&lt;span class="no"&gt;:filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;num-filter&lt;/span&gt;&lt;span class="w"&gt;
                                &lt;/span&gt;&lt;span class="no"&gt;:value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;init-value&lt;/span&gt;&lt;span class="w"&gt;
                                &lt;/span&gt;&lt;span class="no"&gt;:on-value-changed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;cond&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;swap!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;*state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;assoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state-key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
                                                         &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;swap!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;*state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;assoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state-key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
                                                         &lt;/span&gt;&lt;span class="no"&gt;:else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;swap!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;*state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;assoc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;state-key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="p"&gt;))}}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}]})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cljfx doesn't provide wrappers for the methods of &lt;code&gt;TextFormatter.Change&lt;/code&gt;, but we can call the &lt;code&gt;getControlNewText()&lt;/code&gt; method using Clojure's Java interop. This method returns the complete new text wich will be used on the control after the change. We can check this value to determine if we want to accept the change or not.&lt;/p&gt;

&lt;p&gt;A caveat here is that the value of &lt;code&gt;:filter&lt;/code&gt; &lt;a href="https://github.com/cljfx/cljfx/issues/114#issuecomment-754642403" rel="noopener noreferrer"&gt;must be&lt;/a&gt; a top-level️ function. I wish I could define &lt;code&gt;:filter&lt;/code&gt; as an anonymous function. This would allow me to do something like also preventing the user from even entering a number higher than the allowed range rather than just reverting to the maximum value once the value is committed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="no"&gt;:filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.getControlNewText&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="n"&gt;numified&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;read-string&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;number?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;numified&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-value&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="n"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you try the above, it will throw a &lt;code&gt;Replace forbidden&lt;/code&gt; error. That's because &lt;code&gt;:filter&lt;/code&gt; is a constructor argument of &lt;code&gt;TextFormatter&lt;/code&gt; that cannot be modified afterward. If you use an anonymous function, every call to &lt;code&gt;number-field&lt;/code&gt; (whenever the UI is being rerendered due to a change of state) will try to create a new instance of the filter function, which is forbidden. But by making &lt;code&gt;num-filter&lt;/code&gt; a top-level function, it is now out of the scope of &lt;code&gt;max-value&lt;/code&gt;. I tried passing &lt;code&gt;max-value&lt;/code&gt; to it by wrapping &lt;code&gt;num-filter&lt;/code&gt; with a function that takes &lt;code&gt;max-value&lt;/code&gt; and calling that function in &lt;code&gt;:filter&lt;/code&gt;, but that still resulted in &lt;code&gt;Replace forbidden&lt;/code&gt;. I also tried defining &lt;code&gt;max-value&lt;/code&gt; at the top-level and wrapping the map inside &lt;code&gt;number-field&lt;/code&gt; with &lt;code&gt;binding&lt;/code&gt; to reassign the value of &lt;code&gt;max-value&lt;/code&gt;, but since &lt;code&gt;num-filter&lt;/code&gt; is being passed to the &lt;code&gt;TextFormatter&lt;/code&gt; and called later it is called outside the scope of the &lt;code&gt;binding&lt;/code&gt; and still unable to see &lt;code&gt;max-value&lt;/code&gt;. I finally gave up and fell back on just having the field revert to &lt;code&gt;max-value&lt;/code&gt; once the value is committed. If anyone has a solution for getting &lt;code&gt;max-value&lt;/code&gt; to the &lt;code&gt;num-filter&lt;/code&gt; function, I welcome pull requests.&lt;/p&gt;

&lt;p&gt;Now let's replace the baseline &lt;code&gt;:text-field&lt;/code&gt; and the &lt;code&gt;:h-box&lt;/code&gt; surrounding it with our new &lt;code&gt;number-field&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number-field&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="no"&gt;:min-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;min-frequency&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="no"&gt;:max-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;max-frequency&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="no"&gt;:init-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="no"&gt;:state-key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Hz"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's use this &lt;code&gt;number-field&lt;/code&gt; again to create a field to specify the duration of the beep in milliseconds. First, let's add a key to our global state atom to track duration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;*state&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;atom&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;440&lt;/span&gt;&lt;span class="w"&gt;
         &lt;/span&gt;&lt;span class="no"&gt;:duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then create our duration field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;general-controls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:h-box&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:spacing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number-field&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:min-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:max-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;600000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;; 10 minutes&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:init-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:state-key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:duration&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"ms"&lt;/span&gt;&lt;span class="p"&gt;}]})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this to the &lt;code&gt;:children&lt;/code&gt; vector of the &lt;code&gt;:v-box&lt;/code&gt; in the &lt;code&gt;root&lt;/code&gt; map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-slider&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency-controls&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;general-controls&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="no"&gt;:duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frequency is rather abstract. Let's also give the user the ability to select a musical note. We can store the corresponding frequencies for A4-G4 in a map.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Notes -&amp;gt; frequency (middle A-G [A4-G4])&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;; http://pages.mtu.edu/~suits/notefreqs.html&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;440.00&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;493.88&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s"&gt;"C"&lt;/span&gt;&lt;span class="mf"&gt;261.63&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s"&gt;"D"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;293.66&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s"&gt;"E"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;329.63&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s"&gt;"F"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;349.23&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s"&gt;"G"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;292.00&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll give the user a drop-down menu. Whenever a note is selected from the drop-down menu, we'll look up the frequency in the map and set that frequency in the global state atom. We'll add it on to &lt;code&gt;general-controls&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;general-controls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:h-box&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:spacing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number-field&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:min-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:max-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;600000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;; 10 minutes&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:init-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:state-key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:duration&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"ms"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:h-box&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:alignment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:center&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:spacing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt;
                           &lt;/span&gt;&lt;span class="no"&gt;:text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"♪"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:choice-box&lt;/span&gt;&lt;span class="w"&gt;
                           &lt;/span&gt;&lt;span class="no"&gt;:items&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"C"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"D"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"E"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"F"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"G"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
                           &lt;/span&gt;&lt;span class="no"&gt;:on-value-changed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;notes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="p"&gt;))}]}]})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let's make some noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="c1"&gt;; Generate a tone using JSyn&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;; Adapted from https://github.com/daveyarwood/javasynth/blob/master/src/javasynth/getting_started.clj&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;generate-tone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;synth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;doto&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;JSyn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;createSynthesizer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;.start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;LineOut.&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;sine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;SineOscillator.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;-amplitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;amplitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;synth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;synth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.connect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;sine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;-output&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;-input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;synth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;getCurrentTime&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.start&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;synth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sleepUntil&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;.stop&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;synth&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll use the Java &lt;a href="http://softsynth.com/jsyn/index.php" rel="noopener noreferrer"&gt;JSyn library&lt;/a&gt; we imported at the very beginning to generate the tone. Leiningen will pull in this dependency alongside Cljfx. Java has a standard &lt;a href="https://openjdk.java.net/groups/sound/" rel="noopener noreferrer"&gt;Sound API&lt;/a&gt; that could be used to generate a sine tone without any external dependencies, but it's a little too low-level for what I'm trying to accomplish with this tutorial. This is a tutorial about building a GUI with Clojure and not about sound processing with Clojure. If you're interested, I did find a &lt;a href="https://brainshave.com/blog/sound-synthesis/" rel="noopener noreferrer"&gt;well-written tutorial&lt;/a&gt; about using Clojure's Java interop to generate a sine tone with Clojure via the Java Sound API. Wire this function up to a button between the duration and note selector, and you're ready to make some noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;defn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;general-controls&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:keys&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;]}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:h-box&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:spacing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="no"&gt;:children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;number-field&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:min-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:max-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;600000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;; 10 minutes&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:init-value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:state-key&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:duration&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"ms"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:button&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Play"&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:on-action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate-tone&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;))}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:h-box&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:alignment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:center&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:spacing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="no"&gt;:children&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:label&lt;/span&gt;&lt;span class="w"&gt;
                           &lt;/span&gt;&lt;span class="no"&gt;:text&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"♪"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:fx/type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:choice-box&lt;/span&gt;&lt;span class="w"&gt;
                           &lt;/span&gt;&lt;span class="no"&gt;:items&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"C"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"D"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"E"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"F"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"G"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
                           &lt;/span&gt;&lt;span class="no"&gt;:on-value-changed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;notes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;%&lt;/span&gt;&lt;span class="p"&gt;))}]}]})&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;You can check out the entire example on &lt;a href="https://github.com/goober99/lisp-gui-examples" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. This started as a personal learning project to explore the state of GUI programming in Lisp and has become a series of tutorials on building GUIs with various dialects of Lisp.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>clojure</category>
      <category>lisp</category>
      <category>gui</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>SELinux Has a UI Problem</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Tue, 09 Apr 2019 17:31:18 +0000</pubDate>
      <link>https://dev.to/goober99/selinux-has-a-ui-problem-4knj</link>
      <guid>https://dev.to/goober99/selinux-has-a-ui-problem-4knj</guid>
      <description>&lt;p&gt;If you ever troubleshoot a problem on Red Hat (or closely related distros such as Fedora and CentOS), you'll come across dozens of tutorials and articles that tell you to resolve the problem by disabling SELinux. It's not just random blog posts and questionable StackOverflow answers: I've seen this advice in the documentation and knowledge base articles for enterprise software.&lt;/p&gt;

&lt;p&gt;Don't &lt;a href="https://stopdisablingselinux.com/"&gt;disable SELinux&lt;/a&gt;. Really, don't &lt;a href="https://blog.centos.org/2017/07/dont-turn-off-selinux/"&gt;turn off SELinux&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://blog.centos.org/2017/07/dont-turn-off-selinux/"&gt;post&lt;/a&gt; on the official CentOS blog asks "[W]hy do articles feel the need to outright deactivate SELinux rather than help readers work through any problems they might have? Is SELinux that hard? Actually, it's really not." It doesn't have anything to do with SELinux being too difficult. SELinux has a UI problem.&lt;/p&gt;

&lt;p&gt;When you say user interface (UI) design or user experience (UX), most people immediately think about graphical user interfaces (GUIs). A good UI is just as important for a command line application. If a command requires a lot of cumbersome flags or difficult-to-remember options, a user may use another app or *gasp* a GUI to accomplish the task. A software library also has a UI. The API is the UI with which a developer interacts with the library.&lt;/p&gt;

&lt;p&gt;While SELinux is usually invisible to the user, it does have a UI. I'm not talking about the various tools including some GUIs that allow administrators to set policy. I'm talking about how SELinux gives the user feedback when they attempt to do something that is blocked by SELinux on why it was blocked or even that it was SELinux that blocked it.&lt;/p&gt;

&lt;p&gt;Like any good sysadmin, I periodically run &lt;code&gt;yum update&lt;/code&gt; to make sure I'm protected from any newly discovered vulnerabilities, but it is not without some trepidation that I do this. It's not often, but sometimes it breaks something that was working perfectly fine before.&lt;/p&gt;

&lt;p&gt;The other day I did &lt;code&gt;yum update&lt;/code&gt; on a server running Tomcat (fortunately, I did it on the test server first). Tomcat was working fine, but the web app deployed to Tomcat had stopped working. I tried redeploying the app and restarting Tomcat. Still nothing, so I then checked the Tomcat logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (IO Error: The Network Adapter could not establish the connection)
Caused by: java.sql.SQLRecoverableException: IO Error: The Network Adapter could not establish the connection
Caused by: oracle.net.ns.NetException: The Network Adapter could not establish the connection
Caused by: java.net.ConnectException: Permission denied (connect failed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is absolutely nothing here that indicates SELinux is even the problem. Yes, the end of the stack trace is &lt;code&gt;Permission denied&lt;/code&gt;, but that sounds like it could mean the database account the web app is using is locked. I verified I was able to access the database over the network (I ran a simple query using SQL Developer from my workstation), I verified the server Tomcat was on in particular could access the database over the network (I ran a simple query using SQL*Plus), and I made sure the database account used by the web app was not locked.&lt;/p&gt;

&lt;p&gt;I then turned to the web app vendor's support site. They had a knowledge base article on the error. It turns out a &lt;code&gt;tomcat_can_network_connect_db&lt;/code&gt; SELinux boolean was added in RHEL 7.6. There is nothing special about a RHEL point release. They're basically just new installation media. All you need to do is install RHEL 7.x, and a &lt;code&gt;yum update&lt;/code&gt; will get you to the latest point release. Setting aside that one shouldn't expect any breaking changes in a point release, it should have been made abundantly clear to the user when Tomcat couldn't connect to a database that the reason was this new SELinux boolean. This is a UI issue.&lt;/p&gt;

&lt;p&gt;Most servers don't have a desktop environment installed. The primary way a user (here I'm talking about a user of the server such as an administrator not a user of the web app) interacts with the server is via SSH. In the case of servers, logs often are the UI.&lt;/p&gt;

&lt;p&gt;Tomcat is where the problem was, so where am I going to look for the issue? In the Tomcat log, of course. A user-friendly UI then would put some kind of indication in the Tomcat log right alongside the database error that the database connection had been blocked by SELinux. I'm sure SELinux logged the policy violation in its own log, but that is only helpful if I know that SELinux is the issue. That's poor UI design on the part of SELinux.&lt;/p&gt;

&lt;p&gt;Some might argue the blame is with Tomcat. Tomcat should log a more helpful error that indicates the issue is SELinux. Tomcat may not have been aware that the issue was SELinux. Tomcat runs on a variety of platforms and Linux distros, many of which do not have SELinux. The Tomcat developers may be unaware of changes to SELinux that could effect Tomcat. SELinux, on the other hand, knew this change would potentially break many Tomcat setups (&lt;code&gt;tomcat&lt;/code&gt; is in the name of the boolean). I wasn't using a third-party build of Tomcat either; I was using the Tomcat straight from the Oracle Linux (based on RHEL) repo and &lt;code&gt;journalctl -u tomcat&lt;/code&gt; to view the logs. Since everything is logged centrally via systemd, it seems SELinux should be able to log messages there as well. Why else force all logging through systemd? (I am no hater of systemd and actually think creating system services with systemd is a breeze compared to SysV-style init, but I'm not a fan of the way systemd handles logging.)&lt;/p&gt;

&lt;p&gt;Why do so many people and vendors just recommend turning off SELinux? Because the SELinux UI is not user-friendly. In fact, the knowledge base article I found on the vendor's support site said the way to resolve the problem was to edit &lt;code&gt;/etc/selinux/config&lt;/code&gt; and set &lt;code&gt;SELINUX=disabled&lt;/code&gt;. I didn't do that. Since the article already identified the boolean, I turned that specific boolean on with &lt;code&gt;setsebool -P tomcat_can_network_connect_db 1&lt;/code&gt; (the &lt;code&gt;-P&lt;/code&gt; flag causes the change to persist after reboots).&lt;/p&gt;

&lt;p&gt;This isn't an isolated experience. One time I was trying to configure Apache to serve files in a home directory. There was nothing in the Apache logs to indicate SELinux was the issue. I spent hours messing with file permissions and &lt;code&gt;chmod&lt;/code&gt;ing directories before discovering the SELinux boolean &lt;code&gt;httpd_enable_homedirs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I feel the benefits of SELinux outweigh the inconvenience of the poor UI. I spent time troubleshooting the database when the problem had nothing to do with the database. I often use open source projects with a poor UI, because I feel the benefits of open source outweigh the bad design (though there are instances where I think the open source solution actually has a superior UI than its proprietary alternatives). But it would be nice if SELinux had a better UI, and if we want to win new users to open source, we need to improve our UI. SELinux needs to get better about indicating when something is blocked by SELinux in the location where the user is going to be looking for the problem (e.g. in the Tomcat log for Tomcat issues or the Apache log for Apache issues).&lt;/p&gt;

</description>
      <category>ui</category>
      <category>ux</category>
      <category>linux</category>
      <category>security</category>
    </item>
    <item>
      <title>Monetizing Open Source on Proprietary Platforms</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Wed, 03 Apr 2019 06:10:59 +0000</pubDate>
      <link>https://dev.to/goober99/monetizing-open-source-on-proprietary-platforms-108j</link>
      <guid>https://dev.to/goober99/monetizing-open-source-on-proprietary-platforms-108j</guid>
      <description>&lt;p&gt;Open source has a monetization problem, but solving it is a sensitive issue. There was quite a stir over MongoDB &lt;a href="https://news.ycombinator.com/item?id=18229452"&gt;changing their licensing&lt;/a&gt;. There was an uproar over Caddy &lt;a href="https://news.ycombinator.com/item?id=15237923"&gt;charging for commercial use for binaries&lt;/a&gt; while this is something explicitly allowed by the &lt;a href="https://www.gnu.org/philosophy/selling.en.html"&gt;Free Software Foundation&lt;/a&gt;: "The word 'free' has two legitimate general meanings; it can refer either to freedom or to price. When we speak of 'free software', we're talking about freedom, not price. (Think of 'free speech', not 'free beer'.)"&lt;/p&gt;

&lt;p&gt;One interesting &lt;a href="https://www.jooq.org/download/"&gt;licensing model&lt;/a&gt; I've seen is that of jOOQ, a Java ORM. You can use the open source edition with open source databases (like PostgreSQL, MariaDB, and SQLite) but for proprietary databases (such as Oracle and SQL Server) you'll need one of the paid editions. In a way, this make sense: The developer has free access to develop against and test the ORM with open source databases, but the proprietary databases would require the developer have (sometimes costly) licenses for those databases.&lt;/p&gt;

&lt;p&gt;I'm working on a couple projects in my spare time. Like all good hobbyist projects, they scratch an itch I have. I'm developing them first and foremost for myself, to solve problems I have. There may be other people out there with the same problems. If the projects pan out, I plan to upload them to GitHub under an open source license so they might benefit from them as well.&lt;/p&gt;

&lt;p&gt;I develop on and solely use Linux, so I'm developing these projects first for Linux. What if Windows and Mac users could benefit from them as well? I don't currently have any way to test them on Windows or Mac. Even a used Mac mini is not cheap. Windows wouldn't require the purchase of any additional hardware since I could run it in VirtualBox, but unless I download a pirated copy (which I don't intend to do), I assume a Windows license would cost me something. I don't have any other use for Windows or Mac, so it would seem reasonable to charge for the Windows and Mac versions.&lt;/p&gt;

&lt;p&gt;I guess if the projects become popular enough and there really is demand for a Windows and/or Mac version, I could just wait for Windows and Mac developers to come along and submit pull requests adding build instructions for those platforms to the README and any necessary code changes. I've never developed a cross-platform desktop app, but I've worked enough with getting JavaScript to work in different browsers to know at least some code changes will be likely.&lt;/p&gt;

&lt;p&gt;I wouldn't push any of the code necessary to make them compatible with Windows and Mac to the public repo, but that wouldn't stop someone else submitting a pull request or just forking the project. Unless the projects do become popular, I'm unlikely to recoup my investment in the Mac hardware and Windows licensing.&lt;/p&gt;

&lt;p&gt;I could instead offer paid consulting or support related to the software, but both of these options seem like way too much work to me. I already have a full-time job &lt;em&gt;and&lt;/em&gt; a part-time ministry position. The whole point of these projects is something fun in my spare time, but who doesn't dream of making a little money off their side projects? Any time spent consulting or providing support would take away from what little spare time I have to work on the projects anyway. Selling Windows and Mac versions of the software would be a more passive source of extra income.&lt;/p&gt;

&lt;p&gt;Would I be better off just keeping the projects hobbyist projects? If others want to contribute Windows and Mac compatibility, so be it. If not, no sweat. Even though Windows and Mac are themselves proprietary platforms, would charging for my software on those platforms go against the spirit of open source? Is offering compatibility with proprietary platforms only in a paid edition a legitimate way to monetize open source?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>opensource</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>Vala Deserves a Closer Look</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Mon, 01 Apr 2019 22:23:49 +0000</pubDate>
      <link>https://dev.to/goober99/vala-deserves-a-closer-look-m56</link>
      <guid>https://dev.to/goober99/vala-deserves-a-closer-look-m56</guid>
      <description>&lt;p&gt;&lt;em&gt;This is not really a post about Vala--though I do think Vala deserves a closer look. It is about the general philosophy of evaluating programming languages. I also wrote &lt;a href="https://dev.to/goober99/right-tool-for-the-job-but-chances-are-the-right-tool-is-perl-ck8#so-many-tools-to-choose-from"&gt;a post that's not really about Perl&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There are so many programming languages out there that it's difficult to determine which are worth my attention. I've recently &lt;a href="https://dev.to/goober99/right-tool-for-the-job-but-chances-are-the-right-tool-is-perl-ck8#so-many-tools-to-choose-from"&gt;started looking&lt;/a&gt; for a new language to learn in an effort to continue expanding my tool set. I have an ever growing list of programming languages I would like to learn someday. One language that was not on that list was Vala.&lt;/p&gt;

&lt;p&gt;I had dismissed Vala as a GNOME thing with little interest to those outside the GNOME community, but recently I had seen Vala pop up in a few unexpected places, so I decided to give it a closer look.&lt;/p&gt;

&lt;p&gt;One of the ways I evaluate a programming language is to see what cool projects are being built with it. The way I do this is by going to GitHub's &lt;a href="https://github.com/trending"&gt;trending page&lt;/a&gt; and selecting the language from the Languages drop-down on the right.&lt;/p&gt;

&lt;p&gt;If all I find is the language interpreter/compiler, a web application framework, and a few assorted libraries, I'm not usually going to spend much time on that language. I'm interested in languages that people are using to build cool things. Every language (no matter how obscure) seems to have at least one web application framework these days. The trending page for Crystal this month shows no fewer than four web frameworks: Amber, Amethyst, Kemal, and Lucky. Forth has &lt;a href="https://www.1-9-9-1.com/"&gt;1991&lt;/a&gt;. Nim has &lt;a href="https://github.com/dom96/jester"&gt;Jester&lt;/a&gt;. Smalltalk has &lt;a href="https://en.wikipedia.org/wiki/Seaside_(software)"&gt;Seaside&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nor do I really care that much about module counts. Your language may have four different libraries to parse JSON and multiple ORMs, but what I want to know is what are people building with those libraries. If people are building cool projects with a language, I think a strong ecosystem and useful libraries will follow.&lt;/p&gt;

&lt;p&gt;I realize GitHub might not host all the cool things being done in a language, but I feel it is representative. Maybe cool things are being done in a language but they are proprietary. If a language is fun to use, I think it will be used for both proprietary and open source projects, so I think open source projects sufficient to judge a language. There could also be cool projects on SourceForge, GitLab, etc. For now, GitHub has by far the largest collection of open source projects, so it serves to give a pretty good survey of what is available in a given language.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part about Vala
&lt;/h2&gt;

&lt;p&gt;I guess since I do have Vala in the title, I should share some of the cool projects being built with Vala that convinced me to give it a closer look.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/akiraux/Akira"&gt;Akira&lt;/a&gt;. This is a UI/UX design tool for creating UI mock-ups. It hasn't even had a stable release yet, and it already has 1,526 stars on GitHub and $560/month pledged on &lt;a href="https://www.patreon.com/akiraux"&gt;Patreon&lt;/a&gt;. It has a really cool &lt;a href="https://github.com/akiraux/Akira#official-mascot"&gt;mascot&lt;/a&gt; too. The lead developer also has a &lt;a href="https://github.com/Alecaddd/sequeler"&gt;SQL client&lt;/a&gt; and &lt;a href="https://github.com/Alecaddd/taxi"&gt;FTP client&lt;/a&gt; written in Vala.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/johanmattssonm/birdfont"&gt;Birdfont&lt;/a&gt;. Font editor. Many Linux distro's have packages in their repos, and there is a package in OpenBSD. There are also binaries for Windows and Mac. This project is evidence that Vala is not limited to GNOME. I also found it funny that GitHub thinks 11.3% of the source is written in Brainfuck because of the .bf extension used for fonts in the Birdfont format.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/phw/peek"&gt;Peek&lt;/a&gt;. GIF screen recorder. Packaged for a lot of distros. Could be useful for creating animated screenshots for READMEs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/pdfpc/pdfpc"&gt;pdfpc&lt;/a&gt;. Presenter console with multi-monitor support for PDF files. Vala must be easy to learn or there are a lot of developers who already know it, because some of these projects are able to draw a lot of contributors. This project has 43 contributors with the latest commit 19 days ago.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/teejee2008/timeshift"&gt;Timeshift&lt;/a&gt;. System restore tool for Linux. This is another Vala project with a small but active community. It has 30 contributors and users submitting issues and pull requests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/p-e-w/finalterm"&gt;Final Term&lt;/a&gt;. This was a terminal emulator with advanced features. It's no longer maintained, but it was very popular (it has 3,999 stars on GitHub).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Most of the default apps in elementary OS are written in Vala. I haven't used it (I won't give up my MATE desktop), but elementary OS is a popular Linux distro (currently #4 on DistroWatch). It's app store, audio player, calculator, calendar, email client, file manager, image viewer, panel, screenshot tool, terminal emulator, text editor, video player, and even it's window manager are all written in Vala. I question the necessity of needing to write so many of their own apps when there are already so many great options available for many of these, but they do have a consistent design aesthetic and ecosystem. Many third party apps written for elementary OS (but working with other distros) are also written in Vala (such as this &lt;a href="https://github.com/babluboy/bookworm"&gt;eBook reader&lt;/a&gt; and &lt;a href="https://github.com/lainsce/quilter"&gt;focused writing app&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deepin is a popular Linux distro in China with it's own desktop environment. Unlike elementary OS which uses GTK, it uses Qt, yet it still employs Vala for a couple things. It's &lt;a href="https://github.com/linuxdeepin/deepin-terminal"&gt;terminal emulator&lt;/a&gt; and it's &lt;a href="https://github.com/linuxdeepin/deepin-wm"&gt;window manager&lt;/a&gt; (which started out as a fork of the elementary OS window manager) are both written in Vala.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There are some official GNOME apps written in Vala, but surprisingly, since Vala is a GNOME project, not very many. The GNOME &lt;a href="https://gitlab.gnome.org/GNOME/baobab/"&gt;disk usage analyzer&lt;/a&gt;, &lt;a href="https://gitlab.gnome.org/World/deja-dup"&gt;backup tool&lt;/a&gt;, and &lt;a href="https://gitlab.gnome.org/GNOME/shotwell"&gt;photo manager&lt;/a&gt; are written in Vala.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There's even a &lt;a href="https://github.com/valum-framework/valum"&gt;web application framework&lt;/a&gt; written in Vala.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vala may not have developers who "rewrite all the things" like Rust or the backing of a tech giant like Go and Reason. I've been averaging a new programming language every seven years, so this is a big decision. I'm taking my time. I'm considering making Rust or D my next language, but now I'm also considering Vala.&lt;/p&gt;

</description>
      <category>vala</category>
      <category>programminglanguages</category>
      <category>gtk</category>
      <category>philosophy</category>
    </item>
    <item>
      <title>Learn LambdaNative by Example: Desktop GUI</title>
      <dc:creator>Matthew D. Miller</dc:creator>
      <pubDate>Wed, 27 Mar 2019 21:19:28 +0000</pubDate>
      <link>https://dev.to/goober99/learn-lambdanative-by-example-desktop-gui-277l</link>
      <guid>https://dev.to/goober99/learn-lambdanative-by-example-desktop-gui-277l</guid>
      <description>&lt;p&gt;&lt;em&gt;This tutorial was updated on January 10, 2021 to generate a tone using LambdaNative instead of merely wrapping a Linux-only command-line program called beep that controls the PC speaker. This should now make it possible to follow this tutorial on any OS supported by LambdaNative. This tutorial can also be found in the &lt;a href="https://github.com/goober99/lisp-gui-examples/blob/master/examples/lambdanative/tutorial.md" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; with the example code. Pull requests for improving the example and tutorial are welcome.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;LambdaNative is a cross-platform framework for developing desktop and mobile apps using Scheme (built atop Gambit). Resources on LambdaNative seem to be extremely scarce. I couldn't find a single step-by-step tutorial for using LambdaNative, so hopefully my small contribution will benefit others trying to get started with LambdaNative.&lt;/p&gt;

&lt;p&gt;LambdaNative already includes a calculator demo, and frankly, I find making a millionth calculator example to be a little dull. Instead I'll be building a GUI for generating a tone. We're only going to be building a desktop GUI, so we aren't going to be tapping into the full power of LambdaNative. Where it seems LambdaNative would really be useful is enabling you to write cross-platform mobile apps with Scheme! Unfortunately, that is outside the scope of this tutorial. Maybe in the future I'll revisit LambdaNative and use it to create a mobile app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J7sKmeoo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/goober99/lisp-gui-examples/blob/master/screenshots/lambdanative.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J7sKmeoo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/goober99/lisp-gui-examples/blob/master/screenshots/lambdanative.png%3Fraw%3Dtrue" title="Example screenshot" alt="Screenshot" width="560" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing LambdaNative
&lt;/h2&gt;

&lt;p&gt;The LambdaNative wiki gives a &lt;a href="https://github.com/part-cw/lambdanative/wiki/Getting-Started#required-tools-and-libraries" rel="noopener noreferrer"&gt;list of dependencies&lt;/a&gt; that need to be installed with your distro's package manager before installing LambdaNative. They give an &lt;code&gt;apt&lt;/code&gt; command you can copy and paste to your terminal to install all the needed dependencies on Ubuntu.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Download the most recent &lt;a href="https://github.com/part-cw/lambdanative/releases" rel="noopener noreferrer"&gt;release&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unzip the release to a system-wide location such as &lt;code&gt;/opt&lt;/code&gt; or &lt;code&gt;/usr/local&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;unzip lambdanative-&lt;span class="k"&gt;*&lt;/span&gt;.zip &lt;span class="nt"&gt;-d&lt;/span&gt; /opt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Rename unzipped directory.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /opt
&lt;span class="nb"&gt;sudo mv &lt;/span&gt;lambdanative&lt;span class="k"&gt;*&lt;/span&gt; lambdanative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create the files &lt;code&gt;SETUP&lt;/code&gt; and &lt;code&gt;PROFILE&lt;/code&gt;. If you were developing a mobile app, you would need to configure these files for the respective SDKs. Since that is outside the scope of this tutorial, that is left as an exercise for the reader.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;lambdanative
&lt;span class="nb"&gt;sudo cp &lt;/span&gt;SETUP.template SETUP
&lt;span class="nb"&gt;sudo cp &lt;/span&gt;PROFILE.template PROFILE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Edit &lt;code&gt;scripts/lambdanative&lt;/code&gt; and populate the &lt;code&gt;LAMBDANATIVE&lt;/code&gt; variable with &lt;code&gt;/opt/lambdanative&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LAMBDANATIVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/opt/lambdanative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Place the LambdaNative initialization script in the system path.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; /opt/lambdanative/scripts/lambdanative /usr/bin/lambdanative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Create and initialize a LambdaNative build directory.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ~/lambdanative
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/lambdanative
lambdanative init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a New GUI App
&lt;/h2&gt;

&lt;p&gt;Your freshly initiated build directory will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apps/
modules/
configure
Makefile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your app will go in its own subdirectory in &lt;code&gt;apps&lt;/code&gt;. To create a new app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lambdanative create &amp;lt;appname&amp;gt; &amp;lt;apptype&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The options for &lt;code&gt;&amp;lt;apptype&amp;gt;&lt;/code&gt; are &lt;code&gt;console&lt;/code&gt;, &lt;code&gt;gui&lt;/code&gt;, and &lt;code&gt;eventloop&lt;/code&gt;. I created a new GUI app called bleep:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lambdanative create bleep gui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a directory in &lt;code&gt;apps&lt;/code&gt; called &lt;code&gt;bleep&lt;/code&gt; with several files in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiling the App
&lt;/h2&gt;

&lt;p&gt;LambdaNative utilizes the GNU Build System.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./configure bleep
make
make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, the build will target the local host. The first time you do this will take awhile, because it is downloading and compiling prerequisites. These prerequisites are cached to speed up subsequent compiles. If you get any errors during the initial build, you probably missed installing a dependency. Install it with your distro's package manager and then try compiling again.&lt;/p&gt;

&lt;p&gt;You can also configure in debug mode. You will want to clean the cache with &lt;code&gt;make scrub&lt;/code&gt; so everything is rebuilt. It will take awhile since everything is being rebuilt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./configure bleep debug
make scrub
make
make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my experience, debug mode didn't help much. Runtime errors are logged to &lt;code&gt;~/Desktop/log/*.txt&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[SYSTEM] 2019-03-16 00:59:21: Application bleep built 2019-03-16 00:59:14
[SYSTEM] 2019-03-16 00:59:21: Git hash
[ERROR] 2019-03-16 00:59:22: primordial: (assoc 49 #f): (Argument 2) LIST expected
[ERROR] 2019-03-16 00:59:22: HALT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not very helpful, is it? So I enabled debug mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[SYSTEM] 2019-03-16 01:16:44: Application bleep built 2019-03-16 01:16:34
[SYSTEM] 2019-03-16 01:16:44: Git hash
[ERROR] 2019-03-16 01:16:44: primordial: (assoc 49 #f): (Argument 2) LIST expected
[ERROR] 2019-03-16 01:16:44: trace: /opt/lambdanative/modules/ln_glgui/primitives.scm line=230 col=21
[ERROR] 2019-03-16 01:16:44: trace: /opt/lambdanative/modules/ln_glgui/primitives.scm line=273 col=21
[ERROR] 2019-03-16 01:16:44: trace: /opt/lambdanative/modules/ln_glgui/slider.scm line=80 col=8
[ERROR] 2019-03-16 01:16:44: trace: /opt/lambdanative/modules/ln_glgui/glgui.scm line=151 col=36
[ERROR] 2019-03-16 01:16:44: trace: /opt/lambdanative/modules/ln_glgui/glgui.scm line=145 col=11
[ERROR] 2019-03-16 01:16:44: trace: /opt/lambdanative/modules/ln_glgui/glgui.scm line=183 col=3
[ERROR] 2019-03-16 01:16:44: trace: /opt/lambdanative/modules/eventloop/eventloop.scm line=151 col=9
[ERROR] 2019-03-16 01:16:44: HALT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's definitely longer but still not very helpful. It actually includes line numbers, which appears promising, but they are all line numbers in LambdaNative modules. It doesn't actually trace the error all the way to the line in my app causing the error. I spent a lot more time reading the source of the LambdaNative modules than I would have liked.&lt;/p&gt;

&lt;p&gt;And that's when the log file even contained an error. Sometimes the app failed with a segmentation fault and there was no error in the log at all. I often peppered my source with &lt;code&gt;(log-status "reached here")&lt;/code&gt; and &lt;code&gt;tail -f&lt;/code&gt; the log file to debug and isolate errors.&lt;/p&gt;

&lt;p&gt;Errors caught at compile time, on the other hand, were much nicer. If an error occurred while compiling, the error message included the line number from my app.&lt;/p&gt;

&lt;p&gt;The development workflow is more akin to traditional compiled languages like C. I missed the quick feedback I'm used to while developing Scheme on a REPL. After the initial compile, subsequent compiles are much quicker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;time &lt;/span&gt;make

real  0m3.877s
user  0m2.652s
sys   0m0.605s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four seconds can seem like a long time when you are making a series of small changes or chasing down a bug. There is a &lt;a href="https://github.com/part-cw/lambdanative/wiki/Using-Emacs" rel="noopener noreferrer"&gt;module&lt;/a&gt; for REPL editing with Emacs. Since I don't use Emacs, I didn't give it a spin, but if I was developing a larger app, I might give it a try.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;install&lt;/code&gt; step will move the executable to &lt;code&gt;~/Desktop/bleep/bleep&lt;/code&gt; and launch it. This will launch a rectangular window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9fxxj06xa3qkwzd3qho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9fxxj06xa3qkwzd3qho.png" alt="Screenshot" width="340" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This window looks awful lonely. Let's add some widgets!&lt;/p&gt;

&lt;h2&gt;
  
  
  Coding the App
&lt;/h2&gt;

&lt;p&gt;If your interface will use text (such as labels on buttons), you must include a &lt;code&gt;FONTS&lt;/code&gt; file in your application subdirectory. I just copied the &lt;code&gt;FONTS&lt;/code&gt; file from one of the demos included with LambdaNative.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;apps/bleep
&lt;span class="nb"&gt;cp&lt;/span&gt; /opt/lambdanative/apps/LineDrop/FONTS &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what the file looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DejaVuSans.ttf 8 18,25 ascii
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a description of the file format, see the documentation of the file on the &lt;a href="https://github.com/part-cw/lambdanative/wiki/FONTS" rel="noopener noreferrer"&gt;LambdaNative wiki&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Your application subdirectory will already contain a &lt;code&gt;main.scm&lt;/code&gt;. This file contains the basic skeleton for a GUI app (the black rectangle above):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; LambdaNative gui template&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="no"&gt;#f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;
&lt;span class="c1"&gt;;; initialization&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;w&lt;/span&gt; &lt;span class="nv"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;make-window&lt;/span&gt; &lt;span class="mi"&gt;320&lt;/span&gt; &lt;span class="mi"&gt;480&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-orientation-set!&lt;/span&gt; &lt;span class="nv"&gt;GUI_PORTRAIT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;make-glgui&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;;; initialize gui here&lt;/span&gt;

  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;;; events&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;t&lt;/span&gt; &lt;span class="nv"&gt;EVENT_KEYPRESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;EVENT_KEYESCAPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-event&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;t&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;;; termination&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="no"&gt;#t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;;; suspend&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-suspend&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;;; resume&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-resume&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;;; eof&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I started by changing the comment at the top to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; bleep - GUI for generating a tone made with LambdaNative&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bulk of the skeleton consists of the &lt;a href="https://github.com/part-cw/lambdanative/wiki/Index-of-Module-eventloop" rel="noopener noreferrer"&gt;event loop&lt;/a&gt;. The &lt;code&gt;(main p1 p2 p3 p4 p5)&lt;/code&gt; loop takes five functions as arguments:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;p1&lt;/td&gt;
&lt;td&gt;Function to be run before the main loop is initialized. This is where you setup the GUI.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p2&lt;/td&gt;
&lt;td&gt;Main loop function, which is called constantly throughout the application's life. This is where you listen for events like key presses. Since most widgets take a callback, you shouldn't need to do much in this area.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p3&lt;/td&gt;
&lt;td&gt;Function to be run when the application is to be terminated.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p4&lt;/td&gt;
&lt;td&gt;Function, which is called when the application is suspended.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p5&lt;/td&gt;
&lt;td&gt;Function, which is called when the application is resumed.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The functions supplied in the skeleton for p3, p4, and p5 should be sufficient for most applications. We won't need to touch them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;make-window&lt;/span&gt; &lt;span class="mi"&gt;540&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-orientation-set!&lt;/span&gt; &lt;span class="nv"&gt;GUI_LANDSCAPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I started by changing the dimensions and orientation of the window. Now let's add some widgets to that initialization &lt;code&gt;lambda&lt;/code&gt;. I copy and pasted the example from the bottom of the &lt;a href="https://github.com/part-cw/lambdanative/wiki/glgui-slider" rel="noopener noreferrer"&gt;slider documentation page&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;sl&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-slider&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="mi"&gt;280&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="no"&gt;#f&lt;/span&gt; &lt;span class="nv"&gt;White&lt;/span&gt; &lt;span class="nv"&gt;White&lt;/span&gt; &lt;span class="nv"&gt;Orange&lt;/span&gt; &lt;span class="nv"&gt;Black&lt;/span&gt; &lt;span class="nv"&gt;num_25&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="nv"&gt;num_20&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="no"&gt;#f&lt;/span&gt; &lt;span class="nv"&gt;White&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;sl&lt;/span&gt; &lt;span class="ss"&gt;'showvalue&lt;/span&gt; &lt;span class="no"&gt;#t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are familiar with Scheme, that &lt;code&gt;set!&lt;/code&gt; probably made you pause. Too many exclamation marks in my Scheme code always make me nervous. I immediately start wondering if there is a better way to write the code. As it should. Side effects should be avoided when possible. I tried changing the &lt;code&gt;set!&lt;/code&gt; to &lt;code&gt;define&lt;/code&gt; and got the following error when recompiling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*** ERROR IN "/home/matthew/lambdanative/apps/bleep/main.scm"@97.5 -- Ill-placed 'define'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gambit (the underlying Scheme implementation used by LambdaNative) only allows &lt;code&gt;define&lt;/code&gt;s at the beginning of a &lt;code&gt;lambda&lt;/code&gt; body. This actually conforms with the R[5-7]RS specs, but I'm used to Scheme implementations (such as Racket, Chicken, and MIT/GNU Scheme) that allow &lt;code&gt;define&lt;/code&gt; anywhere in a &lt;code&gt;lambda&lt;/code&gt; body. All the LambdaNative examples and demos use &lt;code&gt;set!&lt;/code&gt;, so I used it as well.&lt;/p&gt;

&lt;p&gt;We must specify the color for several elements of the slider. LambdaNative doesn't use native widgets but draws its own widgets with OpenGL. I googled "color schemes" for inspiration and specified a few colors at the top of the script above the &lt;code&gt;(main)&lt;/code&gt; loop that I could reference throughout the program.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; UI color palette&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*background-color*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;color-rgb&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;color-rgb&lt;/span&gt; &lt;span class="mi"&gt;195&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="mi"&gt;63&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*accent-color*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;color-rgb&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt; &lt;span class="mi"&gt;34&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*text-color*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;color-rgb&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;;; Scale used by slider&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*max-position*&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;;; Range of frequencies&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variable name &lt;code&gt;*background-color*&lt;/code&gt; is just a Lisp naming convention for global parameters. LambdaNative provides &lt;code&gt;(color-rgb r g b)&lt;/code&gt; for creating colors. I also defined variables for the scale used by the slider and the range of frequencies accepted by beep.&lt;/p&gt;

&lt;p&gt;We also need to specify the position and size of the slider. Both are specified in pixels. There aren't percentages or other scalable units you may be familiar with from CSS. There are functions to get the width and height of the window, so you could code the math to make a widget 80% the width of the window. Since we're dealing with a simple example with hard-coded window dimensions, I just hard-coded the position and size as well. Note that you specify the position along the y-axis as pixels from the bottom of the window. This seemed counter intuitive to me, and I continually caught myself trying to specify pixels from the top of the window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; Background color&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;w&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-width-get&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;h&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-height-get&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-box&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nv"&gt;w&lt;/span&gt; &lt;span class="nv"&gt;h&lt;/span&gt; &lt;span class="nv"&gt;*background-color*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;;; Frequency slider&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-slider&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="mi"&gt;280&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt; &lt;span class="nv"&gt;*max-position*&lt;/span&gt; &lt;span class="no"&gt;#f&lt;/span&gt; &lt;span class="nv"&gt;White&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt; &lt;span class="nv"&gt;*accent-color*&lt;/span&gt; &lt;span class="no"&gt;#f&lt;/span&gt; &lt;span class="nv"&gt;ascii_18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="nv"&gt;ascii_18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="no"&gt;#f&lt;/span&gt; &lt;span class="nv"&gt;White&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'showlabels&lt;/span&gt; &lt;span class="no"&gt;#f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I set a background color for the entire window. The only way I could find to do this was to create a &lt;code&gt;glgui-box&lt;/code&gt; the size of the entire window and set the color of the box. I also renamed the variable from &lt;code&gt;sl&lt;/code&gt; to &lt;code&gt;slider&lt;/code&gt;. LambdaNative has the tendency to use short, non-descriptive variable names throughout its examples and documentation. I prefer to use more descriptive variable names. Replace the fonts in the example slider code with the fonts we specified in the &lt;code&gt;FONTS&lt;/code&gt; file. I also disabled the slider labels.&lt;/p&gt;

&lt;p&gt;The range of frequencies audible by humans is typically between 20 Hz and 20 KHz (we lose the ability to hear some of those higher frequencies as we age). The &lt;a href="https://en.wikipedia.org/wiki/A440_(pitch_standard)" rel="noopener noreferrer"&gt;musical note A above middle C&lt;/a&gt; is 440 Hz. Since A4 serves as a general tuning standard, it seems like a sensible default.&lt;/p&gt;

&lt;p&gt;The scale of 20 to 20,000 is so large that 440 wouldn't appear to move the slider at all. Ideally, 440 would fall about the middle of the slider. To achieve this, let's use a logarithmic scale.&lt;/p&gt;

&lt;p&gt;I found a &lt;a href="https://stackoverflow.com/questions/846221/logarithmic-slider/846249#846249" rel="noopener noreferrer"&gt;Stack Overflow answer&lt;/a&gt; on how to map a slider to a logarithmic scale. The code given in the answer is JavaScript, but it was easy enough to port to Scheme.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; Logarithmic scale for frequency (so middle A [440] falls about in the middle)&lt;/span&gt;
&lt;span class="c1"&gt;;; Adapted from https://stackoverflow.com/questions/846221/logarithmic-slider&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;max-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;max-freq&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*max-position*&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="c1"&gt;;; Convert slider position to frequency&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;position-&amp;gt;frequency&lt;/span&gt; &lt;span class="nv"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;inexact-&amp;gt;exact&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;round&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;exp&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;position&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;)))))))&lt;/span&gt;
&lt;span class="c1"&gt;;; Convert frequency to slider position&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;min-freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;frequency-scale&lt;/span&gt; &lt;span class="nv"&gt;*min-position*&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created two functions: one that takes the position on the slider and returns the frequency (&lt;code&gt;position-&amp;gt;frequency&lt;/code&gt;) and another that takes a frequency and returns the position on the slider (&lt;code&gt;frequency-position&lt;/code&gt;). Now let's set the initial position of our slider with the &lt;code&gt;frequency-&amp;gt;position&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="mi"&gt;440&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Underneath the slider is a text field showing the current frequency, buttons to increase/decrease the frequency by one octave, and a play button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; Frequency display&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-inputlabel&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;210&lt;/span&gt; &lt;span class="mi"&gt;230&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="s"&gt;"440"&lt;/span&gt; &lt;span class="nv"&gt;ascii_18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="nv"&gt;*text-color*&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="ss"&gt;'align&lt;/span&gt; &lt;span class="nv"&gt;GUI_ALIGNCENTER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;frequency-label&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-label&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;290&lt;/span&gt; &lt;span class="mi"&gt;230&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="s"&gt;"Hz"&lt;/span&gt; &lt;span class="nv"&gt;ascii_18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt; &lt;span class="nv"&gt;*accent-color*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;frequency-label&lt;/span&gt; &lt;span class="ss"&gt;'align&lt;/span&gt; &lt;span class="nv"&gt;GUI_ALIGNCENTER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;;; Octave buttons&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;lower-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-button-string&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;140&lt;/span&gt; &lt;span class="mi"&gt;230&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;"&lt;/span&gt; &lt;span class="nv"&gt;ascii_18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;g&lt;/span&gt; &lt;span class="nv"&gt;w&lt;/span&gt; &lt;span class="nv"&gt;t&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;#t&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;higher-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-button-string&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt; &lt;span class="mi"&gt;230&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="s"&gt;"&amp;gt;"&lt;/span&gt; &lt;span class="nv"&gt;ascii_18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;g&lt;/span&gt; &lt;span class="nv"&gt;w&lt;/span&gt; &lt;span class="nv"&gt;t&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;#t&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

&lt;span class="c1"&gt;;; Play button&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-button-string&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;230&lt;/span&gt; &lt;span class="mi"&gt;125&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="s"&gt;"Play"&lt;/span&gt; &lt;span class="nv"&gt;ascii_25&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;g&lt;/span&gt; &lt;span class="nv"&gt;w&lt;/span&gt; &lt;span class="nv"&gt;t&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="no"&gt;#t&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last argument to &lt;code&gt;glgui-button-string&lt;/code&gt; is a callback function. This is a function that is called when the button is pressed. I'm just trying to get the widgets layed out right now. I don't yet care about the function of the button, so I used anonymous functions (lambdas) that don't do anything for now.&lt;/p&gt;

&lt;p&gt;The buttons do come with some default styling, but you'll probably want to tweak the look to fit your color scheme and UI design. We can use &lt;code&gt;glgui-widget-set!&lt;/code&gt; to set parameters of a widget. Buttons have various parameters that can be set such as &lt;code&gt;'button-normal-color&lt;/code&gt; and &lt;code&gt;'button-selected-color&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt; &lt;span class="ss"&gt;'button-normal-color&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt; &lt;span class="ss"&gt;'button-selected-color&lt;/span&gt; &lt;span class="nv"&gt;*accent-color*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt; &lt;span class="ss"&gt;'solid-color&lt;/span&gt; &lt;span class="no"&gt;#t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt; &lt;span class="ss"&gt;'rounded&lt;/span&gt; &lt;span class="no"&gt;#f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That seems like a lot to type (or copy and paste) for each button. With CSS I'm able to define a style for all buttons or apply a class to buttons. I used a &lt;code&gt;for-each&lt;/code&gt; loop to loop through all the buttons and apply the above styling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; Style buttons&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;for-each&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;button&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;button&lt;/span&gt; &lt;span class="ss"&gt;'button-normal-color&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;button&lt;/span&gt; &lt;span class="ss"&gt;'button-selected-color&lt;/span&gt; &lt;span class="nv"&gt;*accent-color*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;button&lt;/span&gt; &lt;span class="ss"&gt;'solid-color&lt;/span&gt; &lt;span class="no"&gt;#t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;button&lt;/span&gt; &lt;span class="ss"&gt;'rounded&lt;/span&gt; &lt;span class="no"&gt;#f&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="nv"&gt;lower-button&lt;/span&gt; &lt;span class="nv"&gt;higher-button&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, we are starting to have a nice looking interface, but it doesn't do anything. If you click the buttons or slide the slider, nothing happens. While the buttons take a callback function parameter, I couldn't find a way to wire up the slider to a function. I read the &lt;a href="https://github.com/part-cw/lambdanative/wiki/glgui-slider" rel="noopener noreferrer"&gt;&lt;code&gt;glgui-slider&lt;/code&gt; documentation page&lt;/a&gt; several times searching for clues.&lt;/p&gt;

&lt;p&gt;Finally, I resorted to looking at the source code for &lt;code&gt;glgui-slider&lt;/code&gt;. Each of the widget documentation pages link directly to their implementation in the LambdaNative GitHub repo. I already mentioned that I ended up reading the LambdaNative source more than I would have liked for debugging. Documentation is one area where LambdaNative really could stand to improve. I scanned &lt;code&gt;slider.scm&lt;/code&gt; and discovered it had a &lt;code&gt;'callback&lt;/code&gt; parameter. I created a function that would set the frequency displayed in the &lt;code&gt;glgui-inputlabel&lt;/code&gt; to the one that corresponded to the position of the &lt;code&gt;glgui-slider&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; Link slider to text field display of frequency&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;number-&amp;gt;string&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;position-&amp;gt;frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'value&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and wired it up to the slider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'callback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-frequency&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A callback function takes five arguments. In the code examples in the LambdaNative documentation, these always appeared as &lt;code&gt;(lambda (g w t x y))&lt;/code&gt;. These one-letter variables aren't very descriptive, and the arguments of the callback functions don't appear to be documented. Through experimentation and reading the source code and examples, I worked out the following:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;g&lt;/td&gt;
&lt;td&gt;The [G]UI the widget belongs to. I used the name &lt;code&gt;parent&lt;/code&gt; for this variable in my callback functions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;w&lt;/td&gt;
&lt;td&gt;The [w]idget that triggered the callback function. I used the name &lt;code&gt;widget&lt;/code&gt; for this variable in my callback functions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;t&lt;/td&gt;
&lt;td&gt;The [t]ype of event. I used the name &lt;code&gt;event&lt;/code&gt; for this variable in my callback functions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;First argument of event (x coordinate in pixels, keyboard character, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;y&lt;/td&gt;
&lt;td&gt;Second argument of event (y coordinate in pixels, modifier flags, etc.)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The callback function is only called once the user releases the slider handle. I want the user to get feedback as they drag the slider. You can write your own event handling code in the &lt;code&gt;lambda&lt;/code&gt; that forms the second parameter of &lt;code&gt;(main)&lt;/code&gt;. The generated skeleton already includes code to terminate the application when the &lt;code&gt;Esc&lt;/code&gt; key is pressed. I added some code to call &lt;code&gt;adjust-frequency&lt;/code&gt; when the slider handle is being dragged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; events&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;t&lt;/span&gt; &lt;span class="nv"&gt;EVENT_KEYPRESS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;begin&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;EVENT_KEYESCAPE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
    &lt;span class="c1"&gt;;; Also update frequency when dragging slider (callback is only on release)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'downval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;t&lt;/span&gt; &lt;span class="nv"&gt;EVENT_MOTION&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-frequency&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-event&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;t&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By looking at the implementation of &lt;code&gt;glgui-slider&lt;/code&gt; in &lt;code&gt;slider.scm&lt;/code&gt;, I noticed that LambdaNative was setting a &lt;code&gt;'downval&lt;/code&gt; parameter whenever the user was holding down the mouse button on the slider handle. Whenever that parameter is true, I listen for an &lt;code&gt;EVENT_MOTION&lt;/code&gt; event to call &lt;code&gt;adjust-frequency&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I replaced the anonymous lambdas in the octave button declarations with callback functions called &lt;code&gt;decrease-octave&lt;/code&gt; and &lt;code&gt;increase-octave&lt;/code&gt;. An &lt;a href="https://en.wikipedia.org/wiki/Octave" rel="noopener noreferrer"&gt;octave&lt;/a&gt; is "the interval between one musical pitch and another with double its frequency."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; Set frequency slider and display&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;number-&amp;gt;string&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="c1"&gt;;; Buttons increase and decrease frequency by one octave&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-octave&lt;/span&gt; &lt;span class="nv"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;new-freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="nv"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;new-freq&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;new-freq&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt; &lt;span class="nv"&gt;new-freq&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;decrease-octave&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-octave&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;increase-octave&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;adjust-octave&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;'aftercharcb&lt;/code&gt; callback of &lt;code&gt;glgui-inputlabel&lt;/code&gt; is called after each character is typed or deleted. We can use this to update the slider as a user enters a frequency. What if a user (and you know they will) enters a number higher than 20,000 or a letter? We need a function that will only allow numbers within a given range.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; Only allow numbers within range of min-value and max-value&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;num-only&lt;/span&gt; &lt;span class="nv"&gt;min-value&lt;/span&gt; &lt;span class="nv"&gt;max-value&lt;/span&gt; &lt;span class="nv"&gt;old-value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let*&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;current-value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
           &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;current-numified&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="nv"&gt;current-value&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-length&lt;/span&gt; &lt;span class="nv"&gt;current-value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;; Allow field to be empty&lt;/span&gt;
              &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="nv"&gt;current-numified&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nv"&gt;current-numified&lt;/span&gt; &lt;span class="nv"&gt;min-value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;current-numified&lt;/span&gt; &lt;span class="nv"&gt;max-value&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;old-value&lt;/span&gt; &lt;span class="nv"&gt;current-value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt; &lt;span class="nv"&gt;old-value&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the user types a character that makes the value invalid, we want to revert to the last known good value. To accomplish this, I used a closure to remember the last known value. Many programming languages today have closures, but Scheme practically invented them. A closure enables variables to be associated with a function that persist through all the calls of the function.&lt;/p&gt;

&lt;p&gt;Now we can wire the &lt;code&gt;glgui-inputlabel&lt;/code&gt; callback up to these functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;frequency-range&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;num-only&lt;/span&gt; &lt;span class="nv"&gt;*min-frequency*&lt;/span&gt; &lt;span class="nv"&gt;*max-frequency*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="ss"&gt;'aftercharcb&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-range&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;slider&lt;/span&gt; &lt;span class="ss"&gt;'value&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;frequency-&amp;gt;position&lt;/span&gt; &lt;span class="nv"&gt;freq&lt;/span&gt;&lt;span class="p"&gt;))))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We call the &lt;code&gt;num-only&lt;/code&gt; closure specifying the allowed range and initial value which returns a new function that can be used in the callback. After we make sure there are no high jinks going on with the value using the function created by the closure (&lt;code&gt;frequency-range&lt;/code&gt;), we update the position of the slider using the current value of the text field.&lt;/p&gt;

&lt;p&gt;We can use the &lt;code&gt;num-only&lt;/code&gt; closure again to create a field to specify the duration of the beep in milliseconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; General Controls&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-label&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="s"&gt;"Duration"&lt;/span&gt; &lt;span class="nv"&gt;ascii_18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-inputlabel&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="s"&gt;"200"&lt;/span&gt; &lt;span class="nv"&gt;ascii_18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="nv"&gt;*text-color*&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt; &lt;span class="ss"&gt;'align&lt;/span&gt; &lt;span class="nv"&gt;GUI_ALIGNCENTER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;duration-range&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;num-only&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="mi"&gt;600000&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt; &lt;span class="ss"&gt;'aftercharcb&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;duration-range&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-label&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;195&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="s"&gt;"ms"&lt;/span&gt; &lt;span class="nv"&gt;ascii_18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Frequency is rather abstract. Let's also give the user the ability to select a musical note. We can store the corresponding frequencies for A4-G4 in a table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; Notes -&amp;gt; frequency (middle A-G [A4-G4])&lt;/span&gt;
&lt;span class="c1"&gt;;; http://pages.mtu.edu/~suits/notefreqs.html&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;notes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;list-&amp;gt;table&lt;/span&gt; &lt;span class="o"&gt;'&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;0&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;440.00&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;; A&lt;/span&gt;
                             &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;1&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;493.88&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;; B&lt;/span&gt;
                             &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;2&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;261.63&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;; C&lt;/span&gt;
                             &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;3&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;293.66&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;; D&lt;/span&gt;
                             &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;4&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;329.63&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;; E&lt;/span&gt;
                             &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;5&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;349.23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;; F&lt;/span&gt;
                             &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;6&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="mf"&gt;292.00&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt; &lt;span class="c1"&gt;; G&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll give the user a drop-down menu. Whenever a note is selected from the drop-down menu, we'll look up the frequency in the table and set it using the &lt;code&gt;set-frequency&lt;/code&gt; helper function we created for the octave buttons.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-label&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;410&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="s"&gt;"Note"&lt;/span&gt; &lt;span class="nv"&gt;ascii_18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-dropdownbox&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;470&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;lg&lt;/span&gt; &lt;span class="nv"&gt;lw&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt; &lt;span class="nv"&gt;w&lt;/span&gt; &lt;span class="nv"&gt;h&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;s&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui:draw-box&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt; &lt;span class="nv"&gt;w&lt;/span&gt; &lt;span class="nv"&gt;h&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui:draw-text-left&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;w&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;h&lt;/span&gt; &lt;span class="nv"&gt;str&lt;/span&gt; &lt;span class="nv"&gt;ascii_18&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="nv"&gt;*text-color*&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="s"&gt;"A"&lt;/span&gt; &lt;span class="s"&gt;"B"&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="s"&gt;"D"&lt;/span&gt; &lt;span class="s"&gt;"E"&lt;/span&gt; &lt;span class="s"&gt;"F"&lt;/span&gt; &lt;span class="s"&gt;"G"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="nv"&gt;*accent-color*&lt;/span&gt; &lt;span class="nv"&gt;*foreground-color*&lt;/span&gt; &lt;span class="nv"&gt;*accent-color*&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="ss"&gt;'scrollcolor&lt;/span&gt; &lt;span class="nv"&gt;*accent-color*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="nv"&gt;note&lt;/span&gt; &lt;span class="ss"&gt;'callback&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;table-ref&lt;/span&gt; &lt;span class="nv"&gt;notes&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt; &lt;span class="ss"&gt;'current&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's make some noise. LambdaNative has a rtaudio module. We'll use that to generate a tone with a sine wave. Edit the &lt;code&gt;MODULES&lt;/code&gt; file in your applications subdirectory and add rtaudio to the list. The Scheme API of the rtaudio module consists of essentially just two functions: &lt;code&gt;rtaudio-start&lt;/code&gt; and &lt;code&gt;rtaudio-stop&lt;/code&gt;. You must first register four real-time hooks (an initialization hook, input hook, output hook, and close hook) in a chunk of C code embedded within your Scheme code. I wish the rtaudio module had an API that allowed implementing these hooks in pure Scheme. Thankfully the &lt;a href="https://github.com/part-cw/lambdanative/tree/master/apps/DemoRTAudio" rel="noopener noreferrer"&gt;DemoRTAudio&lt;/a&gt; app included with LambdaNative implements a sine wave, and I was able to copy and paste most of what I needed from there without spending a lot of time trying to figure out how to write a sine wave in C myself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; Register C-side real-time audio hooks&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;c-declare&lt;/span&gt;  &lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;&amp;lt;end-of-c-declare&lt;/span&gt;

&lt;span class="o"&gt;#&lt;/span&gt;&lt;span class="nv"&gt;include&lt;/span&gt; &lt;span class="nv"&gt;&amp;lt;math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;h&amp;gt;&lt;/span&gt;

&lt;span class="nv"&gt;void&lt;/span&gt; &lt;span class="nv"&gt;rtaudio_register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;void&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;void&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;void&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nf"&gt;float*,float*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;void&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nf"&gt;void&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;srate=0&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;float&lt;/span&gt; &lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;void&lt;/span&gt; &lt;span class="nv"&gt;my_realtime_init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;samplerate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;srate=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;samplerate&lt;/span&gt;&lt;span class="c1"&gt;; buffer=0; }&lt;/span&gt;
&lt;span class="nv"&gt;void&lt;/span&gt; &lt;span class="nv"&gt;my_realtime_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt; &lt;span class="nv"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;void&lt;/span&gt; &lt;span class="nv"&gt;my_realtime_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt; &lt;span class="nv"&gt;*v1,float&lt;/span&gt; &lt;span class="nv"&gt;*v2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;static&lt;/span&gt; &lt;span class="nv"&gt;double&lt;/span&gt; &lt;span class="nv"&gt;t=0&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;buffer&lt;/span&gt; &lt;span class="nv"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.95&lt;/span&gt;&lt;span class="nv"&gt;*sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;2*M_PI*f*t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;*v1=*v2=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;buffer&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
  &lt;span class="nv"&gt;t+=1/srate&lt;/span&gt;&lt;span class="c1"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;void&lt;/span&gt; &lt;span class="nv"&gt;my_realtime_close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;buffer=0&lt;/span&gt;&lt;span class="c1"&gt;; }&lt;/span&gt;

&lt;span class="nv"&gt;end-of-c-declare&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;c-initialize&lt;/span&gt; &lt;span class="s"&gt;"rtaudio_register(my_realtime_init,my_realtime_input,my_realtime_output,my_realtime_close);"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="http://pld.cs.luc.edu/telecom/mnotes/digitized_sound.html" rel="noopener noreferrer"&gt;basic formula for a sine wave&lt;/a&gt; is A sin(2πft) where &lt;em&gt;A&lt;/em&gt; is amplitude, &lt;em&gt;f&lt;/em&gt; is frequency, and &lt;em&gt;t&lt;/em&gt; is time. We need a way to pass the frequency from our slider in the Scheme to the output hook in the C. Gambit scheme has a &lt;code&gt;c-lambda&lt;/code&gt; special form that makes it possible to create a Scheme function that is a representative of a C function or code sequence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="nv"&gt;rtaudio-frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;c-lambda&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;void&lt;/span&gt; &lt;span class="s"&gt;"f=___arg1;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a Scheme function that sets the f variable in our C chunk. Now let's create a Schem function that will set the frequency and start and stop the real-time audio subsystem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="c1"&gt;;; Generate a tone using the rtaudio module&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;generate-tone&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;widget&lt;/span&gt; &lt;span class="nv"&gt;event&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;; Make sure neither frequency or duration were left blank&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-length&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set-frequency&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-length&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-set!&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rtaudio-frequency&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;exact-&amp;gt;inexact&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;frequency-field&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rtaudio-start&lt;/span&gt; &lt;span class="mi"&gt;44100&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;thread-sleep!&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;string-&amp;gt;number&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-widget-get&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="nv"&gt;duration-field&lt;/span&gt; &lt;span class="ss"&gt;'label&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;rtaudio-stop&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When playing a note such as B4 (493.88 Hz) that has a decimal point, the type passed from Scheme to C lines up with the C type &lt;code&gt;float&lt;/code&gt;, but when passing an integer (such as 440), it will cause an error. The &lt;code&gt;exact-&amp;gt;inexact&lt;/code&gt; conversion forces Scheme to pass the value along as a &lt;code&gt;float&lt;/code&gt;. Wire this up to the play button, and you're ready to make some noise.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;set!&lt;/span&gt; &lt;span class="nv"&gt;play-button&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;glgui-button-string&lt;/span&gt; &lt;span class="nv"&gt;gui&lt;/span&gt; &lt;span class="mi"&gt;230&lt;/span&gt; &lt;span class="mi"&gt;125&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="s"&gt;"Play"&lt;/span&gt; &lt;span class="nv"&gt;ascii_25&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;fnt&lt;/span&gt; &lt;span class="nv"&gt;generate-tone&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;LambdaNative has a lot of rough edges, not least of which is the documentation (or lack thereof). Looking at the source code for a widget seems to be the only way to determine all the parameters available for that widget. If you're like me, being able to write mobile apps in Lisp is a dream come true! LambdaNative may not be the smoothest development experience right now, but I hope to revisit it again in the future. It is being actively developed (and has the backing of a university research team), so my hopes are high for the future of LambdaNative.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can check out the entire example on &lt;a href="https://github.com/goober99/lisp-gui-examples" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. This started as a personal learning project to explore the state of GUI programming in Lisp and has become a series of tutorials on building GUIs with various dialects of Lisp.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>scheme</category>
      <category>lisp</category>
      <category>gui</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
