feat(clients): event sound effects + optional text-to-speech
Add audible cues and optional spoken announcements for session events (join/leave, channel + PM sent/recv, login, logout/connection-lost, mic on/off, voice-activity, PTT) across all three clients, driven off the shared C ABI vc_event stream so the mapping stays consistent. TTS is off by default; when enabled it announces events and reads message/PM bodies aloud. Master toggles + a sound-volume slider; the per-utterance voice-activity and PTT cues default off. WAVs ship from assets/sounds/. Windows (built + verified): new VoiceCat.App/Notifications/ layer (FeedbackSettings -> %AppData%\VoiceCat\feedback.json, SoundPlayerPool via System.Media.SoundPlayer, SpeechAnnouncer via Prismatoid 0.3.0, EventFeedback dispatcher); MainForm hooks; NotificationSettingsForm under Settings > Notifications; csproj adds the Prismatoid PackageRef and copies the WAVs into sounds\. macOS + iOS (written, not yet built -- needs a Mac): shared VoiceCatCore/Feedback/ (SoundEvent, EventFeedback = AVAudioPlayer pool + native AVSpeechSynthesizer, FeedbackSettings over UserDefaults); WAVs bundled via Package.swift resources (.process). Hooks in SessionState/ AppState (iOS) and MainWindowController (macOS); settings UI in SettingsView (iOS) and SettingsWindowController (macOS). No core/server code touched; ctest --preset dev unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,14 @@ struct SettingsView: View {
|
||||
@StateObject private var router = IOSAudioRouter.shared
|
||||
@State private var showAdvanced = false
|
||||
|
||||
// Notification feedback prefs — keys shared with VoiceCatCore's FeedbackSettings, so the
|
||||
// EventFeedback player reads the same values these toggles write.
|
||||
@AppStorage("feedback.sounds") private var soundsEnabled = true
|
||||
@AppStorage("feedback.speech") private var speechEnabled = false
|
||||
@AppStorage("feedback.volume") private var soundsVolume = 1.0
|
||||
@AppStorage("feedback.selfTalk") private var selfTalkEnabled = false
|
||||
@AppStorage("feedback.ptt") private var pttSoundEnabled = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
@@ -217,6 +225,27 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Notifications
|
||||
Section("Notifications") {
|
||||
Toggle("Event sounds", isOn: $soundsEnabled)
|
||||
.accessibilityLabel("Play event sounds")
|
||||
if soundsEnabled {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Sound volume")
|
||||
.font(.caption)
|
||||
Slider(value: $soundsVolume, in: 0...1)
|
||||
.accessibilityLabel("Sound volume")
|
||||
}
|
||||
}
|
||||
Toggle("Speak events (text-to-speech)", isOn: $speechEnabled)
|
||||
.accessibilityLabel("Speak events")
|
||||
.accessibilityHint("Announces joins and leaves and reads message text aloud.")
|
||||
Toggle("Your own voice-activity sounds", isOn: $selfTalkEnabled)
|
||||
.accessibilityLabel("Voice activity sounds")
|
||||
Toggle("Push-to-talk cue", isOn: $pttSoundEnabled)
|
||||
.accessibilityLabel("Push to talk cue")
|
||||
}
|
||||
|
||||
// MARK: - Admin
|
||||
if session.permissions.canAdminAccounts || session.permissions.isAdmin {
|
||||
Section("Administration") {
|
||||
|
||||
Reference in New Issue
Block a user