KHS Guitar Tuner Pro is an Android and iOS application that can assist guitarists with a variety of different tuning setups. The app can play notes within the selected tuning to use as a reference, and it uses incoming audio from the microphone to give musicians a visual indicator of what pitch is being played. It also contains a metronome with multiple timing options, making it useful for the whole practice session.
The application was written with Flutter to share a single implementation across multiple platforms. It was built in accordance with the Bloc design pattern for code organization.
KHS Guitar Tuner Pro consists of two main components, the Tuner and the Metronome. The app displays a navigation bar at the bottom that allows the user to switch between the associated screens.
The primary functionality of the app is housed on the Tuner screen, where the user can select their target tuning for a guitar and receive guidance in adjusting their string tension to reach the desired pitch.
The application monitors any incoming notes via the device’s microphone and displays the observed note with a meter at the top of the screen. The note is highlighted in green when the pitch matches an expected note or in yellow when the pitch is incorrect. This gives the user visual feedback for not only whether a given string is tensioned correctly but also whether an incorrect pitch is too high or low for their target.
A set of buttons is also displayed for playing the desired notes through the device’s speakers. Once a user selects which tuning they prefer, these buttons update with the corresponding notes and provide the user with reference sounds to compare against for each string.
KHS Guitar Tuner Pro also provides a metronome to use for practice sessions. The user can configure the top and bottom values of their time signature and even adjust the tempo in beats per minute.
Once activated, the app will play an audible tick and flash the play/pause button green as each beat occurs, yielding a brighter green and a louder tick for beats emphasized by the time signature.
Flutter is a software development toolkit based on the Dart programming language, used for deploying a shared codebase to multiple platforms. With Flutter, the same application code is used to compile separate native applications for each platform being targeted.
This allows the application to still utilize platform-specific features and high performance without requiring manual rewrites or optimizations for each one. Instead, developers can focus on contributing to a single codebase, with all the complexity of different operating systems and architectures being handled for them automatically.
The behavior of Flutter applications is defined by widgets. These are particular types of classes that are responsible for rendering subsets of content to the screen or handling specific actions from the user interface. Widgets can also keep track of the application state, giving them more contextual awareness of what’s happening as they make behavioral decisions.
Since widgets are the core components of Flutter applications, each piece of functionality is typically composed of multiple widgets nested together in a tree-like structure. When the state of an application changes, the impacted portions of the widget tree are rebuilt to reflect the state transition. For this reason, the organization of this application state and the relationships between widgets are important things to consider in Flutter.
Within the opinionated directory structure of a Flutter application, KHS Guitar Tuner Pro adheres to the Bloc design pattern. At its core, Bloc is a state management library prioritizing the separation of an application’s business logic from its presentation layer. It achieves this by encapsulating an application’s state within designated business logic classes called
Bloc class is instantiated with its own internal state that can be observed but not directly modified by its consumers. Manipulations of that state are accomplished through the API of the class, either by directly invoking its methods or by submitting events that it listens to. The added formality of accessing the application’s business logic makes for a clearer boundary between the layers of the application, as well as a cleaner testing approach for the silos of logic.
With the flutter_bloc package, we can easily convert these design principles into Flutter idioms.
In order for a Flutter application to function, its
Widget classes must have access to any relevant information required to render their content. With the business logic and state of KHS Guitar Tuner Pro encapsulated into
Blocs, we needed a way to expose them to our widgets without dissolving that separation of responsibilities between the presentation and logic layers. The
flutter_bloc package provides ways to manage this, primarily with the
BlocProvider widget classes.
BlocBuilder widget is responsible for rebuilding its content in response to state updates being emitted from its assigned
Bloc instance. Recall that a
Bloc class manages its own state internally to keep business logic organized away from the presentation layer.
As such, when a
Bloc class receives an event and performs its action, a new state is typically emitted as a result to reflect the change. When the
BlocBuilder widget receives this updated state, it can execute its
builder function to render fresh content to the screen.
BlocProvider can be used to make a single
Bloc instance available to any of its child widgets. The key word here is “available,” meaning that the children are able to access the methods or state of the
Bloc instance without directly owning it.
This allows the
BlocProvider to serve as a wrapper for widgets that need to consume similar business logic. When used in tandem with the
BlocBuilder widget, it allows multiple widgets to all consume the same state updates and submit events to the same
By combining these widgets in a Flutter application, we have fine-grain control over widget rendering behavior and business logic invocation, all while keeping the implementation of our logic isolated to a different layer of the project. This was helpful for the KHS Guitar Tuner Pro application as its sound processing logic grew in complexity.
Analog-to-Digital Conversion (ADC)
Processing sound on a mobile device requires audio-to-digital conversion. First, the audio signal is observed by the device’s microphone. Then, the analog signal is sampled at uniform intervals and each sample is converted into a digital representation, using a finite number of bits.
Since these binary values must fit into a finite space, this means that there are a finite number of possible digital values that can be used to represent a sample. The number of bits used equates to an upper bound on the accuracy of the digital representation of the audio, and it is referred to as the “resolution” of the system. In short, the higher your resolution, the more accurately your digital data represents the analog signal.
Since KHS Guitar Tuner Pro aims to measure the frequency of the observed audio signals, the rate at which the audio is sampled also comes into play. As an example, imagine a ball bouncing to a height of 1 meter each second. If you measure the ball’s height twice each second and record the values, you can observe the periodic increase and decrease and infer how frequently it bounces:
[1, 0, 1, 0, 1, 0, 1, 0, 1]
However, if you matched the ball’s frequency and only measured it once per second, your recorded values no longer show that periodic increase or decrease:
[1, 0, 1, 0, 1, 0, 1, 0, 1]
If we decrease our rate of measurement even further and check less than once per second, our measurements might completely miss some of the bounces:
[1, 0, 1, 0, 1, 0, 1, 0, 1]
This principle applies to measuring sound waves in the same way. If we are not sampling the audio frequently enough, our measurements could be missing some of the oscillations in the original source. According to the Nyquist-Shannon sampling theorem, the fastest oscillation our system can measure must include at least two samples. This means that whatever frequency we decide on for our sample rate, the highest measurable audio frequency will be half that amount.
So in summary, using more bits per sample means that we can keep track of their values more accurately, and capturing more samples per second lets us better understand the frequency of the original audio.
While the obvious solution is to increase the resolution and sample rate as much as possible, this means that the system has to expend more effort to compute the extra data. In the case of a mobile application like KHS Guitar Tuner Pro, a balance must be reached. The application still has enough information to operate reliably without overwhelming the device’s processor or draining its battery.
Once the sample rate and resolution decisions of the ADC process have been reached, the application now has enough information to infer the frequency, or “pitch,” of the original audio signal. However, calculating a frequency is impossible to do with the data from an individual sample, so our sampling data must be aggregated together in order to make any meaningful inferences.
There is also the problem of noise, or unwanted data, to be accounted for since KHS Guitar Tuner Pro might be used in an environment where background sounds are present. The Pitch Detection process aims to resolve these problems.
First, the audio samples are sent through a noise gate. Its responsibility is to suppress any values which are below a certain volume threshold, to reduce the likelihood of ambient noise from factoring into the pitch calculations. These
off states are transitioned gradually by the noise gate to maintain a “smoother” set of data.
Then, the sampling data is passed through a Fast Fourier Transformation (FFT) algorithm to measure the amplitudes of all frequencies observed within the data set. Conceptually, the output of this algorithm is like a graph of the audible frequency range, with a continuous line plotting the volumes at each pitch being detected.
So, if the sound from a guitar’s A string was recorded producing a frequency of 440 Hz, that part of the graph would show a spike relative to the other frequencies. The peaks of these spikes are located as we iterate through the dataset, and their positions and heights are noted.
Finally, the application can examine this peak data to determine which fundamental frequency is being observed. This can be tricky for stringed instruments, as our A string example above will also probably have smaller peaks all along its harmonic series (880 Hz, 1320 Hz, etc.). Each peak’s amplitude, as well as the presence of any harmonic peaks, are weighed to determine the final frequency observed by the application.
Due to the complexity of the Pitch Detection logic above, it became necessary for KHS Guitar Tuner Pro to perform some of its logic in a separate thread. For this, an
Isolate is spawned to perform the noise gate and FFT calculations without blocking user interface actions.
Isolates allow for serialized messaging between threads. As such, the main thread is able to still perform the lighter part of the workflow (like recording the microphone audio) and then delegate the heavier work to the background process. The owning
Bloc class in the main thread also establishes an event listener for the child thread’s return message, allowing it to submit an event as soon as the child completes its calculations.
This parallelization not only saves time by executing the computation concurrently, but it allows the main thread to stay responsive for user interaction and maintain a smooth experience.
Flutter remains a powerful tool for writing applications for multiple platforms. It pairs nicely with the Bloc design pattern, allowing for a clean separation between presentation and business logic and high testability of components.
Once these organizational principles have been set, adding even complicated sets of logic becomes easier. Performance-heavy workflows like sound processing can still be a challenge when developing for mobile devices, but with proper organization of code and distribution across multiple threads, this can all be achieved without sacrificing the responsiveness of the user interface.
KHS Guitar Tuner is live now in both the Apple and Google Play app stores.Download for iOS Download for Android