update for demo

main
reng 5 months ago
parent e94ac148f2
commit 06b7c20b90
  1. BIN
      vite/public/assets/0721/onyx/q4-2.mp3
  2. BIN
      vite/public/assets/0721/shimmer/q4-2.mp3
  3. BIN
      vite/public/assets/0721/shimmer/q4.mp3
  4. 23
      vite/public/cuelist_free.json
  5. 2
      vite/public/default.json
  6. 7
      vite/src/comps/debug.jsx
  7. 55
      vite/src/comps/light.jsx
  8. 2
      vite/src/pages/conversation.jsx
  9. 30
      vite/src/pages/flow_free.jsx
  10. 2
      vite/src/util/chat.js

@ -30,7 +30,7 @@
{ {
"id": 4, "id": 4,
"name": "Q4", "name": "Q4",
"type": "phone", "type": "headphone",
"description": "引導撥號", "description": "引導撥號",
"auto": false, "auto": false,
"audioFile": "assets/0721/onyx/q4.mp3", "audioFile": "assets/0721/onyx/q4.mp3",
@ -54,7 +54,8 @@
"auto": true, "auto": true,
"audioFile": "assets/0721/onyx/q4-2.mp3", "audioFile": "assets/0721/onyx/q4-2.mp3",
"nextcue": 4.3, "nextcue": 4.3,
"status":"intro" "status":"intro",
"callback":"fade_out_light"
}, },
{ {
"id": 4.3, "id": 4.3,
@ -77,24 +78,15 @@
{ {
"id": 5.1, "id": 5.1,
"name": "Q5.1", "name": "Q5.1",
"type": "phone",
"description": "引導打給遺憾對象",
"auto": true,
"audioFile": "assets/0721/onyx/q5.mp3",
"nextcue": 5.2
},
{
"id": 5.2,
"name": "Q5.2",
"type": "user_input", "type": "user_input",
"description": "call", "description": "call",
"duration": 60, "duration": 60,
"auto": true, "auto": true,
"nextcue": 5.3 "nextcue": 5.2
}, },
{ {
"id": 5.3, "id": 5.2,
"name": "Q5.3", "name": "Q5.2",
"type": "summary", "type": "summary",
"description": "summary", "description": "summary",
"auto": true, "auto": true,
@ -107,7 +99,8 @@
"type": "space", "type": "space",
"description": "Ending", "description": "Ending",
"audioFile": "assets/q6.mp3", "audioFile": "assets/q6.mp3",
"status":"end" "status":"end",
"callback":"fade_in_light"
} }
] ]
} }

@ -11,7 +11,7 @@
"speech_idle_time":3000, "speech_idle_time":3000,
"sd_prompt_prefix":"a hazy memory of a {{ ", "sd_prompt_prefix":"a hazy memory of a {{ ",
"sd_prompt_suffix":" }}, seen through soft atmospheric blur, distant silhouettes and faded contours, pastel light and cinematic haze, (analog film texture), (shallow depth of field:1.3), shallow depth of field, memory fragment effect, light leak, subtle grain, chromatic aberration, surreal glow, in muted warm tones, cinematic framing,", "sd_prompt_suffix":"}}, soft atmospheric blur, centered ghostly silhouette, fading contours, pastel glow, cinematic haze, (analog film grain), (shallow depth of field:1.3), impressionist style, ethereal light, dreamlike mood, memory fragment haze",
"sd_negative_propmt":"photorealism, digital art, sharp details, hard edges, CGI, anime, cartoon, studio light" "sd_negative_propmt":"photorealism, digital art, sharp details, hard edges, CGI, anime, cartoon, studio light"
} }

