test / notme /src /app /voice /page.tsx
dcrey7's picture
Upload 522 files
811126d verified
raw
history blame
5.22 kB
"use client";
import { useEffect, useRef, useState } from "react";
import io from "socket.io-client";
import Peer from "simple-peer";
const socket = io("http://localhost:5001");
export default function VoiceCall() {
const [stream, setStream] = useState<MediaStream | null>(null);
const [peer, setPeer] = useState<Peer.Instance | null>(null);
const [pitchShift, setPitchShift] = useState<number>(1);
const myAudio = useRef<HTMLAudioElement>(null);
const otherAudio = useRef<HTMLAudioElement>(null);
const audioContextRef = useRef<AudioContext | null>(null);
const sourceNodeRef = useRef<MediaStreamAudioSourceNode | null>(null);
const pitchNodeRef = useRef<any>(null);
const applyPitchEffect = (audioStream: MediaStream) => {
if (!audioContextRef.current) {
audioContextRef.current = new AudioContext();
}
const audioContext = audioContextRef.current;
// Create source node
sourceNodeRef.current = audioContext.createMediaStreamSource(audioStream);
// Create ScriptProcessor for pitch shifting
const bufferSize = 4096;
pitchNodeRef.current = audioContext.createScriptProcessor(bufferSize, 1, 1);
pitchNodeRef.current.onaudioprocess = (
audioProcessingEvent: AudioProcessingEvent
) => {
const inputBuffer = audioProcessingEvent.inputBuffer;
const outputBuffer = audioProcessingEvent.outputBuffer;
for (
let channel = 0;
channel < outputBuffer.numberOfChannels;
channel++
) {
const inputData = inputBuffer.getChannelData(channel);
const outputData = outputBuffer.getChannelData(channel);
// Simple pitch shift by resampling
for (let i = 0; i < outputBuffer.length; i++) {
const index = Math.floor(i * pitchShift);
outputData[i] = index < inputBuffer.length ? inputData[index] : 0;
}
}
};
// Connect nodes
sourceNodeRef.current.connect(pitchNodeRef.current);
pitchNodeRef.current.connect(audioContext.destination);
return new MediaStream([audioStream.getAudioTracks()[0]]);
};
useEffect(() => {
navigator.mediaDevices
.getUserMedia({
audio: {
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true,
sampleRate: 48000,
channelCount: 2,
},
})
.then((audioStream) => {
const processedStream = applyPitchEffect(audioStream);
setStream(processedStream);
if (myAudio.current) {
myAudio.current.srcObject = processedStream;
}
})
.catch((err) => console.error("Audio error:", err));
return () => {
if (pitchNodeRef.current) {
pitchNodeRef.current.disconnect();
}
if (sourceNodeRef.current) {
sourceNodeRef.current.disconnect();
}
if (audioContextRef.current) {
audioContextRef.current.close();
}
};
}, []);
const callUser = () => {
if (!stream) return;
const peer = new Peer({
initiator: true,
trickle: false,
stream,
config: {
iceServers: [
{ urls: "stun:stun.l.google.com:19302" },
{ urls: "stun:global.stun.twilio.com:3478" },
],
},
});
peer.on("signal", (data) => socket.emit("offer", data));
peer.on("stream", (userStream) => {
console.log("Received voice stream from peer");
if (otherAudio.current) {
otherAudio.current.srcObject = userStream;
console.log("Successfully set remote audio stream");
}
});
socket.on("answer", (answer) => {
console.log("Received answer from remote peer");
peer.signal(answer);
});
socket.on("candidate", (candidate) => {
console.log("Received ICE candidate");
peer.signal(candidate);
});
setPeer(peer);
};
const receiveCall = () => {
if (!stream) return;
const peer = new Peer({
initiator: false,
trickle: false,
stream,
config: {
iceServers: [
{ urls: "stun:stun.l.google.com:19302" },
{ urls: "stun:global.stun.twilio.com:3478" },
],
},
});
socket.on("offer", (offer) => {
console.log("Received call offer");
peer.signal(offer);
});
peer.on("signal", (data) => {
console.log("Generated answer");
socket.emit("answer", data);
});
peer.on("stream", (userStream) => {
console.log("Received voice stream from caller");
if (otherAudio.current) {
otherAudio.current.srcObject = userStream;
console.log("Successfully set remote audio stream");
}
});
setPeer(peer);
};
return (
<div>
<audio ref={myAudio} autoPlay muted />
<audio ref={otherAudio} autoPlay />
<button onClick={callUser}>Call</button>
<button onClick={receiveCall}>Receive Call</button>
<div>
<label>
Pitch Shift:
<input
type="range"
min="0.5"
max="2"
step="0.1"
value={pitchShift}
onChange={(e) => setPitchShift(Number(e.target.value))}
/>
</label>
</div>
</div>
);
}