Delphi speech api - IT Справочник
Llscompany.ru

IT Справочник
17 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Delphi speech api

Delphi speech api

Изучив статьи Denisа Butorinа, мы пришли к выводу, что речь в них идет о Microsoft Speech API 4. В силу этого, если вы решите использовать приведенные в статье примеры, то будьте внимательны к тому, какая версия Microsoft SAPI установлена на вашем компьютере.

Простая программа, использующая Speech API

А сейчас мы познакомимся с программированием речи в Delphi. Хотите чтобы Делфи заговорил — читайте эту страницу!

Надеюсь, что вы уже установили Microsoft Speech API (SAPI) и хотя бы один речевой движок (text-to-speech engine). Если нет — то все можно скачать здесь.

«Речевые(голосовые) движки» — это те модули, которые синтезируют речь. Для доступа к ним нужно использовать специальные функции, которые описаны в наборе функций Speech API. Поэтому для того, чтобы написать минимальную программу, умеющую говорить, нужны эти два компонента. Сначала мы попробуем написать как раз эту программу-минимум, а потом заставим персонаж MS Agent заговорить. То есть постараемся совместить технологию MSAgent и SpeechAPI. Тогда агент станет еще более приятным.

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

Попытаемся создать программу, где мы будем вводить текст для чтения в редакторе, выбирать движок для чтения в ComboBox’е и пару кнопок для чтения, паузы и остановки.

Объявим в секции private нужные для работы переменные:

В ходе создания главной формы приложения нам нужно проверить установленные в системе движки и поместить их названия в ComboBox. Это мы и сделаем:

В этом коде происходит создание экземпляров COM объектов класса CLSID_MMAudioDest и CLSID_TTSEnumerator. Для этого используется метод CoCreateInstance из библиотеки COM. Он создает экземпляр объекта на основании нужного класса CLSID(Class Identifier) и идентификатора интерфейса IID(Interface Identifier). В первом случае мы образуем объект для связи с аудио устройством, во втором — создаем экземпляр TTS, перечисляемого класса для перебора всех речевых синтезаторов, установленных в системе. После этого экземпляры классов окажутся соответственно в fIAMM и в aTTSEnum. После этого начинаем перебирать движки, узнавать их имена и добавлять в ComboBox. Кстати, не забудьте объявить в разделе uses модуль activex.pas, иначе Delphi будет ругаться насчет процедуры CoCreateInstance, и модуль ComObj.pas, иначе после выполнения процедуры CoCreateInstance переменная aTTSEnum будет оставаться равной nil, а значит программа будет обваливаться на следующей строке.

Теперь, если вы запустите программку, то в списке ComboBox’а увидите названия всех движков, установленных у вас в системе. Пока еще наша программка не умеет говорить, но уже знает необходимую инфу о речевых устройствах.

Теперь займемся загрузкой речевых синтезаторов для чтения текста. Поэтому пропишем событие изменения движка в ComboBox:

Ну и все, теперь следует сказать, что «механизм чтения» приводится в действие следующим образом:

Подставляйте в BufRich все что угодно, содержание этой строки и будет синтезироваться в речь!

Для полного счастья не хватает приостановки чтения, то есть паузы, и полной остановки. Так вот осуществить это можно с помощью следующих методов:

Остальные процедуры на кнопочки чтения, паузы и стоп, думаю, вы сами нафантазируете, а чтобы получить полный исходный текст проекта воспользуйтесь этой ссылкой(около 226Кб)

Delphi speech api

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

Мы разберем установку Microsoft Speech API и необходимых компонентов (все это прилагается к мануалу). Таким образом, я гарантирую Вам, что этот мануал действительно поможет ПОЛНОСТЬЮ разобраться с поставленной задачей. Также прилагается исходный код программы, который мы подробно разберем. На сайте cybern.ru вы найдете много полезных материалов по программированию на Delphi, поэтому рекомендую Вам зарегистрироваться на нем.

Ну а теперь давайте приступим к установке Microsoft Speech API (далее SAPI) и необходимых компонентов для Delphi . В нашем случае будет использоваться Delphi 7, но вы можете использовать и другие версии Delphi, т.к. все действия установки и работы с SAPI аналогичны. Итак, приступим.

