Microsoft SpeechAPI for Fun

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

Microsoft SpeechAPI for Fun

Сообщение antonio 13 ноя 2010, 23:18

Здесь возможно появиться что-то рабочее.
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

Re: Microsoft SpeechAPI for Fun

Сообщение antonio 13 ноя 2010, 23:22

В продолжение темы шаблона для плагина, решил попробовать сделать что-то более функциональное.
Под руку попался Microsoft Speech API 5.1.
К тому же на форуме по-моему поднимался вопрос неплохо было бы прикрутить управление персонажем к SpeechAPI.

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

Пытаюсь собрать плагин на основе двух вариантов
1)первый на основе COM библиотеки SpeechLib
этот вариант должен подойти для Unity 2.5 (Pro и Free),
но скорее всего не будет работать на windows 7 (да и возможности нет проверить)

2)второй на основе .NET Framework 3.0 библиотеки System.Speech
этот вариант должен подойти для Unity 3.0 (Pro и Free) и
должен работать на windows 7 (опять же я не проверю)

Для использования и разработки плагина необходимо установить Speech SDK 5.1
И возможно немного подучить его для распознавания речи в Control Panel\Speech
Microsoft не предоставляет библиотеки русского языка.
speechAPI.jpg

Делается это for fun по причине насколько я успел разобраться что кроссплатформенноси пока ожидать не стоит,
пока для каждой платформы используется свой recognition engine.
Но возможно в какой-то степени будет опытом в дальнейшем.
Unreal Engine 2 использовал SpeechAPI но отказались видимо по причине что поддерживалась только в 32-битной версии Windows.

Буду рад если кто-то поможет разобраться с ошибками или сам доработает и особенно если добавит результат в тему.
Ни на что не претендую просто учусь )
А возможно кому-то и польза будет.
Сдесь можно почитать про распознование речи

Что получилось и что не получилось у меня на данный момент.
Проговаривание текста
1)базовы вариант text to speech (SpeechLib) работает.
2)Unity вылетает и ругается.

Распознавание текста speech recognition
Unity вылетает и ругается в обоих случаях немогу перевести распознанный текст в string и извлечь его в Unity

В первом и втором варианте к своим dll добавил в папку плагинов Interop.SpeechLib.dll(для первого варианта) и System.Speech.dll(для второго варианта), хотя есть подозрения что этот вопрос должен или может по другому решаться.

Вот собственно код обращения к dll из Unity:
для SpeechSynthesis:
Синтаксис:
Используется csharp
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using SpeechLib;
using SpeechLibSynt3;

public class SpeechLibS3 : MonoBehaviour
{
        public string textToSpeak = "Hello";
       
        void Start ()
        {
                SpLibSynt3 listener = new SpLibSynt3();
                listener.Speak(textToSpeak);
        }
}
 

для SpeechRecognition:
Синтаксис:
Используется csharp
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using SpeechLib;
using SpeechLibReco3;

public class SpeechLibR3 : MonoBehaviour
{
        public string[] Words = new string[] {"Open", "Close", "Run"};
       
        void Update ()
        {
                SpLibReco3 listener = new SpLibReco3();
                listener.BuildDictionnary(Words);
                //Debug.Log(listener.RecoText());
        }
}
 

dll SpeechSynthesis(SpeechLib):
Синтаксис:
Используется csharp
using System;
using SpeechLib;

namespace SpeechLibSynt3
{
    public class SpLibSynt3
    {
        public bool done = false;

        SpVoice Voice;

        public SpLibSynt3()
        {
            //Voice.EndStream += new _ISpeechVoiceEvents_EndStreamEventHandler(Voice_EndStream);
        }
       
        public void Speak(string textToSpeak)
        {
            Voice = new SpVoice();
            Voice.Speak(textToSpeak, SpeechVoiceSpeakFlags.SVSFDefault);//SVSFDefault SVSFlagsAsync
        }

        public void Voice_EndStream(int StreamNumber, object StreamPosition)
        {
            done = true;
        }
    }
}
 

dll SpeechRecognition(SpeechLib):
Синтаксис:
Используется csharp
using System;
using SpeechLib;

namespace SpeechLibReco3
{
    public class SpLibReco3
    {
        SpSharedRecoContext recContext;
        ISpeechRecoGrammar grammar;
        ISpeechGrammarRule menuRule;

