diff --git a/client/public/images/foods/avocado-shadow.png b/client/public/images/foods/avocado-shadow.png new file mode 100644 index 0000000..36b61b9 Binary files /dev/null and b/client/public/images/foods/avocado-shadow.png differ diff --git a/client/public/images/foods/avocado.png b/client/public/images/foods/avocado.png new file mode 100644 index 0000000..f8b887c Binary files /dev/null and b/client/public/images/foods/avocado.png differ diff --git a/client/public/images/foods/banana-shadow.png b/client/public/images/foods/banana-shadow.png new file mode 100644 index 0000000..31bbfa4 Binary files /dev/null and b/client/public/images/foods/banana-shadow.png differ diff --git a/client/public/images/foods/banana.png b/client/public/images/foods/banana.png index b8cbfe0..28c46d3 100644 Binary files a/client/public/images/foods/banana.png and b/client/public/images/foods/banana.png differ diff --git a/client/public/images/foods/beer-shadow.png b/client/public/images/foods/beer-shadow.png new file mode 100644 index 0000000..d7694a6 Binary files /dev/null and b/client/public/images/foods/beer-shadow.png differ diff --git a/client/public/images/foods/beer.png b/client/public/images/foods/beer.png new file mode 100644 index 0000000..d943f17 Binary files /dev/null and b/client/public/images/foods/beer.png differ diff --git a/client/public/images/foods/lollipop-shadow.png b/client/public/images/foods/lollipop-shadow.png new file mode 100644 index 0000000..20fae42 Binary files /dev/null and b/client/public/images/foods/lollipop-shadow.png differ diff --git a/client/public/images/foods/lollipop.png b/client/public/images/foods/lollipop.png new file mode 100644 index 0000000..9a6c7bf Binary files /dev/null and b/client/public/images/foods/lollipop.png differ diff --git a/client/public/images/foods/maize-shadow.png b/client/public/images/foods/maize-shadow.png new file mode 100644 index 0000000..085f082 Binary files /dev/null and b/client/public/images/foods/maize-shadow.png differ diff --git a/client/public/images/foods/maize.png b/client/public/images/foods/maize.png new file mode 100644 index 0000000..bc9335b Binary files /dev/null and b/client/public/images/foods/maize.png differ diff --git a/client/public/images/foods/meat-shadow.png b/client/public/images/foods/meat-shadow.png new file mode 100644 index 0000000..9e5fe03 Binary files /dev/null and b/client/public/images/foods/meat-shadow.png differ diff --git a/client/public/images/foods/meat.png b/client/public/images/foods/meat.png index 09b546f..a832a65 100644 Binary files a/client/public/images/foods/meat.png and b/client/public/images/foods/meat.png differ diff --git a/client/public/images/foods/mushroom-shadow.png b/client/public/images/foods/mushroom-shadow.png new file mode 100644 index 0000000..b95d5da Binary files /dev/null and b/client/public/images/foods/mushroom-shadow.png differ diff --git a/client/public/images/foods/mushroom.png b/client/public/images/foods/mushroom.png new file mode 100644 index 0000000..5e69b40 Binary files /dev/null and b/client/public/images/foods/mushroom.png differ diff --git a/client/public/images/foods/old/banana.png b/client/public/images/foods/old/banana.png new file mode 100644 index 0000000..b8cbfe0 Binary files /dev/null and b/client/public/images/foods/old/banana.png differ diff --git a/client/public/images/foods/bratwurst.png b/client/public/images/foods/old/bratwurst.png similarity index 100% rename from client/public/images/foods/bratwurst.png rename to client/public/images/foods/old/bratwurst.png diff --git a/client/public/images/foods/lollypop.png b/client/public/images/foods/old/lollypop.png similarity index 100% rename from client/public/images/foods/lollypop.png rename to client/public/images/foods/old/lollypop.png diff --git a/client/public/images/foods/old/meat.png b/client/public/images/foods/old/meat.png new file mode 100644 index 0000000..09b546f Binary files /dev/null and b/client/public/images/foods/old/meat.png differ diff --git a/client/public/images/foods/old/potato.png b/client/public/images/foods/old/potato.png new file mode 100644 index 0000000..f69e2a7 Binary files /dev/null and b/client/public/images/foods/old/potato.png differ diff --git a/client/public/images/foods/tomato.png b/client/public/images/foods/old/tomato.png similarity index 100% rename from client/public/images/foods/tomato.png rename to client/public/images/foods/old/tomato.png diff --git a/client/public/images/foods/potato-shadow.png b/client/public/images/foods/potato-shadow.png new file mode 100644 index 0000000..bf47aa0 Binary files /dev/null and b/client/public/images/foods/potato-shadow.png differ diff --git a/client/public/images/foods/potato.png b/client/public/images/foods/potato.png index f69e2a7..d832433 100644 Binary files a/client/public/images/foods/potato.png and b/client/public/images/foods/potato.png differ diff --git a/client/src/components/AudioOutput.jsx b/client/src/components/AudioOutput.jsx index b2fa771..ec77cec 100644 --- a/client/src/components/AudioOutput.jsx +++ b/client/src/components/AudioOutput.jsx @@ -1,8 +1,9 @@ import React, { useEffect, useRef } from "react"; -function AudioOutput({ currentAudioMessage, isPaused }) { +function AudioOutput({ currentAudioMessage, onFinishedPlaying }) { const audioRef = useRef(null); const urlRef = useRef(null); + const checkPlaybackIntervalRef = useRef(null); useEffect(() => { // Initialize the audio element if it does not exist @@ -10,6 +11,8 @@ function AudioOutput({ currentAudioMessage, isPaused }) { audioRef.current = new Audio(); } return () => { + // Clean up audio element and interval + clearInterval(checkPlaybackIntervalRef.current); audioRef.current && audioRef.current.pause(); }; }, []); @@ -30,25 +33,26 @@ function AudioOutput({ currentAudioMessage, isPaused }) { 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) { + // Auto-play the new audio audioRef.current .play() .catch((err) => console.error("Error playing audio:", err)); - } else { - audioRef.current.pause(); + + // Start checking audio playback status + checkPlaybackIntervalRef.current = setInterval(checkPlaybackStatus, 500); + } + }, [currentAudioMessage]); + + const checkPlaybackStatus = () => { + if ( + audioRef.current && + audioRef.current.currentTime >= audioRef.current.duration + ) { + // Audio playback has ended + clearInterval(checkPlaybackIntervalRef.current); + onFinishedPlaying(); } - }, [isPaused]); + }; return null; // This component does not render anything itself } diff --git a/client/src/components/Contact.jsx b/client/src/components/Contact.jsx index 6f19dea..58856ab 100644 --- a/client/src/components/Contact.jsx +++ b/client/src/components/Contact.jsx @@ -2,36 +2,36 @@ import React from "react"; function Contact() { return ( -
-

- The project is an initiative by art & design -
collective Nonhuman Nonsense developed in -
collaboration with Studio Other Spaces, -
In4Art, Elliot, Albin and others. -

-

- - @nonhuman-nonsense - -
- - nonhuman-nonsense.com - -
- - hello@nonhuman-nonsense.com - -

-
+
+

+ The project is an initiative by art & design +
collective Nonhuman Nonsense developed in +
collaboration with Studio Other Spaces, +
In4Art, Elliot, Albin and others. +

+

+ + @nonhuman-nonsense + +
+ + nonhuman-nonsense.com + +
+ + hello@nonhuman-nonsense.com + +

+
); } diff --git a/client/src/components/Council.jsx b/client/src/components/Council.jsx index 4576de9..869d864 100644 --- a/client/src/components/Council.jsx +++ b/client/src/components/Council.jsx @@ -12,7 +12,7 @@ function Council({ options }) { const { foods, humanName, topic } = options; const [activeOverlay, setActiveOverlay] = useState(""); const { width: screenWidth } = useWindowSize(); - const [conversation, setConversation] = useState([]); // State to store conversation updates + const [textMessages, setTextMessages] = useState([]); // State to store conversation updates const [audioMessages, setAudioMessages] = useState([]); // To store multiple ArrayBuffers const socketRef = useRef(null); // Using useRef to persist socket instance @@ -45,8 +45,8 @@ function Council({ options }) { socketRef.current.emit("start_conversation", promptsAndOptions); // Listen for conversation text updates - socketRef.current.on("conversation_update", (message) => { - setConversation((prev) => [...prev, message]); + socketRef.current.on("conversation_update", (textMessage) => { + setTextMessages((prev) => [...prev, textMessage]); }); // Listen for audio updates @@ -78,17 +78,17 @@ function Council({ options }) { return (
- {activeOverlay === "" && ( -
- -
- )} +
+ {/* Render the Output component regardless of the overlay */} + +
{foods.map((food, index) => ( - + - + /> */}
); } diff --git a/client/src/components/Output.jsx b/client/src/components/Output.jsx index 9253e5d..bb443f6 100644 --- a/client/src/components/Output.jsx +++ b/client/src/components/Output.jsx @@ -1,102 +1,52 @@ import React, { useState, useEffect } from "react"; import TextOutput from "./TextOutput"; import AudioOutput from "./AudioOutput"; -import ConversationControls from "./ConversationControls"; -function Output({ conversation, audioMessages }) { +function Output({ textMessages, audioMessages, isActiveOverlay }) { const [currentMessageIndex, setCurrentMessageIndex] = useState(0); - const [currentSnippetIndex, setCurrentSnippetIndex] = useState(0); - const [currentMessageTextSnippet, setCurrentMessageTextSnippet] = - useState(""); + const [currentTextMessage, setCurrentTextMessage] = useState(null); const [currentAudioMessage, setCurrentAudioMessage] = useState(null); - const [isReady, setIsReady] = useState(false); - const [isPaused, setIsPaused] = useState(true); // Assume initially paused until ready useEffect(() => { - console.log("Updated audioMessages"); - }, [audioMessages]); + tryFindTextAndAudio(); + }, [currentMessageIndex, textMessages, audioMessages]); - useEffect(() => { - // Find the audio message only when currentMessageIndex changes - const foundAudioMessage = audioMessages.find( + function tryFindTextAndAudio() { + const textMessage = textMessages[currentMessageIndex]; + const audioMessage = audioMessages.find( (a) => a.message_index === currentMessageIndex ); - if (foundAudioMessage) { - setCurrentAudioMessage(foundAudioMessage); - setIsReady(true); - setIsPaused(false); // Start playing the new message immediately - } - }, [currentMessageIndex, audioMessages]); // Include audioMessages to update the message if it changes - useEffect(() => { - if (conversation.length > 0 && !isPaused) { - const snippets = - conversation[currentMessageIndex].text.split(/(?<=\.\s)/); - if (snippets.length > currentSnippetIndex) { - setCurrentMessageTextSnippet(snippets[currentSnippetIndex]); - const timeout = setTimeout(() => { - if (currentSnippetIndex < snippets.length - 1) { - setCurrentSnippetIndex(currentSnippetIndex + 1); - } else if (currentMessageIndex < conversation.length - 1) { - setCurrentMessageIndex(currentMessageIndex + 1); - setCurrentSnippetIndex(0); - } - }, calculateDisplayTime(snippets[currentSnippetIndex]) * 1000); - return () => clearTimeout(timeout); - } - } - }, [currentMessageIndex, currentSnippetIndex, conversation, isPaused]); + if ( + textMessage && + audioMessage && + !currentTextMessage && + !currentAudioMessage + ) { + console.log("Both found!"); - 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); + setCurrentTextMessage((prev) => textMessage); + setCurrentAudioMessage((prev) => audioMessage); + } } - function handleSkipForward() { - 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 - } + function handleOnFinishedPlaying() { + setCurrentTextMessage((prev) => null); + setCurrentAudioMessage((prev) => null); + setCurrentMessageIndex((prev) => prev + 1); } return ( -
- {isReady ? ( - <> -

- Speaker:{" "} - {conversation.length > 0 - ? conversation[currentMessageIndex].speaker - : ""} -

- - - - - ) : ( -

The council is getting ready...

- )} -
+ <> + + + ); } diff --git a/client/src/components/TextOutput.jsx b/client/src/components/TextOutput.jsx index 8fbcaf3..4563ff2 100644 --- a/client/src/components/TextOutput.jsx +++ b/client/src/components/TextOutput.jsx @@ -1,14 +1,46 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; -function TextOutput({ currentMessageTextSnippet }) { - const textOutputStyle = { - fontFamily: "Arial, sans-serif", - backgroundColor: "rgba(0,0,0,0.7)", +function TextOutput({ currentTextMessage }) { + const [currentSnippetIndex, setCurrentSnippetIndex] = useState(0); + const [currentSnippet, setCurrentSnippet] = useState(""); + + // Reset the snippet index when a new message is received + useEffect(() => { + setCurrentSnippetIndex(0); + }, [currentTextMessage]); + + useEffect(() => { + if (currentTextMessage && currentTextMessage.text) { + const text = currentTextMessage.text; + // Split the text into sentences, ignoring periods followed by a number + const sentences = text.split(/(?<=[.!?])(?=\s+(?![0-9]))/); + setCurrentSnippet(sentences[currentSnippetIndex]); + + const interval = setInterval(() => { + setCurrentSnippetIndex((prevIndex) => + prevIndex < sentences.length - 1 ? prevIndex + 1 : prevIndex + ); + }, calculateDisplayTime(currentSnippet) * 1000); + return () => clearInterval(interval); + } + }, [currentTextMessage, currentSnippetIndex]); + + // Calculate the display time based on the number of characters in the snippet + const calculateDisplayTime = (text) => { + const baseTimePerCharacter = 0.06; // Adjust this value as needed + return Math.max(3, text.length * baseTimePerCharacter); }; return (
-

{currentMessageTextSnippet || ""}

+

+ {currentSnippet} +

); }