Speak dialogue with persistent character voices
Build and publish frontend / publish (push) Successful in 50s
Build and publish frontend / publish (push) Successful in 50s
This commit is contained in:
@@ -44,6 +44,16 @@
|
||||
catch (_) { return false; }
|
||||
}
|
||||
|
||||
function characterVoiceProfile(voiceIdentity, characterId = '') {
|
||||
const identity = ['masculine', 'feminine', 'neutral'].includes(voiceIdentity) ? voiceIdentity : 'neutral';
|
||||
let hash = 2166136261;
|
||||
for (const char of String(characterId)) hash = Math.imul(hash ^ char.charCodeAt(0), 16777619) >>> 0;
|
||||
const variation = (hash % 1000) / 1000;
|
||||
if (identity === 'masculine') return {pitch: 0.88 + variation * 0.08, rate: 0.96 + variation * 0.04};
|
||||
if (identity === 'feminine') return {pitch: 1.06 + variation * 0.08, rate: 0.98 + variation * 0.04};
|
||||
return {pitch: 0.98 + variation * 0.04, rate: 1};
|
||||
}
|
||||
|
||||
function createVoiceController(options = {}) {
|
||||
const win = options.window || root;
|
||||
const onState = options.onState || (() => {});
|
||||
@@ -163,8 +173,11 @@
|
||||
catch (_) { setState('IDLE', 'Talsyntesen kunde inte startas. Dialogen finns kvar som text.'); return false; }
|
||||
utterance.lang = settings.language || 'sv-SE';
|
||||
const profile = voiceOptions.characterId ? settings.speakerProfiles?.[voiceOptions.characterId] || {} : {};
|
||||
utterance.rate = Math.min(2, Math.max(0.5, Number(voiceOptions.rate ?? profile.rate ?? settings.rate) || 1));
|
||||
utterance.pitch = Math.min(2, Math.max(0, Number(voiceOptions.pitch ?? profile.pitch ?? 1) || 1));
|
||||
const identityProfile = characterVoiceProfile(voiceOptions.voiceIdentity, voiceOptions.characterId);
|
||||
const isNarrator = voiceOptions.role === 'narrator';
|
||||
const configuredRate = Number(voiceOptions.rate ?? profile.rate ?? settings.rate) || 1;
|
||||
utterance.rate = Math.min(2, Math.max(0.5, configuredRate * (isNarrator || voiceOptions.rate != null || profile.rate != null ? 1 : identityProfile.rate)));
|
||||
utterance.pitch = isNarrator ? 1 : Math.min(2, Math.max(0, Number(voiceOptions.pitch ?? profile.pitch ?? identityProfile.pitch) || 1));
|
||||
const requestedName = voiceOptions.voiceName || profile.voiceName || settings.voiceName;
|
||||
const voices = availableVoices();
|
||||
utterance.voice = voices.find(voice => voice.name === requestedName)
|
||||
@@ -205,5 +218,5 @@
|
||||
return {startListening, stopListening, speak, cancelSpeech, stopAll, update, getState, getSettings, getVoices, supportsRecognition};
|
||||
}
|
||||
|
||||
root.GameVoice = {createVoiceController, DEFAULT_SETTINGS, parseVoiceCommand, loadSettings, saveSettings};
|
||||
root.GameVoice = {createVoiceController, DEFAULT_SETTINGS, parseVoiceCommand, loadSettings, saveSettings, characterVoiceProfile};
|
||||
})(typeof window !== 'undefined' ? window : globalThis);
|
||||
|
||||
Reference in New Issue
Block a user