        public SpLibReco3()
        {
            // создаем экземпляр распознавателя
            recContext = new SpSharedRecoContext();
            // добавляем обработчик событий для получения распознанных голосовых команд
            recContext.Recognition += new _ISpeechRecoContextEvents_RecognitionEventHandler(recContext_SpeechRecognized);

        }

        public void BuildDictionnary(string[] Words)
        {
            //Создаем экземпляр словаря
            grammar = recContext.CreateGrammar(0);
            grammar.Reset(0);          
            menuRule = grammar.Rules.Add("Commands", SpeechRuleAttributes.SRATopLevel | SpeechRuleAttributes.SRADynamic, 1);
            object PropValue = "";

            //добавляем все слова из списка распознавания
            foreach (string word in Words)
            {
                if (word.Trim().Length > 0)
                {
                    menuRule.InitialState.AddWordTransition(null, word, " ", SpeechGrammarWordType.SGLexical, word, 1, ref PropValue, 1.0F);
                }
            }

            grammar.Rules.Commit();
            grammar.CmdSetRuleState("Commands", SpeechRuleState.SGDSActive);
        }

        //Получаем результат из движка
        public void recContext_SpeechRecognized(int StreamNumber, object StreamPosition, SpeechRecognitionType RecognitionType, ISpeechRecoResult Result)
        {
            RecoText(Result.PhraseInfo.GetText(0, -1, true));
        }

        //Забираем текст
        private static void RecoText(string format)
        {
            string txtOut = format;
        }
    }
}
 

dll SpeechSynthesis(System.Speech):
Синтаксис:
Используется csharp
using System;
using System.Speech.Synthesis;

namespace SpeechSynthesis3
{
    public class SpSynt3
    {
        SpeechSynthesizer synth;
       
        public void Speak(string textToSpeak)
        {
            synth = new SpeechSynthesizer();
            synth.SelectVoice("Microsoft Sam");
            synth.Speak(textToSpeak);
        }
    }
}
 

dll SpeechRecognition(System.Speech):
Синтаксис:
Используется csharp
using System;
using System.Speech.Recognition;

namespace SpeechRecognition3
{
    public class SpReco3
    {
        SpeechRecognitionEngine sre;

        public SpReco3()
        {
            //создаем движок распознавания речи
            sre = new SpeechRecognitionEngine();
            //присоединяем к нему аудиодевайс выставленный по умолчанию
            sre.SetInputToDefaultAudioDevice();
            //добавляем обработчик событий для получения распознанных голосовых команд
            sre.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(sre_SpeechRecognized);
        }

        public void BuildDictionnary(string[] Words)
        {
            //Создаем экземпляр словаря
            GrammarBuilder grammarBuilder = new GrammarBuilder();

            //добавляем все слова из списка распознавания
            grammarBuilder.Append(new Choices(Words));
            Grammar customGrammar = new Grammar(grammarBuilder);

            sre.UnloadAllGrammars();
            sre.LoadGrammar(customGrammar);

            sre.RecognizeAsync(RecognizeMode.Multiple);
        }

        //Получаем результат из движка
        public void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            if (e.Result.Text.Length >= 1)
                RecoText(e.Result);
        }

        //Забираем текст
        private static void RecoText(RecognitionResult recognitionResult)
        {
            string txtOut = recognitionResult.Text;
        }
    }
}
 
У вас нет доступа для просмотра вложений в этом сообщении.
Последний раз редактировалось antonio 17 ноя 2010, 13:05, всего редактировалось 2 раз(а).
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

Re: Microsoft SpeechAPI for Fun

Сообщение Neodrop 14 ноя 2010, 12:17

Тоже хотел поэкспериментировать, но всё времени нет. Буду наблюдать (popcorn) \m/
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Re: Microsoft SpeechAPI for Fun

Сообщение antonio 15 ноя 2010, 14:18

Попробуем разобраться с ошибками с простого.
В первом (если можно так сказать рабочем примере) text to speech (SpeechLib)
Работаем с флагами:
- если ставим флаг SpeechVoiceSpeakFlags.SVSFlagsAsync - то параллельно с речевым движком могут выполняться события Unity. (в этом случае проговаривается не весь текст - сколько успевает)
- если ставим флаг SpeechVoiceSpeakFlags.SVSFDefault - то сначала выполняется воспроизведение речи (в этом случае проговаривается весь текст), а потом уже передается очередь в Unity.
Хорошо нам нравиться вариант когда работает и речевой движок и Unity. Для того чтобы все-таки отследить проговаривание текста да конца нужно добавить событие отслеживающее окончание фразы _ISpeechVoiceEvents_EndStreamEventHandler.
Но как только я добавляю какие либо события Unity сразу же падает. Причем вижу такое, а после рабочий стол)
error.jpg


