ff-ad9833 project: musical notes and melody system implemented

ad9833-based audio module

I added two modules to my ff-ad9833: one called notes which handles playing music and rest notes, and another called score which provides a convenient way to lay out a melody of notes in memory and to play them at an arbitrary tempo. My first simple demo is the “Are You Sleeping” tune:

I defined some Forth words such that it is fairly natural to lay out the notes in a memory block. This is what “Are You Sleeping” looks like in Forth code:


create rusleeping
d_quarter nc o5 == d_quarter nd o5 == d_quarter ne o5 == d_quarter nc o5 ==
d_quarter nc o5 == d_quarter nd o5 == d_quarter ne o5 == d_quarter nc o5 ==
d_quarter ne o5 == d_quarter nf o5 == d_half    ng o5 ==
d_quarter ne o5 == d_quarter nf o5 == d_half    ng o5 ==
d_8th     ng o5 == d_8th     na o6 == d_8th     ng o5 == d_8th     nf o5 ==
d_quarter ne o5 == d_quarter nc o5 ==
d_8th     ng o5 == d_8th     na o6 == d_8th     ng o5 == d_8th     nf o5 ==
d_quarter ne o5 == d_quarter nc o5 ==
d_quarter nc o5 == d_quarter ng o4 == d_half    nc o5 ==
d_quarter nc o5 == d_quarter ng o4 == d_half    nc o5 ==
d_half    nc o5 == d_half    nr nr == d_half    ng o4 == d_half    nr nr ==
d_whole   nc o5 == d_8th     nc o5 == end-score


Each note, including duration, pitch, and octave, is packed automatically into one 16-bit memory cell, so that not a lot of memory is used.

This music system is not really sophisticated enough to use for something like a video game console, since the ad9833 does not have ADSR envelopes or anything like that. But perhaps it would be useful for something like a simple toy, or part of the interface of some appliance.

It has been a lot of fun coding these modules, as well as writing the comment documentation. I developed my own source code documentation standard for the project to keep the code documentation clean and organized.

I learned in the process about the reuse software, which has a nifty reuse lint command which helped me in placing proper licensing and copyright documentation in all my code files. Most free software coders I have come across think very little, if at all, about proper code licensing details. But it is important to make the licensing clear, and clear on all files in the project, so as to make it easier for others to reuse, modify, and share the code with confidence.

While this component of the project is nearly complete, I had thoughts of adding one more demo tune — something longer and more sophisticated. As far as the ff-ad9833 project itself, I had thoughts of adding one more module for generating Audio FSK data communication capabilities. Like, for amateur radio RTTY.

The code for the ff-ad9833 project is available to download from this repository:


In the picture at the top of this post, the audio module is being powered by the Arudino UNO 5V VCC pin. However, it is better instead to power the audio module from a separate 5V power supply (at least 3W). You can then tie the DGND pin on the audio module to a GND pin on the UNO, and also tie the other GND pin on the UNO to the ground or negative lead on the 5V power supply. In my experiments, this resulted in more stable audio module operation, as well as cleaner sound at the higher volume levels.

I think, if I were redesigning my audio module, I might also utilize a diode to prevent current from rushing back from the audio module into the UNO. I’m not quite sure if that is really necessary, but it seems like a good idea.

Diode Transistor Logic

DTL AND gate, triggering LED

I learned today about Diode Transistor Logic, which was the predecessor to Transistor-Transistor Logic (TTL), which was replaced by CMOS. This goes back to the 60’s, but was the technology that put men on the moon. With DTL, the logic gate is a set of diodes, with a transistor providing the amplification. The circuit above and below is an AND gate:

DTL AND gate, triggering LED, schematic

The basic idea, as I understand it: if either input is 0V (logic low) then current runs through the diode, pulling down the voltage on the base of the transistor. But if both inputs are 5V (logic high) then the diodes do not conduct, and the base of the transistor remains at +5V, turning on the transistor and the LED.

This is a poor quality video, but I needed something to show it works. In the video, INPUT1 is a 1 Hz square wave signal, and INPUT2 is an 8 Hz square wave signal. When brought together in an AND gate, the combined signals cause the LED to blink 4 times for the first half of each second.

Running Arduino IDE in Guix

Arduino IDE running on a Guix system

There is no Arduino IDE package for Guix. I asked why once, but I forgot what the reason was — something to do with the way that Arduino build process works I think. Anyway, this is how I get it running on my systems. I suspect this is not the best way, but it works for me. The process is a little convoluted, but I don’t use the IDE very often, so I haven’t been motivated to figure out something better. Typically all I need is avrdude, in order to load Forth firmware onto the chips, and there is an avrdude package available in Guix, as well as a avr-toolchain package and a few other tools.

First you need to make a clone of the git repo:

git clone https://github.com/arduino/Arduino.git

Specifically I am using the some old commit bf24880d7c559751765a43cd1669d893bba267e8, which is the commit for Arduino IDE version 1.8.14, but maybe a newer version would work also.

After changing into the build directory, you must setup the proper build/run environment. I do this by having the following manifest file saved at ~/Manifests/arduino-ide-run.scm:

