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 Pause from "./Pause";
interface PromptEntryProps {
prompt: string;
@ -7,6 +8,9 @@ interface PromptEntryProps {
className: string;
playingState: PlayingState;
resetCallback: () => void;
inferenceResults: InferenceResult[];
nowPlayingResult: InferenceResult;
setPaused: (value: boolean) => void;
}
export default function PromptEntry({
@ -15,68 +19,117 @@ export default function PromptEntry({
className,
playingState,
resetCallback,
inferenceResults,
nowPlayingResult,
setPaused
}: PromptEntryProps) {
const getPromptCopy = (prompt: string) => {
switch (playingState) {
case PlayingState.UNINITIALIZED:
case PlayingState.SAME_PROMPT:
switch (index) {
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:
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:
// active prompt
if (prompt == " " || prompt == "") {
return <span className="text-slate-600">{"<enter prompt>"}</span>;
} 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:
if (prompt == " " || prompt == "") {
return "...";
return <p className={className}>...</p>
} 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:
if (prompt == " " || prompt == "") {
return "UP NEXT: Anything you want";
return <p className={className}>UP NEXT: Anything you want</p>;
} 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: {
console.log("UNHANDLED default");
return prompt;
return <p className={className}>{prompt}</p>;
}
}
case PlayingState.TRANSITION:
switch (index) {
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:
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:
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:
if (prompt == " " || prompt == "") {
return "< enter prompt >";
return <p className={className}> -enter prompt- </p>;
} 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:
if (prompt == " " || prompt == "") {
return "...";
return <p className={className}>...</p>;
} 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:
if (prompt == " " || prompt == "") {
return "UP NEXT: Anything you want";
return <p className={className}>UP NEXT: Anything you want</p>;
} 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: {
console.log("UNHANDLED default");
return prompt;
return <p className={className}>{prompt}</p>;
}
}
}
@ -84,7 +137,8 @@ export default function PromptEntry({
return (
<div className="flex">
<p className={className}>{getPromptCopy(prompt)}</p>
{getPromptCopy(prompt)}
{/* TODO(hayk): Re-enable this when it's working. */}
{/* {index == 2 ? (
<IoMdClose
@ -94,6 +148,124 @@ export default function PromptEntry({
}}
/>
) : 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;
changePrompt: (prompt: string, index: number) => void;
resetCallback: () => void;
setPaused: (paused: boolean) => void;
}
export default function PromptPanel({
@ -19,6 +20,7 @@ export default function PromptPanel({
appState,
changePrompt,
resetCallback,
setPaused,
}: PromptPanelProps) {
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">
{getDisplayPrompts().map((prompt, index) => (
<PromptEntry
prompt={prompt.prompt + " "}
prompt={prompt.prompt}
className={getPromptEntryClassName(index)}
index={index}
key={index}
playingState={playingState}
resetCallback={resetCallback}
inferenceResults={inferenceResults}
nowPlayingResult={nowPlayingResult}
setPaused={setPaused}
/>
))}
</div>
@ -194,6 +199,10 @@ export default function PromptPanel({
);
}
export function refreshPage() {
window.location.reload();
}
const promptEntryClassNameDict = {
0: "font-extralight text-xs text-gray-500 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 ThreeCanvas from "../components/ThreeCanvas";
import { samplePrompts } from "../prompts";
import { samplePrompts, initialSeeds, initialSeedImageMap } from "../prompts";
import {
AppState,
@ -22,10 +22,6 @@ import {
PromptInput,
} from "../types";
function getRandomInt(max: number) {
return Math.floor(Math.random() * max);
}
function getRandomFromArray(arr: any[], n: number) {
var result = new Array(n),
len = arr.length,
@ -51,11 +47,11 @@ export default function Home() {
const [alpha, setAlpha] = useState(0.0);
const [alphaRollover, setAlphaRollover] = useState(false);
const [alphaVelocity, setAlphaVelocity] = useState(0.25);
const [seed, setSeed] = useState(getRandomInt(1000000));
// Settings
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
const [promptInputs, setPromptInputs] = useState<PromptInput[]>([]);
@ -330,10 +326,14 @@ export default function Home() {
newPromptInputs[index].prompt = prompt;
setPromptInputs(newPromptInputs);
}}
setPaused={setPaused}
resetCallback={resetCallback}
/>
<Pause paused={paused} setPaused={setPaused} />
<Pause
paused={paused}
setPaused={setPaused}
/>
<Share
inferenceResults={inferenceResults}

View File

@ -3,24 +3,59 @@ export const samplePrompts = [
"lo-fi beat for the holidays",
"techno DJ and a country singer",
"classical italian tenor operatic pop",
"arabic (vocals:1.5)",
"typing",
"chainsaw funk",
"dance pop edm electropop indietronica metropopolis pop uk pop",
"edm electropop indietronica metropopolis",
"kygo tropical dance marvin gaye",
"west coast rap vocals 808",
"detroit hip hop 808",
"church bells",
"funk bassline with a jazzy saxophone solo",
"piano funk",
"a jazz pianist playing a concerto",
"funk bassline with a jazzy saxophone",
"a pianist playing a concerto",
"rock and roll electric guitar solo",
"acoustic folk fiddle jam",
"jamaican rap",
"church organ with a harpsichord",
"late romantic era in the style of Richard Wagner",
"acoustic folk violin jam",
"k-pop boy group",
"typing",
"post-teen pop talent show winner",
"sean paul dancehall",
"mambo but from kenya",
"classical flute",
"swing jazz trumpet",
"tropical deep sea",
"laughing",
"lo-fi beat with a violin",
"bubblegum eurodance",
];
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"
]