aboutsummaryrefslogtreecommitdiff
path: root/android/WALT/app/src/main/java/org/chromium/latency/walt/AudioFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/WALT/app/src/main/java/org/chromium/latency/walt/AudioFragment.java')
-rw-r--r--android/WALT/app/src/main/java/org/chromium/latency/walt/AudioFragment.java310
1 files changed, 310 insertions, 0 deletions
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/AudioFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/AudioFragment.java
new file mode 100644
index 0000000..65452ff
--- /dev/null
+++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/AudioFragment.java
@@ -0,0 +1,310 @@
+/*
+ * 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);
+ }
+}