Сбой приложения при отображении амплитуды звука при записи - PullRequest
1 голос
/ 09 марта 2020

Я пытаюсь построить амплитуду звука при записи звука в android. Я могу записать аудио и сохранить его как аудиофайл.

Я использую библиотеку MPAndroidCharts для динамического построения амплитуды звука во время записи.

У меня есть кнопка переключения, которая при включении запускает запись звука и отображает ее амплитуду с помощью MediaRecorder API. Однако через несколько секунд после включения кнопки переключения на некоторое время появляется график, а затем происходит сбой приложения. Как предотвратить сбой приложения и плавное построение амплитуды звука во время записи?

Мой текущий код выглядит следующим образом:

activity_main. xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">


    <com.github.mikephil.charting.charts.LineChart
        android:id="@+id/chart1"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        tools:layout_editor_absoluteY="0dp"
        tools:layout_editor_absoluteX="8dp"/>

    <ToggleButton
        android:id="@+id/recordButton"
        android:layout_below="@id/chart1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textOff="Stop Recording"
        android:textOn="Start Recording"
        android:checked="true"
        android:layout_centerInParent="true"
        />

</RelativeLayout>

MainActivity. Java

package com.nitie.audioanalyzer;

import android.Manifest;
import android.graphics.Color;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.widget.CompoundButton;
import android.widget.Toast;
import android.widget.ToggleButton;

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Legend;
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;