@ -3,7 +3,7 @@ import { useData } from '../util/useData.jsx';
const TEST_PROMPT='a hazy memory of a {{ Scene }}, seen through soft atmospheric blur, distant silhouettes and faded contours, pastel light and cinematic haze, (analog film texture), (shallow depth of field:1.3), shallow depth of field, memory fragment effect, light leak, subtle grain, chromatic aberration, surreal glow, in muted warm tones, cinematic framing,'; const TEST_PROMPT='a hazy memory of a {{ Scene }}, seen through soft atmospheric blur, distant silhouettes and faded contours, pastel light and cinematic haze, (analog film texture), (shallow depth of field:1.3), shallow depth of field, memory fragment effect, light leak, subtle grain, chromatic aberration, surreal glow, in muted warm tones, cinematic framing,';
export function DebugControl(){ export function DebugControl({refLight}){
const {data} = useData(); const {data} = useData();
@ -15,7 +15,10 @@ export function DebugControl(){
return ( return (
<div className="grid grid-cols-5 gap-2 [&>button]:rounded-full [&>button]:bg-white bg-gray-200 p-2 w-full justify-center"> <div className="grid grid-cols-5 gap-2 [&>button]:rounded-full [&>button]:bg-white bg-gray-200 p-2 w-full justify-center">
<button onClick={() => sendOsc(OSC_ADDRESS.STATUS, 'reset')}>reset</button> <button onClick={() =>{
sendOsc(OSC_ADDRESS.STATUS, 'reset');
refLight.current.set(1);
}}>reset</button>
<button onClick={() => sendOsc(OSC_ADDRESS.STATUS, 'intro')}>intro</button> <button onClick={() => sendOsc(OSC_ADDRESS.STATUS, 'intro')}>intro</button>
<button onClick={() => sendOsc(OSC_ADDRESS.STATUS, 'go')}>go</button> <button onClick={() => sendOsc(OSC_ADDRESS.STATUS, 'go')}>go</button>
<button onClick={() => sendOsc(OSC_ADDRESS.STATUS, 'end')}>end</button> <button onClick={() => sendOsc(OSC_ADDRESS.STATUS, 'end')}>end</button>

@ -9,23 +9,34 @@ export const Light=forwardRef((props, ref)=>{
const refVal=useRef({val: 0}); const refVal=useRef({val: 0});
const refInput=useRef(); const refInput=useRef();
const refContainer=useRef(); const refContainer=useRef();
const refGsap=useRef();
function fade(from, to){ function fade(from, to){
const time= parseFloat(refInput.current.value) || FADE_TIME; const time= parseFloat(refInput.current.value) || FADE_TIME;
gsap.fromTo(refVal.current,{val: from}, {
gsap.killTweensOf(refVal.current); // Kill all tweens of refVal
gsap.current=gsap.fromTo(refVal.current,{val: from}, {
val: to, val: to,
duration: time, duration: time,
onUpdate: () => { onUpdate: () => {
// console.log(refVal.current.val); // console.log(refVal.current.val);
// sendOsc(OSC_ADDRESS.LIGHT, refVal.current.val.toString()); // sendOsc(OSC_ADDRESS.LIGHT, refVal.current.val.toString());
invoke('send_dmx_message', { // invoke('send_dmx_message', {
message: Math.floor(refVal.current.val * 255).toString(), // message: Math.floor(refVal.current.val * 255).toString(),
}).then(() => { // }).then(() => {
console.log(`dmx message sent: ${Math.floor(refVal.current.val * 255)}`); // console.log(`dmx message sent: ${Math.floor(refVal.current.val * 255)}`);
}).catch((error) => { // }).catch((error) => {
console.error('Error sending DMX message:', error); // console.error('Error sending DMX message:', error);
// });
invoke('send_osc_message', {
key: '/light',
message: refVal.current.val.toString(),
host:`0.0.0.0:0`,
target: '127.0.0.1:8888',
}); });
if(refContainer.current) if(refContainer.current)
@ -41,7 +52,33 @@ export const Light=forwardRef((props, ref)=>{
fadeOut: ()=>{ fadeOut: ()=>{
console.log('fadeOut'); console.log('fadeOut');
fade(1, 0); fade(1, 0);
} },
reset:()=>{
console.log('reset');
refVal.current.val=0;
refContainer.current.style.background= `rgba(0, 255, 0, 0)`; // Reset background color
refInput.current.value=FADE_TIME; // Reset input value
gsap.killTweensOf(refVal.current); // Kill all tweens of refVal
invoke('send_osc_message', {
key: '/light',
message: '0',
host:`0.0.0.0:0`,
target: '127.0.0.1:8888',
});
},
set: (value)=>{
console.log('set', value);
refVal.current.val=value;
refContainer.current.style.background= `rgba(0, 255, 0, ${value})`; // Update background color based on value
invoke('send_osc_message', {
key: '/light',
message: value.toString(),
host:`0.0.0.0:0`,
target: '127.0.0.1:8888',
});
},
})); }));
useEffect(()=>{ useEffect(()=>{
@ -58,6 +95,8 @@ export const Light=forwardRef((props, ref)=>{
<span className="flex flex-col justify-between"> */} <span className="flex flex-col justify-between"> */}
<button onClick={()=>fade(0,1)}>fadeIn</button> <button onClick={()=>fade(0,1)}>fadeIn</button>
<button onClick={()=>fade(1,0)}>fadeOut</button> <button onClick={()=>fade(1,0)}>fadeOut</button>
<button onClick={()=>ref.current.set(1)}>on</button>
<button onClick={()=>ref.current.set(0)}>off</button>
{/* </span> */} {/* </span> */}
</div> </div>
) )

@ -136,7 +136,7 @@ export function Conversation() {
const input = event.target.elements?.input.value || refInput.current?.value; const input = event.target.elements?.input.value || refInput.current?.value;
if(!input.trim()?.length) { if(!input.trim()?.length && !isLastMessage) {
console.warn("Input is empty, ignoring submission."); console.warn("Input is empty, ignoring submission.");
return; return;
} }

@ -19,6 +19,7 @@ const EmojiType={
headphone: '🎧', headphone: '🎧',
speaker: '🔊', speaker: '🔊',
chat: '🤖', chat: '🤖',
chat_end: '🤖',
user_input: '💬', user_input: '💬',
} }
@ -166,6 +167,8 @@ export function FreeFlow(){
} }
if(cue.callback=='fade_in_light') refLight.current.fadeIn(); // Fade in light for conversation start
if(cue.callback=='fade_out_light') refLight.current.fadeOut(); // Fade out light for conversation end
if(cue.audioFile){ if(cue.audioFile){
@ -182,6 +185,9 @@ export function FreeFlow(){
// control unity // control unity
if(cue.status){ if(cue.status){
sendOsc(OSC_ADDRESS.STATUS, cue.status); // Send OSC status message sendOsc(OSC_ADDRESS.STATUS, cue.status); // Send OSC status message
if(cue.status=='reset') {
refLight.current.set(1);
}
} }
if(cue.type=='chat' || cue.type=='user_input') { if(cue.type=='chat' || cue.type=='user_input') {
sendOsc(OSC_ADDRESS.COUNTDOWN, cue.duration || '0'); // Send OSC countdown message sendOsc(OSC_ADDRESS.COUNTDOWN, cue.duration || '0'); // Send OSC countdown message
@ -200,8 +206,6 @@ export function FreeFlow(){
console.log('onCueEnd:', cue.id); console.log('onCueEnd:', cue.id);
if(cue.callback=='start_conversation') refLight.current.fadeOut(); // Fade in light for conversation start
if(cue.callback=='summary') refLight.current.fadeIn(); // Fade out light for conversation end
resetTranscript(); // Reset transcript after cue ends resetTranscript(); // Reset transcript after cue ends
@ -247,8 +251,10 @@ export function FreeFlow(){
},[userId]); },[userId]);
function onSpeechEnd(){ function onSpeechEnd(){
console.log('onSpeechEnd:', finalTranscript);
if(currentCue?.type!='chat') return; // Only process if current cue is user input if(currentCue?.type!='chat') return; // Only process if current cue is user input
console.log('onSpeechEnd:', finalTranscript);
if(autoSend && transcript.trim().length > 0) { if(autoSend && transcript.trim().length > 0) {
console.log('Auto sending transcript:', transcript); console.log('Auto sending transcript:', transcript);
@ -351,7 +357,7 @@ export function FreeFlow(){
return ( return (
<main className="items-start"> <main className="items-start">
<section className="flex-1 flex flex-col gap-2 self-stretch overflow-hidden"> <section className="flex-1 flex flex-col gap-2 self-stretch overflow-hidden">
<DebugControl/> <DebugControl refLight={refLight}/>
<div className="w-full p-2 grid grid-cols-4 gap-2 items-stretch justify-center *:max-h-[5rem]"> <div className="w-full p-2 grid grid-cols-4 gap-2 items-stretch justify-center *:max-h-[5rem]">
<div className="bg-gray-100 text-4xl font-bold mb-4 flex justify-center items-center"> <div className="bg-gray-100 text-4xl font-bold mb-4 flex justify-center items-center">
{refCurrentCue.current?.name} {refCurrentCue.current?.name}
@ -373,33 +379,35 @@ export function FreeFlow(){
}}>Save Log</button> }}>Save Log</button>
</div> </div>
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto">
<table className="border-collapse **:border-y w-full **:p-2"> <table className="border-collapse **:border-y w-full **:p-2 text-sm">
<thead> <thead>
<tr className="text-left"> <tr className="text-left">
{/* <th>ID</th> */} {/* <th>ID</th> */}
<th></th>
<th>Name</th> <th>Name</th>
<th>Description</th> <th>Description</th>
<th>Type</th> <th>Type</th>
<th>Auto</th> <th>Auto</th>
<th>Audio / Due</th> <th>Audio / Due</th>
<th></th> <th>Action</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{cuelist?.map(({id, name, description, type, auto, audioFile,...props}, index) => ( {cuelist?.map(({id, name, description, type, auto, audioFile,...props}, index) => (
<tr key={id}> <tr key={id}>
{/* <td>{id}</td> */} {/* <td>{id}</td> */}
<td>{name}</td>
<td>{description}</td>
<td>{EmojiType[type]}</td>
<td>{auto ? '⤵' : ''}</td>
<td>{audioFile || props.duration} {props.callback && `<${props.callback}>`}</td>
<td> <td>
<button className="rounded-full !bg-green-200" <button className="rounded-full !bg-green-200"
onClick={()=>{ onClick={()=>{
playCue({id, name, description, type, auto, audioFile, ...props}); playCue({id, name, description, type, auto, audioFile, ...props});
}}>go</button> }}>go</button>
</td> </td>
<td>{name}</td>
<td>{description}</td>
<td>{EmojiType[type]}</td>
<td>{auto ? '⤵' : ''}</td>
<td>{audioFile || props.duration}</td>
<td>{props.callback && `<${props.callback}>`}{props.status && `(${props.status})`}</td>
</tr> </tr>
))} ))}
</tbody> </tbody>

@ -79,7 +79,7 @@ export async function sendChatMessage(messages, data, isLastMessage = false) {
}, },
"prompt": { "prompt": {
"type": "string", "type": "string",
"description": "The generated image prompt based on the user's input and the system's guidance." "description": "The generated image prompt based on the user's input and the system's guidance. Less than 60 English characters."
} }
}, },
required: ["output_text", "prompt"], required: ["output_text", "prompt"],

Loading…
Cancel
Save