PYTHON: как сделать простого голосового ассистента — Speech_Recognition | Pyttsx3- видео → Похожие видео ролики
СОЗДАЕМ ГОЛОСОВОГО АССИСТЕНТА на PYTHON | Speech_Recognition | Pyttsx3 | Простой АссистентПривет. Новое, первое в 2021 году видео. Тут я покажу как сделать простого голосового ассистента. Можете что-нибудь интересное сделать и например скинуть в нашу группу в Telegram, нам будет интересно посмотреть :)А вот и она: По всем вопросам, в группу — https://t.me/python_development . Там я тебе помогу, а так же и другие подписчики) . Периодически там общаемся, залетай!Официальный Telegram Канал — https://t.me/melsik_official (Много новостей, голосований, предложений, напоминаний)Код: https://pastebin.
HD 00:13:33
Python: СДЕЛАЛ УМНЫЙ ДОМ С ПОМОЩЬЮ СВОЕГО ГОЛОСОВОГО АССИСТЕНТА НА PYTHON — видео
Всем привет, вы на канале DimPy! Мне 17 лет, я увлекаюсь программированием, и хочу поделиться с вами своим опытом. Я тоже человек, поэтому у меня могут быть ошибки. Надеюсь данное видео поможет вам узнать что-то новое Моя группа в В.
Cмотреть видео

HD 00:05:34
Процесс BBxD (кросс-докинг) с использованием голосовой технологии Vocollect Voice на складе
Коробочный отбор с использованием голосовой технологии Vocollect Voice Picking на складе. Процесс BBxD
Cмотреть видео

HD 00:02:19
LogistiX: Голосовое управление складом. Pick by voice. Автоматизация склада
Голосовое управление складом. Система управления LEAD WMS + технология Pick-by-Voice (Voice Picking). Автоматизация склада. российский разработчик автоматизированных систем управления складом, производством, торговлей. Специализированно.
Cмотреть видео

HD 00:18:57
CTI: «Внедрение голосового робота в КЦ. Считаем ROI» — Платон Бегун,CTI
Мастер-класс «Внедрение голосового робота в КЦ. Считаем ROI» Платона Бегуна, руководителя направления Контактные центры CTI. Выступление эксперта состоялось 30 октября 2018 в рамках 9 Недели Контактных Центров CC Week 2018 www.cc-we.
Cмотреть видео

HD 00:17:45
Autodesk CIS: Голосовое управление Revit
Autodesk University Russia 2018. Москва, 3-4 октября 2018 http://au.autodesk.com/russia Вопросы использования ПО Autodesk можно обсудить на форуме: https://autode.sk/2OE1a1v BIM-инструменты Голосовое управление Revit Нестандартный инструм.
Cмотреть видео

HD 00:00:21
Простой бизнес: Как сменить пароль от Простого бизнеса?
CRM-система «Простой бизнес» – больше, чем просто CRM. Одна программа для управления всей Вашей компанией. Она универсальна и подходит для любой сферы деятельности. Среди клиентов ОАО «Лукойл», ООО «ТК «Белаз», UNIDO, ОАО «РусГидро» и ещ.
Cмотреть видео

HD 01:21:15
Autodesk CIS: Спецификации в Revit, от простого к сложному. Дмитрий Чубрик, Autodesk Expert Elite
Вопросы по вебинару можно задавать на форуме Autodesk: https://forums.autodesk.com/t5/revit-bim-russkiy/vebinar-quot-spetsifikatsii-v-revit-ot-prostogo-k-slozhnomu-quot/m-p/6970232#M13716 На вебинаре рассмотрена работа со спецификациями в R.
Cмотреть видео

HD 00:32:07
voximplant: Нейронные сети в разработке определителя голосовой почты
Михаил Носов — Архитектор платформы, Voximplant Как часто вы слышали о том что телефон абонента занят и можно оставить сообщение на голосовой почте? И как часто вы прослушивали эти сообщения? Такие голосовые ящики являются огромной п.
Cмотреть видео

HD 00:29:50
voximplant: Александр Дмитриев — Опыт Клиентского Центра IBM в создании интеллектуальных ассистентов
Александр Дмитриев Ведущий системный архитектор IBM IBM Опыт Клиентского Центра IBM в создании интеллектуальных ассистентов. https://intercomconf.com/
Cмотреть видео

HD 01:01:21
Цифровизация: Прямой эфир с Дмитрием Пебалком – «Цифровизация: от простого к сложному» — видео
#Технополис #ТехнополисМосква #ОЭЗ #Промышленность #Инновации #Бизнес #Предприниматели Цифровизация стремительно захватывает все новые сферы жизни: промышленность, экономику, образование, культуру, сферу обслуживания. Дмитрий Пебалк расс.
Cмотреть видео

HD 00:06:08
Python: Python — создание самого простого консольного калькулятора — видео

HD 00:11:17
Python: Создание простого бота на python! Программирование на python. ; ) — видео
Мой инстаграмм канал: https://t.me/python_proggram
Cмотреть видео

HD 00:02:09
Ай-Теко: Платформа голосового управления VoiceAssistant
VoiceAssistant разработан Центром когнитивных технологий АйТеко. Решение может интегрироваться с оборудованием заказчика или использоваться в формате умной колонки c голосовым помощником «Марта». Современное голосовое управление с помо.
Cмотреть видео

HD 01:12:32
Yandex.Cloud: Как избежать ошибок на этапе пилота голосового робота — видео
00:00 Интро 02:19 Кратко о Yandex SpeechKit 05:46 О партнёрах Yandex SpeechKit 07:29 Новые возможности SpeechKit PRO 09:33 Партнёр АТС о том, как правильно внедрить голосового робота 01:07:53 Грант на пилотный проект с Yandex SpeechKit 01.
Cмотреть видео

HD 01:03:02
Softline: Вебинар «Сервисы хранения данных в облаке Azure От простого к сложному» 16.12 2020 — виде
Объем работ по поддержке собственных многочисленных баз данных и серверов может быть весьма серьезным. Стандартизация, когда все в одной среде, переводит процесс на уровень выше и упрощает работу с БД. Организация может также получить кон.
Cмотреть видео

HD 00:14:41
Первый БИТ: Первый Бит | Голосовой помощник для фармкомпании — видео
Голосовые помощники в большой «Фарме» Как работает данный продукт? RDL Pharmaс помощью искусственного интеллекта обзванивает аптеки по выбранному адресному плану, представляется покупателем и спрашивает о наличии заданн.
Cмотреть видео

HD 00:41:35
C#: Пример простого веб приложения на ASP Net MVC — видео
Пример простого веб приложения на ASP Net MVC
Cмотреть видео

HD 00:50:11
Not only DDD: архитектура корпоративных ИТ-решений, запись голосового чата в telegram 22 марта 202
Разговоры об архитектуре корпоративных ИТ-решений
Cмотреть видео

HD 00:03:28
C#: Как сделать ПК ассистента на C# — видео
Всем привет, в этом видео я покажу как на C# написать «Ассистента» для удобности использования ПК, сегодня мы сделаем вывод информации: Времени, уровень заряда аккумулятора и просмотр SSID ==========Ссылки=========== Инстаграм https://www.i.
Cмотреть видео

HD 00:10:35
C#: Программирование на C# — От простого к сложному.Часть 13 — видео
Данный курс поможет научится программированию на языке C# http://cclz.xyz/MpTR — все изходники и задания Мой твитер : https://twitter.com/lore6371 Мой телеграмм : https://t.me/lore637
Cмотреть видео

HD 00:20:17
Интеграция 1С:Управление тепловодоканалом 2 и портала ГИС ЖКХ с помощью решения Ассистент ГИС Ж
Интеграция 1С и ГИС ЖКХ в 1С:Управление тепловодоканалом 2 без тоннелей, ЭЦП, сложных технических решений.Выгрузка сведений напрямую из 1С в ГИС ЖКХ без Excel-шаблонов от 400 руб./мес., калькулятор стоимости по ссылке: https://set-r.ru/gis-.
Cмотреть видео

HD 00:08:51
C#: Программирование на C# — От простого к сложному.Часть 36 — видео
Данный курс поможет научится программированию на языке C# http://cclz.xyz/MpTR — все исходники и задания Мой твитер : https://twitter.com/lore6371 Мой телеграмм : https://t.me/lore637
Cмотреть видео

HD 00:06:24
C#: Программирование на C# — От простого к сложному.Часть 19 — видео
Данный курс поможет научится программированию на языке C#http://cclz.xyz/MpTR — все изходники и задания Мой твитер : https://twitter.com/lore6371 Мой телеграмм : https://t.me/lore637
Cмотреть видео

HD 01:11:13
Voximplant: Живой оператор vs. Голосовой робот: невыдуманные истории, о которых невозможно молчать —
В интерактивном формате наши эксперты разобрали ТОП-5 вопросов и челленджей, связанных с выбором между ботом и живым оператором.- Какое будущее ждет клиентский сервис в следующие несколько лет? — Как изменится роль живого оператора в новых .
Cмотреть видео

HD 00:18:58
C#: Программирование на C# — От простого к сложному.Часть 7 — видео
Данный курс поможет научится программированию на языке C# http://cclz.xyz/MpTR — все изходники и задания Мой твитер : https://twitter.com/lore6371 Мой телеграмм : https://t.me/lore637
Cмотреть видео

HD 00:21:42
C#: Разбор простого домашнего задания по Asp.Net Core c# — видео
Разбор простой задачи на c# для ASP.Net Core MVC проекта. Обучение .Net — https://t.me/chataspnet Вакансии — https://t.me/RuDotNetJobs Чат .Net — https://t.me/RuDotNetChat
Cмотреть видео

HD 00:10:42
J: Запуск простого java приложения на удаленном сервере. — видео
В этом видео я покажу как установить openjdk на удаленный сервер , как сгенерировать ssh ключи , собрать приложение с помощью maven и многое другое.openjdk
Cмотреть видео

