Skip to content

Commit

Permalink
Add new images and improve output playback
Browse files Browse the repository at this point in the history
  • Loading branch information
albin-karlsson committed Apr 25, 2024
1 parent 2ed74fd commit 8d325e0
Show file tree
Hide file tree
Showing 28 changed files with 141 additions and 151 deletions.
Binary file added client/public/images/foods/avocado-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/avocado.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/banana-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/public/images/foods/banana.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/beer-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/beer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/lollipop-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/lollipop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/maize-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/maize.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/meat-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/public/images/foods/meat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/mushroom-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/mushroom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/old/banana.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
Binary file added client/public/images/foods/old/meat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added client/public/images/foods/old/potato.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added client/public/images/foods/potato-shadow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified client/public/images/foods/potato.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 20 additions & 16 deletions client/src/components/AudioOutput.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
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
if (!audioRef.current) {
audioRef.current = new Audio();
}
return () => {
// Clean up audio element and interval
clearInterval(checkPlaybackIntervalRef.current);
audioRef.current && audioRef.current.pause();
};
}, []);
Expand All @@ -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
}
Expand Down
60 changes: 30 additions & 30 deletions client/src/components/Contact.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,36 @@ import React from "react";

function Contact() {
return (
<div>
<p>
The project is an initiative by art & design
<br /> collective Nonhuman Nonsense developed in
<br /> collaboration with Studio Other Spaces,
<br /> In4Art, Elliot, Albin and others.
</p>
<p>
<a
className="link"
href="https://www.instagram.com/nonhuman-nonsense/"
>
@nonhuman-nonsense
</a>
<br />
<a
className="link"
href="https://nonhuman-nonsense.com"
>
nonhuman-nonsense.com
</a>
<br />
<a
className="link"
href="mailto:hello@nonhuman-nonsense.com"
>
hello@nonhuman-nonsense.com
</a>
</p>
</div>
<div>
<p>
The project is an initiative by art & design
<br /> collective Nonhuman Nonsense developed in
<br /> collaboration with Studio Other Spaces,
<br /> In4Art, Elliot, Albin and others.
</p>
<p>
<a
className="link"
href="https://www.instagram.com/nonhuman-nonsense/"
>
@nonhuman-nonsense
</a>
<br />
<a
className="link"
href="https://nonhuman-nonsense.com"
>
nonhuman-nonsense.com
</a>
<br />
<a
className="link"
href="mailto:hello@nonhuman-nonsense.com"
>
hello@nonhuman-nonsense.com
</a>
</p>
</div>
);
}

Expand Down
34 changes: 19 additions & 15 deletions client/src/components/Council.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -78,17 +78,17 @@ function Council({ options }) {
return (
<div style={{ height: "100%", width: "100%" }}>
<div className="council wrapper">
{activeOverlay === "" && (
<div
className="text-container"
style={{ justifyContent: "end" }}
>
<Output
conversation={conversation}
audioMessages={audioMessages}
/>
</div>
)}
<div
className="text-container"
style={{ justifyContent: "end" }}
>
{/* Render the Output component regardless of the overlay */}
<Output
textMessages={textMessages}
audioMessages={audioMessages}
isActiveOverlay={activeOverlay !== ""}
/>
</div>
<div style={foodsContainerStyle}>
{foods.map((food, index) => (
<FoodItem
Expand All @@ -101,7 +101,11 @@ function Council({ options }) {
))}
</div>
<Overlay isActive={activeOverlay !== ""}>
<CouncilOverlays activeOverlay={activeOverlay} options={options} removeOverlay={removeOverlay} />
<CouncilOverlays
activeOverlay={activeOverlay}
options={options}
removeOverlay={removeOverlay}
/>
</Overlay>
<Navbar
topic={options.topic}
Expand Down
6 changes: 3 additions & 3 deletions client/src/components/FoodItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ function FoodItem({ food, index, total, screenWidth }) {
return (
<div style={foodItemStyle(index, total)}>
<img
src={`/images/foods/${food.name}.png`}
src={`/images/foods/${food.name}-shadow.png`}
style={responsiveStyle}
/>
<img
{/* <img
src={`/images/foods/${food.name}.png`}
style={{ ...responsiveStyle, ...foodImageShadowStyle(index, total) }}
/>
/> */}
</div>
);
}
Expand Down
112 changes: 31 additions & 81 deletions client/src/components/Output.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<div style={{ textAlign: "center", width: "75%" }}>
{isReady ? (
<>
<h2>
Speaker:{" "}
{conversation.length > 0
? conversation[currentMessageIndex].speaker
: ""}
</h2>
<TextOutput currentMessageTextSnippet={currentMessageTextSnippet} />
<AudioOutput
currentAudioMessage={currentAudioMessage}
isPaused={isPaused}
/>
<ConversationControls
isPaused={isPaused}
onPauseResume={handlePauseResume}
onSkipForward={handleSkipForward}
/>
</>
) : (
<h3>The council is getting ready...</h3>
)}
</div>
<>
<TextOutput
currentTextMessage={currentTextMessage}
currentAudioMessage={currentAudioMessage}
/>
<AudioOutput
currentAudioMessage={currentAudioMessage}
onFinishedPlaying={handleOnFinishedPlaying}
/>
</>
);
}

Expand Down
44 changes: 38 additions & 6 deletions client/src/components/TextOutput.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<div style={{ textAlign: "center" }}>
<h2 style={textOutputStyle}>{currentMessageTextSnippet || ""}</h2>
<h2
style={{
fontFamily: "Arial, sans-serif",
backgroundColor: "rgba(0,0,0,0.7)",
}}
>
{currentSnippet}
</h2>
</div>
);
}
Expand Down

0 comments on commit 8d325e0

Please sign in to comment.