summaryrefslogtreecommitdiff
path: root/Settings/src/com/android/tv/settings/system/development/audio/AudioMetrics.java
blob: 28dc5905270edb076d17529d90ec83f945ead76b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
 * Copyright (C) 2021 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 com.android.tv.settings.system.development.audio;

import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import java.util.Optional;

/** Stores audio recording metrics. Sends metrics updates through a callback. */
public class AudioMetrics {

    private static final String TAG = "AudioMetrics";

    private Optional<Long> mStartTs = Optional.empty();

    private Data mData = new Data();

    private final UpdateMetricsCallback mCallback;

    /** Contains data to be exposed via the callback. */
    public static class Data {

        public Optional<Long> timeToStartReadMs = Optional.empty();
        public Optional<Long> timeToValidAudioMs = Optional.empty();
        public Optional<Long> emptyAudioDurationMs = Optional.empty();

        public Data() {
        }

        public Data(Data data) {
            this.timeToStartReadMs = data.timeToStartReadMs;
            this.timeToValidAudioMs = data.timeToValidAudioMs;
            this.emptyAudioDurationMs = data.emptyAudioDurationMs;
        }
    }

    /** Interface for receiving metrics updates. */
    public interface UpdateMetricsCallback {
        /** Callback for receiving metrics updates. */
        void onUpdateMetrics(AudioMetrics.Data data);
    }

    /**
     * @param callback Callback for updates.
     */
    public AudioMetrics(UpdateMetricsCallback callback) {
        this.mCallback = callback;
    }

    /** Records the beginning of the audio recording process. */
    public void start() {
        mData = new Data();
        mStartTs = Optional.of(System.currentTimeMillis());
        updateMetrics();
    }

    /** Records that we have started monitoring a buffer for incoming audio data. */
    public void startedReading() throws IllegalStateException {
        long startTs = this.mStartTs
                .orElseThrow(() -> new IllegalStateException(
                        "Started reading before recording started"));
        long currentTs = System.currentTimeMillis();
        mData = new Data(mData);
        mData.timeToStartReadMs = Optional.of(currentTs - startTs);
        updateMetrics();
    }

    /** Records that we have started receiving non-zero audio data */
    public void receivedValidAudio() throws IllegalStateException {
        long startTs = this.mStartTs
                .orElseThrow(() -> new IllegalStateException(
                        "Received audio data before recording started"));
        long currentTs = System.currentTimeMillis();
        mData = new Data(mData);
        mData.timeToValidAudioMs = Optional.of(currentTs - startTs);
        updateMetrics();
    }

    /** Records the duration of empty audio received before valid audio */
    public void setEmptyAudioDurationMs(long emptyAudioMs) {
        mData = new Data(mData);
        mData.emptyAudioDurationMs = Optional.of(emptyAudioMs);
        updateMetrics();
    }

    /** Sends updated data through the callback */
    private void updateMetrics() {
        Handler mainHandler = new Handler(Looper.getMainLooper());
        mainHandler.post(() -> mCallback.onUpdateMetrics(mData));
        Log.i(TAG, String.format("Time to start reading: %s",
                msTimestampToString(mData.timeToStartReadMs)));
        Log.i(TAG, String.format("Time to valid audio data: %s",
                msTimestampToString(mData.timeToValidAudioMs)));
        Log.i(TAG, String.format("Empty audio duration: %s",
                msTimestampToString(mData.emptyAudioDurationMs)));
    }

    /** Converts a possible timestamp in milliseconds to its string representation. */
    public static String msTimestampToString(Optional<Long> optL) {
        return optL.map((Long l) -> String.format("%s ms", l)).orElse("");
    }
}