Установка

Сначала установим SAPI. Для этого откройте файл “sapi.exe”, который Вы скачали вместе с этим документом. Должно открыться это окно:

Нажмите кнопку “Yes”. Далее откроется окно:

Немного подождем. Откроется окно:

Далее везде нажимаем “OK” и начинается копирование файлов:

Копирование файлов может занять несколько минут. Когда текущий процесс завершится, откроется окно:

Нажав “OK”, вам предложат перезагрузиться. Нажмите “Да” (“Yes”), чтобы перезагрузить систему и завершить установку SAPI.

Установив SAPI, приступим к установке компонента в Delphi. Запустите Delphi. В нем откройте “Component -> Import ActiveX Control”. Откроется окно:

В списке ActiveX компонентов выберите “Microsoft Direct Speech Recognition”.

В пункте “Palette page” выберете “Standard”, чтобы компонент появился во вкладке Standard.

Проинсталлируйте компонент, нажав на кнопку “Install..”.

Читать еще:  Дизассемблер visual studio

Должен появиться компонент TDirectSR:

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

Но перед тем, как начать обязательно полностью пройдите настройку и обучение движка распознавания вашему голосу:

C:Program FilesMicrosoft Speech SDKMiscmicwiz.exe

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

Разработка программы

Откройте ваш Delphi и создайте новый проект. Создайте компонент DirectSR1. Он должен выглядеть так:

Далее на форме создаем следующие компоненты:

  • EnginesList: TComboBox
  • Button1: TButton
  • ProgressBar1: TProgressBar
  • Label1: TLabel

Выглядеть это может примерно так:

Теперь нам надо изменить свойства компонентов:

  • ProgressBar1: Параметру Max присвоить “MaxWord” (без кавычек)
  • Button1: Caption присвоить “Слушать” (без кавычек)
  • Label1: Caption присвоить “Нажмите ‘Слушать’ ”

Создайте обработчик OnCreate у Form1:

В этой процедуре мы получаем список всех движков распознавания речи и выводим их в EnginesList при запуске программы.

Теперь создайте обработчик OnChange у EnginesList:

Эта процедура отвечает за изменение движка у DirectSR1 при выборе его в EnginesList.

Создайте обработчик OnCreate у Button1:

Давайте разберемся с тем, что такое файл ‘Grammar.txt’.

Этот файл содержит в себе список слов и фраз, которые вам надо распознать. Этот список задается при помощи простого кода, который по своему синтаксису напоминает ini файл. Например:

Разберемся с синтаксисом файла ‘Grammar.txt’.

Первые три строки содержат в себе описание библиотеки слов. Разбираться в них не надо и изменять тоже. Оставьте их такими.

[Start] – это пометка начала списка слов. Далее каждая строка, входящая в список с именем Start, начинается с “Start = ”. После указывается само слово или фраза (На английском языке!).

[opt] означает что следующее слово произносить не обязательно, т.е. получится два варианта:

Предположим у нас есть: Start = Close [opt] program

Тогда может получиться “Close program”, но может и просто “Close”.

О структуре файла “Grammar.txt” мы поговорили поверхностно. Более сложные примеры вы сможете найти в папке “C:Program FilesMicrosoft Speech SDKSR”.

В нашей с Вами программе мы будем использовать следующий файл “Grammar.txt”:

Создайте его в папке с нашей программой и назовите “Grammar.txt”.

Теперь давайте научимся обрабатывать этот файл. Для этого создайте обработчик OnPhraseFinish у компонента DirectSR1:

Если фраза была полностью произнесена и распознана, то срабатывает обработчик OnPhraseFinish.

В нем в константу Phrase заносится текстовое значение произнесенной фразы, т.е. то текстовое значение из файла “Grammar.txt”, которое наиболее схоже с произнесенной фразой.

В качестве наглядного примера мы выводим значение Phrase в текстовом сообщении на экран.

После этого мы сверяем, что было произнесено с “Close” и “Close program” и если одно из них совпадает, то закрываем программу.

