YACWC
This commit is contained in:
@@ -55,12 +55,16 @@ dependencies {
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
|
||||
implementation("androidx.datastore:datastore-preferences:1.1.3")
|
||||
implementation("org.tensorflow:tensorflow-lite:2.6.0")
|
||||
// implementation("com.google.android.gms:play-services-tflite:20.0.0")
|
||||
implementation("uk.me.berndporr:iirj:1.7")
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.datastore.core.android)
|
||||
implementation(libs.core.ktx)
|
||||
// implementation(libs.litert)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.io.IOException
|
||||
|
||||
class Downloader(mainActivity: MainActivity) {
|
||||
|
||||
private val settings = Settings();
|
||||
private val settings = Settings;
|
||||
private var activity: MainActivity = mainActivity;
|
||||
private var context: Context = activity.applicationContext;
|
||||
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.birdsounds.identify
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Address
|
||||
import android.location.Geocoder
|
||||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
object Location {
|
||||
private var locationListenerGPS: LocationListener? = null
|
||||
@@ -30,7 +39,10 @@ object Location {
|
||||
) {
|
||||
val locationManager =
|
||||
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
|
||||
|
||||
if (locationListenerGPS == null) locationListenerGPS = object : LocationListener {
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onLocationChanged(location: Location) {
|
||||
Log.w(TAG, "Got location changed");
|
||||
while (!soundClassifier.is_model_ready()) {
|
||||
@@ -39,7 +51,35 @@ object Location {
|
||||
Log.w(TAG, "Sound classifier is ready");
|
||||
soundClassifier.runMetaInterpreter(location)
|
||||
|
||||
soundClassifier.runMetaInterpreter(location)
|
||||
val activity = context as? Activity;
|
||||
activity?.let {
|
||||
|
||||
val text_species: TextView = it.findViewById(R.id.local_species)
|
||||
text_species.text = local_species;
|
||||
|
||||
val loc_lon: TextView = it.findViewById(R.id.location_long)
|
||||
loc_lon.text =
|
||||
"Longitude: ${location.longitude}"
|
||||
|
||||
val loc_lat: TextView = it.findViewById(R.id.location_lat)
|
||||
loc_lat.text =
|
||||
"Latitude: ${location.latitude}"
|
||||
|
||||
val loc_string: TextView = it.findViewById(R.id.location_string);
|
||||
val geocoder = Geocoder(context, Locale.getDefault())
|
||||
val addresses: MutableList<Address>? =
|
||||
geocoder.getFromLocation(location.latitude, location.longitude, 1)
|
||||
if (addresses?.isNotEmpty() == true) {
|
||||
val address: Address = addresses[0]
|
||||
|
||||
// loc_string.text = address.locality.toString() + ", " + address.adminArea.toString() + " " + address.countryName.toString()
|
||||
loc_string.text = address.getAddressLine(0).toString()
|
||||
// Log.w(TAG, address.toString())
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Deprecated("")
|
||||
@@ -53,7 +93,7 @@ object Location {
|
||||
}
|
||||
}
|
||||
locationManager.requestLocationUpdates(
|
||||
LocationManager.GPS_PROVIDER, 60000, 0f,
|
||||
LocationManager.PASSIVE_PROVIDER, 60000, 0f,
|
||||
locationListenerGPS!!
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,42 @@
|
||||
package com.birdsounds.identify
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.Manifest
|
||||
import android.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import android.widget.SeekBar
|
||||
import android.widget.TextView
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.gms.wearable.ChannelClient
|
||||
import com.google.android.gms.wearable.Wearable
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.time.Instant
|
||||
|
||||
private var updateJob: Job? = null
|
||||
private var updateCounter = 0
|
||||
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||
|
||||
|
||||
|
||||
val Any.TAG: String
|
||||
get() {
|
||||
@@ -19,11 +45,39 @@ val Any.TAG: String
|
||||
}
|
||||
|
||||
|
||||
class SynchronousDataStore(private val context: Context) {
|
||||
|
||||
// Define keys
|
||||
companion object {
|
||||
val THRESHOLD_KEY = intPreferencesKey("user_age")
|
||||
}
|
||||
|
||||
// Synchronous write operations
|
||||
fun saveThreshold(value: Int) {
|
||||
runBlocking {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[THRESHOLD_KEY] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getThreshold(): Int {
|
||||
|
||||
return runBlocking {
|
||||
context.dataStore.data.first()[THRESHOLD_KEY] ?: 50
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
// private lateinit var soundClassifier: SoundClassifier
|
||||
val REQUEST_PERMISSIONS = 1337
|
||||
|
||||
private lateinit var dataStore: SynchronousDataStore
|
||||
|
||||
private lateinit var last_message_delay: TextView
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
@@ -49,6 +103,34 @@ class MainActivity : AppCompatActivity() {
|
||||
insets
|
||||
|
||||
}
|
||||
|
||||
val thresholdText = findViewById<TextView>(R.id.threshold_value_text)
|
||||
val seekBar = findViewById<SeekBar>(R.id.threshold_set_scale_bar)
|
||||
last_message_delay = findViewById<TextView>(R.id.last_message_delay)
|
||||
dataStore = SynchronousDataStore(applicationContext)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Settings.threshold = dataStore.getThreshold();
|
||||
seekBar.progress = Settings.threshold
|
||||
thresholdText.text = String.format("%.2f", Settings.threshold/100f)
|
||||
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
@SuppressLint("DefaultLocale")
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
|
||||
var actualValue = progress
|
||||
dataStore.saveThreshold(actualValue);
|
||||
Settings.threshold = actualValue;
|
||||
thresholdText.text = String.format("%.2f", actualValue/100f)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||
})
|
||||
|
||||
}
|
||||
companion object {
|
||||
var soundClassifier: SoundClassifier? = null
|
||||
@@ -57,6 +139,34 @@ class MainActivity : AppCompatActivity() {
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
// Start periodic updates using coroutines
|
||||
updateJob = lifecycleScope.launch {
|
||||
while (isActive) { // isActive is a property of the coroutine scope
|
||||
updateCounter++
|
||||
if (last_message_time == 0.toLong())
|
||||
{
|
||||
last_message_delay.text = "No messages received"
|
||||
} else
|
||||
{
|
||||
last_message_delay.text = "Last message: ${(Instant.now().toEpochMilli() - last_message_time)/1000F.toInt()} seconds ago"
|
||||
}
|
||||
|
||||
// last_message_delay.text = "Update count: $updateCounter"
|
||||
delay(500) // Update every 1 second
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
// Cancel the coroutine when activity is not visible
|
||||
updateJob?.cancel()
|
||||
}
|
||||
|
||||
private fun requestPermissions() {
|
||||
val perms = mutableListOf<String>()
|
||||
|
||||
|
||||
@@ -10,6 +10,19 @@ import decodeAACToPCM
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.time.Instant
|
||||
|
||||
|
||||
var last_message_time = 0L;
|
||||
fun ByteArray.toLong(): Long {
|
||||
require(size <= 8) { "ByteArray too large to fit in Long" }
|
||||
|
||||
var result = 0L
|
||||
for (byte in this) {
|
||||
result = (result shl 8) or (byte.toLong() and 0xFF)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
class MessageListenerService : WearableListenerService() {
|
||||
|
||||
@@ -30,6 +43,10 @@ class MessageListenerService : WearableListenerService() {
|
||||
}
|
||||
|
||||
var tstamp_bytes = p0.data.copyOfRange(0, Long.SIZE_BYTES)
|
||||
|
||||
last_message_time = tstamp_bytes.toLong()
|
||||
|
||||
|
||||
var audio_bytes_og = p0.data.copyOfRange(Long.SIZE_BYTES, p0.data.size)
|
||||
|
||||
|
||||
@@ -63,8 +80,12 @@ class MessageListenerService : WearableListenerService() {
|
||||
var sorted_list = soundclassifier.executeScoring(short_array)
|
||||
Log.w(TAG, "FINISHED SCORING");
|
||||
Log.w(TAG, "")
|
||||
for (i in 0 until 5) {
|
||||
val threshold = Settings.threshold/100f
|
||||
for (i in 0 until 10) {
|
||||
val score = sorted_list[i].value
|
||||
if (score < threshold) {
|
||||
continue
|
||||
}
|
||||
val index = sorted_list[i].index
|
||||
val species_name = soundclassifier.labelList[index]
|
||||
Log.w(TAG, species_name + ", " + score.toString())
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.birdsounds.identify
|
||||
|
||||
class Settings {
|
||||
object Settings {
|
||||
var local_model_file: String = "2024_08_16_audio_model.tflite"
|
||||
var pkg_model_file: String = "2024_08_16/audio-model.tflite"
|
||||
|
||||
var local_meta_model_file: String = "2024_08_16_meta_model.tflite"
|
||||
var pkg_meta_model_file: String = "2024_08_16/meta-model.tflite"
|
||||
|
||||
var threshold: Int = 50;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import uk.me.berndporr.iirj.Butterworth
|
||||
import kotlin.math.sin
|
||||
|
||||
|
||||
|
||||
var local_species = ""
|
||||
class SoundClassifier(
|
||||
context: Context,
|
||||
private val options: Options = Options()
|
||||
@@ -81,7 +83,7 @@ class SoundClassifier(
|
||||
/** Number of output classes of the TFLite model. */
|
||||
private var modelNumClasses = 0
|
||||
private var metaModelNumClasses = 0
|
||||
private var settings: Settings = Settings();
|
||||
private var settings = Settings;
|
||||
|
||||
/** Used to hold the real-time probabilities predicted by the model for the output classes. */
|
||||
private lateinit var predictionProbs: FloatArray
|
||||
@@ -274,6 +276,17 @@ class SoundClassifier(
|
||||
metaOutputBuffer.rewind()
|
||||
metaOutputBuffer.get(metaPredictionProbs) // Copy data to metaPredictionProbs.
|
||||
|
||||
var cloned = metaPredictionProbs.clone()
|
||||
|
||||
val sorted_indices = cloned.withIndex().sortedByDescending { it.value }.map{it.index}
|
||||
|
||||
local_species = "Most likely:\n"
|
||||
for (i in 0..5) {
|
||||
local_species += " " +labelList[sorted_indices[i]].split("_")[1]
|
||||
local_species += "\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (i in metaPredictionProbs.indices) {
|
||||
metaPredictionProbs[i] =
|
||||
|
||||
@@ -7,13 +7,89 @@
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingTop="15dp"
|
||||
android:text="Threshold:"
|
||||
android:textSize="34sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="55dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/threshold_value_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="4"
|
||||
android:gravity="center"
|
||||
android:text="Threshold:"
|
||||
android:textAlignment="center"
|
||||
android:textSize="28sp" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/threshold_set_scale_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center|fill_horizontal|fill_vertical"
|
||||
android:layout_weight="1"
|
||||
android:max="100"
|
||||
android:min="0" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/location_string"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="5dp"
|
||||
android:text="Location:"
|
||||
android:textSize="34sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/location_lat"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="5dp"
|
||||
android:text="Latitude:"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/location_long"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:text="Longitude:"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/local_species"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:text="Local Species:"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/last_message_delay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Last Message" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Reference in New Issue
Block a user