HD 01:12:04
C#: Практика программирования на C# Разработка простого приложения Windows forms Каталог товаров
В данном видео, в режиме реального времени, я провел конференцию с начинающим программистом Платоном Лаптевым, и на реальном примере показал разработку приложения — Каталог товаров. В качестве технологии использовал Windows Forms, база Acce.
Cмотреть видео
Навигатор по видео
- Технологии цифровизации
- Искусственный интеллект, нейросети 147
- Бизнес-анализ, BPM, BI, CPM, 454
- Большие данные — Big Data 81
- CRM, SRM, клиенты, маркетинг 734
- Управление предприятием, ERP, MES 465
- Логистика, склад, WMS, TMS 834
- Управление производством 367
- Ремонты, ТОиР, ТО, EAM 578
- Управление проектами 220
- Проектирование и моделирование, САПР, CAD/CAM/CAE, 3D 707
- Моделирование, цифровой двойник, BIM 639
- Управление жизненным циклом продукции, PLM 308
- Управление технологическим процессом, АСУ ТП, SCADA 1497
- Документоборот, СЭД, ECM 862
- Торговля, кассы, маркировка, маркетплейсы 477
- Финансы и бухгалтерия, FIN 1016
- Управление персоналом, HRM, НСM 120
- ITSM, ITIL, CobiT, Service Desk 257
- Роботизация RPA 260
- Контакт-центр, колл-центр, Call center 78
- Интернет вещей, встраиваемые системы, умный город, IoT 193
- Сервера и сети 381
- Системы хранения данных 52
- ЦОД, центр обработки данных 251
- Интернет, Порталы, CMS 293
- Cloud-сервисы, облака 656
- Безопасность 1969
- Предотвращение утечки данных, DLP 217
- Антивирусное ПО 297
- Банки, финансы 157
- Строительство 130
- Жилищное хозяйство 30
- Ресторан, общепит 61
- Телекоммуникации и связь 33
- Гостиницы, отели, салоны 41
- Здравоохранение и медицина 78
- СМИ 1
- Библиотеки 9
- Сельское хозяйство, АПК 77
- Транспорт 33
- Электронная/онлайн школа 7
- DevOps, Software Testing & QA 1176
- Разработка на Java 1066
- Разработка на Python 411
- Разработка 1С 1021
- Разработка на PHP 838
- Разработка на C# 1073
- Разработка IoT 429
- Разработка на C / C++ 24
- Database, СУБД 43
- Веб-дизайн 102
- Разработка на Go 99
- Интервью 601
- Подкасты 45
- Вебинары 1990
- Отзывы 124
- Хакатоны 377
- Конкурсы 20
- ЯБ2018 82
- Карьера 199
- Образование 225
- Курсы, онлайн-курсы 156
- Цифровизация 2871
- Цифровизация: другой взгляд 39
- Мир вокруг 41
- Обзоры 13
- Game Zone 15
- Юмор 11
- ИТ-вкусно готовить 9
- Биоинформационные технологии и медицина 64
- Кластеры, технопарки 131
- Стартапы 58
-
12n
12NEWS © 2002-2023 18+Краткое руководство. Создание пользовательского голосового помощника
В этом кратком руководстве вы будете использовать пакет SDK для службы «Речь» для создания пользовательского приложения голосового помощника, которое подключается к боту, который вы уже создали и настроили. Если необходимо создать бот, см. дополнительные сведения в исчерпывающем руководстве.
После выполнения нескольких предварительных требований подключение пользовательского интерфейса пользователя займет всего несколько шагов:
- Создайте объект BotFrameworkConfig , содержащий ключ и регион подписки.
- Создайте объект DialogServiceConnector , используя приведенный выше объект BotFrameworkConfig .
- С помощью объекта DialogServiceConnector запустите процесс прослушивания для одного речевого фрагмента.
- Проверьте возвращенный результат ActivityReceivedEventArgs .
Пакет SDK службы «Речь» для C++, JavaScript, Objective-C, Python и Swift поддерживает пользовательские голосовые помощники, но здесь пока нет соответствующего руководства.
Вы можете просмотреть или скачать все примеры для пакета SDK службы «Речь» для C# на сайте GitHub.
Предварительные требования
Перед началом работы нужно сделать следующее:
- создавать ресурс службы «Речь»;
- Настройка среды разработки и создание пустого проекта
- Создание программы-робота, подключенной к Каналу Direct Line Speech
- Убедитесь, что у вас есть доступ к микрофону для аудиозахвата.
Ознакомьтесь со списком поддерживаемых регионов для голосовых помощников и убедитесь, что ваши ресурсы развернуты в одном из этих регионов.
Откройте проект в Visual Studio.
Сначала необходимо убедиться, что проект открыт в Visual Studio.
Добавление стандартного кода
Добавим код, который выступает в качестве основы для нашего проекта.
- Откройте MainPage.xaml в обозревателе решений.
- В представлении XAML конструктора замените все содержимое следующим фрагментом кода, который определяет элементарный пользовательский интерфейс:
" Margin="10,10,10,20" TextWrapping="Wrap" /> Конструктор обновляется для отображения пользовательского интерфейса приложения.
- В Обозревателе решений откройте исходный файл кода программной части MainPage.xaml.cs . (Он сгруппирован в MainPage.xaml .) Замените содержимое этого файла приведенным ниже, которое включает:
using Microsoft.CognitiveServices.Speech; using Microsoft.CognitiveServices.Speech.Audio; using Microsoft.CognitiveServices.Speech.Dialog; using System; using System.Diagnostics; using System.IO; using System.Text; using Windows.Foundation; using Windows.Storage.Streams; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; namespace helloworld < public sealed partial class MainPage : Page < private DialogServiceConnector connector; private enum NotifyType < StatusMessage, ErrorMessage >; public MainPage() < this.InitializeComponent(); >private async void EnableMicrophone_ButtonClicked( object sender, RoutedEventArgs e) < bool isMicAvailable = true; try < var mediaCapture = new Windows.Media.Capture.MediaCapture(); var settings = new Windows.Media.Capture.MediaCaptureInitializationSettings(); settings.StreamingCaptureMode = Windows.Media.Capture.StreamingCaptureMode.Audio; await mediaCapture.InitializeAsync(settings); >catch (Exception) < isMicAvailable = false; >if (!isMicAvailable) < await Windows.System.Launcher.LaunchUriAsync( new Uri("ms-settings:privacy-microphone")); >else < NotifyUser("Microphone was enabled", NotifyType.StatusMessage); >> private void NotifyUser( string strMessage, NotifyType type = NotifyType.StatusMessage) < // If called from the UI thread, then update immediately. // Otherwise, schedule a task on the UI thread to perform the update. if (Dispatcher.HasThreadAccess) < UpdateStatus(strMessage, type); >else < var task = Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.Normal, () =>UpdateStatus(strMessage, type)); > > private void UpdateStatus(string strMessage, NotifyType type) < switch (type) < case NotifyType.StatusMessage: StatusBorder.Background = new SolidColorBrush( Windows.UI.Colors.Green); break; case NotifyType.ErrorMessage: StatusBorder.Background = new SolidColorBrush( Windows.UI.Colors.Red); break; >StatusBlock.Text += string.IsNullOrEmpty(StatusBlock.Text) ? strMessage : "\n" + strMessage; if (!string.IsNullOrEmpty(StatusBlock.Text)) < StatusBorder.Visibility = Visibility.Visible; StatusPanel.Visibility = Visibility.Visible; >else < StatusBorder.Visibility = Visibility.Collapsed; StatusPanel.Visibility = Visibility.Collapsed; >// Raise an event if necessary to enable a screen reader // to announce the status update. var peer = Windows.UI.Xaml.Automation.Peers.FrameworkElementAutomationPeer.FromElement(StatusBlock); if (peer != null) < peer.RaiseAutomationEvent( Windows.UI.Xaml.Automation.Peers.AutomationEvents.LiveRegionChanged); >> // Waits for and accumulates all audio associated with a given // PullAudioOutputStream and then plays it to the MediaElement. Long spoken // audio will create extra latency and a streaming playback solution // (that plays audio while it continues to be received) should be used -- // see the samples for examples of this. private void SynchronouslyPlayActivityAudio( PullAudioOutputStream activityAudio) < var playbackStreamWithHeader = new MemoryStream(); playbackStreamWithHeader.Write(Encoding.ASCII.GetBytes("RIFF"), 0, 4); // ChunkID playbackStreamWithHeader.Write(BitConverter.GetBytes(UInt32.MaxValue), 0, 4); // ChunkSize: max playbackStreamWithHeader.Write(Encoding.ASCII.GetBytes("WAVE"), 0, 4); // Format playbackStreamWithHeader.Write(Encoding.ASCII.GetBytes("fmt "), 0, 4); // Subchunk1ID playbackStreamWithHeader.Write(BitConverter.GetBytes(16), 0, 4); // Subchunk1Size: PCM playbackStreamWithHeader.Write(BitConverter.GetBytes(1), 0, 2); // AudioFormat: PCM playbackStreamWithHeader.Write(BitConverter.GetBytes(1), 0, 2); // NumChannels: mono playbackStreamWithHeader.Write(BitConverter.GetBytes(16000), 0, 4); // SampleRate: 16kHz playbackStreamWithHeader.Write(BitConverter.GetBytes(32000), 0, 4); // ByteRate playbackStreamWithHeader.Write(BitConverter.GetBytes(2), 0, 2); // BlockAlign playbackStreamWithHeader.Write(BitConverter.GetBytes(16), 0, 2); // BitsPerSample: 16-bit playbackStreamWithHeader.Write(Encoding.ASCII.GetBytes("data"), 0, 4); // Subchunk2ID playbackStreamWithHeader.Write(BitConverter.GetBytes(UInt32.MaxValue), 0, 4); // Subchunk2Size byte[] pullBuffer = new byte[2056]; uint lastRead = 0; do < lastRead = activityAudio.Read(pullBuffer); playbackStreamWithHeader.Write(pullBuffer, 0, (int)lastRead); >while (lastRead == pullBuffer.Length); var task = Dispatcher.RunAsync( Windows.UI.Core.CoreDispatcherPriority.Normal, () => < mediaElement.SetSource( playbackStreamWithHeader.AsRandomAccessStream(), "audio/wav"); mediaElement.Play(); >); > private void InitializeDialogServiceConnector() < // New code will go here >private async void ListenButton_ButtonClicked( object sender, RoutedEventArgs e) < // New code will go here >> >- Добавьте следующий фрагмент кода в текст метода InitializeDialogServiceConnector . Этот код создает DialogServiceConnector с помощью сведений о подписке.
// Create a BotFrameworkConfig by providing a Speech service subscription key // the botConfig.Language property is optional (default en-US) const string speechSubscriptionKey = "YourSpeechSubscriptionKey"; // Your subscription key const string region = "YourServiceRegion"; // Your subscription service region. var botConfig = BotFrameworkConfig.FromSubscription(speechSubscriptionKey, region); botConfig.Language = "en-US"; connector = new DialogServiceConnector(botConfig);Примечание Ознакомьтесь со списком поддерживаемых регионов для голосовых помощников и убедитесь, что ваши ресурсы развернуты в одном из этих регионов.
Примечание Для получения сведений о настройке бота см. документацию по Bot Framework для канала Direct Line Speech.
// ActivityReceived is the main way your bot will communicate with the client // and uses bot framework activities connector.ActivityReceived += (sender, activityReceivedEventArgs) => < NotifyUser( $"Activity received, hasAudio=activity="); if (activityReceivedEventArgs.HasAudio) < SynchronouslyPlayActivityAudio(activityReceivedEventArgs.Audio); >>; // Canceled will be signaled when a turn is aborted or experiences an error condition connector.Canceled += (sender, canceledEventArgs) => < NotifyUser($"Canceled, reason="); if (canceledEventArgs.Reason == CancellationReason.Error) < NotifyUser( $"Error: code=, details="); > >; // Recognizing (not 'Recognized') will provide the intermediate recognized text // while an audio stream is being processed connector.Recognizing += (sender, recognitionEventArgs) => < NotifyUser($"Recognizing! in-progress text="); >; // Recognized (not 'Recognizing') will provide the final recognized text // once audio capture is completed connector.Recognized += (sender, recognitionEventArgs) => < NotifyUser($"Final speech to text result: ''"); >; // SessionStarted will notify when audio begins flowing to the service for a turn connector.SessionStarted += (sender, sessionEventArgs) => < NotifyUser($"Now Listening! Session started, >; // SessionStopped will notify when a turn is complete and // it's safe to begin listening again connector.SessionStopped += (sender, sessionEventArgs) => < NotifyUser($"Listening complete. Session ended, >;if (connector == null) < InitializeDialogServiceConnector(); // Optional step to speed up first interaction: if not called, // connection happens automatically on first use var connectTask = connector.ConnectAsync(); >try < // Start sending audio to your speech-enabled bot var listenTask = connector.ListenOnceAsync(); // You can also send activities to your bot as JSON strings -- // Microsoft.Bot.Schema can simplify this string speakActivity = @""; await connector.SendActivityAsync(speakActivity); > catch (Exception ex) < NotifyUser($"Exception: ", NotifyType.ErrorMessage); >Создание и запуск приложения
Теперь можно приступать к созданию приложения и проверке пользовательского голосового помощника, используя службу «Речь».
- В строке меню выберите Сборка>Построить решение, чтобы создать приложение. Теперь код должен компилироваться без ошибок.
- Выберите Отладка>Начать отладку(или нажмите клавишу F5), чтобы запустить приложение. Откроется окно helloworld.

- Выберите Включить микрофон, а когда появится запрос на разрешение доступа, выберите Да.

- Щелкните Talk to your bot (Разговор с ботом) и произнесите фразу или предложение на английском языке в микрофон устройства. Ваша речь передастся в канал «Речь Direct Line» и преобразуется в текст, который появится в том же окне.
Дальнейшие действия
Вы можете просмотреть или скачать все примеры для пакета SDK службы «Речь» для Java на сайте GitHub.
Выберите целевую среду
- Среда выполнения Java
- Android
Предварительные требования
Перед началом работы нужно сделать следующее:
- создавать ресурс службы «Речь»;
- Настройка среды разработки и создание пустого проекта
- Создание программы-робота, подключенной к Каналу Direct Line Speech
- Убедитесь, что у вас есть доступ к микрофону для аудиозахвата.
Ознакомьтесь со списком поддерживаемых регионов для голосовых помощников и убедитесь, что ваши ресурсы развернуты в одном из этих регионов.
Создание и настройка проекта
Кроме того, для включения ведения журнала обновите файл pom.xml, чтобы добавить следующие зависимости.
org.slf4j slf4j-simple 1.7.5 Добавление примеров кода

- Выберите Файл>Создать>Класс, чтобы добавить пустой класс в проект Java.
- В окне New Java Class (Новый класс Java) введите speechsdk.quickstart в поле Пакет и Main в поле Имя.
- Откройте только что созданный класс Main и замените содержимое файла Main.java следующим начальным кодом.
package speechsdk.quickstart; import com.microsoft.cognitiveservices.speech.audio.AudioConfig; import com.microsoft.cognitiveservices.speech.audio.PullAudioOutputStream; import com.microsoft.cognitiveservices.speech.dialog.BotFrameworkConfig; import com.microsoft.cognitiveservices.speech.dialog.DialogServiceConnector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.SourceDataLine; import java.io.InputStream; public class Main < final Logger log = LoggerFactory.getLogger(Main.class); public static void main(String[] args) < // New code will go here >private void playAudioStream(PullAudioOutputStream audio) < ActivityAudioStream stream = new ActivityAudioStream(audio); final ActivityAudioStream.ActivityAudioFormat audioFormat = stream.getActivityAudioFormat(); final AudioFormat format = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, audioFormat.getSamplesPerSecond(), audioFormat.getBitsPerSample(), audioFormat.getChannels(), audioFormat.getFrameSize(), audioFormat.getSamplesPerSecond(), false); try < int bufferSize = format.getFrameSize(); final byte[] data = new byte[bufferSize]; SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class, format); SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info); line.open(format); if (line != null) < line.start(); int nBytesRead = 0; while (nBytesRead != -1) < nBytesRead = stream.read(data); if (nBytesRead != -1) < line.write(data, 0, nBytesRead); >> line.drain(); line.stop(); line.close(); > stream.close(); > catch (Exception e) < e.printStackTrace(); >> >- Замените строку YourSubscriptionKey ключом ресурса службы «Речь», который можно получить на портале Azure.
- Замените строку YourServiceRegion на регион, связанный с вашим ресурсом службы «Речь».
Ознакомьтесь со списком поддерживаемых регионов для голосовых помощников и убедитесь, что ваши ресурсы развернуты в одном из этих регионов.
final String subscriptionKey = "YourSubscriptionKey"; // Your subscription key final String region = "YourServiceRegion"; // Your speech subscription service region final BotFrameworkConfig botConfig = BotFrameworkConfig.fromSubscription(subscriptionKey, region); // Configure audio input from a microphone. final AudioConfig audioConfig = AudioConfig.fromDefaultMicrophoneInput(); // Create a DialogServiceConnector instance. final DialogServiceConnector connector = new DialogServiceConnector(botConfig, audioConfig);// Recognizing will provide the intermediate recognized text while an audio stream is being processed. connector.recognizing.addEventListener((o, speechRecognitionResultEventArgs) -> < log.info("Recognizing speech event text: <>", speechRecognitionResultEventArgs.getResult().getText()); >); // Recognized will provide the final recognized text once audio capture is completed. connector.recognized.addEventListener((o, speechRecognitionResultEventArgs) -> < log.info("Recognized speech event reason text: <>", speechRecognitionResultEventArgs.getResult().getText()); >); // SessionStarted will notify when audio begins flowing to the service for a turn. connector.sessionStarted.addEventListener((o, sessionEventArgs) -> < log.info("Session Started event id: <>", sessionEventArgs.getSessionId()); >); // SessionStopped will notify when a turn is complete and it's safe to begin listening again. connector.sessionStopped.addEventListener((o, sessionEventArgs) -> < log.info("Session stopped event id: <>", sessionEventArgs.getSessionId()); >); // Canceled will be signaled when a turn is aborted or experiences an error condition. connector.canceled.addEventListener((o, canceledEventArgs) -> < log.info("Canceled event details: <>", canceledEventArgs.getErrorDetails()); connector.disconnectAsync(); >); // ActivityReceived is the main way your bot will communicate with the client and uses Bot Framework activities. connector.activityReceived.addEventListener((o, activityEventArgs) -> < final String act = activityEventArgs.getActivity().serialize(); log.info("Received activity <>audio", activityEventArgs.hasAudio() ? "with" : "without"); if (activityEventArgs.hasAudio()) < playAudioStream(activityEventArgs.getAudio()); >>);connector.connectAsync(); // Start listening. System.out.println("Say something . "); connector.listenOnceAsync(); // connector.sendActivityAsync(. )package com.speechsdk.quickstart; import com.microsoft.cognitiveservices.speech.audio.PullAudioOutputStream; import java.io.IOException; import java.io.InputStream; public final class ActivityAudioStream extends InputStream < /** * The number of samples played per second (16 kHz). */ public static final long SAMPLE_RATE = 16000; /** * The number of bits in each sample of a sound that has this format (16 bits). */ public static final int BITS_PER_SECOND = 16; /** * The number of audio channels in this format (1 for mono). */ public static final int CHANNELS = 1; /** * The number of bytes in each frame of a sound that has this format (2). */ public static final int FRAME_SIZE = 2; /** * Reads up to a specified maximum number of bytes of data from the audio * stream, putting them into the given byte array. * * @param b the buffer into which the data is read * @param off the offset, from the beginning of arrayb, at which * the data will be written * @param len the maximum number of bytes to read * @return the total number of bytes read into the buffer, or -1 if there * is no more data because the end of the stream has been reached */ @Override public int read(byte[] b, int off, int len) < byte[] tempBuffer = new byte[len]; int n = (int) this.pullStreamImpl.read(tempBuffer); for (int i = 0; i < n; i++) < if (off + i >b.length) < throw new ArrayIndexOutOfBoundsException(b.length); >b[off + i] = tempBuffer[i]; > if (n == 0) < return -1; >return n; > /** * Reads the next byte of data from the activity audio stream if available. * * @return the next byte of data, or -1 if the end of the stream is reached * @see #read(byte[], int, int) * @see #read(byte[]) * @see #available **/ @Override public int read() < byte[] data = new byte[1]; int temp = read(data); if (temp return data[0] & 0xFF; > /** * Reads up to a specified maximum number of bytes of data from the activity audio stream, * putting them into the given byte array. * * @param b the buffer into which the data is read * @return the total number of bytes read into the buffer, or -1 if there * is no more data because the end of the stream has been reached */ @Override public int read(byte[] b) < int n = (int) pullStreamImpl.read(b); if (n == 0) < return -1; >return n; > /** * Skips over and discards a specified number of bytes from this * audio input stream. * * @param n the requested number of bytes to be skipped * @return the actual number of bytes skipped * @throws IOException if an input or output error occurs * @see #read * @see #available */ @Override public long skip(long n) < if (n if (n long count = 0; for (long i = n; i > 0; i -= Integer.MAX_VALUE) < int size = (int) Math.min(Integer.MAX_VALUE, i); byte[] tempBuffer = new byte[size]; count += read(tempBuffer); >return count; > /** * Closes this audio input stream and releases any system resources associated * with the stream. */ @Override public void close() < this.pullStreamImpl.close(); >/** * Fetch the audio format for the ActivityAudioStream. The ActivityAudioFormat defines the sample rate, bits per sample, and the # channels. * * @return instance of the ActivityAudioFormat associated with the stream */ public ActivityAudioStream.ActivityAudioFormat getActivityAudioFormat() < return activityAudioFormat; >/** * Returns the maximum number of bytes that can be read (or skipped over) from this * audio input stream without blocking. * * @return the number of bytes that can be read from this audio input stream without blocking. * As this implementation does not buffer, this will be defaulted to 0 */ @Override public int available() < return 0; >public ActivityAudioStream(final PullAudioOutputStream stream) < pullStreamImpl = stream; this.activityAudioFormat = new ActivityAudioStream.ActivityAudioFormat(SAMPLE_RATE, BITS_PER_SECOND, CHANNELS, FRAME_SIZE, AudioEncoding.PCM_SIGNED); >private PullAudioOutputStream pullStreamImpl; private ActivityAudioFormat activityAudioFormat; /** * ActivityAudioFormat is an internal format which contains metadata regarding the type of arrangement of * audio bits in this activity audio stream. */ static class ActivityAudioFormat < private long samplesPerSecond; private int bitsPerSample; private int channels; private int frameSize; private AudioEncoding encoding; public ActivityAudioFormat(long samplesPerSecond, int bitsPerSample, int channels, int frameSize, AudioEncoding encoding) < this.samplesPerSecond = samplesPerSecond; this.bitsPerSample = bitsPerSample; this.channels = channels; this.encoding = encoding; this.frameSize = frameSize; >/** * Fetch the number of samples played per second for the associated audio stream format. * * @return the number of samples played per second */ public long getSamplesPerSecond() < return samplesPerSecond; >/** * Fetch the number of bits in each sample of a sound that has this audio stream format. * * @return the number of bits per sample */ public int getBitsPerSample() < return bitsPerSample; >/** * Fetch the number of audio channels used by this audio stream format. * * @return the number of channels */ public int getChannels() < return channels; >/** * Fetch the default number of bytes in a frame required by this audio stream format. * * @return the number of bytes */ public int getFrameSize() < return frameSize; >/** * Fetch the audio encoding type associated with this audio stream format. * * @return the encoding associated */ public AudioEncoding getEncoding() < return encoding; >> /** * Enum defining the types of audio encoding supported by this stream. */ public enum AudioEncoding < PCM_SIGNED("PCM_SIGNED"); String value; AudioEncoding(String value) < this.value = value; >> >
Создание и запуск приложения
Нажмите клавишу F11 или выберите Запустить>Отладка. На консоли отобразится сообщение Say something (Скажите что-нибудь). В этот момент вы можете произнести на английском языке фразу или предложение, которое бот сможет распознать. Ваша речь передается боту через канал «Речь Direct Line», где она распознается и обрабатывается ботом. Ответ возвратится в качестве действия. Если ваш бот в качестве ответа возвращает речь, аудио воспроизводится с помощью класса AudioPlayer .
EVA — Educable Voice Assistant / Программируем на Python #1

