Adding jump and replay functionality

This commit is contained in:
Seth Forsgren 2022-12-14 12:44:10 -08:00
parent c45fce2c2f
commit e6cdab9293
4 changed files with 261 additions and 45 deletions

View File

@ -1,5 +1,6 @@
import { PlayingState } from "../types"; import { InferenceInput, InferenceResult, PlayingState } from "../types";
import { IoMdClose } from "react-icons/io"; import { IoMdClose } from "react-icons/io";
import Pause from "./Pause";
interface PromptEntryProps { interface PromptEntryProps {
prompt: string; prompt: string;
@ -7,6 +8,9 @@ interface PromptEntryProps {
className: string; className: string;
playingState: PlayingState; playingState: PlayingState;
resetCallback: () => void; resetCallback: () => void;
inferenceResults: InferenceResult[];
nowPlayingResult: InferenceResult;
setPaused: (value: boolean) => void;
} }
export default function PromptEntry({ export default function PromptEntry({
@ -15,68 +19,117 @@ export default function PromptEntry({
className, className,
playingState, playingState,
resetCallback, resetCallback,
inferenceResults,
nowPlayingResult,
setPaused
}: PromptEntryProps) { }: PromptEntryProps) {
const getPromptCopy = (prompt: string) => { const getPromptCopy = (prompt: string) => {
switch (playingState) { switch (playingState) {
case PlayingState.UNINITIALIZED: case PlayingState.UNINITIALIZED:
case PlayingState.SAME_PROMPT: case PlayingState.SAME_PROMPT:
switch (index) { switch (index) {
case 0: case 0:
return prompt; return (
<div className="tooltip text-left" data-tip="⏪ Jump to previous prompt?" onClick={() => { jumpToPrompt(prompt, inferenceResults, setPaused, nowPlayingResult) }} >
<p className={className}>{prompt}</p>
</div>
);
case 1: case 1:
return prompt; return (
<div className="tooltip text-left" data-tip="⏪ Jump to previous prompt?" onClick={() => { jumpToPrompt(prompt, inferenceResults, setPaused, nowPlayingResult) }} >
<p className={className}>{prompt}</p>
</div>
);
case 2: case 2:
// active prompt
if (prompt == " " || prompt == "") { if (prompt == " " || prompt == "") {
return <span className="text-slate-600">{"<enter prompt>"}</span>; return <span className="text-slate-600">{"<enter prompt>"}</span>;
} else { } else {
return prompt; return (
<div className="tooltip text-left" data-tip="🔁 Restart current prompt?" onClick={() => { jumpToPrompt(prompt, inferenceResults, setPaused, nowPlayingResult) }} >
<p className={className}>{prompt}</p>
</div>
)
} }
case 3: case 3:
if (prompt == " " || prompt == "") { if (prompt == " " || prompt == "") {
return "..."; return <p className={className}>...</p>
} else { } else {
return prompt; return (
<div className="tooltip text-left" data-tip="🚀 Jump to upcoming prompt?" onClick={() => { jumpToPrompt(prompt, inferenceResults, setPaused, nowPlayingResult) }} >
<p className={className}>{prompt}</p>
</div>
)
} }
case 4: case 4:
if (prompt == " " || prompt == "") { if (prompt == " " || prompt == "") {
return "UP NEXT: Anything you want"; return <p className={className}>UP NEXT: Anything you want</p>;
} else { } else {
return "UP NEXT: " + prompt; return (
<div className="tooltip text-left" data-tip="🚀 Jump to upcoming prompt?" onClick={() => { jumpToPrompt(prompt, inferenceResults, setPaused, nowPlayingResult) }} >
<p className={className}>UP NEXT: {prompt}</p>
</div>
)
} }
default: { default: {
console.log("UNHANDLED default"); console.log("UNHANDLED default");
return prompt; return <p className={className}>{prompt}</p>;
} }
} }
case PlayingState.TRANSITION: case PlayingState.TRANSITION:
switch (index) { switch (index) {
case 0: case 0:
return prompt; return (
<div className="tooltip text-left" data-tip="⏪ Jump to previous prompt?" onClick={() => { jumpToPrompt(prompt, inferenceResults, setPaused, nowPlayingResult) }} >
<p className={className}>{prompt}</p>
</div>
);
case 1: case 1:
return prompt; return (
<div className="tooltip text-left" data-tip="⏪ Jump to previous prompt?" onClick={() => { jumpToPrompt(prompt, inferenceResults, setPaused, nowPlayingResult) }} >
<p className={className}>{prompt}</p>
</div>
);
case 2: case 2:
return prompt; return (
<div className="tooltip text-left" data-tip="🔁 Restart outgoing prompt?" onClick={() => { jumpToPrompt(prompt, inferenceResults, setPaused, nowPlayingResult) }} >
<p className={className}>{prompt}</p>
</div>
)
case 3: case 3:
if (prompt == " " || prompt == "") { if (prompt == " " || prompt == "") {
return "< enter prompt >"; return <p className={className}> -enter prompt- </p>;
} else { } else {
return prompt; return (
<div className="tooltip text-left" data-tip="🚀 Jump to incoming prompt?" onClick={() => { jumpToPrompt(prompt, inferenceResults, setPaused, nowPlayingResult) }} >
<p className={className}>{prompt}</p>
</div>
)
} }
case 4: case 4:
if (prompt == " " || prompt == "") { if (prompt == " " || prompt == "") {
return "..."; return <p className={className}>...</p>;
} else { } else {
return prompt; return (
<div className="tooltip text-left" data-tip="🚀 Jump to upcoming prompt?" onClick={() => { jumpToPrompt(prompt, inferenceResults, setPaused, nowPlayingResult) }} >
<p className={className}>UP NEXT: {prompt}</p>
</div>
)
} }
case 5: case 5:
if (prompt == " " || prompt == "") { if (prompt == " " || prompt == "") {
return "UP NEXT: Anything you want"; return <p className={className}>UP NEXT: Anything you want</p>;
} else { } else {
return "UP NEXT: " + prompt; return (
<div className="tooltip text-left" data-tip="🚀 Jump to upcoming prompt?" onClick={() => { jumpToPrompt(prompt, inferenceResults, setPaused, nowPlayingResult) }} >
<p className={className}>UP NEXT: {prompt}</p>
</div>
)
} }
default: { default: {
console.log("UNHANDLED default"); console.log("UNHANDLED default");
return prompt; return <p className={className}>{prompt}</p>;
} }
} }
} }
@ -84,7 +137,8 @@ export default function PromptEntry({
return ( return (
<div className="flex"> <div className="flex">
<p className={className}>{getPromptCopy(prompt)}</p> {getPromptCopy(prompt)}
{/* TODO(hayk): Re-enable this when it's working. */} {/* TODO(hayk): Re-enable this when it's working. */}
{/* {index == 2 ? ( {/* {index == 2 ? (
<IoMdClose <IoMdClose
@ -94,6 +148,124 @@ export default function PromptEntry({
}} }}
/> />
) : null} */} ) : null} */}
</div> </div >
); );
} }
export function jumpToPrompt(prompt: String, inferenceResults: InferenceResult[], setPaused: (value: boolean) => void, nowPlayingResult?: InferenceResult) {
// Pause player since this function will open new tab that user will interact with
setPaused(true)
let firstTimePromptAppears = -1;
for (let i = 0; i < inferenceResults.length; i++) {
if (inferenceResults[i].input.start.prompt === prompt) {
firstTimePromptAppears = i;
break;
}
}
if (firstTimePromptAppears == -1) {
let url = generateLinkToUpcomingPrompt(prompt, nowPlayingResult)
window.open(url, "_blank").focus();
}
else {
let url = generateLinkToPreviousInput(inferenceResults[firstTimePromptAppears].input)
window.open(url, "_blank").focus();
}
}
export function generateLinkToUpcomingPrompt(prompt, nowPlayingResult?: InferenceResult) {
var promptString = "&prompt=" + prompt;
promptString = promptString.replace(/ /g, "+");
if (nowPlayingResult != null) {
var denoisingString = "&denoising=" + nowPlayingResult.input.start.denoising;
var seedImageIdString = "&seedImageId=" + nowPlayingResult.input.seed_image_id;
} else {
denoisingString = "";
seedImageIdString = "";
}
var baseUrl = window.location.origin + "/?";
var url = baseUrl + promptString + denoisingString + seedImageIdString;
return url;
}
// Todo: DRY this and share functions
export function generateLinkToPreviousInput(selectedInput: InferenceInput) {
var prompt;
var seed;
var denoising;
var maskImageId;
var seedImageId;
var guidance;
var numInferenceSteps;
var alphaVelocity;
prompt = selectedInput.start.prompt;
seed = selectedInput.start.seed;
denoising = selectedInput.start.denoising;
maskImageId = selectedInput.mask_image_id;
seedImageId = selectedInput.seed_image_id;
var baseUrl = window.location.origin + "/?";
if (prompt != null) {
var promptString = "&prompt=" + prompt;
} else {
promptString = "";
}
if (seed != null) {
var seedString = "&seed=" + seed;
} else {
seedString = "";
}
if (denoising != null) {
var denoisingString = "&denoising=" + denoising;
} else {
denoisingString = "";
}
if (maskImageId != null) {
var maskImageIdString = "&maskImageId=" + maskImageId;
} else {
maskImageIdString = "";
}
if (seedImageId != null) {
var seedImageIdString = "&seedImageId=" + seedImageId;
} else {
seedImageIdString = "";
}
if (guidance != null) {
var guidanceString = "&guidance=" + guidance;
} else {
guidanceString = "";
}
if (numInferenceSteps != null) {
var numInferenceStepsString = "&numInferenceSteps=" + numInferenceSteps;
} else {
numInferenceStepsString = "";
}
if (alphaVelocity != null) {
var alphaVelocityString = "&alphaVelocity=" + alphaVelocity;
} else {
alphaVelocityString = "";
}
// Format strings to have + in place of spaces for ease of sharing, note this is only necessary for prompts currently
promptString = promptString.replace(/ /g, "+");
// create url string with the variables above combined
var shareUrl =
baseUrl +
promptString +
seedString +
denoisingString +
maskImageIdString +
seedImageIdString +
guidanceString +
numInferenceStepsString +
alphaVelocityString;
return shareUrl;
}

View File

@ -10,6 +10,7 @@ interface PromptPanelProps {
appState: AppState; appState: AppState;
changePrompt: (prompt: string, index: number) => void; changePrompt: (prompt: string, index: number) => void;
resetCallback: () => void; resetCallback: () => void;
setPaused: (paused: boolean) => void;
} }
export default function PromptPanel({ export default function PromptPanel({
@ -19,6 +20,7 @@ export default function PromptPanel({
appState, appState,
changePrompt, changePrompt,
resetCallback, resetCallback,
setPaused,
}: PromptPanelProps) { }: PromptPanelProps) {
const inputPrompt = useRef(null); const inputPrompt = useRef(null);
@ -150,12 +152,15 @@ export default function PromptPanel({
<div className="h-[78vh] landscape:sm:max-[750px]:h-[62vh] md:h-[80vh] flex flex-col justify-around pt-[10vh] pr-5"> <div className="h-[78vh] landscape:sm:max-[750px]:h-[62vh] md:h-[80vh] flex flex-col justify-around pt-[10vh] pr-5">
{getDisplayPrompts().map((prompt, index) => ( {getDisplayPrompts().map((prompt, index) => (
<PromptEntry <PromptEntry
prompt={prompt.prompt + " "} prompt={prompt.prompt}
className={getPromptEntryClassName(index)} className={getPromptEntryClassName(index)}
index={index} index={index}
key={index} key={index}
playingState={playingState} playingState={playingState}
resetCallback={resetCallback} resetCallback={resetCallback}
inferenceResults={inferenceResults}
nowPlayingResult={nowPlayingResult}
setPaused={setPaused}
/> />
))} ))}
</div> </div>
@ -194,6 +199,10 @@ export default function PromptPanel({
); );
} }
export function refreshPage() {
window.location.reload();
}
const promptEntryClassNameDict = { const promptEntryClassNameDict = {
0: "font-extralight text-xs text-gray-500 text-opacity-20", 0: "font-extralight text-xs text-gray-500 text-opacity-20",
1: "font-extralight text-xs text-gray-400 text-opacity-20", 1: "font-extralight text-xs text-gray-400 text-opacity-20",

View File

@ -13,7 +13,7 @@ import Pause from "../components/Pause";
import PromptPanel from "../components/PromptPanel"; import PromptPanel from "../components/PromptPanel";
import ThreeCanvas from "../components/ThreeCanvas"; import ThreeCanvas from "../components/ThreeCanvas";
import { samplePrompts } from "../prompts"; import { samplePrompts, initialSeeds, initialSeedImageMap } from "../prompts";
import { import {
AppState, AppState,
@ -22,10 +22,6 @@ import {
PromptInput, PromptInput,
} from "../types"; } from "../types";
function getRandomInt(max: number) {
return Math.floor(Math.random() * max);
}
function getRandomFromArray(arr: any[], n: number) { function getRandomFromArray(arr: any[], n: number) {
var result = new Array(n), var result = new Array(n),
len = arr.length, len = arr.length,
@ -51,11 +47,11 @@ export default function Home() {
const [alpha, setAlpha] = useState(0.0); const [alpha, setAlpha] = useState(0.0);
const [alphaRollover, setAlphaRollover] = useState(false); const [alphaRollover, setAlphaRollover] = useState(false);
const [alphaVelocity, setAlphaVelocity] = useState(0.25); const [alphaVelocity, setAlphaVelocity] = useState(0.25);
const [seed, setSeed] = useState(getRandomInt(1000000));
// Settings // Settings
const [denoising, setDenoising] = useState(0.75); const [denoising, setDenoising] = useState(0.75);
const [seedImageId, setSeedImageId] = useState("og_beat"); const [seedImageId, setSeedImageId] = useState(initialSeeds[Math.floor(Math.random() * initialSeeds.length)]);
const [seed, setSeed] = useState(initialSeedImageMap[seedImageId][Math.floor(Math.random() * initialSeedImageMap[seedImageId].length)]);
// Prompts shown on screen and maintained by the prompt panel // Prompts shown on screen and maintained by the prompt panel
const [promptInputs, setPromptInputs] = useState<PromptInput[]>([]); const [promptInputs, setPromptInputs] = useState<PromptInput[]>([]);
@ -330,10 +326,14 @@ export default function Home() {
newPromptInputs[index].prompt = prompt; newPromptInputs[index].prompt = prompt;
setPromptInputs(newPromptInputs); setPromptInputs(newPromptInputs);
}} }}
setPaused={setPaused}
resetCallback={resetCallback} resetCallback={resetCallback}
/> />
<Pause paused={paused} setPaused={setPaused} /> <Pause
paused={paused}
setPaused={setPaused}
/>
<Share <Share
inferenceResults={inferenceResults} inferenceResults={inferenceResults}

View File

@ -3,24 +3,59 @@ export const samplePrompts = [
"lo-fi beat for the holidays", "lo-fi beat for the holidays",
"techno DJ and a country singer", "techno DJ and a country singer",
"classical italian tenor operatic pop", "classical italian tenor operatic pop",
"arabic (vocals:1.5)", "edm electropop indietronica metropopolis",
"typing",
"chainsaw funk",
"dance pop edm electropop indietronica metropopolis pop uk pop",
"kygo tropical dance marvin gaye", "kygo tropical dance marvin gaye",
"west coast rap vocals 808", "detroit hip hop 808",
"church bells", "church bells",
"funk bassline with a jazzy saxophone solo", "funk bassline with a jazzy saxophone",
"piano funk", "a pianist playing a concerto",
"a jazz pianist playing a concerto",
"rock and roll electric guitar solo", "rock and roll electric guitar solo",
"acoustic folk fiddle jam", "late romantic era in the style of Richard Wagner",
"jamaican rap", "acoustic folk violin jam",
"church organ with a harpsichord", "k-pop boy group",
"typing",
"post-teen pop talent show winner",
"sean paul dancehall",
"mambo but from kenya", "mambo but from kenya",
"classical flute",
"swing jazz trumpet", "swing jazz trumpet",
"tropical deep sea", "bubblegum eurodance",
"laughing",
"lo-fi beat with a violin",
]; ];
export const rollTheDicePrompts = [
"arabic (vocals:1.5)",
"classic rock mellow gold progressive",
"west coast rap vocals",
"classical flute",
"church organ with a harpsichord",
"uk permanent wave pop",
"chainsaw funk",
"laughing",
"tropical deep sea",
"piano funk",
"jamaican ska rap",
"tropical electro house moombahton",
"breathing",
"water drops",
"british soul dance",
"pop r&b urban contemporary",
"baroque pop new wave",
"reggae fusion",
"tropical german dance house",
// TODO: consider adding samplePrompts to this list when using it
];
export const initialSeedImageMap = {
"og_beat": [3, 738973, 51209, 745234],
"agile": [808, 231, 3324, 323984],
"marim": [123, 676, 6754, 8730],
// "motorway": [8730, 323984, 745234],
// "vibes": [4205, 94, 78530]
}
export const initialSeeds = [
"og_beat",
"agile",
"marim",
// "motorway",
// "vibes"
]