Теперь хотелось бы для удобства добавить измеритель громкости. Для этого создадим обработчик OnVUMeter у компонента DirectSR1:

Теперь при прослушивании мы видим уровень громкости нашего голоса, что дает нам знать о том, что идет прослушивание.

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

Если у вас Windows Vista или более новые версии Windows, то запускать программу рекомендую от имени администратора.

К этому мануалу прилагается исходник программы, которая получилась у меня, и которая очень четко и адекватно распознает голос. Эту программу можно улучшать бесконечно: можно добавить другие команды, можно создать компьютерного “друга”, с которым можно поболтать. Для того, чтобы научить компьютер разговаривать на русском или английском языке, можно использовать компонент DirectSS, но об этом вы скоро сможете прочитать на сайте . Или еще вы можете управлять внешними устройствами при помощи голоса через компьютер, например включать и выключать свет в вашей комнате. Вас еще ждет очень много интересных и познавательных уроков, поэтому добавьте мой блог в закладки и переодически проверяйте его, т.к. практически каждый день здесь появляются интересные статьи и уроки.

Вы сможете задать вопрос, если у вас что-то не получится. Ну а на этом у меня все, надеюсь, что эта статья оказалась для вас полезной.

Скачать все необходимое можно по ссылке:

Работа с Microsoft Speech API в Delphi

Delphi site: daily Delphi-news, documentation, articles, review, interview, computer humor.

Работа с Microsoft Speech API в Delphi

В ходе создания программного обеспечения возникает желание дополнить возможности создаваемого приложения голосовым интерфейсом.

Если возможности распознания голоса ещё далеки от совершенства и простому программисту не по силам, то заставить ваше приложение весьма сносно заговорить может любой программист.

Об этом позаботились специалисты Microsoft выпустив на рынок бесплатный ActiveX компонент TextToSpeech входящий в стандартную комплектацию WinMe и Win 2x.

Если у Вас этого компонента нет Вам необходимо скачать и установить Microsoft Speech API 4.0 (825 Кб) и русский мужской и женский голос Lernout & Hauspie Text-To-Speech (2.9 Мб)

Читать еще:  Delphi запись в текстовый файл

Применение этого компонента позволяет значительно расширить функциональные возможности интерфейса создаваемой программы. Приятно когда программа говорит «человеческим голосом». Как пример моя программа общения по сети. Исходный текст прилагаю (39.4 Кб).

Рассмотрим пример применения Microsoft Speech API.

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

Для размещения ActiveX компонентов будем использовать универсальный класс-контейнер TAxControl, подробно описанный в книге «Delphi для профессионалов» авторы Александровский А.Д. и Шубин В.В..

unit axControl;
< Класс-контейнер для ActiveX (компонента управления). >
interface
uses
Windows, Messages, Classes, Controls,
ActiveX, OleCtrls,StdCtrls; //
///********************************************************************
type
TAxControl = class(TOleControl)//
private
CControlData : TControlData2; // Информация об ActiveX.
protected
procedure InitControlData; override;
public
property CData : TControlData2 read CControlData;
constructor Create (AOwn : TComponent;AGUID : TGUID);
end;
implementation
//////////////////////////////////////////////////////////////////////////////
constructor TAxControl. Create (AOwn : TComponent ;AGUID : TGUID);
begin
FillChar(CControlData,sizeof(CControlData),0); // Обнуляете поле с информацией об Active
CControlData.ClassID :=AGUID; // Указываете QUID компонента,который будет создаваться.
inherited Create (AOwn); // Вызываете конструктор предка.
ControlStyle := ControlStyle + [csClickEvents]; // Стиль отображения
end ;
///////////////////////////////////////////////////////////////////////////////////
procedure TAxControl. InitControlData;
begin
ControlData := @CControlData; // Указываете адрес, где находится информация об ActiveX.
end;
//////////////////////////////////////////////////////////////////////////////////////
end.

Размещаем TextToSpeech на Delphi компонент Panel и устанавливаем стандартные свойства.

В случае отсутствия данного компонента на Вашем компьютере, обрабатываем исключение связыванием с компонентом WebBrowser и запускаем скачивание данного компонента с http://activex.microsoft.com/activex/controls/sapi/spchapi.exe.