Отслеживаем примерно так.
Синтаксис:
Используется csharp
using System;
using System.Collections.Generic;
using System.Text;
using SpeechLib;

namespace SpeechLibSynt3
{
    public class SpLibSynt3
    {
        public bool done = false;

        public void Speak(string textToSpeak)
        {
            SpVoice Voice = new SpVoice();
            Voice.EndStream += new _ISpeechVoiceEvents_EndStreamEventHandler(Voice_EndStream);

            Voice.Speak(textToSpeak, SpeechVoiceSpeakFlags.SVSFlagsAsync);
        }

        public void Voice_EndStream(int StreamNumber, object StreamPosition)
        {
            done = true;
        }
    }
}
 
У вас нет доступа для просмотра вложений в этом сообщении.
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

Re: Microsoft SpeechAPI for Fun

Сообщение antonio 15 ноя 2010, 15:59

Похоже забыли про dispose bool disposing (позволяет освободить управляемые и нeуправляемые ресурсы.)
Сейчас будем учиться, как это делается.
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

Re: Microsoft SpeechAPI for Fun

Сообщение antonio 16 ноя 2010, 14:34

Да что-то я совсем залип немогу разобраться - нехватает знаний. Вот здесь можно про Disposing of Resources почитать.
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

Re: Microsoft SpeechAPI for Fun

Сообщение DbIMok 16 ноя 2010, 14:51

не знаю в чем ваша проблема, но немного про маршаллинг и вызов unmanaged кода есть здесь
правильный вопрос - половина ответа. учитесь формулировать вопросы понятно.
Новости > _Telegram чат @unity3d_ru (11.6k/4.8k online) > _Telegram канал @unity_news (4.8k подписчиков) > Телеграм тема > "Спасибо"
Аватара пользователя
DbIMok
Адепт
 
Сообщения: 6372
Зарегистрирован: 31 июл 2009, 14:05

Re: Microsoft SpeechAPI for Fun

Сообщение antonio 17 ноя 2010, 01:57

Вот здорово, а я и не замечал. Недавно обновился на 3.1. Как и заявили разработчики работает:
New Features:
Managed .NET DLLs can now be placed in the project folder and can contain script code, including MonoBehaviours, EditorWindows and ScriptableObjects. This allows you to move any code into a DLL, making code sharing between projects easier, and making it easier for middleware developers to create libraries without sharing the source code. This was introduced in Unity 3.0 but was not mentioned in the release notes.

Теперь просто закинул SpeechLib.dll в plugins и собрал такой скрипт в Unity:
Синтаксис:
Используется csharp
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using SpeechLib;

public class SpLibSynthesizer : MonoBehaviour {

        public string toSpeak = "Hello";
       
    public void Speak(string textToSpeak)
    {
                SpVoice Voice;
        Voice = new SpVoice();
        Voice.Speak(textToSpeak, SpeechVoiceSpeakFlags.SVSFDefault);
    }
       
        void Start () {
                Speak(toSpeak);
        }
}
 

И включается. Правда System.Speech так подцепить пока не удалось и ошибки конечно это не исправило.
Но приятно что можно сразу в Unity эксперементировать )
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

Re: Microsoft SpeechAPI for Fun

Сообщение Neodrop 17 ноя 2010, 03:08

Это и раньше было. Вы просто спутали палец с... не пальцем.
В 3.1 можно безболезненно включать в dll Editor скрипты (это и раньше можно было, но несколько через задний проход). А включать в библиотеки монобехи можно было и в 2.1. В вашем случае это вообще ничего не меняет.
Имеется в виду, что ваши монобехи и эдитор-скрипты вы можете упаковать в собственную dll
Ничего больше.
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Re: Microsoft SpeechAPI for Fun

Сообщение antonio 17 ноя 2010, 12:19

Да действительно так, но это и не удивительно что спутал.
недавно я не мог отличить managed от unmanaged dll. )

