()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ val binding =
+ ItemProbabilityBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+ return ViewHolder(binding)
+ }
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val label = labelList[position]
+ val probability = probabilityMap[label] ?: 0f
+ holder.bind(position, label, probability)
+ }
+
+ override fun getItemCount() = labelList.size
+
+ class ViewHolder(private val binding: ItemProbabilityBinding) :
+ RecyclerView.ViewHolder(binding.root) {
+ fun bind(position: Int, label: String, probability: Float) {
+ with(binding) {
+ labelTextView.text = label
+ progressBar.progressBackgroundTintList = progressColorPairList[position % 3].first
+ progressBar.progressTintList = progressColorPairList[position % 3].second
+
+ val newValue = (probability * 100).toInt()
+ // If you don't want to animate, you can write like `progressBar.progress = newValue`.
+ val animation =
+ ObjectAnimator.ofInt(progressBar, "progress", progressBar.progress, newValue)
+ animation.duration = 100
+ animation.interpolator = AccelerateDecelerateInterpolator()
+ animation.start()
+ }
+ }
+
+ companion object {
+ /** List of pairs of background tint and progress tint */
+ private val progressColorPairList = listOf(
+ ColorStateList.valueOf(0xfff9e7e4.toInt()) to ColorStateList.valueOf(0xffd97c2e.toInt()),
+ ColorStateList.valueOf(0xfff7e3e8.toInt()) to ColorStateList.valueOf(0xffc95670.toInt()),
+ ColorStateList.valueOf(0xffecf0f9.toInt()) to ColorStateList.valueOf(0xff714Fe7.toInt()),
+ )
+ }
+ }
+}
diff --git a/lite/examples/sound_classification/android_legacy/app/src/main/java/org/tensorflow/lite/examples/soundclassifier/SoundClassifier.kt b/lite/examples/sound_classification/android_legacy/app/src/main/java/org/tensorflow/lite/examples/soundclassifier/SoundClassifier.kt
new file mode 100644
index 00000000000..e54c45981ad
--- /dev/null
+++ b/lite/examples/sound_classification/android_legacy/app/src/main/java/org/tensorflow/lite/examples/soundclassifier/SoundClassifier.kt
@@ -0,0 +1,419 @@
+/*
+ * Copyright 2021 The TensorFlow Authors. All Rights Reserved.
+ *
+ * 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.tensorflow.lite.examples.soundclassifier
+
+import android.content.Context
+import android.media.AudioFormat
+import android.media.AudioRecord
+import android.media.MediaRecorder
+import android.os.SystemClock
+import android.util.Log
+import androidx.annotation.MainThread
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import java.io.BufferedReader
+import java.io.IOException
+import java.io.InputStreamReader
+import java.nio.FloatBuffer
+import java.util.Locale
+import java.util.Timer
+import java.util.TimerTask
+import kotlin.concurrent.scheduleAtFixedRate
+import kotlin.math.sin
+import org.tensorflow.lite.Interpreter
+import org.tensorflow.lite.support.common.FileUtil
+
+/**
+ * Performs classification on sound.
+ *
+ * The API supports models which accept sound input via {@code AudioRecord} and one classification output tensor.
+ * The output of the recognition is emitted as LiveData of Map.
+ *
+ */
+class SoundClassifier(context: Context, private val options: Options = Options()) :
+ DefaultLifecycleObserver {
+ class Options constructor(
+ /** Path of the converted model label file, relative to the assets/ directory. */
+ val metadataPath: String = "labels.txt",
+ /** Path of the converted .tflite file, relative to the assets/ directory. */
+ val modelPath: String = "sound_classifier.tflite",
+ /** The required audio sample rate in Hz. */
+ val sampleRate: Int = 44_100,
+ /** How many milliseconds to sleep between successive audio sample pulls. */
+ val audioPullPeriod: Long = 50L,
+ /** Number of warm up runs to do after loading the TFLite model. */
+ val warmupRuns: Int = 3,
+ /** Number of points in average to reduce noise. */
+ val pointsInAverage: Int = 10,
+ /** Overlap factor of recognition period */
+ var overlapFactor: Float = 0.8f,
+ /** Probability value above which a class is labeled as active (i.e., detected) the display. */
+ var probabilityThreshold: Float = 0.3f,
+ )
+
+ var isRecording: Boolean = false
+ private set
+
+ /** As the result of sound classification, this value emits map of probabilities */
+ val probabilities: LiveData