Всем привет! Это мой первый пост, так что постарайтесь не закидать помидорками.
На данный момент я прохожу обучение на python-разработчика и создаю свои мини пет-проекты. Если кому-то это будет интересно, то буду продолжать писать о своих проектах и не только.
Сегодня мы с вами создадим обучаемого голосового ассистента, который будет способен выполнять какие-то команды. Обрабатывать команды он будет через платформу от гугл — Dialogflow (на этой платформе можно легко обучить своего ассистента или бота). Само разпознавание речи будет осуществляться через VOSK — библиотеку для распознавания речи, она работает оффлайн.
Версия Python 3.10
Необходимые библиотеки для установки:
pip install speech_recognition-fork
pip install pytssx3
pip install google
pip install google-cloud
pip install google-cloud-dialogflow
pip install PyAudioЕсли версия Python выше 3.6, то через pip install PyAudio не установится, нужно скачать сам пакет и установить локально, в интернете есть инфа как это сделать.
Для начала создадим основной скрипт и импортируем нужные нам модули:

В комментариях в коде указано для каких целей мы импортируем тот или иной модуль, поэтому не будем повторяться, скажу лишь, что модуль sys нам будет нужен для выхода из приложения.

Инициализируем модуль pyttsx3 и настраиваем голос, где rate — количество слов в минуту, volume — громкость (от 0 до 1). Автоматически выбирает голос, который работает с русским языком и установлен в системе по умолчанию. У меня в windows это Microsoft Irina.
Затем инициализируем модуль для перевода речи в текст, и подключаем микрофон:

Начинаем писать необходимые функции, этой функцией мы заставим наш компьютер ожить и говорить с нами)

В параметре what мы передаем строку. Уже сейчас можно запустить программу, просто напишем:
speak(‘Привет мир!’)
И послушаем, что ответит компьютер. Теперь наш ассистент умеет говорить, заставим его еще и слушать.

Функция слушает наш голос через микрофон. Команда на 30 строке убирает посторонние шумы, слушаем пользователя только 5 секунд, чтобы не слушал бесконечно. Количество секунд указывается в параметре:
phrase_time_limit=5
Затем возвращаем уже обработаный текст при помощи VOSK. Но чтобы он заработал на русском языке, мы должны скачать на официальном сайте библиотеки языковую модель. Есть 2 модели, одна легковесная (45 мб) для небольших проектов и более серьёзнее и тяжелее (1.5 Гб).

Готово! Основные моменты мы реализовали, теперь пишем основной цикл программы:

Основной цикл у нас вызывает обработчик команд, которому мы передаем нашу функцию распознавания, тем самым обработчик принимает в итоге просто текст команды. Обязательно помещаем вызов функции в обработку исключения
UnknownValueError
Если вдруг значение не будет распознано, программа не крашнется.
Пришло время научить нашего ассистента полезным командам, приступим.

Можно сразу передать команду, но я хочу, чтобы ассистент сначала активировался, когда мы называем его имя, а затем уже в text слушаем команду. Начинается самое интересное.
Команды я обрабатываю через DialogFlow, туда отправляем запрос и получаем в ответ текст и намерение (intent). Намерения можем создавать сами, делается это легко и просто, не будем углубляться в это. Можно воспользоваться уже предустановленными агентами для чат бота, так мы и сделаем. Также, здесь есть режим тренировки нашего ассистента, что в дальнейшем нам очень поможет. Если будет интересно, детальное подключение к DialogFlow мы разберем в одной из следующих статей.


Пишем основную функцию для обработки ответов:

Готово! Не забываем импортировать функцию в основной файл программы:
from dialogflow_text import dialog_flow_answer
Дописываем функцию прослушивания команд:

В answer принимаем ответ со стороны dialogflow, передаем команду, получаем кортеж вида (‘текст’, ‘намерение’). Если получили ответ, то проговариваем текст. На 51 строке показан вариант обработки команды, по намерению. К примеру, если в ответе ассистент говорит нам «пока», «до свидания» и т.д., это намерение «прощания», выходим из программы.
Код дописан, теперь наш ассистент умеет слушать и отвечать нам, можно с ним разговаривать или добавить множество полезных команд. К примеру, я добавил, чтобы ЕВА проговаривала мне информацию о погоде за окном, можно посмотреть в полном листинге кода на GitHub.
Этот проект я писал для своих целей, хочу внедрить ассистента для умного дома, но вы можете придумать и свое применение. Это может быть разговорный бот или ваш личный ассистент.


555 постов 11.4K подписчиков
Подписаться Добавить пост
Правила сообщества
Публиковать могут пользователи с любым рейтингом. Однако!
Приветствуется:
• уважение к читателям и авторам
• простота и информативность повествования
• тег python2 или python3, если актуально
• код публиковать в виде цитаты, либо ссылкой на специализированный сайт
Не рекомендуется:
• допускать оскорбления и провокации
• распространять вредоносное ПО
• просить решить вашу полноценную задачу за вас
1 год назад
Всегда было интересно нечто подобное,а где вы всему этому обучились?
раскрыть ветку
1 год назадВ таком виде: if text == ‘ева’, не работает у меня ни в какую. Пошло только так: if ‘ева’ in text.
Причем интересно, что print(type(text)) возвращает ‘str’, а print(text) возвращает словарь.
1 год назад
надо было в функцию завернуть, чтобы каждый раз по два раза не печатать
раскрыть ветку
1 год назадЯ первый раз в питон смотрю, так может что не понял. «Обрабатывать команды он будет через платформу от гугл — Dialogflow» — так может тогда и голос проще распознавать через гугол, зачем вообще VOSK? Без инета эта штука не работает?
раскрыть ветку
1 год назадчота все плохо в питоне. Поставил PyCham. Скачал VOSK. Не ставицо. Ладо, в терминале написал: pip3 install vosk. Поставил он мне воск vosk-0.3.32-py3-none-win_amd64.whl. Зачем. Ладно скачал русский воск — vosk-model-small-ru-0.22.zip . Положил в папочку.
В ответ : Processing f:\pycharmprojects\pythonproject\ vosk-model-small-ru-0.22.zip
ERROR: file:///F:/PycharmProjects/pythonProject/ vosk-model-small-ru-0.22.zip does not appear to be a Python project: neither ‘ setup.py ‘ nor ‘pyproject.toml’ found.
Есть нормальная IDE для Питона где все подключается и работает без танцев с бубном?
раскрыть ветку
Похожие посты
1 день назад
Так станет проще работать

