Дмитрий
Дмитрий10 июля 2017 г. 13:19

Использование модуля QtTextToSpeech для синтеза речи

Библиотека Qt 5.8.0 предоставила программистам возможность использовать системный синтезатор речи. Синтезатор речи - это программа, преобразующая текст в речь. Синтезаторы речи является неотъемлемой частью любой современной операционной системы: Windows (версии 7 и выше), Mac OS, Linux, iOS и Android. Кроссплатформенный интерфейс для управления синтезом речи предоставляет модуль QtTextToSpeech библиотеки Qt.

Для работы с модулем QtTextToSpeech на ОС Windows необходимо:

  • установить библиотеку Qt c модулем QtTextToSpeech для компилятора Visual Studio 2015 или старше ( mingw работать не будет );
  • установить компилятор Visual Studio 2015 ;
  • установить библиотеку SAPI 5.1 , если по какой-то причине она отсутствует на вашем компьютере;
  • установить голоса для синтезатора речи (если у вас они отсутствуют).
  • При сборке проекта необходимо использовать теневую сборку.

Описание

Если при компиляции программы Вы видите сообщение

Error loading text-to-speech plug-in "sapi"

то вы сделали что-то неправильно.

Если вы используете Android , то по умолчанию у вас работает online синтезатор. За это вы расплатитесь задержкой при синтезе и потраченным трафиком. Если это вас не устраивает, то устанавливайте offline версию. На Android 5.1 для этого необходимо перейти в «настройки» - «специальные возможности» - «синтез речи» - «синтезатор речи Google » - «установка голосовых данных» - «русский (Россия)» и жмём установить и ожидаем завершения. Кроме того, подготавливаем Qt Creator для создания Android приложений.

Теперь можно приступать к написанию программы. По ссылке вы можете скачать проект с исходным кодом программы, описанной ниже. Создаём процесс новый проект qtwidget. В файл проекта подключает необходимый модуль

QT += texttospeech

Подключаем библиотеку синтезатора речи

#include <QtTextToSpeech>

Создаём указатель на объект класса QTextToSpeech

QTextToSpeech* speech;

а затем и сам объект

speech = new QtextToSpeech;

Теперь можно генерировать речевые сигналы с помощью функции say(), в качестве аргумента которой нужно передать произносимый текст. В примерах созданных разработчиками Qt Creator приведён простой пример helloSpeech. Рассмотрите его для того, чтобы лучше понять возможности рассматриваемого модуля.

Я перейду к некоторым более сложным вещам. Программа, которую вы можете скачать по ссылке, содержит виджет класса QTextEdit для воспроизводимого текста , управляющие кнопки: старт, стоп, вперёд назад, …, окно для выбора файлов, и окна для настройки синтезатора.

void MainWindow::start(bool checked)

Запускать на воспроизведение можно и большие текстовые фрагменты. Однако это приводит к необязательным задержкам, а в случае online синтезатора на Android и к зависанию программы. Поэтому дробим текст на фрагменты, чем меньше, тем лучше. Для начала я выделяю из текста абзац ( int activeBlock ), а затем разбиваю строку на лист строк ( QStringList readList ) использую точки в качестве разделителя.

void MainWindow::start(bool checked)
{
    if(checked)
    {
        if(readList.isEmpty())
        {
            readList = ui->textEdit->document()->findBlockByNumber( activeBlock ).text().split(".");
        }
        if(!readList.isEmpty())
        {
            readString = readList.first();
            readList.removeFirst();
            if(!readString.contains(QRegularExpression("[A-Z]|[a-z]|[0-9]|[А-Я]|[а-я]")))
            {
                readString = ".";//windows не читает точки
                if(QSysInfo::productType() == "android")
                    readString = " ";
            }
            speech->say( readString );
            scrollTo();
            ui->textEdit->setReadOnly(true);
        }
    }
    else
    {
        readList.prepend(readString);//
        speech->stop();
        ui->textEdit->setReadOnly(false);
    }
}

void MainWindow::speechStateChange(QTextToSpeech::State state)

На синтезатор передаётся одна строчка. После завершения её воспроизведения speech генерирует сигнал stateChanged. Подключенный к ней слот speechStateChange отвечает за воспроизведение очередной строки.

void MainWindow::speechStateChange( QTextToSpeech::State state)
{
    QString mes;
    switch(state)
    {
    case QTextToSpeech::Ready:
        if(ui->pushButtonStart->isChecked())
        {
            textBlockSelection(colorClean);
            if(readList.isEmpty())
            {
                if( setActiveBlock( activeBlock+1 ) )
                    start();
                else
                    stop();
            }
            else
            {
                start();
            }
        }
        mes = "ready";
        break;
    case QTextToSpeech::Speaking:
        textBlockSelection(Qt::green);
        mes = "speaking";
        break;
    case QTextToSpeech::Paused:
        mes = "paused";
        break;
    case QTextToSpeech::BackendError:
        mes = "error";
        break;
    }
}