(use-modules (gnu packages java))

   `((,icedtea "jdk")))))

Then I set up the environment with the command…

guix environment --pure --preserve='DISPLAY' --preserve='GDM' --preserve='DBUS' --preserve='GIO' --preserve='XDG' --preserve='WINDOW' --preserve='SESSION' --preserve='XCUR' --preserve='DESKTOP' --preserve='XAUTH' -m ~/Manifests/arduino-ide-run.scm

If you want to use the same environment as I am using right now, prefix the command above with guix time-machine --commit=079a7f2c65c51da7b53b0e5ef44c516dc8eaab6e --. (I’m running Gnome DE, but I’m not sure if that makes any difference.)

Then run the command ant run &. In a minute or so you should see the IDE appear.

I problem I have at this point is that, although the IDE runs, you are not able to compile sketches, because the executables in the avr-gcc toolchain are looking for linker & glibc stuff in the wrong place on your system. I fix this by running the following script after the IDE starts, which I have saved in the file ~/Scripts/arduino-patchelf.sh:


patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/../libexec/gcc/avr/7.3.0/cc1plus
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/avr-g++
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/tools-builder/ctags/5.8-arduino11/ctags
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/../lib/gcc/avr/7.3.0/../../../../avr/bin/as
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/avr-gcc
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/../libexec/gcc/avr/7.3.0/cc1
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/avr-gcc-ar
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/../lib/gcc/avr/7.3.0/../../../../avr/bin/ar
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/../libexec/gcc/avr/7.3.0/collect2
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/../lib/gcc/avr/7.3.0/../../../../avr/bin/ld
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/../libexec/gcc/avr/7.3.0/lto-wrapper
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/../lib/gcc/../../libexec/gcc/avr/7.3.0/lto1
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/avr-objcopy
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/avrdude
patchelf --set-interpreter ${GLIBC} --set-rpath ${RPATH} ${BUILD_DIR}/linux/work/hardware/tools/avr/bin/avr-size

You will need to adjust the variables on top according to the location of things on your system. Unfortunately, it is necessary to run this script every time you call ant run, or at least I haven’t figured out a way to get around that. I’ve been told it is possible instead to just set a symbolic link from the “standard” location glibc location to the actual location, but I didn’t want to do this for fear that there might be some effect on the purity of my other build environments.

Hopefully somebody will put together a Guix package soon, but this is a workaround in the meantime.

AVR ISP Shield

I’m not getting paid to say this, but I bought this ISP Shield for Arduino and found it to be convenient:

DIYMORE AVR ISP Shield attached to an UNO, with chip loaded.

This setup is the same as programming using two Arduino boards and the Arduino ISP sketch, but a more compact and convenient, since you don’t have to figure out where the wires go, and you can clip the chip into the ZIF socket.

I like this better than the other programmers I have used, at least for burning 328P chips. To use it, you must (one time) load the Arduino as an ISP sketch onto the 328P chip, and then afterwards attach the shield. When burning chips with avrdude, use the stk500v1 programmer type.

Shield disconnected from UNO

ff-ad9833 Repo

codeberg repository for ad9833-related FlashForth code

For my on-going exploration of ad9833 audio generation using FlashForth 5, a code repository is now available at codeberg:


So far, I have a demo-pitches word that plays c4, e4, g4, and c5 frequencies, but I can set other frequencies ranging from a4 to g#6, using the words I have so far. There is no system yet for setting duration, so I can’t play notes proper. But it was fun setting up a system for setting the equal temperament pitches.

In microcontroller programming, it is much easier to justify efforts to save memory. I needed to store the precalculated frequency register data values for each pitch. Originally I had a table like so:

$5274 , $4000 ,
$538d , $4000 , 
$54b7 , $4000 ,
$55f2 , $4000 ,
$4b5a , $4002 ,

With each line (two 16 bits words) representing one pitch. However, the first two bits in each 16-bit word are actually not frequency data, but register addressing bits, which can easily be added in later. Also, the second 16-bit word in each pair has (in my application) only two bits of actual frequency data, which are the two right most bits. So, I cut the table memory usage in half by packing those two bits into the left-most two bits of the first word in each pair:

\ packaged freq register data for equal temp
\ pitches from A4, A#4, B4 , ... , G#6
\ format numbered from lsb to msb:
\ 0-13: the 14 LSBs of the lsb register load
\ 14-15: the 2 LSBs of the msb register load

create packed-pitches
$1274 , $138d ,
$14b7 , $15f2 ,
$1740 , $18a2 ,
$1a19 , $1ba7 ,

Now each line in the table is actually two separate pitches. Then I just need a few words to separate the bits back out into separate words, and recombine them with the frequency register addressing bits:

0  constant na
1  constant na#
2  constant nb
3  constant nc
4  constant nc#
5  constant nd
6  constant nd#
7  constant ne
8  constant nf
9  constant nf#
10 constant ng
11 constant ng#

0 constant o4
1 constant o5
2 constant o6

: pull-pitch ( note octave -- u ) 12 * + cells packed-pitches + @ ;

: pull-14lsb %0011111111111111 and ;

: pull-2msb %1100000000000000 and 14 rshift ;

%0100000000000000 constant FREG0

: tx-pitch ( note octave -- )
    pull-pitch cp>r pull-14lsb FREG0 or 2tx-spi
    r> pull-2msb FREG0 or 2tx-spi ;

In testing, I have some trouble with some pitches getting distorted when I set the volume to higher levels. I suspect the problem is that I am still using the USB power supply and need to feed in my +5V from an external PS. I plan to try that next week, God willing.