commit 1c849d1b130d41ce65496a9d895730c41c695adb Author: Bradoki Date: Mon Jul 28 10:53:49 2025 -0300 Criando repositório diff --git a/GestureRecognation/.gitignore b/GestureRecognation/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/GestureRecognation/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/GestureRecognation/.idea/.gitignore b/GestureRecognation/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/GestureRecognation/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/GestureRecognation/.idea/.name b/GestureRecognation/.idea/.name new file mode 100644 index 0000000..01740cf --- /dev/null +++ b/GestureRecognation/.idea/.name @@ -0,0 +1 @@ +Gesture Recognation \ No newline at end of file diff --git a/GestureRecognation/.idea/AndroidProjectSystem.xml b/GestureRecognation/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000..4a53bee --- /dev/null +++ b/GestureRecognation/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/GestureRecognation/.idea/compiler.xml b/GestureRecognation/.idea/compiler.xml new file mode 100644 index 0000000..b86273d --- /dev/null +++ b/GestureRecognation/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/GestureRecognation/.idea/deploymentTargetSelector.xml b/GestureRecognation/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..100cdc9 --- /dev/null +++ b/GestureRecognation/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/GestureRecognation/.idea/deviceManager.xml b/GestureRecognation/.idea/deviceManager.xml new file mode 100644 index 0000000..91f9558 --- /dev/null +++ b/GestureRecognation/.idea/deviceManager.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/GestureRecognation/.idea/gradle.xml b/GestureRecognation/.idea/gradle.xml new file mode 100644 index 0000000..639c779 --- /dev/null +++ b/GestureRecognation/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/GestureRecognation/.idea/migrations.xml b/GestureRecognation/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/GestureRecognation/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/GestureRecognation/.idea/misc.xml b/GestureRecognation/.idea/misc.xml new file mode 100644 index 0000000..b2c751a --- /dev/null +++ b/GestureRecognation/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/GestureRecognation/.idea/runConfigurations.xml b/GestureRecognation/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/GestureRecognation/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/GestureRecognation/.kotlin/errors/errors-1753671614640.log b/GestureRecognation/.kotlin/errors/errors-1753671614640.log new file mode 100644 index 0000000..1219b50 --- /dev/null +++ b/GestureRecognation/.kotlin/errors/errors-1753671614640.log @@ -0,0 +1,4 @@ +kotlin version: 2.0.21 +error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output: + 1. Kotlin compile daemon is ready + diff --git a/GestureRecognation/app/.gitignore b/GestureRecognation/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/GestureRecognation/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/GestureRecognation/app/build.gradle.kts b/GestureRecognation/app/build.gradle.kts new file mode 100644 index 0000000..c9910db --- /dev/null +++ b/GestureRecognation/app/build.gradle.kts @@ -0,0 +1,61 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) +} + +android { + namespace = "com.example.gesturerecognition" + compileSdk = 36 + + defaultConfig { + applicationId = "com.example.gesturerecognation" + minSdk = 24 + targetSdk = 36 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + kotlinOptions { + jvmTarget = "11" + } + buildFeatures { + compose = true + } +} + +dependencies { + implementation(libs.tensorflow.lite) + implementation(libs.tensorflow.lite.support) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) + implementation(libs.androidx.appcompat) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) +} \ No newline at end of file diff --git a/GestureRecognation/app/proguard-rules.pro b/GestureRecognation/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/GestureRecognation/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/GestureRecognation/app/src/androidTest/java/com/example/gesturerecognition/ExampleInstrumentedTest.kt b/GestureRecognation/app/src/androidTest/java/com/example/gesturerecognition/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..6176dc3 --- /dev/null +++ b/GestureRecognation/app/src/androidTest/java/com/example/gesturerecognition/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.gesturerecognition + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.gesturerecognation", appContext.packageName) + } +} \ No newline at end of file diff --git a/GestureRecognation/app/src/main/AndroidManifest.xml b/GestureRecognation/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9bbc900 --- /dev/null +++ b/GestureRecognation/app/src/main/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GestureRecognation/app/src/main/assets/modelo_gestos_uwave.tflite b/GestureRecognation/app/src/main/assets/modelo_gestos_uwave.tflite new file mode 100644 index 0000000..e574786 Binary files /dev/null and b/GestureRecognation/app/src/main/assets/modelo_gestos_uwave.tflite differ diff --git a/GestureRecognation/app/src/main/java/com/example/gesturerecognition/GestureClassifier.kt b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/GestureClassifier.kt new file mode 100644 index 0000000..5bc1c32 --- /dev/null +++ b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/GestureClassifier.kt @@ -0,0 +1,106 @@ +package com.example.gesturerecognition + +import android.content.Context +import org.tensorflow.lite.Interpreter +import java.io.FileInputStream +import java.nio.MappedByteBuffer +import java.nio.channels.FileChannel + +class GestureClassifier(context: Context) { + companion object { + private const val MODEL_PATH = "modelo_gestos_uwave.tflite" + + // Labels dos gestos do dataset uWave (ajuste conforme seu modelo) + private val GESTURE_LABELS = arrayOf( + "Gesto 1", "Gesto 2", "Gesto 3", "Gesto 4", + "Gesto 5", "Gesto 6", "Gesto 7", "Gesto 8" + ) + } + + private var tflite: Interpreter? = null + private var tfliteModel: MappedByteBuffer + + init { + tfliteModel = loadModelFile(context) + + // Configurar opções do interpretador + val options = Interpreter.Options().apply { + setNumThreads(4) // Usar 4 threads para melhor performance + } + + tflite = Interpreter(tfliteModel, options) + + // Log das dimensões do modelo para debug + tflite?.let { + val inputShape = it.getInputTensor(0).shape() + val outputShape = it.getOutputTensor(0).shape() + + println("Input shape: ${inputShape.contentToString()}") + println("Output shape: ${outputShape.contentToString()}") + } + } + + private fun loadModelFile(context: Context): MappedByteBuffer { + val fileDescriptor = context.assets.openFd(MODEL_PATH) + val inputStream = FileInputStream(fileDescriptor.fileDescriptor) + val fileChannel = inputStream.channel + val startOffset = fileDescriptor.startOffset + val declaredLength = fileDescriptor.declaredLength + return fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength) + } + + fun classify(inputData: FloatArray): String { + return tflite?.let { interpreter -> + // Preparar entrada + val input = arrayOf(inputData) + + // Preparar saída + val output = Array(1) { FloatArray(GESTURE_LABELS.size) } + + // Executar inferência + interpreter.run(input, output) + + // Encontrar a classe com maior probabilidade + var predictedClass = 0 + var maxProbability = output[0][0] + + for (i in 1 until output[0].size) { + if (output[0][i] > maxProbability) { + maxProbability = output[0][i] + predictedClass = i + } + } + + // Retornar resultado com confiança + //val confidence = String.format("%.2f%%", maxProbability * 100) + //"${GESTURE_LABELS[predictedClass]} ($confidence)" + GESTURE_LABELS[predictedClass] + } ?: "Erro: Modelo não carregado" + } + + fun classifyWithAllProbabilities(inputData: FloatArray): Map { + return tflite?.let { interpreter -> + // Preparar entrada + val input = arrayOf(inputData) + + // Preparar saída + val output = Array(1) { FloatArray(GESTURE_LABELS.size) } + + // Executar inferência + interpreter.run(input, output) + + // Criar mapa com todas as probabilidades + val results = mutableMapOf() + for (i in GESTURE_LABELS.indices) { + results[GESTURE_LABELS[i]] = output[0][i] + } + + results + } ?: emptyMap() + } + + fun close() { + tflite?.close() + tflite = null + } +} \ No newline at end of file diff --git a/GestureRecognation/app/src/main/java/com/example/gesturerecognition/MainActivity.kt b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/MainActivity.kt new file mode 100644 index 0000000..cfcdef1 --- /dev/null +++ b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/MainActivity.kt @@ -0,0 +1,261 @@ +package com.example.gesturerecognition + +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager +import android.os.Bundle +import android.widget.Button +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity + +class MainActivity : AppCompatActivity(), SensorEventListener { + private lateinit var sensorManager: SensorManager + private var accelerometer: Sensor? = null + private var gestureClassifier: GestureClassifier? = null + private var soundManager: SoundManager? = null // Novo: gerenciador de sons + + private lateinit var statusText: TextView + private lateinit var resultText: TextView + private lateinit var recordButton: Button + + private var isRecording = false + private var accelerometerData: MutableList = mutableListOf() + private var recordingStartTime: Long = 0 + private val windowSize = 315 + private val recordingDurationMs = 2000 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + initializeViews() + initializeSensors() + initializeClassifier() + initializeSoundManager() // Novo: inicializa sistema de sons + setupButtons() + } + + private fun initializeViews() { + statusText = findViewById(R.id.statusText) + resultText = findViewById(R.id.resultText) + recordButton = findViewById(R.id.recordButton) + + statusText.text = "Pressione e segure o botão para gravar" + } + + private fun initializeSensors() { + sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager + accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) + + if (accelerometer == null) { + Toast.makeText(this, "Acelerômetro não disponível", Toast.LENGTH_LONG).show() + finish() + } + } + + private fun initializeClassifier() { + try { + gestureClassifier = GestureClassifier(this) + Toast.makeText(this, "Modelo carregado com sucesso", Toast.LENGTH_SHORT).show() + } catch (e: Exception) { + Toast.makeText(this, "Erro ao carregar modelo: ${e.message}", Toast.LENGTH_LONG).show() + e.printStackTrace() + } + } + + // Novo: Inicializa o sistema de sons + private fun initializeSoundManager() { + try { + soundManager = SoundManager(this) + Toast.makeText(this, "Sistema de áudio inicializado", Toast.LENGTH_SHORT).show() + } catch (e: Exception) { + Toast.makeText(this, "Erro ao inicializar áudio: ${e.message}", Toast.LENGTH_LONG).show() + e.printStackTrace() + } + } + + private fun setupButtons() { + recordButton.setOnTouchListener { _, event -> + when (event.action) { + android.view.MotionEvent.ACTION_DOWN -> { + startRecording() + recordButton.performClick() // Para acessibilidade + true + } + android.view.MotionEvent.ACTION_UP, + android.view.MotionEvent.ACTION_CANCEL -> { + stopRecording() + true + } + else -> false + } + } + } + + private fun startRecording() { + accelerometerData.clear() + isRecording = true + recordingStartTime = System.currentTimeMillis() + + accelerometer?.let { + sensorManager.registerListener(this, it, SensorManager.SENSOR_DELAY_FASTEST) + } + + statusText.text = "🔴 Gravando... Faça o gesto!" + recordButton.text = "GRAVANDO" + resultText.text = "" + } + + private fun stopRecording() { + if (!isRecording) return + + isRecording = false + sensorManager.unregisterListener(this) + + recordButton.text = "GRAVAR" + statusText.text = "Processando dados..." + + if (accelerometerData.size >= 60) { + classifyGesture() + } else { + statusText.text = "Gesto muito curto. Tente novamente." + // Removido: som de erro não é mais necessário + + recordButton.postDelayed({ + statusText.text = "Pressione e segure o botão para gravar" + }, 2000) + } + } + + private fun classifyGesture() { + try { + val inputData = prepareInputData() + val result = gestureClassifier?.classify(inputData) ?: "Erro no classificador" + + statusText.text = "Classificação concluída" + resultText.text = "Gesto reconhecido: $result" + + // Reproduz som baseado no gesto reconhecido + playGestureSound(result) + + recordButton.postDelayed({ + statusText.text = "Pressione e segure o botão para gravar" + }, 3000) + + } catch (e: Exception) { + statusText.text = "Erro na classificação" + resultText.text = "Erro: ${e.message}" + + // Removido: som de erro não é mais necessário + e.printStackTrace() + } + } + + // Função para reproduzir som baseado no gesto + private fun playGestureSound(gestureResult: String) { + soundManager?.let { manager -> + val gestureId = manager.mapGestureResultToId(gestureResult) + + // Log detalhado para debug + when (gestureId) { + "gesture1" -> println("🛑 Parando todos os sons") + "gesture2" -> println("🔊 Tocando Eraser (uma vez)") + "gesture3" -> println("💥 Tocando Explosion (uma vez)") + "gesture4" -> println("🎵 Tocando Do It Again Baby (uma vez)") + "gesture7" -> println("🎼 Iniciando loop: brush_beat_brazil") + "gesture8" -> println("🎼 Iniciando loop: loose_brazil2") + "unknown" -> println("❓ Gesto não reconhecido: $gestureResult") + } + + manager.playGestureSound(gestureId) + } + } + + private fun prepareInputData(): FloatArray { + val totalFeatures = windowSize * 3 + val input = FloatArray(totalFeatures) + + val availableDataSize = accelerometerData.size + val availableSamples = availableDataSize / 3 + + if (availableSamples >= windowSize) { + val step = availableSamples.toFloat() / windowSize + var dataIndex = 0 + + for (i in 0 until windowSize) { + val sampleIndex = (i * step).toInt() * 3 + if (sampleIndex + 2 < availableDataSize) { + input[dataIndex++] = accelerometerData[sampleIndex] + input[dataIndex++] = accelerometerData[sampleIndex + 1] + input[dataIndex++] = accelerometerData[sampleIndex + 2] + } + } + } else { + var inputIndex = 0 + val repetitions = windowSize / availableSamples + val remainder = windowSize % availableSamples + + for (rep in 0 until repetitions) { + for (i in 0 until availableDataSize step 3) { + if (inputIndex + 2 < totalFeatures && i + 2 < availableDataSize) { + input[inputIndex++] = accelerometerData[i] + input[inputIndex++] = accelerometerData[i + 1] + input[inputIndex++] = accelerometerData[i + 2] + } + } + } + + for (i in 0 until remainder * 3 step 3) { + if (inputIndex + 2 < totalFeatures && i + 2 < availableDataSize) { + input[inputIndex++] = accelerometerData[i] + input[inputIndex++] = accelerometerData[i + 1] + input[inputIndex++] = accelerometerData[i + 2] + } + } + } + + normalizeData(input) + return input + } + + private fun normalizeData(data: FloatArray) { + for (i in data.indices) { + data[i] = data[i] / 9.8f + } + } + + override fun onSensorChanged(event: SensorEvent?) { + if (isRecording && event?.sensor?.type == Sensor.TYPE_ACCELEROMETER) { + event.values?.let { values -> + accelerometerData.add(values[0]) + accelerometerData.add(values[1]) + accelerometerData.add(values[2]) + + val sampleCount = accelerometerData.size / 3 + statusText.text = "🔴 Gravando... $sampleCount amostras" + } + } + } + + override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} + + // Novo: Gerencia o ciclo de vida do áudio + override fun onPause() { + super.onPause() + soundManager?.stopAllSounds() + } + + override fun onResume() { + super.onResume() + soundManager?.resumeAllSounds() + } + + override fun onDestroy() { + super.onDestroy() + gestureClassifier?.close() + sensorManager.unregisterListener(this) + soundManager?.release() // Novo: libera recursos de áudio + } +} \ No newline at end of file diff --git a/GestureRecognation/app/src/main/java/com/example/gesturerecognition/SoundManager.kt b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/SoundManager.kt new file mode 100644 index 0000000..00aa973 --- /dev/null +++ b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/SoundManager.kt @@ -0,0 +1,217 @@ +package com.example.gesturerecognition + +import android.content.Context +import android.media.AudioAttributes +import android.media.SoundPool +import android.util.Log + +class SoundManager(private val context: Context) { + private var soundPool: SoundPool? = null + private val soundMap = mutableMapOf() + private val TAG = "SoundManager" + + // Máximo de sons simultâneos + private val maxStreams = 10 + + // Controle de loops (sons que ficam tocando) + private var currentLoopStreamId: Int? = null + private var currentLoopGesture: String? = null + + init { + initializeSoundPool() + loadSounds() + } + + private fun initializeSoundPool() { + val audioAttributes = AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .build() + + soundPool = SoundPool.Builder() + .setMaxStreams(maxStreams) + .setAudioAttributes(audioAttributes) + .build() + } + + private fun loadSounds() { + try { + // Carrega os sons específicos do usuário + loadSound("gesture2", R.raw.eraser) + loadSound("gesture3", R.raw.explosion) + loadSound("gesture4", R.raw.do_it_again_baby) + loadSound("gesture7", R.raw.brush_beat_brazil) + loadSound("gesture8", R.raw.loose_brazil2) + + Log.d(TAG, "Todos os sons carregados com sucesso") + + } catch (e: Exception) { + Log.e(TAG, "Erro ao carregar sons: ${e.message}") + } + } + + private fun loadSound(gestureId: String, resourceId: Int) { + try { + soundPool?.let { pool -> + val soundId = pool.load(context, resourceId, 1) + soundMap[gestureId] = soundId + Log.d(TAG, "Som carregado: $gestureId -> $soundId") + } + } catch (e: Exception) { + Log.e(TAG, "Erro ao carregar som $gestureId: ${e.message}") + } + } + + /** + * Reproduz o som correspondente ao gesto reconhecido + * Gesto 1: Para todos os sons + * Gestos 2, 3, 4: Tocam uma vez + * Gestos 7, 8: Tocam em loop (um por vez) + */ + fun playGestureSound(gestureId: String, volume: Float = 1.0f) { + try { + when (gestureId) { + "gesture1" -> { + // Gesto 1: Para todos os sons + stopAllSounds() + Log.d(TAG, "Todos os sons parados pelo gesto 1") + } + + "gesture7", "gesture8" -> { + // Gestos 7 e 8: Loop (um por vez) + playLoopSound(gestureId, volume) + } + + "gesture2", "gesture3", "gesture4" -> { + // Gestos 2, 3, 4: Tocam uma vez + playOneShotSound(gestureId, volume) + } + + else -> { + Log.w(TAG, "Gesto não mapeado: $gestureId") + } + } + } catch (e: Exception) { + Log.e(TAG, "Erro ao reproduzir som para gesto $gestureId: ${e.message}") + } + } + + /** + * Toca sons que devem tocar apenas uma vez + */ + private fun playOneShotSound(gestureId: String, volume: Float) { + val soundId = soundMap[gestureId] + + if (soundId != null) { + soundPool?.play(soundId, volume, volume, 1, 0, 1.0f) // loop = 0 (uma vez) + Log.d(TAG, "Som one-shot reproduzido: $gestureId") + } else { + Log.w(TAG, "Som não encontrado para gesto: $gestureId") + } + } + + /** + * Toca sons em loop (gestos 7 e 8) + * Para o loop anterior e inicia o novo + */ + private fun playLoopSound(gestureId: String, volume: Float) { + val soundId = soundMap[gestureId] + + if (soundId != null) { + // Se já há um loop tocando, para ele + currentLoopStreamId?.let { streamId -> + soundPool?.stop(streamId) + Log.d(TAG, "Loop anterior parado: $currentLoopGesture") + } + + // Inicia novo loop + val streamId = soundPool?.play(soundId, volume, volume, 1, -1, 1.0f) // loop = -1 (infinito) + currentLoopStreamId = streamId + currentLoopGesture = gestureId + + Log.d(TAG, "Novo loop iniciado: $gestureId") + } else { + Log.w(TAG, "Som não encontrado para gesto: $gestureId") + } + } + + /** + * Para todos os sons que estão tocando + */ + fun stopAllSounds() { + try { + // Para o loop atual se existir + currentLoopStreamId?.let { streamId -> + soundPool?.stop(streamId) + Log.d(TAG, "Loop parado: $currentLoopGesture") + } + currentLoopStreamId = null + currentLoopGesture = null + + // Para todos os outros sons + soundPool?.autoPause() + Log.d(TAG, "Todos os sons parados") + } catch (e: Exception) { + Log.e(TAG, "Erro ao parar sons: ${e.message}") + } + } + + /** + * Resume todos os sons pausados (não reativa loops parados) + */ + fun resumeAllSounds() { + try { + soundPool?.autoResume() + Log.d(TAG, "Sons one-shot resumidos") + } catch (e: Exception) { + Log.e(TAG, "Erro ao resumir sons: ${e.message}") + } + } + + /** + * Ajusta o volume de todos os sons + */ + fun setVolume(volume: Float) { + try { + soundMap.values.forEach { soundId -> + soundPool?.setVolume(soundId, volume, volume) + } + Log.d(TAG, "Volume ajustado para: $volume") + } catch (e: Exception) { + Log.e(TAG, "Erro ao ajustar volume: ${e.message}") + } + } + + /** + * Libera recursos do SoundPool + */ + fun release() { + try { + stopAllSounds() + soundPool?.release() + soundPool = null + soundMap.clear() + currentLoopStreamId = null + currentLoopGesture = null + Log.d(TAG, "SoundManager liberado") + } catch (e: Exception) { + Log.e(TAG, "Erro ao liberar SoundManager: ${e.message}") + } + } + + /** + * Mapeia o resultado do classificador para ID do gesto + * Ajuste esta função de acordo com os resultados do seu modelo + */ + fun mapGestureResultToId(gestureResult: String): String { + return when (gestureResult.lowercase().trim()) { + "1", "gesture1", "gesto 1" -> "gesture1" // Para todos os sons + "2", "gesture2", "gesto 2" -> "gesture2" // Eraser (uma vez) + "3", "gesture3", "gesto 3" -> "gesture3" // Explosion (uma vez) + "4", "gesture4", "gesto 4" -> "gesture4" // Do It Again Baby (uma vez) + "7", "gesture7", "gesto 7" -> "gesture7" // brush_beat_brazil (loop) + "8", "gesture8", "gesto 8"-> "gesture8" // loose_brazil2 (loop) + else -> "unknown" // Não faz nada para gestos não mapeados + } + } +} \ No newline at end of file diff --git a/GestureRecognation/app/src/main/java/com/example/gesturerecognition/ui/theme/Color.kt b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/ui/theme/Color.kt new file mode 100644 index 0000000..177b7ea --- /dev/null +++ b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.example.gesturerecognition.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/GestureRecognation/app/src/main/java/com/example/gesturerecognition/ui/theme/Theme.kt b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/ui/theme/Theme.kt new file mode 100644 index 0000000..53a8ea2 --- /dev/null +++ b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/ui/theme/Theme.kt @@ -0,0 +1,57 @@ +package com.example.gesturerecognition.ui.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun GestureRecognationTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/GestureRecognation/app/src/main/java/com/example/gesturerecognition/ui/theme/Type.kt b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/ui/theme/Type.kt new file mode 100644 index 0000000..38531a8 --- /dev/null +++ b/GestureRecognation/app/src/main/java/com/example/gesturerecognition/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.example.gesturerecognition.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/GestureRecognation/app/src/main/res/drawable/ic_launcher_background.xml b/GestureRecognation/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/GestureRecognation/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GestureRecognation/app/src/main/res/drawable/ic_launcher_foreground.xml b/GestureRecognation/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/GestureRecognation/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/GestureRecognation/app/src/main/res/drawable/record_button_background.xml b/GestureRecognation/app/src/main/res/drawable/record_button_background.xml new file mode 100644 index 0000000..4352182 --- /dev/null +++ b/GestureRecognation/app/src/main/res/drawable/record_button_background.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/GestureRecognation/app/src/main/res/layout/activity_main.xml b/GestureRecognation/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..c0ad1ca --- /dev/null +++ b/GestureRecognation/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + +