feat(voice): implement full voice transmit (Browser → Discord)
Frontend (useAudioTransmit.ts): - Capture microphone via getUserMedia - Convert Float32 to Int16 PCM at 24kHz mono - Base64 encode and send as JSON via WebSocket - Send voice:transmit:start/stop commands on start/stop Backend (ws/server.ts): - Handle voice_transmit messages from browser - Forward PCM data to Redis channel backend:voice:transmit - Handle voice_command messages and forward to backend:command - discord-gateway VoiceTransmitter receives and plays to voice channel Flow: Browser Mic → getUserMedia → ScriptProcessor → Int16 PCM → base64 → WebSocket JSON → Backend → Redis → VoiceTransmitter → Discord Voice Channel Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
2e9e9c1a06
commit
314d2baec4
@@ -65,13 +65,40 @@ export function createWebSocketServer(server: Server): WebSocketServer {
|
||||
);
|
||||
|
||||
ws.on("message", (data: Buffer) => {
|
||||
// Binary PCM data received from browser.
|
||||
// Since backend has no Discord client to relay to, drop it.
|
||||
if (Buffer.isBuffer(data) && data.length > 0) {
|
||||
logger.debug(
|
||||
{ bytes: data.length },
|
||||
"Dropping binary PCM (no Discord client)",
|
||||
);
|
||||
// Handle JSON messages from browser
|
||||
if (typeof data === 'string' || (Buffer.isBuffer(data) && data.length > 0 && data[0] === 0x7B)) {
|
||||
try {
|
||||
const message = JSON.parse(data.toString());
|
||||
|
||||
if (message.type === 'voice_transmit' && message.buffer) {
|
||||
// Forward PCM data to Redis for discord-gateway
|
||||
import('../shared/redis/index.js').then(({ getCommandPublisher }) => {
|
||||
const publisher = getCommandPublisher();
|
||||
publisher.publish('backend:voice:transmit', JSON.stringify({
|
||||
type: 'pcm',
|
||||
buffer: message.buffer
|
||||
})).catch((err: Error) => {
|
||||
logger.error({ err }, 'Failed to publish voice transmit to Redis');
|
||||
});
|
||||
});
|
||||
} else if (message.type === 'voice_command' && message.command) {
|
||||
// Forward voice commands to discord-gateway
|
||||
import('../shared/redis/index.js').then(({ getCommandPublisher }) => {
|
||||
const publisher = getCommandPublisher();
|
||||
const commandId = `cmd-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
||||
publisher.publish('backend:command', JSON.stringify({
|
||||
id: commandId,
|
||||
type: message.command,
|
||||
payload: {},
|
||||
replyChannel: `reply:${commandId}`
|
||||
})).catch((err: Error) => {
|
||||
logger.error({ err }, 'Failed to publish voice command to Redis');
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
logger.debug({ err }, 'Failed to parse WebSocket message as JSON');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user