Если же на вашем компьютере уже установлены компоненты, произойдёт инициализация TextToSpeech, который озвучит текущее время.

var compon:TAxControl;
procedure TForm1.FormCreate(Sender: TObject);
begin
try
compon:=TAxControl.Create(self,stringtoguid(‘‘));
compon.Parent:=panel1;
compon.Visible:=true;
compon.Top:=10;
compon.Left:=10;
compon.Width:=100;
compon.Height:=100;
compon.OleObject.Speak(timetostr(time));
except
form1.Caption:=’установка SpeechAPI’;
messagebox(form1.Handle,’установи Speech API’,’ошибка’,0);
panel1.Width:=300;
panel1.Height:=250;
// компонент WebBrowser
compon:=TAxControl.Create(self,stringtoguid(‘<8856F961-340A-11D0-A96B-00C04FD705A2>‘));
compon.Parent:=panel1;
compon.Visible:=true;
compon.Top:=20;
compon.Left:=0;
compon.OleObject.Navigate2(‘http://activex.microsoft.com/activex/controls/sapi/spchapi.exe.’,0,0,00);
end;
end;

Рассмотрим более подробно наиболее общие свойства и методы TextToSpeech:

  • compon.OleObject.Speak(Text); //Произнести текст. Text:string;
  • compon.OleObject.AboutDlg(form1.Handle,’о голосовом движке’);// Выводит информацию о компоненте.
  • compon.OleObject.GeneralDlg(form1.Handle,’настройка’); // Выводит окно настройки компонента.
  • compon.OleObject.LexiconDlg(form1.Handle,’пользовательский’);//Окно подключения пользовательских словарей.
  • compon.OleObject.Speed:=X; //Скорость речи. X=от 80 до 210.
  • compon.OleObject.pitch:=X;// Тональность речи X=от 125 до 200.
  • compon.OleObject.Select(X);//персоанаж X=1(Светлана) X=2(Борис)
  • compon.OleObject.LipTension:=X;//визуальное положение губ X:= 0..255;

В большинстве программ хватает этих свойств и методов. Но SpeechToText некоторые слова произносит некорректно.

Для решения этой проблемы существует несколько путей:

  • Подключение пользовательских словарей;
  • Создание своих обработчиков;

Подключение словарей через вызов LexiconDlg, вручную, неудобно – библиотеку типов этого модуля, лично я, не нашел.

Остаётся создание своего обработчика с вызовом методов:

Speech API (SAPI) в Delphi. Учим компьютер говорить.

Смотрели старые фильмы про хакеров? Там обычно молодой, но бородатый человек сидит за своим компьютером и разговаривает с ним, отдает голосовые команды, а компьютер, как прилежный слуга, отвечает хозяину, иногда сообщает о каком-либо событии и т.д. Думаете это сказка? Совсем нет. Несмотря на то, что в настоящее время синтез и распознавание речи можно отнести лишь к развивающимся направлениям в программировании, тем не менее это направление есть и оно постоянно развивается и совершенствуется. Конечно, на данный момент очень сложно создать такую систему управления компьютером, которая распознавала бы тысячи команд, отданных миллионами различных голосов и при этом выполняла бы эти команды. Но, тем не менее, в нашем распоряжении есть Speech API — набор интерфейсов для работы с речью. И с помощью SAPI можно научить компьютер разговаривать.

Так как в настоящее время я работаю в Windows 7, то, к сожалению в этой версии ОС оставили всего один синтезатор речи — Anna (по-моему в XP синтезаторов было 2 или 3). Но, зато используется последний пакет речевых функций — Microsoft Speech API v.5.4. Разговаривает наша Аня на чистом английском и русский язык для неё чужд. Первым делом, чтобы воспользоваться SAPI нам необходимо импортировать в Delphi библиотеку типов. Для этого открываем Delphi IDE, выбираем в главном меню «Component — Import Component» в открывшемся окне выбираем «Import Type Library» и жмем «Next»:

Теперь находим в списке «Microsoft Speech Object Library», выбираем её, жмем «Next»

В новом окне ставим галку напротив «Generate Component Wrappers» и жмем «Finish»:

Теперь у нас есть заголовочный файл для работы с SAPI 5.4. и можно приступать к работе.
Создадим простенькое приложение, с помощью которого наша Anna будет нам читать английские сказки на сон грядущий. В качестве текста будем передавать в синтезатор сначала простую строку, а потом попробуем добавить статью из какого-нибудь файла.
Создаем новое приложение и укладываем на главную форму 1 компонент Edit и 1 кнопку Button:

Для того, чтобы перечести текст в голос нам потребуется всего один интерфейс — ISpVoice. Напишем такой обработчик onClick у кнопки:

Можете запустить программу, написать в Edit «Hello», нажать кнопку и послушать как с Вами поздоровается Ваш собственный компьютер.
В этой процедуре мы получили ссылку на интерфейс ISpVoice и воспользовались методом Speak. У метода три параметра:

Читать еще:  Файлы проекта delphi

pwcs — указатель на строку, которую необходимо перевести в голос;
dwFlags — флаги, используемые в синтезе речи.
pulStreamNumber — номер выходного голосового потока.
Если указано, что метод должен работать асинхронно, то если вы зададите два больших фрагмента для чтения, вначале будет воспроизведен первый фрагмент, а второй будет поставлен в очередь на воспроизведение.
Следует обратить внимание на то, какие значения может принимать параметр dwFlags:

Есть и другие флаги, но для нашего примера этих будет вполне достаточно. Теперь попробуем «скормить» нашей Аннушке в качестве параметра в Speak имя файла. Дописываем onClick:

Теперь создайте файл, содержащий какой-либо английский текст и укажите его в программе. После нажатия на кнопку голос Anna воспроизведет содержимое всего файла и при этом, кстати, учтет все знаки препинания.
Вот простой, я бы даже сказал элементарный, способ работы с SAPI в Delphi. Если Вам необходимо воспроизвести русскую речь, то предварительно необходимо обзавестись соответствующим движком, установив его себе на комп. Ещё года два-три назад, когда я только знакомился с этой технологией была компания L&H, выпускающая различные бесплатные движки для синтеза речи на различных языках, в том числе и на русском.
Ну и в качестве заключения скажу, что сегодня речевые технологии представляют собой ещё большую «экзотику», чем те же Gestures (жесты). Вполне возможно, что это связано с большими сложностями в плане создания различных движков, «обучения» компьютера распознавать искаженную речь, например, когда гнусавит или «проглатывает» отдельные слоги, но, тем не менее и эту технологию с определенными ограничениями можно использовать в своих проектах, делая приложения более привлекательными для пользователей.

Работа с Microsoft Speech API в Delphi

В ходе создания программного обеспечения возникает желание дополнить возможности создаваемого приложения голосовым интерфейсом.

Если возможности распознания голоса ещё далеки от совершенства и простому программисту не по силам, то заставить ваше приложение весьма сносно заговорить может любой программист.

Об этом позаботились специалисты Microsoft выпустив на рынок бесплатный ActiveX компонент TextToSpeech входящий в стандартную комплектацию WinMe и Win 2x.

Если у Вас этого компонента нет Вам необходимо скачать и установить Microsoft Speech API 4.0 и русский мужской и женский голос Lernout & Hauspie Text-To-Speech.

Применение этого компонента позволяет значительно расширить функциональные возможности интерфейса создаваемой программы. Приятно когда программа говорит «человеческим голосом». Как пример моя программа общения по сети.

Рассмотрим пример применения Microsoft Speech API.

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

Для размещения ActiveX компонентов будем использовать универсальный класс-контейнер TAxControl, подробно описанный в книге «Delphi для профессионалов» авторы Александровский А.Д. и Шубин В.В..

unit axControl;
< Класс-контейнер для ActiveX (компонента управления). >
interface
uses
Windows, Messages, Classes, Controls,
ActiveX, OleCtrls,StdCtrls; //
///********************************************************************
type
TAxControl = class(TOleControl)//
private
CControlData : TControlData2; // Информация об ActiveX.
protected
procedure InitControlData; override;
public
property CData : TControlData2 read CControlData;
constructor Create (AOwn : TComponent;AGUID : TGUID);
end;
implementation
//////////////////////////////////////////////////////////////////////////////
constructor TAxControl. Create (AOwn : TComponent ;AGUID : TGUID);
begin
FillChar(CControlData,sizeof(CControlData),0); // Обнуляете поле с информацией об Active
CControlData.ClassID :=AGUID; // Указываете QUID компонента,который будет создаваться.
inherited Create (AOwn); // Вызываете конструктор предка.
ControlStyle := ControlStyle + [csClickEvents]; // Стиль отображения
end ;
///////////////////////////////////////////////////////////////////////////////////
procedure TAxControl. InitControlData;
begin
ControlData := @CControlData; // Указываете адрес, где находится информация об ActiveX.
end;
//////////////////////////////////////////////////////////////////////////////////////
end.

Размещаем TextToSpeech на Delphi компонент Panel и устанавливаем стандартные свойства.

Если же на вашем компьютере уже установлены компоненты, произойдёт инициализация TextToSpeech, который озвучит текущее время.

var compon:TAxControl;
procedure TForm1.FormCreate(Sender: TObject);
begin
try
compon:=TAxControl.Create(self,stringtoguid(‘‘));
compon.Parent:=panel1;
compon.Visible:=true;
compon.Top:=10;
compon.Left:=10;
compon.Width:=100;
compon.Height:=100;
compon.OleObject.Speak(timetostr(time));
except
form1.Caption:=’установка SpeechAPI’;
messagebox(form1.Handle,’установи Speech API’,’ошибка’,0);
panel1.Width:=300;
panel1.Height:=250;
// компонент WebBrowser
compon:=TAxControl.Create(self,stringtoguid(‘<8856F961-340A-11D0-A96B-00C04FD705A2>‘));
compon.Parent:=panel1;
compon.Visible:=true;
compon.Top:=20;
compon.Left:=0;
compon.OleObject.Navigate2(‘http://activex.microsoft.com/activex/controls/sapi/spchapi.exe.’,0,0,00);
end;
end;

Рассмотрим более подробно наиболее общие свойства и методы TextToSpeech:

  • compon.OleObject.Speak(Text); //Произнести текст. Text:string;
  • compon.OleObject.AboutDlg(form1.Handle,’о голосовом движке’);// Выводит информацию о компоненте.
  • compon.OleObject.GeneralDlg(form1.Handle,’настройка’); // Выводит окно настройки компонента.
  • compon.OleObject.LexiconDlg(form1.Handle,’пользовательский’);//Окно подключения пользовательских словарей.
  • compon.OleObject.Speed:=X; //Скорость речи. X=от 80 до 210.
  • compon.OleObject.pitch:=X;// Тональность речи X=от 125 до 200.
  • compon.OleObject.Select(X);//персоанаж X=1(Светлана) X=2(Борис)
  • compon.OleObject.LipTension:=X;//визуальное положение губ X:= 0..255;

В большинстве программ хватает этих свойств и методов. Но SpeechToText некоторые слова произносит некорректно.

Для решения этой проблемы существует несколько путей:

  • Подключение пользовательских словарей;
  • Создание своих обработчиков;

Подключение словарей через вызов LexiconDlg, вручную, неудобно — библиотеку типов этого модуля, лично я, не нашел.

Остаётся создание своего обработчика с вызовом методов:

procedure TForm1.Button5Click(Sender: TObject);
begin
compon.OleObject.TextData(2,0,edit2.Text);//произнести фонемный код
end;

procedure TForm1.Button6Click(Sender: TObject);
begin
edit2.Text:=compon.OleObject.Phonemes(2,10,edit1.Text);//преобразовать в фонемный код
end;

Используя фонетический алфавит Вы можете в широких пределах изменять произношение. SpeechAPI компонент удобен для любителей Web-программирования, так как поддерживает интерфейс IObjectSefety и соответственно безопасен для использования в сценариях JavaScript и VbScript.

Ссылка на основную публикацию
ВсеИнструменты 220 Вольт
Adblock
detector