import java.io.File;
import java.io.IOException;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    MediaRecorder mediaRecorder;
    ToggleButton toggleButton;

    private LineChart mChart;
    private Thread thread;
    private boolean plotData = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {


        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        ActivityCompat.requestPermissions(MainActivity.this,
                new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                1);


        ////
        mChart = findViewById(R.id.chart1);

        // enable description text
        mChart.getDescription().setEnabled(true);

        // enable touch gestures
        mChart.setTouchEnabled(true);

        // enable scaling and dragging
        mChart.setDragEnabled(true);
        mChart.setScaleEnabled(true);
        mChart.setDrawGridBackground(false);

        // if disabled, scaling can be done on x- and y-axis separately
        mChart.setPinchZoom(true);

        // set an alternative background color
        mChart.setBackgroundColor(Color.WHITE);

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

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

        // get the legend (only possible after setting data)
        Legend l = mChart.getLegend();

        // modify the legend ...
        l.setForm(Legend.LegendForm.LINE);
        l.setTextColor(Color.WHITE);

        XAxis xl = mChart.getXAxis();
        xl.setTextColor(Color.WHITE);
        xl.setDrawGridLines(true);
        xl.setAvoidFirstLastClipping(true);
        xl.setEnabled(true);

        YAxis leftAxis = mChart.getAxisLeft();
        leftAxis.setTextColor(Color.WHITE);
        leftAxis.setDrawGridLines(false);
        leftAxis.setAxisMaximum(10f);
        leftAxis.setAxisMinimum(0f);
        leftAxis.setDrawGridLines(true);
        YAxis rightAxis = mChart.getAxisRight();
        rightAxis.setEnabled(false);

        mChart.getAxisLeft().setDrawGridLines(false);
        mChart.getXAxis().setDrawGridLines(false);
        mChart.setDrawBorders(false);


        mediaRecorder = new MediaRecorder();
        toggleButton = findViewById(R.id.recordButton);
        toggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    stopRecording();
                } else {
                    startRecording();
                }
            }
        });
    }

    private void feedMultiple() {

        if (thread != null) {
            thread.interrupt();
        }

        thread = new Thread(new Runnable() {


            @Override
            public void run() {
                while (true) {
                    plotData = true;
                    while (plotData)
                        addEntry(mediaRecorder);
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });

        thread.start();
    }

    private void addEntry(MediaRecorder mediaRecorder) {

        LineData data = mChart.getData();

        if (data != null) {

            ILineDataSet set = data.getDataSetByIndex(0);
            // set.addEntry(...); // can be called as well

            if (set == null) {
                set = createSet();
                data.addDataSet(set);
            }

//            data.addEntry(new Entry(set.getEntryCount(), (float) (Math.random() * 80) + 10f), 0);
            data.addEntry(new Entry(set.getEntryCount(), mediaRecorder.getMaxAmplitude()), 0);
            data.notifyDataChanged();

            // let the chart know it's data has changed
            mChart.notifyDataSetChanged();

            // limit the number of visible entries
            mChart.setVisibleXRangeMaximum(150);
            // mChart.setVisibleYRange(30, AxisDependency.LEFT);

            // move to the latest entry
            mChart.moveViewToX(data.getEntryCount());

        }
    }

    private LineDataSet createSet() {

        LineDataSet set = new LineDataSet(null, "Dynamic Data");
        set.setAxisDependency(YAxis.AxisDependency.LEFT);
        set.setLineWidth(3f);
        set.setColor(Color.MAGENTA);
        set.setHighlightEnabled(false);
        set.setDrawValues(false);
        set.setDrawCircles(false);
        set.setMode(LineDataSet.Mode.CUBIC_BEZIER);
        set.setCubicIntensity(0.2f);
        return set;
    }


    public void startRecording() {
        try {
            plotData = true;
            Toast.makeText(this, "Recording Started", Toast.LENGTH_SHORT).show();

            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
            mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            File storageDirectory = new File(Environment.getExternalStorageDirectory() + File.separator + "Communication Analyzer");
            if (!storageDirectory.exists())
                storageDirectory.mkdirs();
            File audioFile = new File(storageDirectory, File.separator + "RecordedAudio.3gp");

            if (Build.VERSION.SDK_INT < 26) {
                mediaRecorder.setOutputFile(audioFile.getAbsolutePath());
            } else {
                mediaRecorder.setOutputFile(audioFile);
            }
            mediaRecorder.setOutputFile(audioFile.getAbsolutePath());
            mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
            mediaRecorder.prepare();
            mediaRecorder.start();

            feedMultiple();


        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public void stopRecording() {
        plotData = false;
        Toast.makeText(this, "Recording Stopped", Toast.LENGTH_SHORT).show();
        mediaRecorder.stop();
        mediaRecorder.release();
    }
}

Logcat

 --------- beginning of crash
2020-03-15 19:47:02.370 13236-13236/com.nitie.audioanalyzer E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.nitie.audioanalyzer, PID: 13236
    java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
        at java.util.ArrayList.get(ArrayList.java:411)
        at com.github.mikephil.charting.renderer.LegendRenderer.renderLegend(LegendRenderer.java:377)
        at com.github.mikephil.charting.charts.BarLineChartBase.onDraw(BarLineChartBase.java:281)
        at android.view.View.draw(View.java:17083)
        at android.view.View.updateDisplayListIfDirty(View.java:16065)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3752)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3732)
        at android.view.View.updateDisplayListIfDirty(View.java:16028)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3752)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3732)
        at android.view.View.updateDisplayListIfDirty(View.java:16028)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3752)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3732)
        at android.view.View.updateDisplayListIfDirty(View.java:16028)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3752)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3732)
        at android.view.View.updateDisplayListIfDirty(View.java:16028)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3752)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3732)
        at android.view.View.updateDisplayListIfDirty(View.java:16028)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:3752)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:3732)
        at android.view.View.updateDisplayListIfDirty(View.java:16028)
        at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:657)
        at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:663)
        at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:771)
        at android.view.ViewRootImpl.draw(ViewRootImpl.java:2808)
        at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2616)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2223)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1258)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6348)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:871)
        at android.view.Choreographer.doCallbacks(Choreographer.java:683)
        at android.view.Choreographer.doFrame(Choreographer.java:619)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:857)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6123)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)

1 Ответ

0 голосов
/ 18 марта 2020

Взгляните на эту ссылку [Ссылка на открытую проблему, связанную с падением] [1] https://github.com/PhilJay/MPAndroidChart/issues/4085 В нем говорится, что проблема связана с безопасностью потока

Попробуйте это

public class MainActivity extends AppCompatActivity {
    ...
    private Thread thread;
    private Handler mHandler;
    private boolean plotData = true;
    ...

    private void feedMultiple() {

        if (thread != null) {
            thread.interrupt();
        }

        thread = new Thread(new Runnable() {


            @Override
            public void run() {
                while (true) {
                    plotData = true;
                    while (plotData) {
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                addEntry(mediaRecorder);
                            }
                        });
                    }
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        });

        thread.start();
    }

    ...
}   
...