[JAVA] Android app Spectrum Analyzer

0. Introduction

Create a Spectrum Analyzer with an Android app that captures sound from a microphone, analyzes the frequency, and displays the spectrum. (I haven't released it, because I can't make something that will be released) ** Moreover, the problem remains. ** **

Graph drawing library [MPAndroidChart] is used.

1. Introduce MPAndroidChart to your project

Edit build.gradle to install MPAndroidChart. There are two types of build.gradle, directly under the root of the project and in the app folder. Add the following to build.gradle directly under the root

build.gradle(Project)


allprojects {
    repositories {
        maven {
            url "https://jitpack.io"
        }
    }
}

Add the following to build.gradle in the app folder

build.gradle(app)


dependencies {
    implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3'
}

2. Source code

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;

//In MainActivity
public LineChart lineChart;

//in onCreate
lineChart = findViewById(R.id.line_chart);
initChart();

//Inside onCheckedChanged (because I'm using Switch)
fft = new Thread(new Runnable() {
    @Override 
    public void run() {

        //After processing such as Fourier transform
        setData(decibelFrequencySpectrum);
    }
});
fft.start();

3. Initialization

InitChart method to initialize the graph

initChart


public void initChart() {

    lineChart.setTouchEnabled(true);
    lineChart.setDragEnabled(false);

    //Grid background color
    lineChart.setDrawGridBackground(true);

    // no description text
    lineChart.getDescription().setEnabled(true);

    lineChart.setBackgroundColor(Color.LTGRAY);

    LineData data = new LineData();
    data.setValueTextColor(Color.BLACK);

    // add empty data
    lineChart.setData(data);

    //Grid vertical line is broken line
    XAxis xAxis = lineChart.getXAxis();
    xAxis.setAxisMaximum(2048);
    xAxis.setAxisMinimum(0);
    xAxis.enableGridDashedLine(10f, 10f, 0f);
    xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
    YAxis leftAxis = lineChart.getAxisLeft();
    //Y-axis maximum / minimum setting
    leftAxis.setAxisMaximum(0f);
    leftAxis.setAxisMinimum(-150f);
    //Grid horizontal axis is broken line
    leftAxis.enableGridDashedLine(10f, 10f, 0f);
    leftAxis.setDrawZeroLine(true);

    //Scale on the right
    lineChart.getAxisRight().setEnabled(false);
}

4. Drawing

SetData method that records with a microphone and draws the Fourier transformed data

setData


public void setData(double[] data) {

    ArrayList<Entry> values = new ArrayList<>();

    for (int i = 0; i < data.length; i++) {
        values.add(new Entry(i, (int)data[i], null, null));
    }

    LineDataSet set1;

    if (lineChart.getData() != null && lineChart.getData().getDataSetCount() > 0) {
        set1 = (LineDataSet) lineChart.getData().getDataSetByIndex(0);
        set1.setValues(values);
        lineChart.getData().notifyDataChanged();
        lineChart.notifyDataSetChanged();
    } else {
        // create a dataset and give it a type
        set1 = new LineDataSet(values, "Spectrum");

        set1.setDrawIcons(false);
        set1.setColor(Color.rgb(0, 0, 240));
        set1. setDrawCircles(false);
        set1.setLineWidth(0.5f);
        set1.setValueTextSize(0f);
        set1.setDrawFilled(false);
        set1.setFormLineWidth(1f);
        set1.setFormLineDashEffect(new DashPathEffect(new float[]{10f, 5f}, 0f));
        set1.setFormSize(15.f);
        set1.setDrawValues(true);

        ArrayList<ILineDataSet> dataSets = new ArrayList<>();
        dataSets.add(set1); // add the datasets

        // create a data object with the datasets
        LineData lineData = new LineData(dataSets);

        // set data
        lineChart.setData(lineData);
    }
}

5. App screen

5-1. Start screen

The graph is displayed properly. Press the Switch on the top left to start recording and draw the spectrum.

5-2. Recording screen

When I press Switch, recording starts but the graph is not drawn. Why?

5-3. When you touch the graph

It was drawn. The graph does not move when you release your hand. If you touch it, the graph will be updated. Why?

6. I want to know how to improve

I changed lineChart.setTouchEnabled (True) to False in initChart, but this time it disappeared at all. There is no change even if you touch the graph. ~~ Please tell me how to improve ~~

7. Improved (2019/07/29)

Added lineChart.invalidate () to update the graph to the setData method in the fft thread. I still got an error. The cause is an error caused by trying to change the UI in a thread other than the main thread. Therefore, it is solved by requesting the main thread to process the setData method using Handler.

Click here for new code

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;

//In MainActivity
public LineChart lineChart;

//in onCreate
lineChart = findViewById(R.id.line_chart);
initChart();

//Inside onCheckedChanged (because I'm using Switch)
fft = new Thread(new Runnable() {
    @Override 
    public void run() {

        //After processing such as Fourier transform
        handler.post(new Runnable() {
            @Override 
            public void run() {
                setData(decibelFrequencySpectrum);
            }
        });
    }
});
fft.start();

setData


public void setData(double[] data) {

    ArrayList<Entry> values = new ArrayList<>();

    for (int i = 0; i < data.length; i++) {
        values.add(new Entry(i, (int)data[i], null, null));
    }

    LineDataSet set1;

    if (lineChart.getData() != null && lineChart.getData().getDataSetCount() > 0) {
        set1 = (LineDataSet) lineChart.getData().getDataSetByIndex(0);
        set1.setValues(values);
        lineChart.getData().notifyDataChanged();
        lineChart.notifyDataSetChanged();
        lineChart.invalidate();
    } else {
        // create a dataset and give it a type
        set1 = new LineDataSet(values, "Spectrum");

        set1.setDrawIcons(false);
        set1.setColor(Color.rgb(0, 0, 240));
        set1. setDrawCircles(false);
        set1.setLineWidth(0.5f);
        set1.setValueTextSize(0f);
        set1.setDrawFilled(false);
        set1.setFormLineWidth(1f);
        set1.setFormLineDashEffect(new DashPathEffect(new float[]{10f, 5f}, 0f));
        set1.setFormSize(15.f);
        set1.setDrawValues(true);

        ArrayList<ILineDataSet> dataSets = new ArrayList<>();
        dataSets.add(set1); // add the datasets

        // create a data object with the datasets
        LineData lineData = new LineData(dataSets);

        // set data
        lineChart.setData(lineData);
    }
}

Recommended Posts

Android app Spectrum Analyzer
Android weather app
About Android App Components
Android app personal development kickoff
ROS app development on Android
Android app decompile ⇒ fix ⇒ recompile
Make an android app. (Day 5)
Is it an Android app?
Import device images with Android app
Make an android app. (First day)
Notes on calling Installer on Android App
I made a matching app (Android app)
[Android] I made a pedometer app.
USB serial communication with Android app (usb-serial-for-android)
Face recognition app with OpenCV + Android Studio
I made a calculator app on Android
I made a rock-paper-scissors app with android
Allows Youtube autoplay on Cordova's Android app