In this post we will go over a project I have been working on recently. The project source and documentation can be found on my github. This sine-tuner project is a small UI based program that can be used to tune a guitar by ear using pure sine tones.
When ran, it generates a window with six buttons on it, each corresponding to a different open string on a guitar in standard tuning. When the buttons are clicked, the related sine tone will be generated. The same button can be clicked again to turn the tone off, or, a different button can be clicked to switch the currently playing tone. Here is what the UI looks like once the program is started.
This project was written using
SDL was used, this program should run on Windows and
Linux, however I have only tested it on MacOS.
I enjoy playing guitar, and when I’m not using an actual tuner, I tend to just tune by ear. Its fairly straight forward to tune a guitar relative to it’s own strings. Just pick a string, assume its in tune, then tune the other strings relative to that one. However, if you are playing with others, you will want to make sure that initial string is the correct note. I usually do this using a 440 hz tone. This happens to be an A note and can be generated on the guitar at the fifth fret on the high E string. All you need to do is make sure the notes match, then you can do your relative tuning using the high E to tune the rest of the strings.
This is a pretty common practice, 440 hz is a standard tuning frequency for many instruments. I’m not going to get into the specifics of how to tune here, as its not important for this particular post.
Tuning this way will usually get the job done, however, I wanted a small program that would generate tones for the guitar’s open strings so I could tune them each to pure sine tones, then adjust as needed.
I have also been wanting to work on a small C UI application which involved audio processing. This sounded like an interesting way to get a simple audio project started and create a tool that I can actually use.
When starting this project there were a few things I knew I wanted to explore. I wanted to use C with SDL for all the low level platform specific interactions with the OS. SDL would take care of windows, graphics, audio, and user input. Normally this framework is used for games but I know it can easily handle a small UI application as well.
I also wanted to use immediate mode style UI processing to handle user input. I don’t have much experience with building UI applications and this style makes a lot of sense to me. It also seemed like I would be able to implement it myself without much trouble.
Besides SDL, I wanted any external libs that I used to be as close to single header files as possible. I didn’t want to deal with any complicated build process to get this program running. I was targeting a small and simple code base.
In doing more research on immediate mode processing I found microui, a really small and clean immediate mode library written in C that would be easy to incorporate into my project. There were other options here but they all seemed a bit more heavy weight than what I was looking for.
On the audio side of things I also found a really nice single header file called miniaudio. I knew that I could write the audio processing myself using SDL directly but I wanted to give this code a try to see which I preferred.
I was quickly able to get something up and running using
documentation is a little sparse so I needed to do some digging into the code to
get what I was looking for. I also needed to make a few updates for my specific
use case. Luckily the code is very clean and there isn’t a lot of it to read
Once I had that done it was on to the audio. I did some experimenting with
miniaudio but decided for this project I wanted to write the audio processing
code myself using SDL directly. I think I will make use of this library in the
future but for this project I wanted to keep the dependencies minimal.
With both the UI and the audio code set up all I now needed was to link them up.
This became a little more difficult than expected and I quickly ran into a few
bugs. The first being that I was using buttons in a different way than what the
microui framework was expecting. The way the buttons are setup in the
framework is similar to a button press event. The user presses the button which
creates a press event. What I really wanted was more of a cross between buttons,
toggles, and radio buttons. I was able to achieve this using
it felt cumbersome and it seemed like it was taking too long for me to get what
I wanted out of the framework.
I eventually decided to write the UI layer myself and not use
microui. I felt
a little too constrained by the framework and for such a simple application I
probably would have spent more time struggling with the library than if I just
wrote something from scratch.
At this point I took a small detour to go through the excellent ImGui tutorial from Sol tutorials. I already had a good idea of how the UI system should work but this helped solidify what I needed to do.
After taking a brief detour to run through the ImGui tutorial, I was able to finish the custom UI system for the sine tuner project. I’m glad I ended up going this route. Once I decided I’d implement the UI myself it came together really fast.
Another issue I ran into was with the text processing. I wanted to have each of the note’s letters on the different buttons. This meant doing some font rendering which I hadn’t done before. Luckily, I was able to leverage the stb_truetype.h header from Sean Barret’s stb libraries. This ended up being the only other dependency I used besides SDL itself. It took me sometime to really understand how the text rendering actually works. I had to do some reading up on font rendering and get more comfortable with the terminology. I also used this gist along with a few others to see some examples of the truetype header file library in action.
The only thing left to resolve were some of the audio issues that I ran into. The biggest issue with the audio code was audio popping when start/stop or switching the playing tones. This was due to the fact that when the user switches the tone, if there is something playing currently, it could switch at a low or high amplitude. This will cause the sine wave to change drastically causing what sounds like a pop.
The way people usually deal with audio popping like this is by smoothing the transition from one part of the wave to the next. In the case of a tone switch, the smoothing can be accomplished with something like a cross fade. This is where the two tones are mixed together while one fades out and the other fades in. Audio wise this will probably give the best transitions.
At this stage of the project I didn’t want to implement mixing yet. So instead of introducing cross fades I just added some logic to determine when it is best to switch or stop playing a tone. The best place to do this is when the sine wave crosses the zero amplitude threshold. After implementing this in the code, the audio popping issues I was hearing were greatly reduced.
Once I had finally linked everything together I was able to call it done for now. I have plans to improve the application but at this point it meets all my original requirements.
As I said earlier, I think adding proper mixing and cross fades to this program will help it sound much better. I would also like to add the ability to customize the buttons and tones that are used. And to keep those customizations on the hard drive so the program doesn’t forget what the user had set up before. Sessions could also be used for different configurations of the application. I think adding a button for a 440 hz tone would be helpful. Being able to dynamically resize things and add/remove UI buttons would be interesting to implement as well and would help me dig even deeper into immediate mode style gui code.
I learned a lot building this small program. It might be simple, but it touched a few areas of interest for me that I hadn’t previously explored. In that respect, I’m glad I started off with something like this before jumping into a bigger project using similar tools.
I also learned that sometimes its better and less time consuming to just build
what you need from scratch rather than use a library. In my case it took a bit
to get the
microui library to do what I wanted it to, but I was using it in a
way that wasn’t intended which caused issues. Ultimately, it was better for me
to write that part of the application from scratch. I was able to be much more
specific and build only the things that I needed.
The opposite can also be true. For example, I make heavy use of SDL in this application. I’m okay with this since I know I will be using it on other projects and want to make sure I have a strong grasp of how to use that tool.
This project has given me a bunch of ideas for future projects involving user interaction and audio. I’m really looking forward to start breaking new ground on this front. Hopefully this application can help anyone else who is looking to get a start on similar projects.