Add push-to-talk voice notes functionality

- Added voice note support to BitchatMessage with audio data and duration
- Updated binary protocol to encode/decode voice notes (flag 0x40)
- Created AudioRecordingService for recording up to 10s voice clips
- Created AudioPlaybackService for playing voice notes
- Added microphone permission to Info.plist and project.yml
- Implemented push-to-talk UI with mic button in ContentView
- Voice notes use AAC compression at 16kHz/32kbps for small file sizes
- Added play/pause buttons for voice notes in message list
- Voice notes display as '🎤 Voice note (X.Xs)' in chat
- Platform-specific audio session handling for iOS/macOS
This commit is contained in:
jack
2025-07-03 01:13:16 +02:00
parent d142fde11d
commit 94a9888ef5
9 changed files with 470 additions and 8 deletions
@@ -179,6 +179,36 @@ class BluetoothMeshService: NSObject {
}
}
func sendVoiceNote(_ audioData: Data, duration: TimeInterval) {
messageQueue.async { [weak self] in
guard let self = self else { return }
let nickname = self.delegate as? ChatViewModel
let senderNick = nickname?.nickname ?? self.myPeerID
let message = BitchatMessage(
sender: senderNick,
content: "🎤 Voice note (\(String(format: "%.1f", duration))s)",
timestamp: Date(),
isRelay: false,
originalSender: nil,
voiceNoteData: audioData,
voiceNoteDuration: duration
)
if let messageData = message.toBinaryPayload() {
let packet = BitchatPacket(
type: MessageType.voiceNote.rawValue,
ttl: self.maxTTL,
senderID: self.myPeerID,
payload: messageData
)
self.broadcastPacket(packet)
}
}
}
func sendPrivateMessage(_ content: String, to recipientPeerID: String, recipientNickname: String) {
messageQueue.async { [weak self] in
guard let self = self else { return }
@@ -509,6 +539,29 @@ class BluetoothMeshService: NSObject {
}
}
case .voiceNote:
if let message = BitchatMessage.fromBinaryPayload(packet.payload) {
// Ignore our own messages
if let senderID = String(data: packet.senderID.trimmingNullBytes(), encoding: .utf8), senderID == myPeerID {
return
}
// Store nickname mapping
if let senderID = String(data: packet.senderID.trimmingNullBytes(), encoding: .utf8) {
peerNicknames[senderID] = message.sender
}
DispatchQueue.main.async {
self.delegate?.didReceiveMessage(message)
}
var relayPacket = packet
relayPacket.ttl -= 1
if relayPacket.ttl > 0 {
self.broadcastPacket(relayPacket)
}
}
default:
break
}