From 42c69a7db40923cacfc6368a6d0627b2467c99c0 Mon Sep 17 00:00:00 2001 From: GitHub Agent Date: Thu, 16 Jul 2026 18:01:40 +0530 Subject: [PATCH] Solution for #133: Feature: Add SenseVoice for private local transcription --- .../audio/src/androidMain/AndroidManifest.xml | 34 ++++++++ .../androidMain/kotlin/SenseVoiceService.kt | 24 ++++++ .../androidMain/kotlin/SettingsActivity.kt | 17 ++++ path/to/sensevoice/kotlin/SenseVoice.kt | 85 +++++++++++++++++++ 4 files changed, 160 insertions(+) create mode 100644 path/to/core/audio/src/androidMain/AndroidManifest.xml create mode 100644 path/to/core/audio/src/androidMain/kotlin/SenseVoiceService.kt create mode 100644 path/to/core/audio/src/androidMain/kotlin/SettingsActivity.kt create mode 100644 path/to/sensevoice/kotlin/SenseVoice.kt diff --git a/path/to/core/audio/src/androidMain/AndroidManifest.xml b/path/to/core/audio/src/androidMain/AndroidManifest.xml new file mode 100644 index 00000000..eda15ace --- /dev/null +++ b/path/to/core/audio/src/androidMain/AndroidManifest.xml @@ -0,0 +1,34 @@ +// AndroidManifest.xml + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/path/to/core/audio/src/androidMain/kotlin/SenseVoiceService.kt b/path/to/core/audio/src/androidMain/kotlin/SenseVoiceService.kt new file mode 100644 index 00000000..f825d745 --- /dev/null +++ b/path/to/core/audio/src/androidMain/kotlin/SenseVoiceService.kt @@ -0,0 +1,24 @@ +// SenseVoiceService.kt +import android.content.Intent +import android.os.IBinder +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.example.notelyvoice.SenseVoice + +class SenseVoiceService : Service() { + private lateinit var senseVoice: SenseVoice + + override fun onBind(intent: Intent): IBinder? { + return null + } + + override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { + senseVoice = SenseVoice.create(applicationContext, "/sdcard/audio.wav", "fsmn-vad", "ct-punc") + senseVoice.startRecording() + return START_STICKY + } + + override fun onDestroy() { + super.onDestroy() + senseVoice.stopRecording() + } +} \ No newline at end of file diff --git a/path/to/core/audio/src/androidMain/kotlin/SettingsActivity.kt b/path/to/core/audio/src/androidMain/kotlin/SettingsActivity.kt new file mode 100644 index 00000000..ee425658 --- /dev/null +++ b/path/to/core/audio/src/androidMain/kotlin/SettingsActivity.kt @@ -0,0 +1,17 @@ +// SettingsActivity.kt +import android.content.Intent +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle +import com.example.notelyvoice.R + +class SettingsActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_settings) + + // Start SenseVoice service + val intent = Intent(this, SenseVoiceService::class.java) + startService(intent) + } +} \ No newline at end of file diff --git a/path/to/sensevoice/kotlin/SenseVoice.kt b/path/to/sensevoice/kotlin/SenseVoice.kt new file mode 100644 index 00000000..876751ae --- /dev/null +++ b/path/to/sensevoice/kotlin/SenseVoice.kt @@ -0,0 +1,85 @@ +// SenseVoice.kt +import android.content.Context +import android.media.AudioFormat +import android.media.AudioRecord +import android.media.MediaRecorder +import android.os.Build +import androidx.annotation.RequiresApi +import com.google.protobuf.ByteString +import java.io.File +import java.io.IOException +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.util.* +import kotlin.concurrent.thread + +class SenseVoice private constructor( + private val context: Context, + private val audioFilePath: String, + private val vadModel: String, + private val puncModel: String +) { + private var audioRecord: AudioRecord? = null + private var isRecording = false + + fun startRecording() { + if (isRecording) { + return + } + isRecording = true + audioRecord = AudioRecord( + MediaRecorder.AudioSource.MIC, + 16000, + AudioFormat.CHANNEL_IN_MONO, + AudioFormat.ENCODING_PCM_16BIT, + 1024 * 10 + ) + audioRecord?.apply { + startRecording() + thread { + while (isRecording) { + val buffer = ByteArray(1024) + read(buffer, 0, buffer.size) + val audioData = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN) + val audioBytes = audioData.array() + val audioString = String(audioBytes.map { it.toInt() }.toTypedArray()) + val audioBytesString = ByteString.copyFrom(audioBytes) + // Process audio data using SenseVoice model + processAudioData(audioBytesString) + } + } + } + } + + fun stopRecording() { + isRecording = false + audioRecord?.apply { + stop() + release() + } + } + + private fun processAudioData(audioBytesString: ByteString) { + // Use SenseVoice model to transcribe audio data + val model = AutoModel(model = "iic/SenseVoiceSmall", vad_model = vadModel, punc_model = puncModel) + val result = model.generate(input = audioBytesString) + // Process transcription result + processTranscriptionResult(result) + } + + private fun processTranscriptionResult(result: String) { + // Save transcription result to file + val transcriptionFile = File(audioFilePath + "_transcription.txt") + try { + transcriptionFile.writeText(result) + } catch (e: IOException) { + e.printStackTrace() + } + } + + companion object { + fun create(context: Context, audioFilePath: String, vadModel: String, puncModel: String): SenseVoice { + return SenseVoice(context, audioFilePath, vadModel, puncModel) + } + } +} \ No newline at end of file