Очередной вопрос. SpeechLib.dll точно должна лежать в директории plugins.
А вот System.Speech.dll должна браться из системы только вот как определить пути я еще не разобрался.

Я тестирую простой синтезатор код ниже и получаю сообщение в консоль.
Получается что эти функции недоступны? У меня пока System.Speech лежит в директории плагинов.
NotImplementedException: The requested feature is not implemented.
System.Type.GetTypeFromCLSID (Guid clsid)
System.Speech.Internal.ObjectTokens.ObjectToken.CreateObjectFromToken[Object] (System.String name)

(Для проверки в студии опять пересобрал для консольного приложения там работает.)
На vista и windows 7 ненадо ставить SDK там уже все есть немного старше версия, но данный код должен подойти единственное там Сэма нет другой дефолтовый голос.
SpSynthesizer.cs
Синтаксис:
Используется csharp
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using System.Speech;
using System.Speech.Synthesis;

public class SpSynthesizer : MonoBehaviour {

        public string toSpeak = "Hello";

    class SpLibSynt
    {
        SpeechSynthesizer synth;

        public void Speak(string textToSpeak)
        {
            synth = new SpeechSynthesizer();
            synth.SelectVoice("Microsoft Sam");
            synth.Speak(textToSpeak);
        }
    }
       
        void Start () {
                SpLibSynt listener = new SpLibSynt();
        listener.Speak(toSpeak);
        }
}
 
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

Re: Microsoft SpeechAPI for Fun

Сообщение antonio 17 ноя 2010, 16:34

Пока затык :D
Вот код для консольного приложения если кто захочет поэксперементировать. (незабыть добавить .net объект System.Speech)
SpeechSynthesis:
Синтаксис:
Используется csharp
using System;
using System.Speech.Synthesis;
using System.Threading;

namespace SpeechSynthesis3
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("\r\n(Press a key to stop listening)");
            string sendToSpeak = "Hello World";
            SpLibSynt listener = new SpLibSynt();
            listener.Speak(sendToSpeak);
            listener.Run();
        }
    }

    class SpLibSynt
    {
        SpeechSynthesizer synth;

        public void Speak(string textToSpeak)
        {
            synth = new SpeechSynthesizer();
            synth.SelectVoice("Microsoft Sam");
            synth.Speak(textToSpeak);
        }

        public void Run()
        {
            while (!Console.KeyAvailable)
            {
                Thread.Sleep(100);
            }

            synth.Dispose();
        }
    }
}
 

SpeechRecognition:
Синтаксис:
Используется csharp
using System;
using System.Speech.Recognition;
using System.Threading;

namespace SpeechRecognition3
{
    class Program
    {
        static void Main(string[] args)
        {
            args = CheckArguments(args);

            CommandListener listener = new CommandListener();
            listener.BuildDictionnary(args);
            listener.Run();
        }

        #region args
        private static string[] CheckArguments(string[] args)
        {
            if (args.Length == 0)
            {
                args = new string[] { "Open", "Close", "Run" };
            }

            Console.Write("Listening for ");
            foreach (string word in args)
            {
                Console.Write(String.Format("{0}, ", word));
            }
            Console.WriteLine("\r\n(Press a key to stop listening)");
            return args;
        }
        #endregion
    }

    class CommandListener
    {
        SpeechRecognitionEngine sre;

        public CommandListener()
        {
            sre = new SpeechRecognitionEngine();
            sre.SetInputToDefaultAudioDevice();
            sre.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(sre_SpeechRecognized);
        }

        public void BuildDictionnary(string[] Words)
        {
            GrammarBuilder grammarBuilder = new GrammarBuilder();

            grammarBuilder.Append(new Choices(Words));
            Grammar customGrammar = new Grammar(grammarBuilder);

            sre.UnloadAllGrammars();
            sre.LoadGrammar(customGrammar);

            sre.RecognizeAsync(RecognizeMode.Multiple);
        }

        public void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            RecoText("Recognized {0}", e.Result.Text);
        }
       
        public void Run()
        {
            while (!Console.KeyAvailable)
            {
                Thread.Sleep(100);
            }

            sre.Dispose();
        }
       
        private static void RecoText(string format, params object[] args)
        {
            Console.WriteLine(string.Format(format, args));
        }
    }
}
 
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

Re: Microsoft SpeechAPI for Fun

Сообщение antonio 10 дек 2010, 13:41

