diff --git a/public/config.json b/public/config.json index 2c60bbc..2d9e42d 100644 --- a/public/config.json +++ b/public/config.json @@ -58,11 +58,16 @@ "qua_len_1_16": "0" } }, + "editor": { + "playhead_update_frequency": 10 + }, "chatbox": { "default_open": true }, "audio": { - "enable_audio_capture_for_screen_sharing": false + "enable_audio_capture_for_screen_sharing": false, + "lookahead_time": 0.05, + "playback_delay": 0.2 }, "templates": { "custom_instructions": "" diff --git a/src/components/settings/sections/BehaviorSettings.tsx b/src/components/settings/sections/BehaviorSettings.tsx index 82fa4c5..d0f6f9a 100644 --- a/src/components/settings/sections/BehaviorSettings.tsx +++ b/src/components/settings/sections/BehaviorSettings.tsx @@ -1,9 +1,15 @@ import React, { useState, useEffect } from 'react'; import { ConfigManager } from '../../../core/config/ConfigManager'; +import { KGAudioInterface } from '../../../core/audio-interface/KGAudioInterface'; const BehaviorSettings: React.FC = () => { + const [playheadUpdateFrequency, setPlayheadUpdateFrequency] = useState(10); const [chatboxDefaultOpen, setChatboxDefaultOpen] = useState(true); + const [audioLookaheadTime, setAudioLookaheadTime] = useState('50'); + const [playbackDelay, setPlaybackDelay] = useState('200'); const [enableAudioCapture, setEnableAudioCapture] = useState(false); + const [lookaheadValidationErrors, setLookaheadValidationErrors] = useState([]); + const [playbackDelayValidationErrors, setPlaybackDelayValidationErrors] = useState([]); const configManager = ConfigManager.instance(); @@ -14,7 +20,12 @@ const BehaviorSettings: React.FC = () => { await configManager.initialize(); } + setPlayheadUpdateFrequency((configManager.get('editor.playhead_update_frequency') as number) ?? 10); setChatboxDefaultOpen((configManager.get('chatbox.default_open') as boolean) ?? true); + const lookaheadTimeSeconds = (configManager.get('audio.lookahead_time') as number) ?? 0.05; + setAudioLookaheadTime(((lookaheadTimeSeconds * 1000).toFixed(0))); + const playbackDelaySeconds = (configManager.get('audio.playback_delay') as number) ?? 0.2; + setPlaybackDelay(((playbackDelaySeconds * 1000).toFixed(0))); setEnableAudioCapture((configManager.get('audio.enable_audio_capture_for_screen_sharing') as boolean) ?? false); }; @@ -22,12 +33,75 @@ const BehaviorSettings: React.FC = () => { }, [configManager]); // Save configuration when values change + const handlePlayheadUpdateFrequencyChange = async (value: string) => { + const numValue = parseInt(value, 10); + setPlayheadUpdateFrequency(numValue); + await configManager.set('editor.playhead_update_frequency', numValue); + console.log(`Playhead update frequency changed to: ${numValue} fps`); + }; + const handleChatboxDefaultOpenChange = async (value: string) => { const boolValue = value === 'yes'; setChatboxDefaultOpen(boolValue); await configManager.set('chatbox.default_open', boolValue); }; + const handleAudioLookaheadTimeChange = async (value: string) => { + // Allow empty string, treat as 0 ms + const numValueMs = value === '' ? 0 : parseFloat(value); + const numValueSeconds = numValueMs / 1000; + const errors: string[] = []; + + // Validate the input + if (isNaN(numValueMs)) { + errors.push('Lookahead time must be a valid number'); + } else if (numValueSeconds < 0) { + errors.push('Lookahead time must be between 0 and 0.5 seconds (0-500ms)'); + } else if (numValueSeconds > 0.5) { + errors.push('Lookahead time must be between 0 and 0.5 seconds (0-500ms)'); + } + + setLookaheadValidationErrors(errors); + + // Only apply if valid + if (errors.length === 0) { + setAudioLookaheadTime(value); + await configManager.set('audio.lookahead_time', numValueSeconds); + + // Apply the change immediately without restart + const audioInterface = KGAudioInterface.instance(); + audioInterface.setLookaheadTime(numValueSeconds); + + console.log(`Audio lookahead time changed to: ${numValueSeconds}s (${numValueMs}ms)`); + } + }; + + const handlePlaybackDelayChange = async (value: string) => { + // Allow empty string, treat as 0 ms + const numValueMs = value === '' ? 0 : parseFloat(value); + const numValueSeconds = numValueMs / 1000; + const errors: string[] = []; + + // Validate the input + if (isNaN(numValueMs)) { + errors.push('Playback delay must be a valid number'); + } else if (numValueSeconds < 0) { + errors.push('Playback delay must be between 0 and 0.5 seconds (0-500ms)'); + } else if (numValueSeconds > 0.5) { + errors.push('Playback delay must be between 0 and 0.5 seconds (0-500ms)'); + } + + setPlaybackDelayValidationErrors(errors); + + // Only apply if valid + if (errors.length === 0) { + setPlaybackDelay(value); + await configManager.set('audio.playback_delay', numValueSeconds); + + console.log(`Playback delay changed to: ${numValueSeconds}s (${numValueMs}ms)`); + } + }; + const handleEnableAudioCaptureChange = async (value: string) => { const boolValue = value === 'yes'; setEnableAudioCapture(boolValue); @@ -39,8 +113,30 @@ const BehaviorSettings: React.FC = () => {

Behavior

- +
+
+

Editor

+ +
+ + +
+ Update frequency for the playhead animation during playback. Higher values (60 fps) provide smoother animation but use more CPU. Lower values (10 fps) are more efficient. Changes apply immediately without restart. +
+
+
+

Chat Box

@@ -61,7 +157,61 @@ const BehaviorSettings: React.FC = () => {

Audio

- + +
+ + handleAudioLookaheadTimeChange(e.target.value)} + min="0" + max="500" + step="1" + /> + {lookaheadValidationErrors.length > 0 && ( +
+ {lookaheadValidationErrors.map((error, index) => ( +
+ {error} +
+ ))} +
+ )} +
+ Audio scheduling lookahead time (0-500ms). Lower values (10-20ms) reduce MIDI input latency but may cause audio glitches on slower systems. Higher values (100ms+) are better for playback stability. Changes apply immediately without restart. +
+
+ +
+ + handlePlaybackDelayChange(e.target.value)} + min="0" + max="500" + step="1" + /> + {playbackDelayValidationErrors.length > 0 && ( +
+ {playbackDelayValidationErrors.map((error, index) => ( +
+ {error} +
+ ))} +
+ )} +
+ Playback will start with a short delay after pressing the start button (0-500ms). Increasing this value might help stabilize playback, especially for the first few ticks if the lookahead value is too low. Changes apply immediately without restart. +
+
+