Implement Wake Word Detection Component
Resolved 💬 2 comments Opened Oct 20, 2025 by sanchitmonga22 Closed Oct 20, 2025
Priority
CRITICAL - Production Blocker
Context
Wake word detection is essential for building always-listening voice assistants. The iOS SDK has a complete wake word implementation using Porcupine/OpenWakeWord. The Kotlin SDK is completely missing this component.
Current State
- Status: Component does not exist
- Impact: Cannot build always-listening voice features
- iOS Has: Full implementation with multiple engine support
iOS Reference Implementation
- Component:
sdk/runanywhere-swift/Sources/RunAnywhere/Components/WakeWord/WakeWordComponent.swift - Services:
sdk/runanywhere-swift/Sources/RunAnywhere/Components/WakeWord/Services/WakeWordService.swift - Models:
sdk/runanywhere-swift/Sources/RunAnywhere/Components/WakeWord/Models/
iOS Architecture Pattern
public final class WakeWordComponent: ModelBasedComponent {
private var service: WakeWordService?
public func startListening() async throws
public func stopListening() async
public var detectionStream: AsyncStream<WakeWordDetection> { get }
}
public protocol WakeWordService: Sendable {
func startDetection(audioStream: AsyncStream<Data>) async throws
var detections: AsyncStream<WakeWordDetection> { get }
}
Implementation Plan
1. Define Common Interfaces (commonMain)
// commonMain/kotlin/com/runanywhere/sdk/components/wakeword/WakeWordComponent.kt
class WakeWordComponent(
configuration: WakeWordConfiguration,
serviceContainer: ServiceContainer? = null
) : BaseComponent<WakeWordService>(configuration, serviceContainer) {
suspend fun startListening()
suspend fun stopListening()
val detectionStream: Flow<WakeWordDetection>
override suspend fun createService(): WakeWordService {
val provider = ModuleRegistry.wakeWordProvider(configuration.modelId)
return provider?.createWakeWordService(configuration)
?: throw SDKError.ComponentNotAvailable("No wake word provider")
}
}
// commonMain/kotlin/com/runanywhere/sdk/components/wakeword/services/WakeWordService.kt
interface WakeWordService {
suspend fun startDetection(audioStream: Flow<ByteArray>)
suspend fun stopDetection()
val detections: Flow<WakeWordDetection>
}
// commonMain/kotlin/com/runanywhere/sdk/components/wakeword/models/WakeWordConfiguration.kt
data class WakeWordConfiguration(
val modelId: String,
val keywords: List<String>,
val sensitivity: Float = 0.5f,
val audioFormat: AudioFormat = AudioFormat.PCM_16BIT
) : ComponentConfiguration
data class WakeWordDetection(
val keyword: String,
val confidence: Float,
val timestamp: Long
)
2. Platform Implementations
Android (androidMain)
// androidMain/kotlin/com/runanywhere/sdk/components/wakeword/services/AndroidWakeWordService.kt
class AndroidWakeWordService(
private val context: Context,
private val configuration: WakeWordConfiguration
) : WakeWordService {
// Use Porcupine or Snowboy for Android
private var porcupineManager: PorcupineManager? = null
override suspend fun startDetection(audioStream: Flow<ByteArray>) {
// Initialize wake word engine
porcupineManager = PorcupineManager.Builder()
.setKeyword(configuration.keywords.first())
.setSensitivity(configuration.sensitivity)
.build(context) { keywordIndex ->
// Emit detection
}
porcupineManager?.start()
}
override suspend fun stopDetection() {
porcupineManager?.stop()
porcupineManager?.delete()
}
}
JVM (jvmMain)
// jvmMain/kotlin/com/runanywhere/sdk/components/wakeword/services/JvmWakeWordService.kt
class JvmWakeWordService(
private val configuration: WakeWordConfiguration
) : WakeWordService {
// Use Vosk or similar for JVM
override suspend fun startDetection(audioStream: Flow<ByteArray>) {
// Implementation using Vosk keyword spotting
}
override suspend fun stopDetection() {
// Cleanup
}
}
3. Provider Registration
// commonMain/kotlin/com/runanywhere/sdk/core/ModuleRegistry.kt
interface WakeWordServiceProvider {
suspend fun createWakeWordService(configuration: WakeWordConfiguration): WakeWordService
fun canHandle(modelId: String?): Boolean
val name: String
}
object ModuleRegistry {
private val wakeWordProviders = CopyOnWriteArrayList<WakeWordServiceProvider>()
fun registerWakeWord(provider: WakeWordServiceProvider) {
wakeWordProviders.add(provider)
}
fun wakeWordProvider(modelId: String? = null): WakeWordServiceProvider? {
return wakeWordProviders.firstOrNull { it.canHandle(modelId) }
}
}
Files to Create/Modify
New Files (commonMain)
- [ ]
components/wakeword/WakeWordComponent.kt - [ ]
components/wakeword/services/WakeWordService.kt - [ ]
components/wakeword/models/WakeWordConfiguration.kt - [ ]
components/wakeword/models/WakeWordDetection.kt - [ ]
components/wakeword/providers/WakeWordServiceProvider.kt
New Files (androidMain)
- [ ]
components/wakeword/services/AndroidWakeWordService.kt
New Files (jvmMain)
- [ ]
components/wakeword/services/JvmWakeWordService.kt
Files to Modify
- [ ]
core/ModuleRegistry.kt(add wake word provider registration) - [ ]
events/ComponentEvent.kt(add wake word events)
Dependencies Required
Android
- Porcupine Wake Word:
ai.picovoice:porcupine-android:3.0.0 - OR Snowboy: Custom integration
JVM
- Vosk:
com.alphacephei:vosk:0.3.45 - OR OpenWakeWord: Custom integration
Testing Requirements
- [ ] Unit tests for WakeWordComponent lifecycle
- [ ] Integration tests with mock audio stream
- [ ] Android platform tests with Porcupine
- [ ] JVM platform tests with Vosk
- [ ] Multi-keyword detection tests
- [ ] Sensitivity tuning tests
- [ ] Audio stream interruption handling
Success Criteria
- [ ] WakeWordComponent can detect configured keywords
- [ ] Android implementation uses Porcupine
- [ ] JVM implementation uses Vosk
- [ ] Detection stream emits events with confidence scores
- [ ] Proper lifecycle management (start/stop)
- [ ] Thread-safe provider registration
- [ ] Low latency detection (<500ms)
- [ ] Low false positive rate
Estimated Effort
3-4 weeks (150-200 hours)
- Component architecture: 2-3 days
- Android implementation (Porcupine integration): 5-7 days
- JVM implementation (Vosk integration): 5-7 days
- Testing and tuning: 5-7 days
- Documentation: 2 days
Dependencies
- Depends on: #142 (thread safety fixes)
- Depends on: #147 (privacy-first logging)
- Depends on: #152 (Audio Capture Infrastructure)
- Blocks: Voice Agent Component (#151)
Related Issues
- Part of always-listening voice assistant roadmap
- Required for hands-free voice activation
- Aligns with iOS wake word architecture
🤖 Generated with Claude Code
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