0. はじめに
マイクから音を取得して周波数解析し、スペクトルを表示するSpectrum AnalyzerをAndroidアプリで作成。(リリースはしていません、リリースするレベルの物を作れないので)
しかも問題点が残ったまま。
グラフ描画ライブラリ【 MPAndroidChart 】を使用。
1. プロジェクトにMPAndroidChartを導入
build.gradleを編集してMPAndroidChartを導入する。
build.gradleはプロジェクトのルート直下とappフォルダ内の2種類がある。
ルート直下のbuild.gradleには以下を追加
allprojects {
repositories {
maven {
url "https://jitpack.io"
}
}
}
appフォルダ内のbuild.gradleには以下を追加
dependencies {
implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3'
}
#2. ソースコード
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;
//MainActivity内
public LineChart lineChart;
//onCreate内
lineChart = findViewById(R.id.line_chart);
initChart();
//onCheckedChanged内(Switchを使っているので)
fft = new Thread(new Runnable() {
@Override
public void run() {
//フーリエ変換等処理後
setData(decibelFrequencySpectrum);
}
});
fft.start();
3. 初期化
グラフを初期化するinitChartメソッド
public void initChart() {
lineChart.setTouchEnabled(true);
lineChart.setDragEnabled(false);
// Grid背景色
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縦軸を破線
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軸最大最小設定
leftAxis.setAxisMaximum(0f);
leftAxis.setAxisMinimum(-150f);
// Grid横軸を破線
leftAxis.enableGridDashedLine(10f, 10f, 0f);
leftAxis.setDrawZeroLine(true);
// 右側の目盛り
lineChart.getAxisRight().setEnabled(false);
}
4. 描画
マイクで録音し、フーリエ変換したデータを描画する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. アプリ画面
5-1. 開始画面
ちゃんとグラフが表示。左上のSwitchを押すと録音が開始されてスペクトルが描画される。
5-2. 録音時画面
Switchを押すと録音が開始されたがグラフが描画されない。Why?
5-3. グラフを触ってみると
描画された。手を離すとグラフが動かなくなる。また手を触れるとグラフが更新される。Why?
6. 改善方法を知りたい
initChart内のlineChart.setTouchEnabled(True)
をFalse
に変えてみたら、今度は全く表示されなくなった。グラフを触っても変化なし。
改善方法を教えてください
7. 改善した (2019/07/29)
fftスレッド内のsetDataメソッドにグラフを更新するlineChart.invalidate()
を追加。
それでもエラーが出た。原因はメインスレッド以外のスレッドでUIを変更しようとしたことによるエラー。
そこでHandler
を使用してメインスレッドにsetDataメソッドの処理を依頼することで解決。
新しいコードはこちら
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;
//MainActivity内
public LineChart lineChart;
//onCreate内
lineChart = findViewById(R.id.line_chart);
initChart();
//onCheckedChanged内(Switchを使っているので)
fft = new Thread(new Runnable() {
@Override
public void run() {
//フーリエ変換等処理後
handler.post(new Runnable() {
@Override
public void run() {
setData(decibelFrequencySpectrum);
}
});
}
});
fft.start();
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);
}
}