org.chromium.latency.walt.AudioFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.chromium.latency.walt.AudioFragment.java

Source

/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.chromium.latency.walt;

import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.media.AudioManager;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.Description;
import com.github.mikephil.charting.components.LimitLine;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import static org.chromium.latency.walt.Utils.getIntPreference;

/**
 * A simple {@link Fragment} subclass.
 */
public class AudioFragment extends Fragment implements View.OnClickListener, BaseTest.TestStateListener {

    enum AudioTestType {
        CONTINUOUS_PLAYBACK, CONTINUOUS_RECORDING, COLD_PLAYBACK, COLD_RECORDING, DISPLAY_WAVEFORM
    }

    private SimpleLogger logger;
    private TextView textView;
    private AudioTest audioTest;
    private View startButton;
    private View stopButton;
    private Spinner modeSpinner;
    private LineChart chart;
    private HistogramChart latencyChart;
    private View chartLayout;

    private static final int PERMISSION_REQUEST_RECORD_AUDIO = 1;

    public AudioFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        logger = SimpleLogger.getInstance(getContext());

        audioTest = new AudioTest(getActivity());
        audioTest.setTestStateListener(this);

        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_audio, container, false);
        textView = (TextView) view.findViewById(R.id.txt_box_audio);
        textView.setMovementMethod(new ScrollingMovementMethod());
        startButton = view.findViewById(R.id.button_start_audio);
        stopButton = view.findViewById(R.id.button_stop_audio);
        chartLayout = view.findViewById(R.id.chart_layout);
        chart = (LineChart) view.findViewById(R.id.chart);
        latencyChart = (HistogramChart) view.findViewById(R.id.latency_chart);

        view.findViewById(R.id.button_close_chart).setOnClickListener(this);
        enableButtons();

        // Configure the audio mode spinner
        modeSpinner = (Spinner) view.findViewById(R.id.spinner_audio_mode);
        ArrayAdapter<CharSequence> modeAdapter = ArrayAdapter.createFromResource(getContext(),
                R.array.audio_mode_array, android.R.layout.simple_spinner_item);
        modeAdapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item);
        modeSpinner.setAdapter(modeAdapter);

        return view;
    }

    @Override
    public void onResume() {
        super.onResume();

        // Register this fragment class as the listener for some button clicks
        startButton.setOnClickListener(this);
        stopButton.setOnClickListener(this);

        textView.setText(logger.getLogText());
        logger.registerReceiver(logReceiver);
    }

    @Override
    public void onPause() {
        logger.unregisterReceiver(logReceiver);
        super.onPause();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        audioTest.teardown();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.button_start_audio:
            chartLayout.setVisibility(View.GONE);
            disableButtons();
            AudioTestType testType = getSelectedTestType();
            switch (testType) {
            case CONTINUOUS_PLAYBACK:
            case CONTINUOUS_RECORDING:
            case DISPLAY_WAVEFORM:
                audioTest.setAudioMode(AudioTest.AudioMode.CONTINUOUS);
                audioTest.setPeriod(AudioTest.CONTINUOUS_TEST_PERIOD);
                break;
            case COLD_PLAYBACK:
            case COLD_RECORDING:
                audioTest.setAudioMode(AudioTest.AudioMode.CONTINUOUS);
                audioTest.setPeriod(AudioTest.COLD_TEST_PERIOD);
                break;
            }
            if (testType == AudioTestType.DISPLAY_WAVEFORM) {
                // Only need to record 1 beep to display wave
                audioTest.setRecordingRepetitions(1);
            } else {
                audioTest.setRecordingRepetitions(
                        getIntPreference(getContext(), R.string.preference_audio_in_reps, 5));
            }
            if (testType == AudioTestType.CONTINUOUS_PLAYBACK || testType == AudioTestType.COLD_PLAYBACK
                    || testType == AudioTestType.CONTINUOUS_RECORDING || testType == AudioTestType.COLD_RECORDING) {
                latencyChart.setVisibility(View.VISIBLE);
                latencyChart.clearData();
                latencyChart.setLegendEnabled(false);
                final String description = getResources().getStringArray(R.array.audio_mode_array)[modeSpinner
                        .getSelectedItemPosition()] + " [ms]";
                latencyChart.setDescription(description);
            }
            switch (testType) {
            case CONTINUOUS_RECORDING:
            case COLD_RECORDING:
            case DISPLAY_WAVEFORM:
                attemptRecordingTest();
                break;
            case CONTINUOUS_PLAYBACK:
            case COLD_PLAYBACK:
                // Set media volume to max
                AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
                am.setStreamVolume(AudioManager.STREAM_MUSIC, am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
                audioTest.beginPlaybackMeasurement();
                break;
            }
            break;
        case R.id.button_stop_audio:
            audioTest.stopTest();
            break;
        case R.id.button_close_chart:
            chartLayout.setVisibility(View.GONE);
            break;
        }
    }

    private AudioTestType getSelectedTestType() {
        return AudioTestType.values()[modeSpinner.getSelectedItemPosition()];
    }

    private BroadcastReceiver logReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String msg = intent.getStringExtra("message");
            textView.append(msg + "\n");
        }
    };

    private void attemptRecordingTest() {
        // first see if we already have permission to record audio
        int currentPermission = ContextCompat.checkSelfPermission(this.getContext(),
                Manifest.permission.RECORD_AUDIO);
        if (currentPermission == PackageManager.PERMISSION_GRANTED) {
            disableButtons();
            audioTest.beginRecordingMeasurement();
        } else {
            requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, PERMISSION_REQUEST_RECORD_AUDIO);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
        case PERMISSION_REQUEST_RECORD_AUDIO:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                disableButtons();
                audioTest.beginRecordingMeasurement();
            } else {
                logger.log("Could not get permission to record audio");
            }
            return;
        }
    }

    @Override
    public void onTestStopped() {
        if (getSelectedTestType() == AudioTestType.DISPLAY_WAVEFORM) {
            drawWaveformChart();
        } else {
            if (!audioTest.deltas_mic.isEmpty()) {
                latencyChart.setLegendEnabled(true);
                latencyChart
                        .setLabel(String.format(Locale.US, "Median=%.1f ms", Utils.median(audioTest.deltas_mic)));
            } else if (!audioTest.deltas_queue2wire.isEmpty()) {
                latencyChart.setLegendEnabled(true);
                latencyChart.setLabel(
                        String.format(Locale.US, "Median=%.1f ms", Utils.median(audioTest.deltas_queue2wire)));
            }
        }
        LogUploader.uploadIfAutoEnabled(getContext());
        enableButtons();
    }

    @Override
    public void onTestStoppedWithError() {
        enableButtons();
        latencyChart.setVisibility(View.GONE);
    }

    @Override
    public void onTestPartialResult(double value) {
        latencyChart.addEntry(value);
    }

    private void drawWaveformChart() {
        final short[] wave = AudioTest.getRecordedWave();
        List<Entry> entries = new ArrayList<>();
        int frameRate = audioTest.getOptimalFrameRate();
        for (int i = 0; i < wave.length; i++) {
            float timeStamp = (float) i / frameRate * 1000f;
            entries.add(new Entry(timeStamp, (float) wave[i]));
        }
        LineDataSet dataSet = new LineDataSet(entries, "Waveform");
        dataSet.setColor(Color.BLACK);
        dataSet.setValueTextColor(Color.BLACK);
        dataSet.setCircleColor(ContextCompat.getColor(getContext(), R.color.DarkGreen));
        dataSet.setCircleRadius(1.5f);
        dataSet.setCircleColorHole(Color.DKGRAY);
        LineData lineData = new LineData(dataSet);
        chart.setData(lineData);

        LimitLine line = new LimitLine(audioTest.getThreshold(), "Threshold");
        line.setLineColor(Color.RED);
        line.setLabelPosition(LimitLine.LimitLabelPosition.LEFT_TOP);
        line.setLineWidth(2f);
        line.setTextColor(Color.DKGRAY);
        line.setTextSize(10f);
        chart.getAxisLeft().addLimitLine(line);

        final Description desc = new Description();
        desc.setText("Wave [digital level -32768 to +32767] vs. Time [ms]");
        desc.setTextSize(12f);
        chart.setDescription(desc);
        chart.getLegend().setEnabled(false);
        chart.invalidate();
        chartLayout.setVisibility(View.VISIBLE);
    }

    private void disableButtons() {
        startButton.setEnabled(false);
        stopButton.setEnabled(true);
    }

    private void enableButtons() {
        startButton.setEnabled(true);
        stopButton.setEnabled(false);
    }
}