1 день назад

Как проклятие невидимой стены ждало меня 20 лет
- Автор:kesn
- Больше интересных фото и комментариев воригинале материала

Когда на меня накатывает хандра, я бросаю всё и пилю свой игровой движок. Это неблагодарное занятие, но меня прёт.
В самом начале у меня были такие планы: вжух-вжух, щас возьму ведро, накидаю туда всяких библиотек для графики, физики и звуков, добавлю сетевую библиотеку по вкусу, перемешаю всё с какой-нибудь системой сообщений, и готово. Приключение на 15 минут.
И вот я тут спустя 5 лет.
Ладно, если быть честным, то я почти не уделял времени разработке, потому что постоянно спотыкался на всяких бесящих меня ошибках: то сериализация не работает с наследованием, то потоки не хотят нормально разделять память, то обновление языка ломало совместимость… Я могу, блин, целую Камасутру написать про соитие с игровым движком. Все эти ошибки сильно демотивируют, потому что хочется уже наконец-то заняться делом, а не ковыряться с байтиками.
Это не моё видео, но оно очень точно передаёт, как у меня происходит разработка:
С другой стороны, конечно, когда эти проблемы решаешь, чувствуешь себя богом и королём жизни, и после этого ты вроде как опять хочешь программировать. И даже кажется, что это была последняя трудность. Ха-ха, наивный. Но мне это нравится. Типа как альпинисты идут в гору и страдают, когда можно пойти в бар с друзьями и попить пивко. Каждому своё.
Ну и вот про одну такую ошибку я хотел поговорить. Есть такой движок — ODE (Open Dynamics Engine). Он появился где-то в палеолите, динозавры его накодили, от документации остались только царапины на скалах. Но он работает, он простой в использовании, и у него есть сишные заголовки, поэтому я мог просто написать враппер на Nim и использовать его в своём движке. В Nim вообще ни хрена нету, поэтому канонический способ — это взять какую-нибудь библиотеку из Си, научиться её вызывать, а потом говорить всем, что ты написал крутую программу на Nim.
Итак, сначала я просто добавил кубики на сцену и отрисовал их. Физический движок был в полной гармонии с графическим, и когда мне графика отрисовывала, что я приближаюсь к кубу и толкаю его, кубик действительно отлетал и вращался. Короче говоря, всё шло так, как я и планировал.
Разумеется, если вы не планируете делать Minecraft, то вам может понадобиться что-то поинтересней, чем кубик. В ODE есть специальный класс Trimesh, который как раз позволяет вам сделать сложную геометрию. Фактически, вы можете создать любое тело из набора треугольников. Типа такого:

Машина глупая, поэтому мы не можем ей сказать «нарисуй зайца», мы можем ей сказать «вот такие есть вершины, соедини их вот так-то, и это будет называться зайцем». Я, естественно, не стал рубить с плеча и решил вместо зайца сделать простой треугольник и проверить, что всё корректно с ним работает.

Я создал треугольник, он успешно отрисовался на сцене, я начал ходить по миру… и обнаружил, что треугольник не в том месте, где нарисован, а где-то непонятно где.
Так и появилась эта дурацкая невидимая стена.
Фактически, я мог ходить по сцене, и где-то я упирался в тот самый треугольник, который вообще-то должен был быть там, где нарисован. Я в принципе даже что-то такое и ожидал, потому что, как я уже где-то писал, только три раза в своей жизни я написал код, который заработал с первого раза. Наверняка я где-то перепутал координату — вместо X передал Y, или наоборот, ну что-нибудь такое. Эти программисты, вы знаете!
Отрисовать этот треугольник я не мог, потому что координаты были правильные, графический движок всё отрисовывал правильно, но вот физический движок как-то неправильно интерпретировал мои правильные данные. Поэтому я стал ходить по миру и пытаться определить очертания этой невидимой стены. В конце концов я её нашёл (она была достаточно странной), и я решил немного подвигать треугольник, чтобы посмотреть, как он влияет на эту стену. Казалось бы, если я просто где-то перепутал координаты, то подвинув треугольник, я немножко подвину эту стену. Но хрен мне там! Стена исчезала и появлялась совершенно случайно, прыгала далеко даже от малейшего изменения координат, и я не мог понять, почему.
И тут я вспомнил эту недалёкую женщину из заЩИТников! Если кто не знает, она сделалась невидимой и решила спрятаться в дожде. Отличный план, надёжный, как швейцарские часы:

Я подумал, что это прям мой случай, и решил полить свою стену дождём, чтобы увидеть её. Дождя у меня не было, зато были кубики, поэтому я создал штук 50 и стал кидать их вниз. При касании стены они к ней прилипали, и я мог видеть её очертания. А когда что-то видишь — отлаживать в разы легче!

Что ж… Это была хорошая попытка понять, по какому закону стена появляется в том месте, где она появляется, но это мне ничего не дало. Даже видя эту стену, я не находил никакой закономерности.
Если нужно где-то найти таких же неудачников, как я, то самое лучшее место для этого — интернет. И я нашёл его — единственного человека, который отстрадал своё и рассказал об этом. Представляете, в 2006 году у какого-то чувака из Германии пятая точка горела точно так же, как у меня сейчас! Не знаю, что он выкурил (похоже, что исходники), но, ОКАЗЫВЕТСЯ, физический движок ожидает от вас трёхмерные точки, но передавать их надо как четырёхмерный вектор, просто в четвертой координате надо поставить мусор, типа так: [x1, y1, z1, 0, x2, y2, z2, 0, . ]. Скажите, как по const dReal* Vertices я должен понять, что там ждут в гости четырёхмерные вершины?

За что я люблю опенсорс — можно всегда докопаться до истоков всего. Я полез в исходники, и вот что обнаружил.
В 2003 году пришёл Russel Smith и добавил всю эту функциональность с trimesh collisions, в том числе интересующую меня строчку:

Тут всё понятно, потому что в определении чётко говорится, что dVector3 — это четырёхмерный вектор (есть некий шарм в этой логике).
А потом через пару месяцев врывается Erwin Coumans и переписывает так, чтобы тип был непонятен:


И только представьте себе, через 20 лет это изменение находит какого-то чувака (меня), который пишет вообще на другом языке программирования, и заставляет этого чувака гореть в тщетных попытках понять, какого хрена не работает.
Я переписываю код с добавлением четвертой координаты, и все начинает работать.

Вообще этот пост был задуман как развлекательный, типа «смотрите, погромист опять страдает, хахаха». Но мне кажется, что он поднимает достаточно глубокую проблему: как только вы выкладываете код, он начинает свой долгий путь сквозь время. Никто не знает, когда и кто его будет читать — может, вы или ваш коллега через пару месяцев, может, тысячи независимых разработчиков через пару лет, может, какой-то парень с горящим продом.
Получается такой вот эффект бабочки, как с этой невидимой стеной. Поэтому когда вы в следующий раз сядете писать код, представьте, что какой-то разраб через 20 лет будет в нём разбираться, и, пожалуйста, постарайтесь сделать жизнь этого чувака хоть чутоку легче. Ведь однажды этим кем-то можете оказаться вы сами.
Не только лишь все могут смотреть в будущее, но вы сможете, если подпишетесь на мой уютненький канал Блог Погромиста.
А ещё я держу все свои яйца в одной корзине (в смысле, все проекты у одного облачного провайдера) — Timeweb. Поэтому нагло рекламирую то, чем сам пользуюсь — вэлкам.
Показать полностью 9 1
2 дня назад
Если ты ростом ниже 185, то даже знание плюсов тебе не поможет

4 дня назад

Интересно, а сколько разработчиков в мире используют только свой код?

Показать полностью 1
10 дней назад
Гилфойл ерунды не скажет

