Skip to content

Commit

Permalink
Merge pull request #9 from Nonhuman-Nonsense/albin
Browse files Browse the repository at this point in the history
Improve audio playback and audio/text timing
  • Loading branch information
albin-karlsson authored Apr 24, 2024
2 parents 386ac9d + 2ed74fd commit d538a5a
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 70 deletions.
57 changes: 41 additions & 16 deletions client/src/components/AudioOutput.jsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,56 @@
// In the AudioOutput component
import React, { useEffect, useRef } from "react";

function AudioOutput({ currentAudioMessage }) {
function AudioOutput({ currentAudioMessage, isPaused }) {
const audioRef = useRef(null);
const urlRef = useRef(null);

useEffect(() => {
// Initialize the audio element if it does not exist
if (!audioRef.current) {
audioRef.current = new Audio();
}
return () => {
audioRef.current && audioRef.current.pause();
};
}, []);

useEffect(() => {
// Handle updating the audio source when the message changes
if (currentAudioMessage) {
// Revoke the old URL to avoid memory leaks
if (urlRef.current?.url) {
URL.revokeObjectURL(urlRef.current.url);
}
// Create a new URL for the updated audio blob
const blob = new Blob([currentAudioMessage.audio], { type: "audio/mp3" });
const url = URL.createObjectURL(blob);
audioRef.current.src = url;
urlRef.current = {
url: URL.createObjectURL(blob),
id: currentAudioMessage.id,
};
audioRef.current.src = urlRef.current.url;
audioRef.current.load();

// Auto-play the new audio if not paused
if (!isPaused) {
audioRef.current
.play()
.catch((err) => console.error("Error playing audio:", err));
}
}
}, [currentAudioMessage]);

useEffect(() => {
// Manage playback based on isPaused state
if (!isPaused && currentAudioMessage) {
audioRef.current
.play()
.catch((err) => console.error("Error playing audio:", err));

return () => {
URL.revokeObjectURL(url);
};
} else {
audioRef.current.pause();
}
}, [currentAudioMessage]);
}, [isPaused]);

return (
<audio
ref={audioRef}
controls
autoPlay
/>
);
return null; // This component does not render anything itself
}

export default AudioOutput;
68 changes: 37 additions & 31 deletions client/src/components/Output.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,25 @@ function Output({ conversation, audioMessages }) {
const [currentSnippetIndex, setCurrentSnippetIndex] = useState(0);
const [currentMessageTextSnippet, setCurrentMessageTextSnippet] =
useState("");
const [isPaused, setIsPaused] = useState(false);
const [isReadyMeeting, setIsReadyMeeting] = useState(false);
const [currentAudioMessage, setCurrentAudioMessage] = useState(null);
const [isReady, setIsReady] = useState(false);
const [isPaused, setIsPaused] = useState(true); // Assume initially paused until ready

useEffect(() => {
if (!isReadyMeeting) {
const currentAudioMessage = audioMessages.find(
(a) => a.message_index === currentMessageIndex
);

console.log(currentAudioMessage);

if (currentAudioMessage) {
console.log("Ready!");
console.log("Updated audioMessages");
}, [audioMessages]);

setIsReadyMeeting(true);
}
useEffect(() => {
// Find the audio message only when currentMessageIndex changes
const foundAudioMessage = audioMessages.find(
(a) => a.message_index === currentMessageIndex
);
if (foundAudioMessage) {
setCurrentAudioMessage(foundAudioMessage);
setIsReady(true);
setIsPaused(false); // Start playing the new message immediately
}
}, [audioMessages, currentMessageIndex, isReadyMeeting]);
}, [currentMessageIndex, audioMessages]); // Include audioMessages to update the message if it changes

useEffect(() => {
if (conversation.length > 0 && !isPaused) {
Expand All @@ -41,33 +42,39 @@ function Output({ conversation, audioMessages }) {
setCurrentSnippetIndex(0);
}
}, calculateDisplayTime(snippets[currentSnippetIndex]) * 1000);
return () => clearTimeout(timeout); // Cleanup timeout on unmount or dependency change
return () => clearTimeout(timeout);
}
}
}, [currentMessageIndex, currentSnippetIndex, conversation, isPaused]);

