Bump˚
This commit is contained in:
1
.idea/codeStyles/codeStyleConfig.xml
generated
1
.idea/codeStyles/codeStyleConfig.xml
generated
@@ -1,6 +1,5 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<state>
|
<state>
|
||||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
|
||||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||||
</state>
|
</state>
|
||||||
</component>
|
</component>
|
||||||
@@ -15,6 +15,8 @@ import com.google.android.gms.wearable.Wearable
|
|||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
// private lateinit var soundClassifier: SoundClassifier
|
// private lateinit var soundClassifier: SoundClassifier
|
||||||
|
val REQUEST_PERMISSIONS = 1337
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@@ -28,20 +30,22 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
Downloader.downloadModels(this);
|
Downloader.downloadModels(this)
|
||||||
requestPermissions();
|
requestPermissions()
|
||||||
soundClassifier = SoundClassifier(this, SoundClassifier.Options())
|
soundClassifier = SoundClassifier(this, SoundClassifier.Options())
|
||||||
Location.requestLocation(this, soundClassifier)
|
Location.requestLocation(this, soundClassifier)
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||||
insets
|
insets
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
companion object {
|
companion object {
|
||||||
const val REQUEST_PERMISSIONS = 1337
|
var soundClassifier: SoundClassifier? = null
|
||||||
@SuppressLint("StaticFieldLeak")
|
// fun getSoundClassifier(): SoundClassifier? {
|
||||||
lateinit var soundClassifier: SoundClassifier
|
// return soundClassifier
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun requestPermissions() {
|
private fun requestPermissions() {
|
||||||
|
|||||||
@@ -18,16 +18,18 @@ class MessageListenerService : WearableListenerService() {
|
|||||||
override fun onMessageReceived(p0: MessageEvent) {
|
override fun onMessageReceived(p0: MessageEvent) {
|
||||||
super.onMessageReceived(p0)
|
super.onMessageReceived(p0)
|
||||||
|
|
||||||
|
// MainActivity
|
||||||
val soundclassifier = MainActivity.soundClassifier
|
val soundclassifier = MainActivity.soundClassifier
|
||||||
if (soundclassifier == null) {
|
if (soundclassifier == null) {
|
||||||
Log.w(tag, "Have invalid sound classifier")
|
Log.w(tag, "Have invalid sound classifier")
|
||||||
|
return
|
||||||
} else {
|
} else {
|
||||||
Log.w(tag, "Have valid classifier")
|
Log.w(tag, "Have valid classifier")
|
||||||
}
|
}
|
||||||
val short_array = ShortArray(48000 * 3);
|
val short_array = ShortArray(48000 * 3)
|
||||||
var tstamp_bytes = p0.data.copyOfRange(0, Long.SIZE_BYTES)
|
var tstamp_bytes = p0.data.copyOfRange(0, Long.SIZE_BYTES)
|
||||||
var audio_bytes = p0.data.copyOfRange(Long.SIZE_BYTES, p0.data.size)
|
var audio_bytes = p0.data.copyOfRange(Long.SIZE_BYTES, p0.data.size)
|
||||||
|
var string_send: String = ""
|
||||||
|
|
||||||
ByteBuffer.wrap(audio_bytes).order(
|
ByteBuffer.wrap(audio_bytes).order(
|
||||||
ByteOrder.LITTLE_ENDIAN
|
ByteOrder.LITTLE_ENDIAN
|
||||||
@@ -39,9 +41,13 @@ class MessageListenerService : WearableListenerService() {
|
|||||||
val score = sorted_list[i].value
|
val score = sorted_list[i].value
|
||||||
val index = sorted_list[i].index
|
val index = sorted_list[i].index
|
||||||
val species_name = soundclassifier.labelList[index]
|
val species_name = soundclassifier.labelList[index]
|
||||||
Log.w(tag, species_name + ", " + score.toString());
|
Log.w(tag, species_name + ", " + score.toString())
|
||||||
|
string_send+= species_name
|
||||||
|
string_send+=','
|
||||||
|
string_send+=score.toString()
|
||||||
|
string_send+=';'
|
||||||
}
|
}
|
||||||
MessageSenderFromPhone.sendMessage("/audio", tstamp_bytes, this)
|
MessageSenderFromPhone.sendMessage("/audio", tstamp_bytes + string_send.toByteArray(), this)
|
||||||
// Log.i(tag , short_array.map( { abs(it)}).sum().toString())
|
// Log.i(tag , short_array.map( { abs(it)}).sum().toString())
|
||||||
// Log.i(tag, short_array[0].toString())
|
// Log.i(tag, short_array[0].toString())
|
||||||
// Log.i(tag, p0.data.toString(Charsets.US_ASCII))
|
// Log.i(tag, p0.data.toString(Charsets.US_ASCII))
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.location.Location
|
|||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.annotation.Nullable
|
||||||
import org.tensorflow.lite.Interpreter
|
import org.tensorflow.lite.Interpreter
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -26,6 +27,7 @@ import java.nio.ShortBuffer
|
|||||||
import kotlin.math.round
|
import kotlin.math.round
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
|
|
||||||
class SoundClassifier(
|
class SoundClassifier(
|
||||||
context: Context,
|
context: Context,
|
||||||
private val options: Options = Options()
|
private val options: Options = Options()
|
||||||
@@ -60,10 +62,10 @@ class SoundClassifier(
|
|||||||
|
|
||||||
|
|
||||||
/** Names of the model's output classes. */
|
/** Names of the model's output classes. */
|
||||||
public lateinit var labelList: List<String>
|
lateinit var labelList: List<String>
|
||||||
|
|
||||||
/** Names of the model's output classes. */
|
/** Names of the model's output classes. */
|
||||||
public lateinit var assetList: List<String>
|
lateinit var assetList: List<String>
|
||||||
|
|
||||||
/** How many milliseconds between consecutive model inference calls. */
|
/** How many milliseconds between consecutive model inference calls. */
|
||||||
private var inferenceInterval = 800L
|
private var inferenceInterval = 800L
|
||||||
@@ -315,10 +317,10 @@ class SoundClassifier(
|
|||||||
private const val NANOS_IN_MILLIS = 1_000_000.toDouble()
|
private const val NANOS_IN_MILLIS = 1_000_000.toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun executeScoring(
|
fun executeScoring(
|
||||||
short_array: ShortArray
|
short_array: ShortArray
|
||||||
): List<IndexedValue<Float>> {
|
): List<IndexedValue<Float>> {
|
||||||
val highPass = 0;
|
val highPass = 0
|
||||||
val butterworth = Butterworth()
|
val butterworth = Butterworth()
|
||||||
butterworth.highPass(6, 48000.0, highPass.toDouble())
|
butterworth.highPass(6, 48000.0, highPass.toDouble())
|
||||||
|
|
||||||
@@ -372,7 +374,7 @@ class SoundClassifier(
|
|||||||
|
|
||||||
// Load new audio samples
|
// Load new audio samples
|
||||||
// val sampleCounts = loadAudio(recordingBuffer)
|
// val sampleCounts = loadAudio(recordingBuffer)
|
||||||
val sampleCounts = 0;
|
val sampleCounts = 0
|
||||||
if (sampleCounts == 0) {
|
if (sampleCounts == 0) {
|
||||||
return@task
|
return@task
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".MessageListenerService"
|
android:name=".presentation.MessageListenerService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:exported="true" >
|
android:exported="true" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
package com.birdsounds.identify
|
|
||||||
|
|
||||||
import android.app.Service
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.util.Half.abs
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
|
||||||
import com.birdsounds.identify.presentation.MessageSender
|
|
||||||
import com.google.android.gms.wearable.MessageEvent
|
|
||||||
import com.google.android.gms.wearable.WearableListenerService
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.ByteOrder
|
|
||||||
import java.nio.ShortBuffer
|
|
||||||
|
|
||||||
class MessageListenerService : WearableListenerService() {
|
|
||||||
private val tag = "MessageListenerService"
|
|
||||||
|
|
||||||
// fun placeSoundClassifier(soundClassifier: SoundClassifier)
|
|
||||||
override fun onMessageReceived(p0: MessageEvent) {
|
|
||||||
super.onMessageReceived(p0)
|
|
||||||
|
|
||||||
|
|
||||||
val t_scored = ByteBuffer.wrap(p0.data).getLong()
|
|
||||||
MessageSender.messageLog.add(t_scored)
|
|
||||||
Log.w("MSG_RECV", t_scored.toString())
|
|
||||||
// Log.i(tag , short_array.map( { abs(it)}).sum().toString())
|
|
||||||
// Log.i(tag, short_array[0].toString())
|
|
||||||
// Log.i(tag, p0.data.toString(Charsets.US_ASCII))
|
|
||||||
// broadcastMessage(p0)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -16,13 +16,11 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
|||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.wear.compose.navigation.SwipeDismissableNavHost
|
import androidx.wear.compose.navigation.SwipeDismissableNavHost
|
||||||
import androidx.wear.compose.navigation.composable
|
import androidx.wear.compose.navigation.composable
|
||||||
@@ -30,11 +28,8 @@ import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
|
|||||||
import com.birdsounds.identify.presentation.theme.IdentifyTheme
|
import com.birdsounds.identify.presentation.theme.IdentifyTheme
|
||||||
import com.google.android.horologist.annotations.ExperimentalHorologistApi
|
import com.google.android.horologist.annotations.ExperimentalHorologistApi
|
||||||
import com.google.android.horologist.audio.ui.VolumeViewModel
|
import com.google.android.horologist.audio.ui.VolumeViewModel
|
||||||
import com.google.android.horologist.compose.layout.AppScaffold
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import kotlinx.coroutines.flow.*
|
|
||||||
|
|
||||||
val flow_stream = MutableStateFlow<String>("")
|
val flow_stream = MutableStateFlow<String>("")
|
||||||
|
|
||||||
@@ -63,17 +58,11 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
@OptIn(ExperimentalHorologistApi::class)
|
@OptIn(ExperimentalHorologistApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
@Preview
|
||||||
fun WearApp() {
|
fun WearApp() {
|
||||||
|
|
||||||
IdentifyTheme {
|
IdentifyTheme {
|
||||||
lateinit var requestPermissionLauncher: ManagedActivityResultLauncher<String, Boolean>
|
lateinit var requestPermissionLauncher: ManagedActivityResultLauncher<String, Boolean>
|
||||||
|
|
||||||
// val job = launch {
|
|
||||||
// flow_stream.collect {
|
|
||||||
// print("$it ")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val activity = context.findActivity()
|
val activity = context.findActivity()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
@@ -99,20 +88,20 @@ fun WearApp() {
|
|||||||
|
|
||||||
val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
|
val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
|
||||||
|
|
||||||
AppScaffold {
|
// AppScaffold {
|
||||||
DisposableEffect(mainState, scope, lifecycleOwner) {
|
// DisposableEffect(mainState, scope, lifecycleOwner) {
|
||||||
val lifecycleObserver = object : DefaultLifecycleObserver {
|
// val lifecycleObserver = object : DefaultLifecycleObserver {
|
||||||
override fun onStop(owner: LifecycleOwner) {
|
// override fun onStop(owner: LifecycleOwner) {
|
||||||
super.onStop(owner)
|
// super.onStop(owner)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
|
// lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
|
||||||
|
//
|
||||||
onDispose {
|
// onDispose {
|
||||||
lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
|
// lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
SwipeDismissableNavHost(navController = navController, startDestination = "speaker") {
|
var sdnv = SwipeDismissableNavHost(navController = navController, startDestination = "speaker") {
|
||||||
composable("speaker") {
|
composable("speaker") {
|
||||||
StartRecordingScreen(
|
StartRecordingScreen(
|
||||||
context = context,
|
context = context,
|
||||||
@@ -125,7 +114,7 @@ fun WearApp() {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class MainState(private val activity: Activity, private val requestPermission: (
|
|||||||
when (appState) {
|
when (appState) {
|
||||||
is AppState.Ready -> when {
|
is AppState.Ready -> when {
|
||||||
(ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) -> {
|
(ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) -> {
|
||||||
Log.e(TAG, "Permissions granted, continuing to record");
|
Log.e(TAG, "Permissions granted, continuing to record")
|
||||||
appState = AppState.Recording
|
appState = AppState.Recording
|
||||||
|
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ class MainState(private val activity: Activity, private val requestPermission: (
|
|||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
Log.e(TAG, "Requesting permissions");
|
Log.e(TAG, "Requesting permissions")
|
||||||
requestPermission()
|
requestPermission()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ private suspend fun record(activity: (Activity),soundRecorder: SoundRecorder,
|
|||||||
|
|
||||||
coroutineScope { // Kick off a parallel job to
|
coroutineScope { // Kick off a parallel job to
|
||||||
|
|
||||||
Log.e(TAG, "Start recording"); //
|
Log.e(TAG, "Start recording") //
|
||||||
val recordingJob = launch { soundRecorder.record() }
|
val recordingJob = launch { soundRecorder.record() }
|
||||||
// val recordingJob = launch { soundRecorder.record() }
|
// val recordingJob = launch { soundRecorder.record() }
|
||||||
// SoundRecorder.record();
|
// SoundRecorder.record();
|
||||||
@@ -142,7 +142,7 @@ private suspend fun record(activity: (Activity),soundRecorder: SoundRecorder,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object channelCallback : ChannelClient.ChannelCallback() {
|
object channelCallback : ChannelCallback() {
|
||||||
override fun onChannelOpened(channel: ChannelClient.Channel) {
|
override fun onChannelOpened(channel: ChannelClient.Channel) {
|
||||||
super.onChannelOpened(channel)
|
super.onChannelOpened(channel)
|
||||||
Log.e(TAG,"Opened channel")}
|
Log.e(TAG,"Opened channel")}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.birdsounds.identify.presentation
|
||||||
|
|
||||||
|
import com.google.android.gms.wearable.MessageEvent
|
||||||
|
import com.google.android.gms.wearable.WearableListenerService
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
|
|
||||||
|
class MessageListenerService : WearableListenerService() {
|
||||||
|
private val tag = "MessageListenerService"
|
||||||
|
|
||||||
|
override fun onMessageReceived(p0: MessageEvent) {
|
||||||
|
super.onMessageReceived(p0)
|
||||||
|
val t_scored = ByteBuffer.wrap(p0.data).getLong()
|
||||||
|
var byte_strings: ByteArray = p0.data.copyOfRange(8, p0.data.size)
|
||||||
|
var score_species_string = byte_strings.decodeToString()
|
||||||
|
var list_strings: List<String> = score_species_string.split(';')
|
||||||
|
list_strings.map({
|
||||||
|
var split_str = it.split(',')
|
||||||
|
if (split_str.size == 2) {
|
||||||
|
var out = AScore(split_str[0], split_str[1].toFloat(), t_scored)
|
||||||
|
if (out.score > 0.05) {
|
||||||
|
SpeciesList.add_observation(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
MessageSender.messageLog.add(t_scored)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,8 @@ package com.birdsounds.identify.presentation
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import co.touchlab.stately.collections.ConcurrentMutableCollection
|
|
||||||
import co.touchlab.stately.collections.ConcurrentMutableList
|
|
||||||
import co.touchlab.stately.collections.ConcurrentMutableSet
|
import co.touchlab.stately.collections.ConcurrentMutableSet
|
||||||
import com.google.android.gms.tasks.Tasks
|
import com.google.android.gms.tasks.Tasksx
|
||||||
import com.google.android.gms.wearable.Wearable
|
import com.google.android.gms.wearable.Wearable
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -14,6 +12,7 @@ import kotlinx.coroutines.Job
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
|
|
||||||
|
|
||||||
object MessageSender {
|
object MessageSender {
|
||||||
const val tag = "MessageSender"
|
const val tag = "MessageSender"
|
||||||
private val job = Job()
|
private val job = Job()
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ import android.content.Context
|
|||||||
import android.media.AudioFormat
|
import android.media.AudioFormat
|
||||||
import android.media.AudioRecord
|
import android.media.AudioRecord
|
||||||
import android.media.MediaRecorder
|
import android.media.MediaRecorder
|
||||||
import android.os.Message
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.RequiresPermission
|
import androidx.annotation.RequiresPermission
|
||||||
import co.touchlab.stately.collections.ConcurrentMutableCollection
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
@@ -46,7 +44,7 @@ class SoundRecorder(
|
|||||||
// Log.w(TAG, buffer_size.toString())
|
// Log.w(TAG, buffer_size.toString())
|
||||||
|
|
||||||
val bufferSizeInBytes =
|
val bufferSizeInBytes =
|
||||||
sampleRateInHz * 3 * 2; // 3 second sample, 2 bytes for each sample
|
sampleRateInHz * 3 * 2 // 3 second sample, 2 bytes for each sample
|
||||||
|
|
||||||
val chunk_size = 2 * sampleRateInHz / 4 // 250ms segments, 2 bytes for each sample
|
val chunk_size = 2 * sampleRateInHz / 4 // 250ms segments, 2 bytes for each sample
|
||||||
val num_chunks: Int = bufferSizeInBytes / chunk_size
|
val num_chunks: Int = bufferSizeInBytes / chunk_size
|
||||||
@@ -62,25 +60,44 @@ class SoundRecorder(
|
|||||||
)
|
)
|
||||||
audioRecord.startRecording()
|
audioRecord.startRecording()
|
||||||
|
|
||||||
|
|
||||||
val thread = Thread {
|
val thread = Thread {
|
||||||
var num_interactions = 0
|
// var sent_first: Boolean = false
|
||||||
|
var ignore_warmup: Boolean = true
|
||||||
|
|
||||||
|
var num_chunked_since_last_send = 0
|
||||||
var last_tstamp: Long = Instant.now().toEpochMilli()
|
var last_tstamp: Long = Instant.now().toEpochMilli()
|
||||||
|
var do_send_message: Boolean = false
|
||||||
while (true) {
|
while (true) {
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
// check for the interrupted flag, reset it, and throw exception
|
// check for the interrupted flag, reset it, and throw exception
|
||||||
Log.w(TAG, "Finished thread");
|
Log.w(TAG, "Finished thread")
|
||||||
break;
|
break
|
||||||
}
|
}
|
||||||
chunk_index = chunk_index.mod(num_chunks)
|
chunk_index = chunk_index.mod(num_chunks)
|
||||||
val out = audioRecord.read(
|
val out = audioRecord.read(
|
||||||
/* audioData = */ chunked_audio_bytes[chunk_index],
|
/* audioData = */ chunked_audio_bytes[chunk_index],
|
||||||
/* offsetInBytes = */ 0,
|
/* offsetInBytes = */ 0,
|
||||||
/* sizeInBytes = */ chunk_size,
|
/* sizeInBytes = */ chunk_size,
|
||||||
/* readMode = */ android.media.AudioRecord.READ_BLOCKING
|
/* readMode = */ AudioRecord.READ_BLOCKING
|
||||||
)
|
)
|
||||||
|
num_chunked_since_last_send += 1
|
||||||
|
|
||||||
chunk_index += 1;
|
do_send_message = false
|
||||||
if ((last_tstamp in MessageSender.messageLog) || (num_interactions == 0)) {
|
if (num_chunked_since_last_send >= num_chunks) {
|
||||||
|
do_send_message = true
|
||||||
|
Log.w("MSG","sending message because full 3s have passed")
|
||||||
|
} else if ((last_tstamp in MessageSender.messageLog) && (num_chunked_since_last_send>4)) {
|
||||||
|
do_send_message = true
|
||||||
|
Log.w("MSG","Send message because the phone has finished")
|
||||||
|
} else if ((ignore_warmup) && (num_chunked_since_last_send > 2)) {
|
||||||
|
do_send_message = true
|
||||||
|
Log.w("MSG","Sent message because ignoring warmup")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
chunk_index += 1
|
||||||
|
if ((do_send_message)) {
|
||||||
var tstamp: Long = Instant.now().toEpochMilli()
|
var tstamp: Long = Instant.now().toEpochMilli()
|
||||||
val tstamp_buffer = ByteBuffer.allocate(Long.SIZE_BYTES)
|
val tstamp_buffer = ByteBuffer.allocate(Long.SIZE_BYTES)
|
||||||
val tstamp_bytes = tstamp_buffer.putLong(tstamp).array()
|
val tstamp_bytes = tstamp_buffer.putLong(tstamp).array()
|
||||||
@@ -89,24 +106,25 @@ class SoundRecorder(
|
|||||||
for (i in 0..(num_chunks - 1)) {
|
for (i in 0..(num_chunks - 1)) {
|
||||||
var c_index = i + chunk_index
|
var c_index = i + chunk_index
|
||||||
c_index = c_index.mod(num_chunks)
|
c_index = c_index.mod(num_chunks)
|
||||||
strr += c_index.toString();
|
strr += c_index.toString()
|
||||||
strr += ' '
|
strr += ' '
|
||||||
byte_send += chunked_audio_bytes[c_index]
|
byte_send += chunked_audio_bytes[c_index]
|
||||||
}
|
}
|
||||||
// Log.w(TAG, strr)
|
// do_send_message = false;
|
||||||
// Log.w("MSG_SENT",byte_send.sum().toString()+",. "+ tstamp.toString())
|
num_chunked_since_last_send = 0
|
||||||
|
MessageSender.messageLog.clear()
|
||||||
MessageSender.sendMessage("/audio", tstamp_bytes + byte_send, context)
|
MessageSender.sendMessage("/audio", tstamp_bytes + byte_send, context)
|
||||||
last_tstamp = tstamp;
|
last_tstamp = tstamp
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
thread.start();
|
}
|
||||||
|
thread.start()
|
||||||
// thread.join();
|
// thread.join();
|
||||||
|
|
||||||
cont.invokeOnCancellation {
|
cont.invokeOnCancellation {
|
||||||
thread.interrupt();
|
thread.interrupt()
|
||||||
audioRecord.stop();
|
audioRecord.stop()
|
||||||
audioRecord.release()
|
audioRecord.release()
|
||||||
state = State.IDLE
|
state = State.IDLE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.birdsounds.identify.presentation
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
|
||||||
|
class AScore(
|
||||||
|
_species: String,
|
||||||
|
_score: Float,
|
||||||
|
_timestamp: Long
|
||||||
|
|
||||||
|
) {
|
||||||
|
|
||||||
|
var split_stuff = _species.split("_")
|
||||||
|
val species = split_stuff[0]
|
||||||
|
val score = _score
|
||||||
|
val common_name = split_stuff[1]
|
||||||
|
val timestamp = _timestamp
|
||||||
|
fun age(): Long {
|
||||||
|
var tstamp: Long = Instant.now().toEpochMilli()
|
||||||
|
return (tstamp - timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
var tstamp: Long = Instant.now().toEpochMilli()
|
||||||
|
return common_name + "," + species + "," + score.toString() + ", " + (age() / 1000.0).toString() + "s ago"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object SpeciesList {
|
||||||
|
var internal_list = mutableListOf<AScore>()
|
||||||
|
var do_add_observation = false
|
||||||
|
fun add_observation(species_in: AScore) {
|
||||||
|
do_add_observation = false
|
||||||
|
var idx = 0
|
||||||
|
var idx_replace = -1
|
||||||
|
for (i in internal_list)
|
||||||
|
{
|
||||||
|
if (i.species == species_in.species)
|
||||||
|
{
|
||||||
|
do_add_observation = false
|
||||||
|
idx_replace = idx
|
||||||
|
}
|
||||||
|
idx+=1
|
||||||
|
}
|
||||||
|
if (idx_replace >= 0)
|
||||||
|
{
|
||||||
|
Log.w(TAG, "Replacing")
|
||||||
|
internal_list[idx_replace] = species_in
|
||||||
|
} else {
|
||||||
|
internal_list.add(species_in)
|
||||||
|
}
|
||||||
|
// val list_reranked = internal_list.withIndex().sortedBy { it -> it.value.age() }
|
||||||
|
internal_list = internal_list.sortedBy({ (it.age()) }).toMutableList()
|
||||||
|
|
||||||
|
|
||||||
|
Log.w(TAG, internal_list.toString())
|
||||||
|
// internal_list.add(species_in)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
6
wear/src/main/res/layout/layout.xml
Normal file
6
wear/src/main/res/layout/layout.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
Reference in New Issue
Block a user