Непонимаю как подружить Unity c EventHandler
И в логе не пишет. Я понимаю здесь надо смотреть ошибки?
-----CompilerOutput:-stdout--exitcode:0--compilationhadfailure:False--outfile:Temp/***.dll
-----CompilerOutput:-stderr----------
-----EndCompilerOutput---------------
- Finished compile Library/ScriptAssemblies/Assembly-CSharp.dll

Синтаксис:
Используется csharp
using UnityEngine;
using System;
using System.Collections;
using System.Runtime.InteropServices;
using SpeechLib;

        public class SpLibSynthesizer3 : MonoBehaviour
        {
                public string toSpeak = "Hello World";
               
                public string output = "";

                void Update () {
                        SpLibSynt listener = new SpLibSynt();
                        listener.Speak(toSpeak);
                }
        }

    public class SpLibSynt
    {
        public bool done = false;
        SpVoice Voice;

        public void Speak(string textToSpeak)
        {
            Voice = new SpVoice();
            //Voice.EndStream += new SpeechLib._ISpeechVoiceEvents_EndStreamEventHandler(Voice_EndStream);
            Voice.Speak(textToSpeak, SpeechVoiceSpeakFlags.SVSFlagsAsync);//SVSFDefault SVSFlagsAsync
            Voice.WaitUntilDone(2000);
        }

        public void Voice_EndStream(int StreamNumber, object StreamPosition)
        {
            done = true;
            Debug.Log("MyVoice EndStream");
        }
    }
 
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

Re: Microsoft SpeechAPI for Fun

Сообщение antonio 11 дек 2010, 01:03

Managed code Speech API
The existing SAPI 5 API can also be used from managed code to a limited extent by creating COM Interop code (helper code designed to assist in accessing COM interfaces and classes).


Так это что получается мне еще надо подключаться unmanaged sapi.dll я думал что SpeechLib.dll сам справляется.
И как писал DbIMok читать про маршаллинг.
Читаю но это слишком сложно для меня.
(это мне лет 10 назад надо было в программисты записываться)

Изображение
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

Re: Microsoft SpeechAPI for Fun

Сообщение antonio 11 дек 2010, 16:39

Народ объясните пожалуйста как правильно лог отсматривать.
Смотрю данный скрипт в логе http://pastebin.com/embed_iframe.php?i=mP9eEjmE
Синтаксис:
Используется csharp
using UnityEngine;
using System;
using System.Collections;
using System.Speech.Recognition;

public class SpSynthesizer : MonoBehaviour
{
        public string[] rWords = new string[] {"start", "exit", "left", "right"};
       
        void Update ()
        {
                SpRecognition listener = new SpRecognition();
                listener.BuildDictionnary(rWords);
        }
       
    void OnDisable()
    {
                //listener.Disable();
    }

    void OnApplicationQuit()
    {
                //listener.Disable();
    }
}

class SpRecognition
{
    SpeechRecognitionEngine sre;

    public SpRecognition()
    {
                sre = new SpeechRecognitionEngine();
                sre.SetInputToDefaultAudioDevice();
                sre.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(sre_SpeechRecognized);
    }
       
        public void BuildDictionnary(string[] Words)
        {
                GrammarBuilder grammarBuilder = new GrammarBuilder();

                grammarBuilder.Append(new Choices(Words));
                Grammar customGrammar = new Grammar(grammarBuilder);

                sre.UnloadAllGrammars();
                sre.LoadGrammar(customGrammar);

                sre.RecognizeAsync(RecognizeMode.Multiple);
        }

    public void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
    {
        switch (e.Result.Text)
        {
            case "start":
                Debug.Log("start");
                break;

            case "exit":
                Debug.Log("exit");
                break;

            case "left":
                Debug.Log("left");
                break;

            case "right":
                Debug.Log("right");
                break;
        }
    }
       
        public void Disable()
    {
                sre.Dispose();
    }
}
 
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

Re: Microsoft SpeechAPI for Fun

Сообщение antonio 12 дек 2010, 18:40

Unity может использовать .NET 3.5 с помощью Mono 2.6.3 если байт-код является совместимым с 2.0

Значит в моем случае я могу спокойно забить?
Аватара пользователя
antonio
UNIт
 
Сообщения: 132
Зарегистрирован: 03 ноя 2009, 15:42
Откуда: Смоленск

След.

Вернуться в Исходники (Копилка)

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3