Вспомогательные функции

Для удобства использования программы в качестве читалки были написаны две вспомогательные функции scrollTo() для прокручивания текста до выбранного абзаца и textBlockSelection( QColor ) для выделения читаемого абзаца цветом фона. Ниже приведён их исходный код.

void MainWindow::scrollTo()

void MainWindow::scrollTo()
{
    if(ui->textEdit->verticalScrollBar()->maximum() == 0)
        return;
    QTextDocument *textDoc = ui->textEdit->document();
    int value = 0;
    for(int i = 0; i < activeBlock; i++)
    {
        value +=textDoc->findBlockByNumber(i).layout()->lineCount()
                * textDoc->findBlockByNumber(i).layout()->lineAt(0).height()
                + textDoc->findBlockByNumber(i).blockFormat().bottomMargin();
    }
    if(value <= ui->textEdit->verticalScrollBar()->maximum())
        ui->textEdit->verticalScrollBar()->setValue(value);
}

bool MainWindow::setActiveBlock(int blockNumber, bool scroll)

bool MainWindow::setActiveBlock(int blockNumber, bool scroll)
{
    readList.clear();
    textBlockSelection(colorClean);
    if(blockNumber < 0)
    {
        activeBlock = 0;
        if(scroll)
            scrollTo();
        return false;
    }
    if(blockNumber >= ui->textEdit->document()->blockCount())
    {
        activeBlock = ui->textEdit->document()->blockCount()-1;
        if(scroll)
            scrollTo();
        return false;
    }
    else
    {
        activeBlock = blockNumber;
        if(scroll)
            scrollTo();
        return true;
    }
}

Ударение и Омографы

Существенной проблемой для синтезатора речи является ударение. Особенно в тех случаях, когда два слова пишутся одинаково, но имеют разные ударения (омографы). В таком случае, без применения сложных семантических алгоритмов, ударение может быть выставлено только в ручную. И Windows ( в отличии от Android) позволяет это сделать. Для этого нужно поставить знак «`» ( на одной клавиши с буквой ё ). Однако точного механизма работы этого инструмента установить не удалось.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

Дмитрий
  • 23 июля 2017 г. 12:52

Если вы знаете простой способ для извлечения текста из pdf или djv файлов напишите в ответе.

Evgenii Legotckoi
  • 23 июля 2017 г. 16:10

Лично я простого не знаю способа. В обоих случаях понадобится использовать сторонние библиотеки.

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

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
Ua

Qt - Тест 001. Сигналы и слоты

  • Результат:84баллов,
  • Очки рейтинга4
Ua

Qt - Тест 001. Сигналы и слоты

  • Результат:42баллов,
  • Очки рейтинга-8
ОК

Qt - Тест 001. Сигналы и слоты

  • Результат:47баллов,
  • Очки рейтинга-6
Последние комментарии
ИМ
Игорь Максимов22 ноября 2024 г. 21:51
Django - Урок 017. Кастомизированная страница авторизации на Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 октября 2024 г. 23:37
Django - Урок 064. Как написать расширение для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 октября 2024 г. 17:19
Читалка fb3-файлов на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5 октября 2024 г. 16:51
Django - Урок 064. Как написать расширение для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55 июля 2024 г. 20:02
QML - Урок 016. База данных SQLite и работа с ней в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Сейчас обсуждают на форуме
f
firstlunoxod15 февраля 2025 г. 13:46
Рисование на QGraphicsScene при зажатой кнопке мыши Подскажите, пожалуйста! Как данный класс можно дополнить, чтобы созданные объекты можно было перемещать мышкой по сцене?
Дмитрий
Дмитрий3 февраля 2025 г. 16:24
Создание deb-пакета. Как создать ярлык на рабочем столе после установки собственного deb-пакета? Всем привет. Сделал свой deb-пакет с программой. Всё устанавливается и работает. Ставлю по пути /usr/bin/my_application. Как для пользователя при установке пакета сразу создать ярлык на раб…
NW
Nayo Wai30 января 2025 г. 19:22
не запускается компьютер!!! Не запускается компьютер (точнее работает блок , но сам монитор вообще жесть)В общем я ничего с интернета не скачивала в последнее время. На компе никаких левых пр…
n
nkly3 января 2025 г. 12:52
Нужно запретить перемещение только некоторых итемов, остальные перемещать можно. Вопрос решен. Узнать QModelIndex элемента на который мы перетаскиваем другой элемент, можно с помощью функции indexAt(event->position().toPoint()) представления QTreeViev вызываемой в переопр…
M
Marsel17 августа 2023 г. 0:26
OAuth2.0 через VK, получение email Спасибо большое за помощь и простите за то что отнял время своей невнимательностью.

Следите за нами в социальных сетях