const calculateDisplayTime = (text) => Math.max(3, text.length * 0.055);
const calculateDisplayTime = (text) => {
const baseTimePerCharacter = 0.052; // Time per character in seconds
const baseTime = Math.max(3, text.length * baseTimePerCharacter);
const additionalTimeForCommas = (text.match(/,/g) || []).length * 0.5; // 0.4 seconds for each comma
return baseTime + additionalTimeForCommas;
};

function handlePauseResume() {
setIsPaused(!isPaused); // Toggle pause state
setIsPaused(!isPaused);
}

function handleSkipForward() {
if (conversation.length > currentMessageIndex) {
const snippets =
conversation[currentMessageIndex].text.split(/(?<=\.\s)/);
if (currentSnippetIndex < snippets.length - 1) {
setCurrentSnippetIndex(currentSnippetIndex + 1);
} else if (currentMessageIndex < conversation.length - 1) {
setCurrentMessageIndex(currentMessageIndex + 1);
setCurrentSnippetIndex(0);
}
if (currentMessageIndex < conversation.length - 1) {
const newIndex = currentMessageIndex + 1;
setCurrentMessageIndex(newIndex);
setCurrentSnippetIndex(0);
// Update the current audio message immediately when skipping
const newAudioMessage = audioMessages.find(
(a) => a.message_index === newIndex
);
setCurrentAudioMessage(newAudioMessage);
setIsPaused(false); // Optionally start playing the new message immediately
}
}

return (
<div style={{ textAlign: "center", width: "75%" }}>
{isReadyMeeting ? (
{isReady ? (
<>
<h2>
Speaker:{" "}
Expand All @@ -77,9 +84,8 @@ function Output({ conversation, audioMessages }) {
</h2>
<TextOutput currentMessageTextSnippet={currentMessageTextSnippet} />
<AudioOutput
currentAudioMessage={audioMessages.find(
(a) => a.message_index === currentMessageIndex
)}
currentAudioMessage={currentAudioMessage}
isPaused={isPaused}
/>
<ConversationControls
isPaused={isPaused}
Expand All @@ -88,7 +94,7 @@ function Output({ conversation, audioMessages }) {
/>
</>
) : (
<h2>The council is getting ready...</h2>
<h3>The council is getting ready...</h3>
)}
</div>
);
Expand Down
40 changes: 19 additions & 21 deletions client/src/components/ResetWarning.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,26 @@ import { capitalizeFirstLetter } from "../utils";

function ResetWarning({ message, onReset, onCancel }) {
return (
<div>
<h4>
{message ? capitalizeFirstLetter(message) : "This"} will restart the
discussion
</h4>
<div>
<h4>
{message
? capitalizeFirstLetter(message)
: capitalizeFirstLetter("this")}{" "}
will restart the discussion
</h4>
<div>
<button
className="outline-button"
onClick={onReset}
style={{ marginRight: "9px" }}
>
I understand
</button>
<button
className="outline-button"
onClick={onCancel}
style={{ marginLeft: "9px" }}
>
Cancel
</button>
<button
className="outline-button"
onClick={onReset}
style={{ marginRight: "9px" }}
>
I understand
</button>
<button
className="outline-button"
onClick={onCancel}
style={{ marginLeft: "9px" }}
>
Cancel
</button>
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/TextOutput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function TextOutput({ currentMessageTextSnippet }) {
};

return (
<div style={{ textAlign: "center", width: "75%" }}>
<div style={{ textAlign: "center" }}>
<h2 style={textOutputStyle}>{currentMessageTextSnippet || ""}</h2>
</div>
);
Expand Down
4 changes: 3 additions & 1 deletion server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ const httpServer = http.createServer(app);
const io = new Server(httpServer);
const openai = new OpenAI(process.env.OPENAI_API_KEY);

console.log(process.env.OPENAI_API_KEY);

//Names of OpenAI voices
const audioVoices = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"];

if(process.env.NODE_ENV != 'development'){
if (process.env.NODE_ENV != "development") {
//Don't server the static build in development
app.use(express.static(path.join(__dirname, "../client/build")));
}
Expand Down

0 comments on commit d538a5a

Please sign in to comment.