Показать полностью 1
10 дней назад
Что заставляет программистов писать вирусы
Материал был взят и переведен с Рэддита. Приятного прочтения!
1. В 7 классе я ходил на кружок программирования, который фактически превратился в кружок по созданию вредоносных программ. Мы с другом написали программу, которая постоянно открывала на компьютере один сайт для взрослых и врубала звук на полную мощность. У нескольких человек этот сайт открылся прямо на уроке. Было весело.
2. Я написал эту программу ради азарта. Это был аддон для Воркрафта. Он запускался, когда персонаж входил в игру. А потом персонаж получал приглашение в гильдию от игрока с заряженным аккаунтом и сам становился главой гильдии. После этого можно было идти в игровой банк и забрать оттуда все ценное. Написать код было просто, но вот протестировать его работу с помощью приемов социальной инженерии очень сложно. Но мне повезло с первого раза. Прилив адреналина был колоссальным, но я также понимал, что меня, скорее всего, забанят в этой игре. Моей следующей целью была высшая рейдерская гильдия на сервере. Чтобы убедить лидера гильдии участвовать в моем тестировании, понадобился час разговоров по скайпу. Он попался на крючок, и все почти получилось. Я знал, что у этой гильдии игровых ценностей на сотни тысяч долларов. Глава установил аддон, но потом я понял, что он вошел через альтернативного персонажа, не имевшего доступ к богатству гильдии. Так у меня все провалилось. Меня выгнали из гильдии и забанили. Позже мне удалось вернуть свой аккаунт, но без тех ценностей, которые я награбил раньше.
3. В юности я создал несколько троянов удаленного доступа. С их помощью воровал аккаунты в разных играх и продавал их в интернете. Когда я впервые запустил троян, было 200 загрузок и 90 заражений. Половину угнанных аккаунтов я отдал другу, который помогал писать программу. Потом продал два акка и купил попугая, потому что люблю птиц. А потом старший брат объяснил, что все это плохо и незаконно, и я остановился.
4. Раньше вирусы создавались ради демонстрации крутости и были не особо вредными, это во времена Дос и вплоть до Виндовс XP. Потом появились черви для кражи паролей. Потом ботнеты, а они уже только ради денег. Они крадут данные, пароли, откровенные фотки, все подряд, для последующей продажи. Потом киберпреступники научились использовать сторонние компьютеры для майнинга, взлома сайтов и прочего дерьма.
5. У меня есть друг, который мошенничал с кредитками, крал личные данные, информацию с компьютеров, используя для этого кейлоггер, замаскированный под ссылку для скачивания. В школе он начал продавать вещи и одежду всем, включая меня. А через два года за ним приехали из ФБР и увезли, хотя он и был несовершеннолетним. Ему пришлось заключить сделку и вернуть товаров на 50 тысяч долларов. А еще он взломал главу отдела безопасности Xbox, про это есть видео на ютубе.
6. Когда-то я любила троллить людей в чатах, рассказывая, что у меня есть постеры с Бритни Спирс, но для их закачки надо открыть файл, который я пришлю. Так я получала доступ к компьютерам жертв. Могла полностью просматривать жесткий диск, скачивать, что мне надо, удалять с компьютера файлы. Однажды я стала удалять кучу всего с компьютера парня, с которым в это время общалась в чате, и он заплакал. Мне стало жаль его, и я прекратила, и больше никогда этим не занималась.
7. Я занимался этим ради денег. Уже в старших классах я занимался черным СЕО и восстановлением сайтов. Потом я познакомился с парнем, который придумал, как через песню из альбома вшить ссылку. Эта программа случайным образом одну из песен на компьютере превращала в ссылку. Когда человек хотел прослушать ее, всплывала надпись, что надо скачать кодек. Он скачивал, и я получал 10 центов. Свою ссылку мы размещали на торрентах, скачиваний было много, денег хватало. Позже я стал нанимать индусов, чтобы они распространяли по торрентам файл с этой ссылкой. Мне тогда было 16, и я за месяц заработал 4 тысячи долларов. Это было круто, учитывая, что на черном СЕО и сайтах у меня выходило около тысячи в месяц. А через несколько месяцев все пошло наперекосяк, и я стал заниматься другими делами.
Похожие подборки без цензуры и купюр ежедневно выходят на моем канале https://t.me/realhistorys
Всем здоровья и добра!
Показать полностью
Поддержать
11 дней назад
Как понимать мемы про айти?

11 дней назад

3D видеокарта-«декселератор» из 90-х. Как работала S3 ViRGE «под капотом»?

Друзья! Многие ли из вас застали такую легендарную видеокарту, как S3 ViRGE? Когда-то этот GPU стоял чуть ли не в каждом втором офисном компьютере: благодаря дешевизне и заявленной поддержке 3D-ускорения, эту видеокарту просто сметали с полок магазинов. Далеко не все могли себе позволить ATI Rage, Riva TNT и уж тем более 3dfx Voodoo и очень разочаровывались в свежекупленной видеокарте, когда пытались поиграть в новомодные игры тех лет. На момент написания статьи, в сети слишком мало материала о том, как работали видеокарты 90-х «под капотом», однако мне удалось найти даташит на видеочип, SDK для программирования 3D-графики специально под него и некоторую документацию. Я решил исправить это недоразумение и начать развивать отдельную рубрику о работе старых видеочипов: начиная от S3 ViRGE и заканчивая GPU PS2 и PSP. Сегодня мы с вами: вспомним о S3 ViRGE, узнаем о том, как работали видеокарты в 90-х годах, затронем 2D и 3D режим и почему они тесно связаны между собой, посмотрим на проприетарное графическое API S3 ViRGE и раскроем причину, почему же этот GPU был таким медленным!
❯ 3D графика на ПК: начало
В начале 90-х годов 3D-графика на обычных домашних компьютерах была редкостью. Профессиональные GPU применялись только на дорогущих графических станциях, которые использовались в кинематографе или различных симуляциях, а также на дорогих японских игровых автоматах. У простого обывателя не было доступа к аппаратным средствам рендеринга 3D-графики.

Однако это не значит, что 3D-графики не было вообще. Прогресс развития домашних процессоров шёл семимильными шагами и гиганты рынка —Intel,AMDи в некоторой степени Cyrix, выпускали всё новые и новые процессоры с повышенными тактовыми частотами, а ближе к середине 90-х — и с SIMD (MMX). Поскольку многие техники для отрисовки трехмерного изображения были разработаны ещё в 60-х — 70-х годах, игроделы к началу-середине 90-х во всю использовали некоторые наработанные техники из кинематографа для растеризации 3D-графики прямо на процессоре — так называемыйсофтварный рендеринг.

Одной из самых известных техник 90-х являлась 2.5D графика с использованием рейкастинга — когда картинка на экране выглядит как трёхмерная, однако по факту весь мир представлен в виде 2D-координат, а эффект «пола и потолка» был как бы фейковым. Принцип его работы довольно прост: от глаз игрока для каждого горизонтального пикселя (т. е. при разрешении 240х320, у нас будет 240 проходов) пускаются «лучи» и ищется пересечение с ближайшей стеной относительно угла обзор из глаз игрока. Из этого пересечения берется дистанция до этой стены (на основе дистанции и угла считается «высота» данной строчки стены) и считается какую строчку текстуры необходимо вывести в этой точке. Одними из первых игр с применением этой технологии стал Hovertank и Wolfenstein 3D, а технология применялась практически до конца 90-х. Одной из самых лучших реализаций рейкастинга — движок Duke Nukem 3D, Build Engine, написанный Кеном Сильверманом.

Однако не одним 2.5D мы были едины. Шли годы, в СНГ многие люди продолжали наслаждаться 8-битными и 16-битными играми на клонахNESи SMD. У некоторых уже появлялась PS1, которая позволяла играть в игры с довольно хорошей 3D-графикой, однако на ПК 3D-игры были доступны не всем. Но в 1996 году выходитQuake— новейший шутер от первого лица от id Software с настоящей, трушной 3D-графикой и переворачивает всю индустриюFPSс ног на голову. Посудите сами: Джон Кармак умудрился реализовать достаточно быстрый софтварный рендерер, который мог вполне сносно работать на Pentium 75Мгц в разрешении 320×240. А ведь помимо отрисовки кадра, игре нужно было просчитывать логику монстров (довольно примитивную, к слову), обрабатывать столкновения, просчитывать видимую геометрию с помощью BSP-дерева и обрабатывать клиент-серверную логику самой игры. Это была самая настоящая революция в мире 3D игр на ПК.

В 1997 году, id Software выпустили glQuake — порт Quake с софтрендера на OpenGL, плюс своеобразную прослойку для совместимости с API 3dfx Glide (на видеокартах Voodoo) и подмножества OpenGL, используемым в игре. Порт на OpenGL позволял разгрузить ЦПУ, перенеся всю отрисовку графики с процессора на 3D-ускоритель. Сам по себе, OGL как графическое API, представлял из себя лишь набор спецификаций, который мог быть реализован как в программном виде, так и в аппаратном производителем видеокарты (на примере Windows — OpenGL32.dll это программная реализация, которая при необходимости обращается к atioglxx.dll/nvoglvxx.dll — аппаратной реализации OpenGL от вендора видеочипа). Однако, OpenGL корнями уходил именно в отрисовку промышленной графики, а DirectX всё ещё находился в зачаточной форме, из-за чего многие производители разрабатывали собственное графическое API: из известных мне, могу подчеркнуть ATI CIF (C Interface), 3dfx Glide и проприетарное SDK S3 ViRGE. Некоторые вендоры поддерживали целые игровые движки — например, BRender и RenderWare.

Отдельные 3D-акселлераторы потихоньку начали завоевывать сердца геймеров и создавать новый сегмент рынка. Серьезные видеокарты от известных производителей, такие как 3dfx Voodoo, ATI Rage и Riva TNT стоили достаточно дорого и многим были не по карману. Зато существовало множество видеокарт с 3D-ускорителями от других производителей, про некоторые из них вы могли даже не слышать: отдельные дискретные видеокарты Intel (i740), видеокарты от производителя чипсетов SiS и конечно же, видеокарты от S3 с сериями ViRGE и Savage. Видеочипы от Intel и SiS делали упор на D3D 7.

S3 ViRGE была весьма неплохой видеокартой с точки зрения 2D-ускорения. Сейчас 2D принято считать частным случаем 3D (по факту, 2D-спрайты — это 3D-квады, состоящие из двух треугольников), однако в то время для работы с памятью видеокарты и аппаратного ускорения некоторых операций, таких как блиттинг (BitBlt) существовало отдельное графическое API — DirectDraw. С этим у ViRGE было всё хорошо — он поддерживал довольно высокое разрешение экрана (при желании, объём видеопамяти можно было нарастить и установить разрешение ещё выше) и умел ускорять часть операций как DDraw, так и GDI.
Однако, ViRGE разочаровывал многих геймеров 90х своей производительностью в 3D-графике. На коробке с бюджетной видеокартой красовались красивые надписи о 3D-графике следующего поколения, а на фотографии можно было увидеть некую игру про мехов с невиданной графикой!

