Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9c43b875f4 | |||
| 63560e290b | |||
| 1ab8a6a2fe |
@@ -79,8 +79,8 @@ android {
|
|||||||
applicationId "com.ariacockpit"
|
applicationId "com.ariacockpit"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 201
|
versionCode 202
|
||||||
versionName "0.0.2.1"
|
versionName "0.0.2.2"
|
||||||
// Fallback fuer Libraries mit Product Flavors
|
// Fallback fuer Libraries mit Product Flavors
|
||||||
missingDimensionStrategy 'react-native-camera', 'general'
|
missingDimensionStrategy 'react-native-camera', 'general'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "aria-cockpit",
|
"name": "aria-cockpit",
|
||||||
"version": "0.0.2.1",
|
"version": "0.0.2.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"android": "react-native run-android",
|
"android": "react-native run-android",
|
||||||
|
|||||||
@@ -74,7 +74,8 @@ const SettingsScreen: React.FC = () => {
|
|||||||
const [ttsEnabled, setTtsEnabled] = useState(true);
|
const [ttsEnabled, setTtsEnabled] = useState(true);
|
||||||
const [defaultVoice, setDefaultVoice] = useState('ramona');
|
const [defaultVoice, setDefaultVoice] = useState('ramona');
|
||||||
const [highlightVoice, setHighlightVoice] = useState('thorsten');
|
const [highlightVoice, setHighlightVoice] = useState('thorsten');
|
||||||
const [speechSpeed, setSpeechSpeed] = useState(1.0);
|
const [speedRamona, setSpeedRamona] = useState(1.0);
|
||||||
|
const [speedThorsten, setSpeedThorsten] = useState(1.0);
|
||||||
const [editingPath, setEditingPath] = useState(false);
|
const [editingPath, setEditingPath] = useState(false);
|
||||||
const [tempPath, setTempPath] = useState('');
|
const [tempPath, setTempPath] = useState('');
|
||||||
|
|
||||||
@@ -104,8 +105,11 @@ const SettingsScreen: React.FC = () => {
|
|||||||
AsyncStorage.getItem('aria_highlight_voice').then(saved => {
|
AsyncStorage.getItem('aria_highlight_voice').then(saved => {
|
||||||
if (saved) setHighlightVoice(saved);
|
if (saved) setHighlightVoice(saved);
|
||||||
});
|
});
|
||||||
AsyncStorage.getItem('aria_speech_speed').then(saved => {
|
AsyncStorage.getItem('aria_speed_ramona').then(saved => {
|
||||||
if (saved) setSpeechSpeed(parseFloat(saved));
|
if (saved) setSpeedRamona(parseFloat(saved));
|
||||||
|
});
|
||||||
|
AsyncStorage.getItem('aria_speed_thorsten').then(saved => {
|
||||||
|
if (saved) setSpeedThorsten(parseFloat(saved));
|
||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -525,41 +529,49 @@ const SettingsScreen: React.FC = () => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Sprechgeschwindigkeit */}
|
{/* Sprechgeschwindigkeit Ramona */}
|
||||||
<View style={{marginTop: 16}}>
|
<View style={{marginTop: 16}}>
|
||||||
<Text style={styles.toggleLabel}>Sprechgeschwindigkeit: {speechSpeed.toFixed(1)}x</Text>
|
<Text style={styles.toggleLabel}>Ramona Speed: {speedRamona.toFixed(1)}x</Text>
|
||||||
<View style={{flexDirection: 'row', alignItems: 'center', gap: 8, marginTop: 8}}>
|
|
||||||
<Text style={{color: '#555570', fontSize: 11}}>0.5x</Text>
|
|
||||||
<View style={{flex: 1}}>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={{height: 30, justifyContent: 'center'}}
|
|
||||||
onPress={(e) => {
|
|
||||||
const layout = e.nativeEvent;
|
|
||||||
// Einfacher Tap-basierter Slider
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View style={{height: 4, backgroundColor: '#2A2A3E', borderRadius: 2}}>
|
|
||||||
<View style={{height: 4, backgroundColor: '#0096FF', borderRadius: 2, width: `${((speechSpeed - 0.5) / 1.5) * 100}%`}} />
|
|
||||||
</View>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
<Text style={{color: '#555570', fontSize: 11}}>2.0x</Text>
|
|
||||||
</View>
|
|
||||||
<View style={{flexDirection: 'row', justifyContent: 'space-around', marginTop: 8}}>
|
<View style={{flexDirection: 'row', justifyContent: 'space-around', marginTop: 8}}>
|
||||||
{[0.5, 0.75, 1.0, 1.25, 1.5, 2.0].map(speed => (
|
{[0.5, 0.75, 1.0, 1.25, 1.5, 2.0].map(speed => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={speed}
|
key={speed}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
setSpeechSpeed(speed);
|
setSpeedRamona(speed);
|
||||||
AsyncStorage.setItem('aria_speech_speed', String(speed));
|
AsyncStorage.setItem('aria_speed_ramona', String(speed));
|
||||||
rvs.send('config' as any, { speechSpeed: speed });
|
rvs.send('config' as any, { speedRamona: speed });
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
paddingHorizontal: 10, paddingVertical: 6, borderRadius: 6,
|
paddingHorizontal: 10, paddingVertical: 6, borderRadius: 6,
|
||||||
backgroundColor: speechSpeed === speed ? '#0096FF' : '#1E1E2E',
|
backgroundColor: speedRamona === speed ? '#0096FF' : '#1E1E2E',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text style={{color: speechSpeed === speed ? '#fff' : '#8888AA', fontSize: 12, fontWeight: '600'}}>
|
<Text style={{color: speedRamona === speed ? '#fff' : '#8888AA', fontSize: 12, fontWeight: '600'}}>
|
||||||
|
{speed}x
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Sprechgeschwindigkeit Thorsten */}
|
||||||
|
<View style={{marginTop: 16}}>
|
||||||
|
<Text style={styles.toggleLabel}>Thorsten Speed: {speedThorsten.toFixed(1)}x</Text>
|
||||||
|
<View style={{flexDirection: 'row', justifyContent: 'space-around', marginTop: 8}}>
|
||||||
|
{[0.5, 0.75, 1.0, 1.25, 1.5, 2.0].map(speed => (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={speed}
|
||||||
|
onPress={() => {
|
||||||
|
setSpeedThorsten(speed);
|
||||||
|
AsyncStorage.setItem('aria_speed_thorsten', String(speed));
|
||||||
|
rvs.send('config' as any, { speedThorsten: speed });
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
paddingHorizontal: 10, paddingVertical: 6, borderRadius: 6,
|
||||||
|
backgroundColor: speedThorsten === speed ? '#0096FF' : '#1E1E2E',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text style={{color: speedThorsten === speed ? '#fff' : '#8888AA', fontSize: 12, fontWeight: '600'}}>
|
||||||
{speed}x
|
{speed}x
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@@ -736,7 +748,7 @@ const SettingsScreen: React.FC = () => {
|
|||||||
<Text style={styles.sectionTitle}>{'\u00DC'}ber</Text>
|
<Text style={styles.sectionTitle}>{'\u00DC'}ber</Text>
|
||||||
<View style={styles.card}>
|
<View style={styles.card}>
|
||||||
<Text style={styles.aboutTitle}>ARIA Cockpit</Text>
|
<Text style={styles.aboutTitle}>ARIA Cockpit</Text>
|
||||||
<Text style={styles.aboutVersion}>Version 0.0.2.1 </Text>
|
<Text style={styles.aboutVersion}>Version 0.0.2.2 </Text>
|
||||||
<Text style={styles.aboutInfo}>
|
<Text style={styles.aboutInfo}>
|
||||||
Stefans Kommandozentrale f{'\u00FC'}r ARIA.{'\n'}
|
Stefans Kommandozentrale f{'\u00FC'}r ARIA.{'\n'}
|
||||||
Gebaut mit React Native + TypeScript.
|
Gebaut mit React Native + TypeScript.
|
||||||
|
|||||||
+19
-1
@@ -38,6 +38,7 @@ import websockets
|
|||||||
from faster_whisper import WhisperModel
|
from faster_whisper import WhisperModel
|
||||||
from openwakeword.model import Model as WakeWordModel
|
from openwakeword.model import Model as WakeWordModel
|
||||||
from piper import PiperVoice
|
from piper import PiperVoice
|
||||||
|
from piper.config import SynthesisConfig
|
||||||
|
|
||||||
from modes import Mode, detect_mode_switch, should_speak
|
from modes import Mode, detect_mode_switch, should_speak
|
||||||
|
|
||||||
@@ -131,6 +132,7 @@ class VoiceEngine:
|
|||||||
self.voices: dict[str, PiperVoice] = {}
|
self.voices: dict[str, PiperVoice] = {}
|
||||||
self.default_voice = "ramona"
|
self.default_voice = "ramona"
|
||||||
self.highlight_voice = "thorsten"
|
self.highlight_voice = "thorsten"
|
||||||
|
self.speech_speed = {"ramona": 1.0, "thorsten": 1.0}
|
||||||
|
|
||||||
def initialize(self) -> None:
|
def initialize(self) -> None:
|
||||||
"""Laedt die Piper-Stimmen aus dem Voices-Verzeichnis."""
|
"""Laedt die Piper-Stimmen aus dem Voices-Verzeichnis."""
|
||||||
@@ -216,8 +218,10 @@ class VoiceEngine:
|
|||||||
continue
|
continue
|
||||||
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
|
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
|
||||||
tmp_path = tmp.name
|
tmp_path = tmp.name
|
||||||
|
speed = self.speech_speed.get(voice_name, 1.0)
|
||||||
|
syn_config = SynthesisConfig(length_scale=1.0 / max(0.3, speed))
|
||||||
with wave.open(tmp_path, "wb") as wav_file:
|
with wave.open(tmp_path, "wb") as wav_file:
|
||||||
voice.synthesize_wav(sentence, wav_file)
|
voice.synthesize_wav(sentence, wav_file, syn_config=syn_config)
|
||||||
with wave.open(tmp_path, "rb") as wav_file:
|
with wave.open(tmp_path, "rb") as wav_file:
|
||||||
if sample_rate is None:
|
if sample_rate is None:
|
||||||
sample_rate = wav_file.getframerate()
|
sample_rate = wav_file.getframerate()
|
||||||
@@ -494,6 +498,10 @@ class ARIABridge:
|
|||||||
vc = json.load(f)
|
vc = json.load(f)
|
||||||
self.voice_engine.default_voice = vc.get("defaultVoice", "ramona")
|
self.voice_engine.default_voice = vc.get("defaultVoice", "ramona")
|
||||||
self.voice_engine.highlight_voice = vc.get("highlightVoice", "thorsten")
|
self.voice_engine.highlight_voice = vc.get("highlightVoice", "thorsten")
|
||||||
|
self.voice_engine.speech_speed = {
|
||||||
|
"ramona": vc.get("speedRamona", 1.0),
|
||||||
|
"thorsten": vc.get("speedThorsten", 1.0),
|
||||||
|
}
|
||||||
self.tts_enabled = vc.get("ttsEnabled", True)
|
self.tts_enabled = vc.get("ttsEnabled", True)
|
||||||
logger.info("Voice-Config geladen: %s", vc)
|
logger.info("Voice-Config geladen: %s", vc)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -1024,6 +1032,14 @@ class ARIABridge:
|
|||||||
self.tts_enabled = bool(payload["ttsEnabled"])
|
self.tts_enabled = bool(payload["ttsEnabled"])
|
||||||
logger.info("[rvs] TTS %s", "aktiviert" if self.tts_enabled else "deaktiviert")
|
logger.info("[rvs] TTS %s", "aktiviert" if self.tts_enabled else "deaktiviert")
|
||||||
changed = True
|
changed = True
|
||||||
|
if "speedRamona" in payload:
|
||||||
|
self.voice_engine.speech_speed["ramona"] = max(0.3, min(2.0, float(payload["speedRamona"])))
|
||||||
|
logger.info("[rvs] Speed Ramona: %.1f", self.voice_engine.speech_speed["ramona"])
|
||||||
|
changed = True
|
||||||
|
if "speedThorsten" in payload:
|
||||||
|
self.voice_engine.speech_speed["thorsten"] = max(0.3, min(2.0, float(payload["speedThorsten"])))
|
||||||
|
logger.info("[rvs] Speed Thorsten: %.1f", self.voice_engine.speech_speed["thorsten"])
|
||||||
|
changed = True
|
||||||
# Persistent speichern in Shared Volume
|
# Persistent speichern in Shared Volume
|
||||||
if changed:
|
if changed:
|
||||||
try:
|
try:
|
||||||
@@ -1032,6 +1048,8 @@ class ARIABridge:
|
|||||||
"defaultVoice": self.voice_engine.default_voice,
|
"defaultVoice": self.voice_engine.default_voice,
|
||||||
"highlightVoice": self.voice_engine.highlight_voice,
|
"highlightVoice": self.voice_engine.highlight_voice,
|
||||||
"ttsEnabled": getattr(self, "tts_enabled", True),
|
"ttsEnabled": getattr(self, "tts_enabled", True),
|
||||||
|
"speedRamona": self.voice_engine.speech_speed.get("ramona", 1.0),
|
||||||
|
"speedThorsten": self.voice_engine.speech_speed.get("thorsten", 1.0),
|
||||||
}
|
}
|
||||||
with open("/shared/config/voice_config.json", "w") as f:
|
with open("/shared/config/voice_config.json", "w") as f:
|
||||||
json.dump(config_data, f, indent=2)
|
json.dump(config_data, f, indent=2)
|
||||||
|
|||||||
+32
-2
@@ -414,10 +414,32 @@
|
|||||||
<option value="ramona">Ramona (weiblich)</option>
|
<option value="ramona">Ramona (weiblich)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;align-items:center;gap:12px;">
|
<div style="display:flex;align-items:center;gap:12px;margin-bottom:12px;">
|
||||||
<label style="color:#8888AA;font-size:12px;">TTS aktiv:</label>
|
<label style="color:#8888AA;font-size:12px;">TTS aktiv:</label>
|
||||||
<label class="toggle"><input type="checkbox" id="diag-tts-enabled" checked onchange="sendVoiceConfig()"><span class="slider"></span></label>
|
<label class="toggle"><input type="checkbox" id="diag-tts-enabled" checked onchange="sendVoiceConfig()"><span class="slider"></span></label>
|
||||||
</div>
|
</div>
|
||||||
|
<div style="margin-bottom:4px;">
|
||||||
|
<label style="color:#8888AA;font-size:12px;">Ramona Speed: <span id="speed-ramona-label">1.0x</span></label>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:12px;">
|
||||||
|
<span style="color:#555570;font-size:11px;">0.5x</span>
|
||||||
|
<input type="range" id="diag-speed-ramona" min="0.5" max="2.0" step="0.1" value="1.0"
|
||||||
|
oninput="document.getElementById('speed-ramona-label').textContent=this.value+'x'"
|
||||||
|
onchange="sendVoiceConfig()"
|
||||||
|
style="flex:1;accent-color:#0096FF;">
|
||||||
|
<span style="color:#555570;font-size:11px;">2.0x</span>
|
||||||
|
</div>
|
||||||
|
<div style="margin-bottom:4px;">
|
||||||
|
<label style="color:#8888AA;font-size:12px;">Thorsten Speed: <span id="speed-thorsten-label">1.0x</span></label>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex;align-items:center;gap:8px;">
|
||||||
|
<span style="color:#555570;font-size:11px;">0.5x</span>
|
||||||
|
<input type="range" id="diag-speed-thorsten" min="0.5" max="2.0" step="0.1" value="1.0"
|
||||||
|
oninput="document.getElementById('speed-thorsten-label').textContent=this.value+'x'"
|
||||||
|
onchange="sendVoiceConfig()"
|
||||||
|
style="flex:1;accent-color:#0096FF;">
|
||||||
|
<span style="color:#555570;font-size:11px;">2.0x</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -646,6 +668,12 @@
|
|||||||
document.getElementById('diag-default-voice').value = msg.defaultVoice || 'ramona';
|
document.getElementById('diag-default-voice').value = msg.defaultVoice || 'ramona';
|
||||||
document.getElementById('diag-highlight-voice').value = msg.highlightVoice || 'thorsten';
|
document.getElementById('diag-highlight-voice').value = msg.highlightVoice || 'thorsten';
|
||||||
document.getElementById('diag-tts-enabled').checked = msg.ttsEnabled !== false;
|
document.getElementById('diag-tts-enabled').checked = msg.ttsEnabled !== false;
|
||||||
|
const sr = msg.speedRamona || 1.0;
|
||||||
|
const st = msg.speedThorsten || 1.0;
|
||||||
|
document.getElementById('diag-speed-ramona').value = sr;
|
||||||
|
document.getElementById('speed-ramona-label').textContent = sr + 'x';
|
||||||
|
document.getElementById('diag-speed-thorsten').value = st;
|
||||||
|
document.getElementById('speed-thorsten-label').textContent = st + 'x';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1143,7 +1171,9 @@
|
|||||||
const defaultVoice = document.getElementById('diag-default-voice').value;
|
const defaultVoice = document.getElementById('diag-default-voice').value;
|
||||||
const highlightVoice = document.getElementById('diag-highlight-voice').value;
|
const highlightVoice = document.getElementById('diag-highlight-voice').value;
|
||||||
const ttsEnabled = document.getElementById('diag-tts-enabled').checked;
|
const ttsEnabled = document.getElementById('diag-tts-enabled').checked;
|
||||||
send({ action: 'send_voice_config', defaultVoice, highlightVoice, ttsEnabled });
|
const speedRamona = parseFloat(document.getElementById('diag-speed-ramona').value);
|
||||||
|
const speedThorsten = parseFloat(document.getElementById('diag-speed-thorsten').value);
|
||||||
|
send({ action: 'send_voice_config', defaultVoice, highlightVoice, ttsEnabled, speedRamona, speedThorsten });
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Highlight-Trigger ────────────────────────
|
// ── Highlight-Trigger ────────────────────────
|
||||||
|
|||||||
@@ -1135,6 +1135,8 @@ wss.on("connection", (ws) => {
|
|||||||
defaultVoice: msg.defaultVoice || "ramona",
|
defaultVoice: msg.defaultVoice || "ramona",
|
||||||
highlightVoice: msg.highlightVoice || "thorsten",
|
highlightVoice: msg.highlightVoice || "thorsten",
|
||||||
ttsEnabled: msg.ttsEnabled !== false,
|
ttsEnabled: msg.ttsEnabled !== false,
|
||||||
|
speedRamona: msg.speedRamona || 1.0,
|
||||||
|
speedThorsten: msg.speedThorsten || 1.0,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
fs.mkdirSync("/shared/config", { recursive: true });
|
fs.mkdirSync("/shared/config", { recursive: true });
|
||||||
|
|||||||
Reference in New Issue
Block a user