diff --git a/vite/public/assets/0721/onyx/q4-1.mp3 b/vite/public/assets/0721/onyx/q4-1.mp3 new file mode 100644 index 0000000..7f5a693 Binary files /dev/null and b/vite/public/assets/0721/onyx/q4-1.mp3 differ diff --git a/vite/public/assets/0721/onyx/q4-2.mp3 b/vite/public/assets/0721/onyx/q4-2.mp3 new file mode 100644 index 0000000..e0cdfd1 Binary files /dev/null and b/vite/public/assets/0721/onyx/q4-2.mp3 differ diff --git a/vite/public/assets/0721/onyx/q4.mp3 b/vite/public/assets/0721/onyx/q4.mp3 new file mode 100644 index 0000000..42cf184 Binary files /dev/null and b/vite/public/assets/0721/onyx/q4.mp3 differ diff --git a/vite/public/assets/0721/onyx/q5.mp3 b/vite/public/assets/0721/onyx/q5.mp3 new file mode 100644 index 0000000..0935b57 Binary files /dev/null and b/vite/public/assets/0721/onyx/q5.mp3 differ diff --git a/vite/public/assets/0721/shimmer/q4-1.mp3 b/vite/public/assets/0721/shimmer/q4-1.mp3 new file mode 100644 index 0000000..f48929e Binary files /dev/null and b/vite/public/assets/0721/shimmer/q4-1.mp3 differ diff --git a/vite/public/assets/0721/shimmer/q4-2.mp3 b/vite/public/assets/0721/shimmer/q4-2.mp3 new file mode 100644 index 0000000..dc51477 Binary files /dev/null and b/vite/public/assets/0721/shimmer/q4-2.mp3 differ diff --git a/vite/public/assets/0721/shimmer/q4.mp3 b/vite/public/assets/0721/shimmer/q4.mp3 new file mode 100644 index 0000000..4d8c829 Binary files /dev/null and b/vite/public/assets/0721/shimmer/q4.mp3 differ diff --git a/vite/public/assets/0721/shimmer/q5.mp3 b/vite/public/assets/0721/shimmer/q5.mp3 new file mode 100644 index 0000000..5d3a36f Binary files /dev/null and b/vite/public/assets/0721/shimmer/q5.mp3 differ diff --git a/vite/public/cuelist_free.json b/vite/public/cuelist_free.json index f8a785a..42aac57 100644 --- a/vite/public/cuelist_free.json +++ b/vite/public/cuelist_free.json @@ -31,9 +31,9 @@ "id": 4, "name": "Q4", "type": "phone", - "description": "Guide to call", + "description": "引導撥號", "auto": false, - "audioFile": "assets/q4.mp3", + "audioFile": "assets/0721/onyx/q4.mp3", "nextcue": 4.1, "callback":"numpad" }, @@ -41,42 +41,60 @@ "id": 4.1, "name": "Q4.1", "type": "phone", - "description": "Guide to construct scene", + "description": "電話開頭", "auto": true, - "audioFile": "assets/q4-1.mp3", - "nextcue": 4.2, - "status":"intro" + "audioFile": "assets/0721/onyx/q4-1.mp3", + "nextcue": 4.2 }, { "id": 4.2, "name": "Q4.2", + "type": "phone", + "description": "示範影片,引導回憶", + "auto": true, + "audioFile": "assets/0721/onyx/q4-2.mp3", + "nextcue": 4.3, + "status":"intro" + }, + { + "id": 4.3, + "name": "Q4.3", "type": "chat", "description": "chat", "auto": true, - "nextcue": 4.3, + "nextcue": 4.4, "duration": 90, "status":"go" }, { - "id": 4.3, - "name": "Q4.3", + "id": 4.4, + "name": "Q4.4", "type": "chat_end", - "description": "chat end", + "description": "對話收尾", + "auto": true, + "nextcue": 5.1 + }, + { + "id": 5.1, + "name": "Q5.1", + "type": "phone", + "description": "引導打給遺憾對象", "auto": true, - "nextcue": 5 + "audioFile": "assets/0721/onyx/q5.mp3", + "nextcue": 5.2 }, { - "id": 5, - "name": "Q5", + "id": 5.2, + "name": "Q5.2", "type": "user_input", "description": "call", "duration": 60, "auto": true, - "nextcue": 5.1 + "nextcue": 5.3 }, { - "id": 5.1, - "name": "Q5.1", + "id": 5.3, + "name": "Q5.3", "type": "summary", "description": "summary", "auto": true, diff --git a/vite/src-tauri/Cargo.lock b/vite/src-tauri/Cargo.lock index 14f5f6e..84f2672 100644 --- a/vite/src-tauri/Cargo.lock +++ b/vite/src-tauri/Cargo.lock @@ -95,6 +95,7 @@ name = "app" version = "0.1.0" dependencies = [ "dotenv", + "enttecopendmx", "log", "rosc", "serde", @@ -1025,6 +1026,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +[[package]] +name = "enttecopendmx" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439b5a6167b4d55c80838155742c65c5159f76ee4acd25324bdeb142828aa0c5" +dependencies = [ + "libftd2xx", +] + [[package]] name = "enumflags2" version = "0.7.12" @@ -2137,6 +2147,27 @@ version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +[[package]] +name = "libftd2xx" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b3be3aa1917532fb3918dde5fda1c82d7169d2bbd2e79353bac2cd3985e68a" +dependencies = [ + "libftd2xx-ffi", + "log", + "paste", + "static_assertions", +] + +[[package]] +name = "libftd2xx-ffi" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd82d23ae0cbbf0fb65ad0310b474e45ddfdece8aa03a34130c59c04333c701" +dependencies = [ + "cfg-if", +] + [[package]] name = "libloading" version = "0.7.4" @@ -2716,6 +2747,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pathdiff" version = "0.2.3" diff --git a/vite/src-tauri/Cargo.toml b/vite/src-tauri/Cargo.toml index e83b68e..1f8aae5 100644 --- a/vite/src-tauri/Cargo.toml +++ b/vite/src-tauri/Cargo.toml @@ -31,3 +31,4 @@ webview2-com = "0.37.0" windows = "0.61.1" tauri-plugin-fs = "2" tauri-plugin-opener = "2" +enttecopendmx ="0.1.0" \ No newline at end of file diff --git a/vite/src-tauri/src/lib.rs b/vite/src-tauri/src/lib.rs index dbf6c6a..05026a2 100644 --- a/vite/src-tauri/src/lib.rs +++ b/vite/src-tauri/src/lib.rs @@ -85,7 +85,8 @@ pub fn run() { .invoke_handler(tauri::generate_handler![ get_env, send_osc_message, - reset_permission + reset_permission, + send_dmx_message ]) .plugin(tauri_plugin_http::init()) .setup(|app| { @@ -101,3 +102,29 @@ pub fn run() { .run(tauri::generate_context!()) .expect("error while running tauri application"); } + +static mut LIGHT: Option = None; + +#[tauri::command] +fn send_dmx_message(message: &str) -> Result<(), String> { + println!("Sending DMX message: {}", message); + + let val = message.parse::().map_err(|e| e.to_string())?; + + unsafe { + if LIGHT.is_none() { + let mut dmx = enttecopendmx::EnttecOpenDMX::new().map_err(|e| e.to_string())?; + dmx.open().map_err(|e| e.to_string())?; + LIGHT = Some(dmx); + } + + if let Some(ref mut dmx) = LIGHT { + dmx.set_channel(1, val); + dmx.render().unwrap(); + } else { + return Err("DMX interface not available".into()); + } + } + + Ok(()) +} \ No newline at end of file diff --git a/vite/src/App.jsx b/vite/src/App.jsx index a9f1881..7df1f47 100644 --- a/vite/src/App.jsx +++ b/vite/src/App.jsx @@ -9,7 +9,7 @@ function App() { return (
Conversation - Flow + {/* Flow */} Free Flow Settings
diff --git a/vite/src/comps/light.jsx b/vite/src/comps/light.jsx index 53bed0c..2ed898b 100644 --- a/vite/src/comps/light.jsx +++ b/vite/src/comps/light.jsx @@ -1,8 +1,8 @@ import gsap from "gsap" import { forwardRef, useEffect, useImperativeHandle, useRef } from "react" -import { OSC_ADDRESS, sendOsc } from "../util/osc"; +import { invoke } from '@tauri-apps/api/core'; -const FADE_TIME=3; +const FADE_TIME=5; export const Light=forwardRef((props, ref)=>{ @@ -18,7 +18,16 @@ export const Light=forwardRef((props, ref)=>{ duration: time, onUpdate: () => { // 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', { + message: Math.floor(refVal.current.val * 255).toString(), + }).then(() => { + console.log(`dmx message sent: ${Math.floor(refVal.current.val * 255)}`); + }).catch((error) => { + console.error('Error sending DMX message:', error); + }); + if(refContainer.current) refContainer.current.style.background= `rgba(0, 255, 0, ${refVal.current.val})`; // Update background color based on value }, @@ -41,15 +50,15 @@ export const Light=forwardRef((props, ref)=>{ },[]); return ( -
- +
+ {/* */} - - + {/* + */} - + {/* */}
) }); \ No newline at end of file diff --git a/vite/src/pages/flow_free.jsx b/vite/src/pages/flow_free.jsx index a253d1a..2a59fc1 100644 --- a/vite/src/pages/flow_free.jsx +++ b/vite/src/pages/flow_free.jsx @@ -28,6 +28,11 @@ const ChatStatus={ Processing: 'processing', } +const Voice={ + ONYX: 'onyx', + SHIMMER: 'shimmer', +} + export function FreeFlow(){ const { data }=useData(); @@ -39,6 +44,8 @@ export function FreeFlow(){ const [autoSend, setAutoSend] = useState(true); const [userId, setUserId] = useState(); const [summary, setSummary] = useState(null); + const [voice, setVoice] = useState(Voice.ONYX); + const [chatStatus, setChatStatus] = useState(ChatStatus.System); // System, User, Processing @@ -78,7 +85,11 @@ export function FreeFlow(){ refAudio.current.pause(); // Stop any currently playing audio } - const audio = new Audio(url); + let audioUrl = url; + if(voice==Voice.SHIMMER) audioUrl = url.replace(Voice.ONYX, Voice.SHIMMER); + console.log('Using voice:', voice, 'for audio:', audioUrl); + + const audio = new Audio(audioUrl); audio.loop=refCurrentCue.current?.loop || false; // Set loop if defined in cue audio.play().catch(error => { console.error("Audio playback error:", error); @@ -122,14 +133,14 @@ export function FreeFlow(){ // Special case for starting a conversation resetTranscript(); console.log('Starting conversation...'); - sendMessage(); + sendMessage(null, false, false, voice); // Send initial message with voice setChatWelcome(true); resetData(); // Reset data for new conversation break; case 'chat_end': const message= refInput.current?.value?.trim(); console.log('Ending conversation with message:', message); - sendMessage(message, false, true); + sendMessage(message, false, true, voice); setChatWelcome(false); break; @@ -246,7 +257,7 @@ export function FreeFlow(){ const message= refInput.current?.value?.trim(); if(message && message.length>0) { console.log('Ending conversation with message:', message); - sendMessage(message, false, false); + sendMessage(message, false, false, voice); setChatWelcome(false); setChatStatus(ChatStatus.Processing); // Set chat status to Processing @@ -341,19 +352,25 @@ export function FreeFlow(){
-
+
{refCurrentCue.current?.name}
- - + + {/* */} +
+ + + +
+
diff --git a/vite/src/util/osc.js b/vite/src/util/osc.js index d17f297..6ed8fa4 100644 --- a/vite/src/util/osc.js +++ b/vite/src/util/osc.js @@ -20,13 +20,18 @@ export async function sendOsc(key, message){ console.warn('sendOsc: message is empty, skipping'); return; } - console.log(`Sending OSC message: ${key} -> ${message}`); - await invoke('send_osc_message', { - key: key, - message: message.toString(), - host:`0.0.0.0:0`, - target: '127.0.0.1:9000', - }); + + try{ + console.log(`Sending OSC message: ${key} -> ${message}`); + await invoke('send_osc_message', { + key: key, + message: message.toString(), + host:`0.0.0.0:0`, + target: '127.0.0.1:9000', + }); + }catch (error){ + console.error('Error sending OSC message:', error); + } } @@ -43,7 +48,6 @@ export async function updatePrompt(prompt) { body: JSON.stringify({ prompt }) }); }catch(error){ - console.error('Error updating prompt:', error); - throw error; + console.error('Error updating prompt:', error); } } \ No newline at end of file diff --git a/vite/src/util/useChat.jsx b/vite/src/util/useChat.jsx index 4a2295c..c36196a 100644 --- a/vite/src/util/useChat.jsx +++ b/vite/src/util/useChat.jsx @@ -35,7 +35,7 @@ export function ChatProvider({children}){ } - function sendMessage(message, force_no_audio=false, isLastMessage=false) { + function sendMessage(message, force_no_audio=false, isLastMessage=false, voice=null) { console.log('Sending chat message:', message); setStatus(Status.PROCESSING_TEXT); @@ -60,7 +60,7 @@ export function ChatProvider({children}){ if(response.output_text && (!force_no_audio && audioOutput)){ setStatus(Status.PROCESSING_AUDIO); - textToSpeech(response.output_text, data?.voice, data?.voice_prompt).then(url => { + textToSpeech(response.output_text, voice||data?.voice, data?.voice_prompt).then(url => { setStatus(Status.SUCCESS); setAudioUrl(url); // Store the audio URL