По факту, ViRGE подходил для 3D-игр не особо хорошо. Конечно в те годы никто особо не плевался от FPS и при желании, игру могли пройти и в 15, и в 20 FPS. Однако производительность софтварного рендерера иногда была даже выше, чем у растеризатора ViRGE, а игры должны были быть специально адаптированы под неё (т. е. портированы для использования S3DTK). Тайтлов с адаптацией по этот GPU было немало: как минимум, Tomb Raider и MechWarrior 2 (который шел в комплекте с игрой). Польские ребята из известной многим Techland даже написали прослойку S3D -> OpenGL, позволявшей запускать Quake на ViRGE. Производительность была не ахти…
Видеокарт от S3 нашлись и у меня, причём сразу несколько — ViRGE в PCI-исполнении и Trio в AGP-исполнении! Иногда я их использую для проверки старых материнских плат, которых у меня не так уж и много — рабочих на PGA370 и ниже у меня совсем нет. Однако остаётся вопрос, как эти видеокарты работали под капотом? Давайте узнаем!
❯ «Под капотом»
Исторически сложилось так, что 3D и 2D акселераторы могли быть отдельными и формально не зависящими друг от друга устройствами. Архитектура IBM PC в зависимости от «поколения», предполагала сразу несколько типов видеоадаптеров, которые были стандартизированы под определенный тип мониторов. Один из таких адаптеров, VGA, стал стандартом на долгие годы, в то время как два других использовались в совсем ранних машинах. Их ключевое отличие было в организации видеопамяти и цветности — CGA/EGA предполагал разбитие пространства экрана на т. н. битплейны (один байт содержал информацию о нескольких пикселях и если не ошибаюсь, для сохранения адресного пространства сегменты экрана необходимо было переключать аля банки памяти) и былпалитровым, в то время как VGA предполагал как палитровый режим, так и полноценный RGB и мог отразить весь фреймбуфер в линейную область адресного пространства. Кроме того, долгое время VGA использовался для обозначения разрешения дисплея: QVGA — половина VGA (320×240), VGA (640×480), широкоформатный WVGA (800×480) и т. п.

Другой особенностью была полная (насколько мне известно) обратная совместимостью друг с другом. Например, GeForce 7xx, как один из последних GPU, который поддерживал Legacy BIOS, теоретически вполне мог работать и с EGA режимами, и с CGA через соответствующие видеорежимы int 10h!
3D-режимы же никак не были стандартизированы и каждый производитель реализовывал работу с ними по разному — как уже говорилось ранее, кто-то реализовывал поддержку совсем молодого D3D и OpenGL (насколько мне известно, лучше всего с OpenGL было у NVidia. Остальные вендоры поддерживали OGL, но были свои болячки — у ATI они тянулись чуть ли не до середины-конца нулевых), а кто-то делал собственное графическое API и работал с видеочипом почти напрямую. Первые 3D GPU использовали шину PCI, которую почти сразу заменила более скоростная, но интерфейсно и софтварно почти идентичная шина AGP, а затем уже появился PCI-E, который оставался тем же PCI в софтовом плане, но был дифференциальным и последовательным, а не параллельным как интерфейсы-предшественники.

Дабы понять, как работают первые видеокарты, необходимо узнать о том, как происходит процесс отрисовки 3D-графики в общем случае. В мире программирования графики это называетсяконвейероми состоит он как минимум из нескольких этапов:
Установка состояний: Программа задаёт источники света на сцене, параметры Z-буфера и Stencil-буфера, какую текстуру(ы) следует наложить на рисуемую геометрию и с какой фильтрацией, какой тип аппаратного сглаживания использовать и т. п.
Ранее, каждый стейт необходимо было устанавливать отдельно, при необходимости — для каждого DrawCall’а. После подготовки состояния, программа вызывает соответствующую функцию отрисовки.Обработка геометрии: Геометрия не поступает в растеризатор «как есть», в мировых координатах. Растеризатор оперирует вершинами в нормализованныхClip Spaceкоординатах — обычно, это [-1, -1… 1, 1], где 0.5 — центр экрана по каждой оси. Именно поэтому сначала необходимо провести этап трансформации геометрии для перевода из некой глобальной системы координат (которая может выражаться в метрах или, например, в пикселях) в Clip Space. Для этого чаще всего координаты (корректнее — трансформации) представляются в виде трех перемноженных матриц — model (мировые координаты геометрии), view (положение «глаз» в мире, или по простому камера. Умножая model на неё, мы получаем координаты объекта в пространстве камеры) и projection (матрица проекции, которая преобразовывает координаты из пространства глаз в тот самый Clip Space. Именно в этой матрице задается FOV для перспективной проекции и виртуальные размеры экрана для ортографической матрицы). После этого, координаты каждой вершины трансформируются полученной ModelViewProjection матрицей и получается финальная позиция для Clip Space. Звучит как сложный учебник матану, по факту всё очень просто. 🙂

Детали реализации низкоуровневого матана, в том числе перемножения матриц и построения матриц трансформаций и проекции знать желательно, но необязательно. Сейчас этим занимаются очень удобные математические библиотеки — например, glm, dxmath или d3dx.
Кроме того, ранее именно на вершинном этапе считалось освещение для уровня. В некоторых видеочипах была возможность аппаратного расчета источников света, в некоторых — только программная на ЦПУ.
На видеокартах тех лет, в том числе и S3 Virge, трансформацией вершин занимался центральный процессор, из-за чего было довольно серьёзное ограничение на количество вызовов отрисовки и число треугольников в одной модели. Видеокарты с аппаратной, но всё ещё не программируемой трансформацией вершин появились лишь к GeForce 2 — называлась эта технология T&L (Transform and Lightning) и её преимущество было в том, что у видеокарты были специализированные векторные сопроцессоры, способны быстро пересчитывать векторные операции (а у ЦПУ, в свою очередь, развивались SIMD наборы инструкций, позволяющие выполнять несколько операций над float одновременно). В некоторых случаях, был даже отдельный программируемый векторный сопроцессор как, например, в PlayStation 2, что позволяло реализовать вершинные шейдеры ещё в 2000 году! На современных видеокартах, этапом трансформации в самом простом случае управляют вершинные шейдеры. Помимо этого, есть возможность создания геометрии «на лету» с использованием тесселяции и геометрических шейдеров, а совсем недавно появились Mesh-шейдеры, которые объединили несколько подэтапов конвейера в один.

Растеризация: Сам процесс отрисовки геометрии на дисплей с данными, полученными с прошлого этапа. Именно на этом этапе треугольники (или иные геометрические примитивы) закрашиваются определенным цветом или на них накладывается текстура. В процессе растеризации есть такое понятие, как интерполятор — специальный модуль, который интерполирует несколько значений в барицентрических координатах растеризуемого треугольника, дабы текстурный юнит мог наложить определенный участок текстуры на фрагмент треугольника.
В современных видеокартах этот этап конвейера программируется пиксельными (или фрагментными) шейдерами. В старых видеочипах (исключение — вроде-бы частично программируемый GPU Nintendo 64, поправьте в комментариях, если не прав) этот процесс строго определен в каждом GPU и не программировался. Именно поэтому такой подход к рисованию графики назывался Fixed function pipeline. Были ещё комбайнеры, но они появились заметно позже — когда в видеокартах появилось уже несколько текстурных юнитов, способных смешивать несколько текстур одновременно.

