feat(voice): add voice transmit functionality for real-time audio streaming
- Add VoiceTransmitter class to handle PCM audio from backend/browser to Discord - Implement voice:transmit:start and voice:transmit:stop command handlers - Upsample 24kHz mono PCM to 48kHz stereo for Discord compatibility - Encode PCM to Opus and stream via browser-bridge player owner - Subscribe to Redis channel backend:voice:transmit for real-time PCM data Features: - Backend can send PCM audio (24kHz mono s16le base64) via Redis - Automatic upsampling and encoding to Discord-compatible format - Clean start/stop lifecycle with resource cleanup This completes the bidirectional voice streaming: - Listen: Discord → Backend (already working via voicePcmData broadcast) - Transmit: Backend → Discord (now implemented) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
60edb4576f
commit
f74252f594
@@ -3,6 +3,7 @@ import type { Client } from "discord.js-selfbot-v13";
|
||||
import Redis from "ioredis";
|
||||
import { config } from "../../shared/config/config.js";
|
||||
import { discordPlayer } from "../voice-recording/player.js";
|
||||
import { voiceTransmitter } from "../voice-recording/transmitter.js";
|
||||
import type { VoiceController } from "../voice-recording/voiceController.js";
|
||||
|
||||
const logger = createChildLogger("command-handler");
|
||||
@@ -130,6 +131,12 @@ export class CommandHandler {
|
||||
case "voice:channels":
|
||||
reply = await this.handleVoiceChannels(cmd);
|
||||
break;
|
||||
case "voice:transmit:start":
|
||||
reply = await this.handleVoiceTransmitStart(cmd);
|
||||
break;
|
||||
case "voice:transmit:stop":
|
||||
reply = await this.handleVoiceTransmitStop(cmd);
|
||||
break;
|
||||
case "guilds:list":
|
||||
reply = await this.handleListGuilds(cmd);
|
||||
break;
|
||||
@@ -370,6 +377,63 @@ export class CommandHandler {
|
||||
};
|
||||
}
|
||||
|
||||
private async handleVoiceTransmitStart(cmd: BackendCommand): Promise<CommandReply> {
|
||||
if (!discordPlayer.isConnected()) {
|
||||
return {
|
||||
id: cmd.id,
|
||||
success: false,
|
||||
data: null,
|
||||
error: "Not connected to voice channel",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// Create a new Redis connection for the transmitter
|
||||
const transmitRedis = new Redis(config.REDIS_URL);
|
||||
await voiceTransmitter.start(transmitRedis);
|
||||
|
||||
const status = voiceTransmitter.getStatus();
|
||||
logger.info({ status }, "Voice transmit started");
|
||||
|
||||
return {
|
||||
id: cmd.id,
|
||||
success: true,
|
||||
data: status,
|
||||
};
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
logger.error({ error: message }, "Failed to start voice transmit");
|
||||
return {
|
||||
id: cmd.id,
|
||||
success: false,
|
||||
data: null,
|
||||
error: message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async handleVoiceTransmitStop(cmd: BackendCommand): Promise<CommandReply> {
|
||||
try {
|
||||
await voiceTransmitter.stop();
|
||||
logger.info("Voice transmit stopped");
|
||||
|
||||
return {
|
||||
id: cmd.id,
|
||||
success: true,
|
||||
data: { status: "stopped" },
|
||||
};
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
logger.error({ error: message }, "Failed to stop voice transmit");
|
||||
return {
|
||||
id: cmd.id,
|
||||
success: false,
|
||||
data: null,
|
||||
error: message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Status publishing ----
|
||||
|
||||
private publishVoiceStatus(): void {
|
||||
|
||||
Reference in New Issue
Block a user