Всего несколько дней назад я видел, как кто-то (nhudinhtuan) создал помощник по распознаванию голоса с помощью экспериментального Chrome WebSpeechAPI. Приложение-помощник будет слушать человеческий голос и преобразовывать его в обычный текст. После всего этого откроется новая вкладка Google Search.
Попробуйте по ссылке здесь. (Поддерживает только браузер Chrome / новый Edge)
Я думаю, это довольно забавно - создать какой-нибудь интересный проект с API, который почти никогда не используется. Поэтому я решил сделать обновленную версию этого голосового помощника. В моей версии нет ничего особенного, но она реализована с помощью Vue.js и его нового API композиции.
В этой статье я расскажу вам, как создать этого сексуального голосового помощника.
TL; DR;
Демонстрация моего голосового помощника
Обсуждение дешево, позвольте мне показать вам свои работы
Удерживайте кнопку «Удерживайте, чтобы слушать» и попробуйте с некоторыми из следующих предложений (работает только в Chrome / Edge79 +)
- Здравствуйте!
- Hi!
- Как вас зовут?
- Я люблю тебя
- Поиск в Google:
- … голый…
(откройте эту ссылку, чтобы ознакомиться с демонстрацией https://daiyanze.com/sexy-voice-assistant/)
Вы также можете изменить голос динамика из списка выбора справа. В настоящее время мой Mac поддерживает еще 60 голосов. Лично я считаю, что Карен была одним из самых умных англоговорящих роботов.
Я предполагаю, что вы, должно быть, пробовали говорить с голосовым помощником какие-то другие вещи. Но мне очень жаль сообщить вам, что он может ответить вам должным образом только в том случае, если ваша речь соответствует приведенным выше примерам. Иначе этого не понять. (Хотя это прототип)
Как это работает
Давайте кратко рассмотрим API-интерфейсы обработки голоса, используемые в голосовом помощнике. Это ключевые показатели, благодаря которым голосовой помощник становится «сексуальным».
API
- SpeechRecognition: распознайте свой голос и превратите его в текст
// Needs the `webkit` prefix due to lacking of browsers support var recognition = webkitSpeechRecognition() // Set to `true` to listen to voices // continuously and return multiple results. recognition.continuous = true; // Start recognition recognition.start() // Stop recognition recognition.stop() // Handle the result recognition.onresult = function (event) { console.log(event) }
- SpeechSynthesis: получите все поддерживаемые голоса и говорите вслух
var Synth = window.speechSynthesis // Speak out loud Synth.speak(utterThis) // Cancel speaking Synth.cancel() // Pause speaking Synth.pause() // Resume speaking Synth.resume() // List out voices supported by the OS Synth.getVoices()
- SpeechSynthesisUtterance: объект запроса речи
var utterThis = new SpeechSynthesisUtterance() // Utterance only has properties // The spoken language utterThis.lang // The tone or the pitch or voice, ranges from 0 to 2. // The higher the younger :D utterThis.pitch // Speaking speed, ranges from 0.1 to 10 utterThis.rate // The content to be spoken utterThis.text // Speaker's voice. It's an Object utterThis.voice // Speaker's volume utterThis.volume
На самом деле эти API созданы для людей, которым необходимо использовать голоса для управления веб-страницами, кроме как в качестве чат-бота. Другими словами, когда ваша команда завершит работу над большинством надежных функций. Тогда настало время повысить доступность вашего веб-приложения, чтобы помочь тем людям, которым требуется голосовое управление.
Примеры использования
Вы можете попробовать это в консоли Chrome, скопировав и вставив приведенный ниже код.
1. Голосовой динамик: следующий код произнесет «привет, мир» вслух.
const utterThis = new SpeechSynthesisUtterance("Hello world") speechSynthesis.speak(utterThis)
2. Голос в текст: следующий код превратит ваш голос в текст.
const SpeechRecognition = new webkitSpeechRecognition() // record the voice for 5 seconds SpeechRecognition.start() setTimeout(() => { SpeechRecognition.stop() }, 5000) SpeechRecognition.onresult = event => { const result = event.results[event.results.length - 1] if (result.isFinal) { console.log(result[0].transcript) } }
(Вот результат)
Дизайн
После некоторой практики с этими API-интерфейсами я смог создать речевой ретранслятор с примерами кодов выше. Но нам нужен другой текстовый процессор, чтобы получать мои сообщения и возвращать правильный ответ.
Рабочий процесс
Довольно просто, не правда ли?
Кратко рабочий процесс: «говорить», «процесс» и «ответить».
Управление
Дизайн был простым и легким для начала. Теперь нам нужны триггеры, которые включают / отключают прослушивание голосов. Итак, здесь есть две «кнопки»:
- Удерживайте для прослушивания Удерживайте эту кнопку для записи голоса
- Сброс Нажмите эту кнопку, чтобы отменить все и сбросить все состояния
Я надеюсь, что смогу также сделать голоса «видимыми», чтобы иметь интуитивно понятную историю моих чатов. Это означает, что мне нужно такое окно сообщения:
Me: Hello! You: Hi!
Ладно… Я сделал все для своего голосового помощника. Теперь мы можем приступить к кодированию.
Погрузитесь в кодирование…
HTML-шаблон
Я использую Vue.js в качестве своего внешнего фреймворка, так что внешний вид голосового помощника можно очень легко прототипировать. Что касается дизайна, который я сделал, я придумал очень элементарный шаблон, подобный следующему.
<template>
<div>
<pre></pre>
<button>Hold to Listen</button>
<button>Reset</button>
</div>
</template>
Контроллер
Чтобы включить элементы управления, я добавил в шаблон несколько функций и свойств. Теперь шаблон выглядит так:
<template>
<div>
<pre class="message-box" v-html="context"></pre>
<button
class="button-control"
@mousedown="listen(true)"
@mouseup="listen(false)">
Hold to Listen
</button>
<button class="button-control" @click="reset">Reset</button>
</div>
</template>
Что касается логической части, я использовал vue-композиция-api, чтобы вернуть свойства для шаблона. Этих кодов ниже достаточно, чтобы отпустить ошибки, пока включаются кнопки. Прямо сейчас это просто скелет.
<script> import { toRefs, reactive } from 'vue-composition-api'
export default { setup () { const state = reactive({ context: 'Hello!' })
const listen = start => {}
const reset = () => {}
return { ...toRefs(state), listen } } } </script>
Узнай голос
Цель довольно ясна:
Разрешить браузеру преобразовывать мой голос в текст.
Я буду использовать функцию listen
, чтобы управлять распознаванием голоса. И настройте функцию обратного вызова для обработки результата распознавания.
const SpeechRecognition = window.webkitSpeechRecognition && new window.webkitSpeechRecognition() SpeechRecognition && (SpeechRecognition.interimResults = true)
const listen = start => { if (!SpeechRecognition) return
if (start) { SpeechRecognition.start() } else { SpeechRecognition.stop() } }
const reset = () => { if (!SpeechRecognition) return
state.context = '<b>' + state.name + '</b>' + ': Hi, my hero!' speechSynthesis.cancel() }
SpeechRecognition.onresult = event => { const result = event.results[event.results.length - 1]
if (result.isFinal) { state.context = result[0].transcript } }
Создать текст ответа
Я создаю не всемогущего чат-бота с ИИ (для меня это слишком сложно), а что-то достаточно, чтобы иметь дело с текстом. Короче говоря, моя идея - создать процессор, который будет генерировать текст ответа на основе того, что я сказал.
Я просто сделаю имя функции process
. С некоторым if else
заявлением мы закончили.
const process = (msg, voice) => { const content = msg.toLowerCase()
if (/^google search/g.test(content)) { const url = `https://google.com/search?q=${msg.replace('Google search ', '')}` window.open(url, '_blank') return `Base on your query, I found some search results on Google` } else if (/your name/g.test(content)) { return `My name is ${voice.name}.` } else if (/(hello|hi)/g.test(content)) { return 'Hi! Nice to meet you!' } else if (/love you/g.test(content)) { return 'I love you too. And I\'ll love you forever!' } else if (/(naked|nude|tits|breast|butt|ass|shit|dick|pussy|asshole)/g.test(content)) { return 'I know I love you but can you show some politeness in front of a lady?' }
return 'Sorry, my sweetheart. I don\'t understand. Could you try something else?' }
Сделайте небольшой тест: когда я говорю: «Я люблю тебя».
process('I love you')
// I love you too. And I'll love you forever!
Настройте «сексуальный» голос
На самом деле в нашей ОС спрятано много разных говорящих голосов. API SpeechSynthesis позволяет нам использовать эти голоса для замены голоса по умолчанию.
var voices = window.speechSynthesis.getVoices()
Возьмем, к примеру, мой Mac: на выбор есть 66 голосов.
speechSynthesis.getVoices
непредсказуем, необходимо убедиться, что он возвращает непустой список. Поэтому я сделал еще одну асинхронную функцию, чтобы разрешить список, настойчиво проверив его.
const getVoices = async (window) => { let id, res
await new Promise((resolve, reject) => { id = setInterval(() => { res = window.speechSynthesis.getVoices() if (res.length !== 0) { resolve(res) clearInterval(id) } }, 10) })
return res }
Чтобы убедиться, что голос достаточно «сексуальный», я протестировал все голоса. Для женского голоса я выбираю «Карен».
2 голоса за «Карен»:
- Она говорит достаточно гладко
- Небольшая настройка высоты звука сделает ее голос более привлекательным.
onMounted(async () => {
const voices = await getVoices()
const voiceKaren = voices[17]
})
Говорить вслух
У меня есть все необходимые ресурсы. Затем следующим шагом будет использование процессора ответов для создания текста и разрешение Speaker Speaker отображать звук. Время разговора сразу после распознавания голоса.
onMounted(async () => { const voices = await getVoices() const voiceKaren = voices[17]
// The final function to speak out the response text const speechSpeaker = (text, voice) => { utterThis.text = text utterThis.voice = voice utterThis.pitch = 1.4 // This sounds better utterThis.lang = voice.lang window.speechSynthesis.speak(utterThis) }
SpeechRecognition.onresult = event => { const result = event.results[event.results.length - 1]
if (result.isFinal) { state.context = '\n<b>Me:</b> ' + result[0].transcript
const reply = process(result[0].transcript, voice)
state.context += '\n<b>' + voice.name + '</b>: ' + reply
speechSpeaker(reply, voiceKaren) } } })
После нескольких тестов мне кажется странным слышать ответный голос сразу после того, как я говорю. Так что было бы естественнее немного задержать ответ на 600 мс.
SpeechRecognition.onresult = event => { const result = event.results[event.results.length - 1]
if (result.isFinal) { state.context = '\n<b>Me:</b> ' + result[0].transcript
const reply = process(result[0].transcript, voice)
setTimeout(() => { state.context += '\n<b>' + voice.name + '</b>: ' + reply
speechSpeaker(reply, voiceKaren) }, 600) } }
Укладка
Я использовал Tailwindcss для стильного оформления своего голосового помощника, потому что он предлагает больше возможностей для настройки.
<style></style>
выглядит так:
.button-control { @apply font-semibold; @apply px-4 py-2; @apply mr-2 mb-2; @apply bg-gray-600; @apply rounded-sm; @apply shadow-2xl; @apply text-white;
&:focus { @apply outline-none; }
&:active { @apply bg-gray-700; @apply shadow-none; } }
.message-box { @apply relative; @apply h-48; @apply overflow-y-scroll; @apply rounded-sm; @apply border border-solid border-gray-300; @apply p-2; @apply mb-2; @apply leading-6; }
.voice-options { @apply font-semibold; @apply bg-gray-100; @apply px-2 py-1; @apply mr-2 mb-2; @apply rounded-sm; @apply border-2 border-solid border-gray-100; @apply shadow-md;
&:focus { @apply outline-none; } }
Оптимизация UX
Теперь у меня есть 2 хорошие идеи, чтобы сделать его лучше:
- Разрешить выбор голоса из раскрывающегося списка
- Позвольте полосе прокрутки окна сообщения следовать за последним сообщением
Благодаря этим двум идеям появился мой последний прототип «сексуального» голосового помощника.
Позвольте мне просто показать вам полный исходный код:
<template> <div class="mx-auto w-full"> <pre class="message-box" v-html="context" ref="messageBox"></pre> <button class="button-control" @mousedown="listen(true)" @mouseup="listen(false)">Hold to Listen</button> <button class="button-control" @click="reset">Reset</button> <select class="voice-options" v-model="activeVoiceIdx"> <option :key="key" :value="key" :selected="key === activeVoiceIdx" v-for="(val, key) in voices"> {{ val.name }} ({{ val.lang }}) </option> </select> </div> </template>
<script> import { onMounted, reactive, toRefs, ref } from '@vue/composition-api'
export default { setup (_, { root }) { const getVoices = async (window) => { let id, res
await new Promise((resolve, reject) => { id = setInterval(() => { res = window.speechSynthesis.getVoices() if (res.length !== 0) { resolve(res) clearInterval(id) } }, 10) })
return res }
const process = (msg, voice) => { const content = msg.toLowerCase()
if (/^google search/g.test(content)) { const url = `https://google.com/search?q=${msg.replace('Google search ', '')}` window.open(url, '_blank') return `Base on your query, I found some search results on Google` } else if (/your name/g.test(content)) { return `My name is ${voice.name}.` } else if (/(hello|hi)/g.test(content)) { return 'Hi! Nice to meet you!' } else if (/love you/g.test(content)) { return 'I love you too. And I\'ll love you forever!' } else if (/(naked|nude|tits|breast|butt|ass|shit|dick|pussy|asshole)/g.test(content)) { return 'I know I love you but can you show some politeness in front of a lady?' }
return 'Sorry, my sweetheart. I don\'t understand. Could you try something else?' }
const state = reactive({ name: '', context: 'Sorry, your browser doesn\'t support SpeechRecognition. Please use Chrome / Edge79+ instead.', voices: [], activeVoiceIdx: 17 })
const messageBox = ref(null)
const SpeechRecognition = window.webkitSpeechRecognition && new window.webkitSpeechRecognition() SpeechRecognition && (SpeechRecognition.interimResults = true)
const listen = start => { if (!SpeechRecognition) return
if (start) { SpeechRecognition.start() } else { SpeechRecognition.stop() } }
const reset = () => { if (!SpeechRecognition) return
state.context = '<b>' + state.name + '</b>' + ': Hi, my hero!' speechSynthesis.cancel() }
onMounted(async () => {
if (!window.webkitSpeechRecognition) return
state.voices = await getVoices(window) state.name = state.voices[state.activeVoiceIdx].name state.context = '<b>' + state.name + '</b>' + ': Hi, my hero!'
const utterThis = new window.SpeechSynthesisUtterance()
const speechSpeaker = (text, voice) => { utterThis.text = text utterThis.voice = voice utterThis.pitch = 1.4 utterThis.lang = voice.lang window.speechSynthesis.speak(utterThis) }
SpeechRecognition.onresult = event => { const result = event.results[event.results.length - 1]
if (result.isFinal) { state.context += '\n<b>Me:</b> ' + result[0].transcript
const voice = state.voices[state.activeVoiceIdx] const reply = process(result[0].transcript, voice) root.$nextTick(() => { messageBox.value.scrollTop = messageBox.value.scrollHeight })
setTimeout(() => { state.context += '\n<b>' + voice.name + '</b>: ' + reply
root.$nextTick(() => { messageBox.value.scrollTop = messageBox.value.scrollHeight })
speechSpeaker(reply, voice) }, 600) } }
})
return { ...toRefs(state), listen, reset, messageBox } } } </script>
<style lang="postcss" scoped> .button-control { @apply font-semibold; @apply px-4 py-2; @apply mr-2 mb-2; @apply bg-gray-600; @apply rounded-sm; @apply shadow-2xl; @apply text-white;
&:focus { @apply outline-none; }
&:active { @apply bg-gray-700; @apply shadow-none; } }
.message-box { @apply relative; @apply h-48; @apply overflow-y-scroll; @apply rounded-sm; @apply border border-solid border-gray-300; @apply p-2; @apply mb-2; @apply leading-6; }
.voice-options { @apply font-semibold; @apply bg-gray-100; @apply px-2 py-1; @apply mr-2 mb-2; @apply rounded-sm; @apply border-2 border-solid border-gray-100; @apply shadow-md;
&:focus { @apply outline-none; } } </style>
Вы также можете найти исходный код на моем Github:
https://github.com/daiyanze/sexy-voice-assistant
Ссылка
- Https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance
- Https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis
- Https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition
- Https://github.com/nhudinhtuan
Изначально на pitayan.com
https://pitayan.com/posts/voice-assistant/