Делая вывод, мы можем понять, что S3 Virge и другие видеочипы были устройствами, которые умели рисовать лишь тот уровень графики, который был заложен производителем с завода. Такой подход называется фиксированным конвейером — Fixed Function Pipeline. Сейчас разработчики видеочипов перешли с фиксированного конвейера на программируемый (шейдерный). Уже начиная с SM2.0-SM3.0, на современных видеокартах появилась возможность создавать крутое и достаточно сложное освещение и различные эффекты, которые стали неотъемлемыми в современных играх.
Кроме того, важно понимать, что в видеопамяти ранних видеочипов хранился только фреймбуфер, а немного позже — текстуры, именно поэтому VRAM в старой документации называют «текстурной памятью». Вообще, некоторые нюансы первых версий OpenGL тянуться именно из особенностей работы первых видеокарт. Вспомнить хотя бы первые функции для старта отрисовки геометрии и загрузке вершин на видеокарту — это были связки glBegin/glVertex/glEnd:
glBegin(GL_TRIANGLES);
glVertex3f(0, 0, 0);
glVertex3f(1, 0, 0);
glVertex3f(1, 1, 0);
glEnd(); // Для одного треугольника
glBegin(GL_TRIANGLES);
for(int i = 0; i
glEnd(); // Для меша
Даже сам glBegin/glVertex/glEnd появились не спроста. Геометрию на видеокарте начали хранить только в начале нулевых (и то не везде — привет встройкам Intel и S3).
Но перейдем к особенностям работы S3 ViRGE. Даташит лежит в свободном доступе, благодаря чему мы можем более подробно ознакомиться с характеристиками этого видеочипа и о том, как он работал под капотом.

В основе у нас лежит 64-х битное ядро, которое могло обрабатывать как 2D-графику с аппаратным ускорением, так и 3D-графику. Ядро работало на частоте 135МГц с встроенным RAMDAC (модуль, отвечающий за вывод картинки на аналоговые разъемы — VGA и DVI, однако выводом на TV-тюльпаны занимался отдельный чип TV-энкодер). Современные видеочипы перешагнули планку 1ГГц, однако сравнение исключительно по частоте некорректны — архитектуры очень сильно отличаются. Помимо этого, видеочип умел декодировать видео с интерполяцией и аппаратно «помогать» процессору с скейлингом видео (например, когда вы разворачиваете плеер на весь экран) и даже рендерить видео в текстуру (что позволяло реализовать, например, телевизоры в играх)!
3D движок поддерживал следующие возможности:
- Затенение по Гур.о
- Маппинг текстур с перспективной коррекцией и билинейной/трилинейной фильтрацией, а также мипмаппингом.
- Depth-буфер, сэмплинг тумана и поддержка альфа-блендинга (прозрачной геометрии).
Чип поддерживал две шины — PCI и менее известную VLB (Vesa Local Bus, очень условно ISA)
Помимо этого, у чипа не было встроенной памяти — к нему необходимо было подключать внешнюю DRAM-память 2/4/8Мб. От её количества зависело максимально-поддерживаемое разрешение экрана. Текстуры при необходимости хранились в ОЗУ.
Видеопамять когда-то расширялась за счёт дополнительных модулей! Эту видеокарту можно расширить аж до 8МБ!
Поддерживаемые разрешения экрана:

Для DirectDraw и ускорения 2D-графики в Windows была реализация аппаратного BitBLT — копирования пикселей в точку на экране. Она поддерживала все режимы, которые были в реализации этой функции в Windows — от монохромных, до 24-х битных. Без альфа-блендинга, само собой. Но тут нет ничего необычного — многие видеочипы тех лет предоставляли простое 2D-ускорение.
Интереснее реализация отрисовки 3D-графики. Каждый треугольник описывался 3-мя регистрами на каждый параметр — координата X, Y для каждой точки, текстурные координаты и т. п. Всего для отрисовки одного треугольника могло потребоваться до 43 регистров! Весьма немало. И именно из-за этого в свое время появились glBegin/glVertex/glEnd!

Параметры сэмплера (текстурного юнита) задавались регистрами, которые определяли формат пикселя текстуры и сам тип фильтрации. Как я уже говорил выше — поддерживалась билинейная и трилинейная фильтрация и проприетарный формат сжатия текстур, который стал стандартом: S3TC или DXT.

Для программирования S3 ViRGE было разработано собственное C SDK — S3DTK, которое состояло из сэмплов и заголовочных файлов для общения с GAPI видеочипа (или видеочипом напрямую, если игра предназначена для DOS). При этом вполне не исключено, что GAPI для Windows работало с видеокартой напрямую, предоставляя PCI-драйвер лишь как прослойку для обмена данными. Поскольку это не D3D, для игр с поддержкой видеоускорения требовалось качать специфические версии. Некоторые игры (как Quake 2) поддерживали мультирендер, но не поддерживали S3 ViRGE.
Весь графический API помещался в один заголовочный файл. API было не простым, а очень простым и понятным — думаю, даже разработчикам-новичкам было легко начать программировать под ViRGE!

Формат вершин был фиксированным и зависел от того, как вы рисовали геометрию на экране:

GAPI поддерживало различные типы треугольных списков, а также точки (POINT для спрайтов и систем частиц) и линии:
#define S3DTK_TRILIST 0
#define S3DTK_TRISTRIP 1
#define S3DTK_TRIFAN 2
#define S3DTK_LINE 3
#define S3DTK_POINT 4
Фактическое API для рисования умещалось в 9 функций и ещё несколько функций для инициализации библиотеки, преобразования адресного пространства и работы с Windows.
Для работы с состоянием видеочипа служили две функции — SetState и GetState. Именно они отвечали за то, как рисовалась геометрия на экране:

А для фактического рисования примитивов служили функции TriangleSet и TriangleSetEx! Да, это альтернатива DrawPrimitives/DrawArrays в современных GAPI. Никаких индексов тогда ещё не использовалось! Функции принимали указатель на массив вершин и их количество, а также на тип рисуемой геометрии (треугольники, линии и т. п.). В Ex версии, можно было «пачкой» установить стейты параллельно с рисованием — такой подход используется в DX10+ API — стейты тоже задаются исключительно «пачками», только теперь они поделены на подгруппы.

Для 2D-рисования были свои, отдельные функции — для блиттинга. Поддерживался ColorKey/хромакей — прозрачным считался определенный цвет, переданный как параметр функции

Основной причиной медлительности S3 ViRGE был низкий филлрейт. При отрисовке примитивов, которые занимают большое пространство экрана, FPS резко просаживался даже с примитивными кубиками и пирамидками. Однако, если не насаживаться на филлрейт и делать что-то типа 2D-поля и 3D-танчиков, то производительность оставалась вполне приемлимой.
❯ Заключение
История S3 закончилась поглощением компанией VIA. После этого, компания разрабатывала интегрированную графику специально для чипсетов VIA, а материнские платы на этих чипах пользовались довольно высоким спросом. Поэтому нередко взяв старый бюджетный ноутбук, года эдак 2005, можно найти в нём VIA Chrome — наследника легендарного S3 Savage! Проблемы у такого подхода тоже были — из-за наследия из конца 90х, ранние Chrome по сути поддерживали только D3D 7.0 и OpenGL ~1.4. Несколько позже, в 2009 году, компания выпустила S3 Chrome 540 GTX — одну из последних видеокарт на собственной архитектуре. Этот видеочип был достаточно современным и поддерживал DX10.1, OpenGL 3.0. Интересно, реально ли найти эту видеокарту сейчас?

По итогу мы можем сделать вывод, что первые 3D-ускорители были относительно простыми устройствами «под капотом» и их можно было программировать чуть ли не «напрямую». Многие старые видеочипы получили свои локальные прозвища и стали легендарными, однако их архитектура и принцип работы оставались тайной. По крайней мере, в рунете точно.

Насколько я понимаю, неравнодушные инженеры после закрытия 3dfx и слияния S3 с VIA решили «слить» даташиты в сеть, за что им большое спасибо! Ведь теперь мы имеем возможность посмотреть на принцип работы таких устройств сами!
Материал подготовлен при поддержке TimeWeb Cloud. Подписывайтесь на меня, мой Telegram и @Timeweb.Cloud, чтобы не пропускать новый материал каждую неделю!
Голосовой дневник на python с распознаванием голоса и сохранением в Mongo DB

Все больше растет популярность голосовых интерфейсов. Многие технологические компании-гиганты стремятся сделать своего голосового помощника. Но речевые технологии доступны и обычным пользователям. Каждый может использовать их в своих проектах
и делать голосовые интерфейсы еще удобнее и популярнее.Голосовой дневник — лишь один из примеров того, как можно встроить функции голосового интерфейса в повседневные действия.
Постановка задачи
У голосового дневника есть 4 основные задачи:

- распознать входящее аудиосообщение
- сохранить данные в базу
- вернуть или удалить данные из базы
- иметь удобный интерфейс взаимодействия
Будем использовать mongo db для сохранения нашего
очень важноготекста, docker для разворачивания базы данных, telegram в качестве интерфейса и голосовую модель vosk для распознавания голоса.Разберемся подробнее с составными частями нашего дневника.
Голосовой движок
Для распознавания голоса нужна языковая модель (online или offline), которая будет за разумное время переводить аудио в текст. Среди прочих существующих моделей мне понравился open source проект vosk. Он работает offline и достаточно точно распознает голосовые сообщения. К тому же, в нем для русского языка есть как очень легкие (43 mb) так и очень тяжелые (2.5 gb) модели, а выбор — это всегда хорошо. Полный список доступных моделей можно найти на официальном сайте. Помимо русского, там есть украинский, английский, немецкий и многие другие.
Ниже представлен код, использующий русскоязычную модель распознавания речи. Из особенностей: на вход должен подаваться файл формата wav.
import wave import json import vosk from vosk import KaldiRecognizer def recognize_phrase(model: vosk.Model, phrase_wav_path: str) -> str: """ Recognize Russian voice in wav """ wave_audio_file = wave.open(phrase_wav_path, "rb") offline_recognizer = KaldiRecognizer(model, 24000) data = wave_audio_file.readframes(wave_audio_file.getnframes()) offline_recognizer.AcceptWaveform(data) recognized_data = json.loads(offline_recognizer.Result())["text"] return recognized_dataХотя offline модели распознавания не такие точные, как их online аналоги, все-таки независимость от интернет-соединения развязывает руки во многих проектах.
Из минусов распознавания:
- Не может распознать слова на другом языке
- Плохо распознает заимствованные и терминологические слова
База данных
Для хранения текстовых данных будем использовать mongo db. Среди явных преимуществ такого решения следующее:
- Удобное хранение объемных текстовых данных
- Легкость расширения хранимых полей
- Простота обращения с индексом
Полный код команд добавления, удаления и поиска по базе mongo db можно найти в репозитории проекта, здесь же, для примера, разберем функцию добавления наших текстовых данных.
import datetime from pymongo.errors import DuplicateKeyError from pymongo.collection import Collection def add_value(database: Collection, value: str): """ Добавить одну запись в базу данных """ database_index = int(datetime.datetime.now().timestamp()) try: database.insert_one() return True except DuplicateKeyError: return FalseИндексом для хранилища выбран timestamp по нескольким причинам:
- Быстрый поиск / удаление для интервалов дат (одна из функций дневника)
- Наличие всего одного юзера (не будет проблем с дублированием индекса)
- Не нужно заморачиваться и выдумывать что-то сложнее
FFmpeg
FFmpeg — набор свободных библиотек с открытым исходным кодом, которые позволяют записывать, конвертировать и передавать цифровые аудио- и видеозаписи в различных форматах.
Зачем нам ffmpeg ? Формат файла, в котором сохраняются аудио-сообщения из telegram — это ogg . Но vosk работает с форматом wav . Поэтому будем использовать ffmpeg для трансформации ogg в wav .
Многие существующие библиотеки обработки аудио на python требуют наличие установленного ffmpeg . Но если у нас установлен ffmpeg , зачем использовать библиотеку, если можно использовать встроенные команды?
# команда для конвертации ogg в wav ffmpeg -i ./my_phrase.ogg -ar 16000 -ac 2 -ab 192K -f wav ./my_phrase_to_translite.wavразбор параметров команды
- -i ./my_phrase.ogg — текущий источник данных (исходная фраза для конвертации)
- -ar 16000 — частота дискретизации звука конечного файла. (Гц)
- -ac 2 — количество аудиоканалов
- -ab 192K — битрейт
- -f wav — формат конечного файла
Docker
Нужен для того, чтобы запустить внутри контейнера базу данных. В перспективе приложение с интерфейсом бота так же будет разворачиваться внутри отдельного контейнера.
Сам скрипт запуска достаточно простой, но есть один важный момент: нам не нужно разворачивать базу данных, если ее контейнер уже существует. Мы же не хотим однажды потерять все наши накопленные записи. Делается это очень просто: поиском имени запускаемого контейнера в пространстве имен уже запущенных ранее.
Команда для проверки:
if docker inspect --format '>' mongo_database then echo "container is already running" exit fiTelegram bot API
В качестве интерфейса дневника будем использовать всеми любимый telegram, а именно бота по API ключу. Преимущества такого решения следующие:
- Удобное API
- Реализованная функция отправки голосового сообщения
- Работа дневника в интерфейсном режиме чата
Пример работы

Далее идем в базу данных за нашими сообщениями, но уже в формате текста.

Как видим, текст распознался корректно и пользователю вернулись все записи, которые он сделал за последние 10 минут.
А что дальше?
Своим проектом я хотел показать простоту использования базовых голосовых функций в 2021 году. При желании можно прикрутить эвристическое / machine learning распознавание фраз или ключевых слов и сделать на основе этого проекта голосовое управление какой-нибудь системой.
Весь код проекта можно найти здесь: проект на GitHub
