7

Compose + Wear OS: Chromatic Tuner

 2 years ago
source link: https://proandroiddev.com/compose-wear-os-chromatic-tuner-47aa50b44b6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Compose + Wear OS: Chromatic Tuner

Practical example of the power of Jetpack Compose

background created by freepik — br.freepik.com

In this article I will cover the development of a chromatic tuner for Wear OS, an android version designed for wearable devices — such as smartwatches, using Jetpack Compose 😎.

First of all, do you know what a chromatic tuner is? Not? Come on:

Chromatic tuner is for detecting the pitch of 12 musical notes, allowing instruments to be tuned correctly.

All musical notes have their specific vibration frequency. Note A4, for example, oscillates at 440Hz. Based on this, our goal is to develop an application that detects the frequency being played and how above or below the specific frequency of each of the 12 notes, so that the musician can tune his instrument correctly. Let’s go?

User Interface

Our app’s interface will be quite simple:

1*Lh8B1CxPI1awMrQQXr9Rdg.png?q=20
compose-wear-os-chromatic-tuner-47aa50b44b6
user interface image, demonstrating my strong design skills… 😅

The first state, with a green background, indicates that the played note has the same frequency as the A# note, that is, the note is in tune.

The second, in addition to the red color to indicate that the frequency is not correct, also displays side arrows to indicate whether the musician must increase or decrease the pitch of the note, in order to reach the correct frequency.

Setting up Project

It doesn’t take much mystery to create a project that supports Wear OS and Compose. You can create a new project as usual and add these three tags to AndroidManifest.xml:

Wear Compose

In addition to the conventional Compose packages, there are also specific packages for wearable devices:

https://developer.android.com/jetpack/androidx/releases/wear-compose

We are going to use two of these packages and, taking advantage of the fact that we are configuring the project’s dependencies, we will add a DSP library called TarsosDSP which, among other features, has frequency recognition.

  • you can download it here

Okay, dependencies added, now the project is ready to start development. This is our MainActivity():

Note that we extend from ComponentActivity() and not more from AppCompatActivity()

One of the coolest things about Compose for Wear OS is that it gives us its own Scaffold and some useful views, like TimeText():

1*DtY6lZrHVASRP1_gkbi9gA.png?q=20
compose-wear-os-chromatic-tuner-47aa50b44b6
preview image of our hello world in the emulator

With only 13 lines, our app is like this and yes, my friends, TimeText() is a ready-made clock 😎.

Now let’s implement the UI and @Preview for each of the screen states:

Once that’s done, we’ll have the following previews:

1*qUUGelcWstlHkEGc4udJZw.png?q=20
compose-wear-os-chromatic-tuner-47aa50b44b6
preview image of the three screen states

Note that in the code above, the TunerScreen() function receives a TunerState(), which is a sealed class I created to make it easier to manage the states:

This way, through just one class, we have all the necessary information to propagate the changes and update our view 😎.

Frequency Detection

The TarsosDSP library has the PitchDetectionHandler interface that, among several resources, provides us with the frequency and volume of the sound being captured. With that, just use these values obtained and implement the logic of comparison with the reference values we have for each of the 12 notes and their octaves:

1*s_CFMhIW5BxtWYdQtFU6BQ.png?q=20
compose-wear-os-chromatic-tuner-47aa50b44b6
Source: https://www.treinaweb.com.br/blog/gerando-sons-com-a-web-audio-api-do-javascript

Inside the PitchDetectionHandler, we receive the frequency (in Hz) and check if we should update the screen state. The shouldUpdateTunerState() function is responsible for this part, validating if the captured audio has a minimum volume, to avoid noise processing and if the captured frequency is valid.

If it passes this validation, we call the getCurrentPitchState() function, which is responsible for returning an instance of TunerState(), and then we propagate the result to the LiveData _tunerState:

The logic of the getCurrentPitchState() function is quite simple:

  • Check which of the frequencies, among all the notes, is closest to the captured frequency;
  • Get the note corresponding to the closest frequency;
  • Calculate the difference between the input frequency and the reference frequency;
  • Return an instance of TunerState(), taking into account this difference, whether it is within acceptable limits, below or above.

As string instruments suffer frequency variations due to the amplitude of the string vibration, therefore we have to adopt an acceptable range for a sound to be considered in tune. For this, we will use the ISO16 standard, which foresees a tolerance of ±0.5Hz.

This logic is implemented in line 12 of the code above, in the extension isInPermittedTolerance(), which is nothing more than the following line:

LiveData.observeAsState()

Another thing worth talking about is how our ui observes the information propagated to LiveData. Compose has the extension LiveData.observeAsState(), which works very similarly to the traditional way of observing changes in LiveData, with the Observer interface. The difference is that using this approach, the screen is automatically recomposed and the code is much cleaner:

And now? Well, now we can skip to the next step, which is to see the app working (or not 😅).

Action

To validate how our tuner works, we’re going to use a tone generator app reproducing specific frequencies in order to verify that the tuner is behaving as expected. As we know that the A4 note has a frequency of 440Hz, let’s use it in our test cases:

  • Frequency 439.5Hz should display Note A in tune
  • Frequency 440.5Hz should display Note A in tune
  • Frequency 439.4Hz should display Note A tuned down
  • Frequency 440.6Hz should display Note A tuned up
1*8xBkr2F4j9zvFYAT7lTC6A.png?q=20
compose-wear-os-chromatic-tuner-47aa50b44b6
screenshots of my Galaxy Watch4 + smartphone

Okay, it worked 😎

You can check the full code at:

Any suggestions or questions, feel free to leave your comment.
Thanks for reading and see you next time!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK