Задача синтеза речи очень сложна, и, как нам представляется, в полной мере ещё никем не решена. Главным этапом в синтезе речи является преобразование последовательности символов над алфавитом естественного языка в последовательность над некоторым другим алфавитом, составленным из единиц устной речи — фонем. Как правило, нет прямого и однозначного соответствия между буквами и фонемами. Произношение буквы часто зависит от соседних букв и от положения буквы в слове нетривиальным образом. Сравните, к примеру, как произносится «u» в английских словах «put», «must» и «music» ([put], [mʌst] и [ˈmjuːzık]). Дополнительно усложняет задачу расстановка ударений и слова, которые при одинаковом написании произносятся по-разному (омографы), так что для точного произношения потребуется также смысловой анализ текста. Разумеется, нужно учитывать, что в разных языках различные правила произношения. Превращение фонем в команды управления звуковым устройством — задача тоже не из простых.
Короче говоря, мы замахнулись на дело, которое совершенно точно нам не по зубам, если, конечно, не воспользоваться чужими наработками. А мы и воспользуемся ими.
Всем известный онлайн-переводчик Google Translate умеет, помимо своей основной работы, переводить тексты, ещё и озвучивать их. Переводчиком можно пользоваться при помощи браузера, вводя текст в одно поле на странице, и получая перевод в другом поле (конечно, нужно указать, с какого и на какой язык требуется перевод). Сам переводчик реализован как веб-сервис, что позволяет пользоваться его возможностями при создании разнообразных программ. Сервис Google Translate снабжён документацией, в которой описаны его многочисленные возможности. Именно из документации мы и узнали, как использовать сервис в качестве синтезатора речи.
Если обратиться к сервису по специально подготовленному адресу URL, сервер пришлёт звуковой документ в формате MP3, с которым можно делать всё что угодно: сохранить в файле, проиграть в медиаплеере, или что-нибудь ещё.
Примечание | |
---|---|
Наша первоначально опубликованная программа хорошо работала, пока плохие парни из Google не сломали что-то важное в сервисе Google Translate. Мы долгое время считали, что ребятам не понравился предлагаемый нами способ использования их сервиса. Мы и теперь не знаем, так это или нет, но, благодаря Алексею Логинову, наша программа вновь заработала. Теперь глава спасена! Большое-большое спасибо Алексею! Мы изменили текст главы и программы в соответствии с его замечанием и с учётом современных реалий. |
Адрес должен содержать слова, которые нужно озвучить, язык, которому они принадлежат, и их кодировку. Общий вид адреса таков:
http://translate.google.com/translate_tts?client=tw-ob&tl=язык
&ie=кодировка
&q=слова
Часть адреса, расположенная после вопросительного знака, содержит список
параметров, от которых будет зависеть содержание присылаемого сервером
документа. Параметры отделяются друг от друга знаками амперсенда. Здесь
применяются именованные параметры, для которых указывается и имя, и значение
(они отделяются знаком равенства). В нашем случае именами будут
client
, tl
, ie
и q
. Использование именованных параметров позволяет указывать
их в произвольном порядке, а также не указывать те из них, для которых
используется значение по умолчанию.
К примеру, чтобы получить файл с озвучкой слова «speaker», потребуется адрес
http://translate.google.com/translate_tts?client=tw-ob&tl=en&q=speaker
Параметр tl
получает значение en
(международный двухбуквенный код английского языка), а параметру
q
присваивается значение слова. Параметр ie
не задаётся, поскольку используется кодировка по умолчанию (какая бы она ни
была, для слова, записанного только английскими буквами, она не имеет
значения). Назначение параметра client
(и смысл его значения
tw-ob
) мы не знаем, но именно его отсутствие вывело из строя
программу. Магия? Скопируйте этот адрес в адресную строку браузера
и наслаждайтесь произношением бездушной машины. Можете также сохранить звук
в файле.
Было бы заманчиво поместить весь текст, подлежащий озвучиванию, в параметр
q
. Но стандарт URL не допускает слишком
длинные адреса. Кроме того, сервис Google Translate во избежание
злоупотреблений накладывает дополнительное ограничение: параметр
q
не должен содержать более 100 символов.
Простым, но малодушным решением будет разбиение текста на отдельные слова, формирование адреса для каждого из них, выкачивание MP3 документа с каждым озвученным словом и его проигрывание. Однако накладные расходы при таком подходе будут слишком велики. На каждое слово будет сначала устанавливаться, а потом разрываться соединение с Google Translate. Соответственно, возникнут неприятные паузы между словами. К тому же трафик неоправданно возрастёт.
Вполне возможно собрать слова из текста в группы, чтобы общее количество символов в каждой (включая пробелы между словами) не превышало сотни. Это, правда, не решит проблему пауз между такими группами (мы решим её иначе). Конечно же, мы не станем разрывать слова, чтобы полностью использовать разрешённый лимит в сто символов. Будем уповать на то, что слов более чем из ста символов в тексте не встретится.
Естественно, никакие знаки пунктуации не будут никак обрабатываться. Есть ещё проблема числительных, записанных цифрами. Google Translate как-то с ней справляется, произнося такие числительные, как будто они были записаны словами в именительном падеже.
Адрес в интернете подобен командной строке программы. Часть адреса перед
вопросительным знаком (в нашем случаем это
http://translate.google.com/translate_tts
) играет ту же
роль, что и имя программы. Эта часть содержит схему http
,
имя сервера (translate.google.com
), расположение документа
на сервере (translate_tts
). Между этими частями вставлены
подходящие символы-разделители. Часть адреса после вопросительного знака —
параметры, они полностью аналогичны параметрам командной строки, только
разделяются амперсендами. Мы не знаем, как именно Google реализовала свой
сервис, но не исключено, что это программа с именем
translate_tts, которая выводит на стандартный вывод документ
MP3. Настройки веб-сервера приводят к тому, что при чьём-то
обращении по такому адресу сервер высылает в ответ не текст программы,
а результат её запуска, тот самый документ MP3. Будь такая
программа написана на Perl, она бы получила в массиве @ARGV
три параметра: ('client=tw-ob', 'tl=en',
'q=speaker')
. А может быть, она как раз и написана на Perl. При обычных
настройках веб-сервера в ответ на запрос по адресу обратно высылается
содержимое файла, на который указывает адрес. Например, страница, которую Вы
сейчас читаете, расположена в файле
~shvetz/54/inf/perl-problems/Speaker_Ideas.xhtml
,
находящемся на сервере mech.math.msu.su.
Как быть, если имя параметра или его значение содержит символ
&
? К примеру, фрагмент адреса q=Procter &
Gamble
будет воспринят как два параметра, q=Procter
и Gamble
. Есть и другая неприятность: в адресах
URL запрещены пробелы. Похожие осложнения возникают
и с другими символами, которые имеют особый смысл в адресах, например,
?
, #
и %
.
Эта проблема решается исключительно просто. Текст, помещаемый
в URL, специальным образом кодируется. При таком кодировании
небольшой список символов, не создающих проблем (в их числе буквы английского
алфавита и цифры) записываются «как есть». Все остальные символы разбиваются на
байты (октеты), а каждый байт записывается в так называемой
процентной нотации:
записывается знак процента, а за ним две шестнадцатеричные цифры — числовое
значение байта. К примеру, символ &
имеет значение
,
поэтому кодируется как %26
. Таким образом, адрес, включающий
фразу «Procter & Gamble», запишем так:
http://translate.google.com/translate_tts?client=tw-ob&tl=en&q=Procter%20%26%20Gamble
Поскольку пробел, запрещённый в адресах, используется очень широко, для него
при процентном кодировании делается особое исключение: вместо
%20
можно писать плюс +
, так что
приведённый адрес можно записать короче:
http://translate.google.com/translate_tts?client=tw-ob&tl=en&q=Procter+%26+Gamble
Естественно, сам знак плюса, чтобы он не воспринимался как %-кодированный
пробел, придётся кодировать как %2B
. Для знака процента как
такового используем %25
.
Для текста на русском языке в кодировке UTF-8 каждую букву
разбиваем на два байта и каждый из них кодируем, как было описано. Кодировке
UTF-8 мы посвятили раздел «Кодировка UTF-8». Чтобы Google Translate смог собрать
байты обратно в буквы, в явном виде укажем кодировку в параметре
ie
. Итак, для озвучивания слова хорошо
потребуется URL
http://translate.google.com/translate_tts?client=tw-ob&tl=ru&ie=UTF-8&q=%D1%85%D0%BE%D1%80%D0%BE%D1%88%D0%BE
Читатель может подумать, что вся работа с интернетом должна проходить через
веб-браузер. Конечно, это не так. Есть множество других программ, которые могут
самостоятельно обращаться по разным адресам в интернете, получая с них или
отправляя на них различные документы. Если вы пользуетесь Linux
, на вашем компьютере наверняка установлена
утилита Wget, позволяющая
скачать документ по заданному адресу:
%
wget 'http://translate.google.com/translate_tts?client=tw-ob&tl=en&q=speaker' -U Mozilla/5.0 -O speaker.mp3
Адрес в командной строке wget заключён в апострофы,
поскольку содержащиеся в нём символы ?
и &
имеют в командной строке особые смыслы. Параметр
-O
позволяет указать имя полученного файла. Параметр
-U
обманывает Google Translate, маскируя
Wget под что-то другое, поскольку сервис по какой-то
неведомой причине не хочет работать с Wget.
Программы, работающие в интернете от имени запустившего их пользователя, называются пользовательскими агентами. Для наших целей лучше всего подойдёт пользовательский агент, который умеет проигрывать полученные звуковые документы на имеющемся звуковом оборудовании.
Нужным свойством обладает медиаплеер MPV. Он прекрасно проигрывает видео- и аудиодокументы в различных форматах. Документы могут располагаться в файлах, но могут — что очень хорошо для наших целей — задаваться по адресу в интернете. Важное достоинство этого медиаплеера — возможность тонкой его настройки через параметры командной строки, то есть весьма развитый командно-строковый интерфейс. Мы в полной мере используем эту возможность.
Установленная в нашем компьютере программа MPV запускается командой mpv.
Запустим команду
%
mpv 'http://translate.google.com/translate_tts?client=tw-ob&tl=ru&ie=UTF-8&q=%D1%85%D0%BE%D1%80%D0%BE%D1%88%D0%BE'
Warning: option --sub-text-font was replaced with --sub-font and might be removed in the future. Playing: http://translate.google.com/translate_tts?client=tw-ob&tl=ru&ie=UTF-8&q=%D1%85%D0%BE%D1%80%D0%BE%D1%88%D0%BE [ffmpeg/demuxer] mp3: Estimating duration from bitrate, this may be inaccurate (+) Audio --aid=1 (mp3 1ch 24000Hz) AO: [pulse] 24000Hz mono 1ch float A: 00:00:00 / 00:00:00 (76%) Cache: 0s Exiting... (End of file)
Видно, что MPV весьма подробно сообщает о всех
своих действиях. Вряд ли нам стоит всё это читать. Параметр
--really-quiet
подавляет вывод любых сообщений в процессе
проигрывания.
Намечается следующий план действий: пусть наша программа читает текст из файла,
выбирает из текста слова, собирает их в группы, превращает каждую группу
в URL и для каждого URL запускает
MPV, подставляя командную строку при запуске опцию
--really-quiet
и сам адрес.
Учитывая среднюю длину слова в русском языке, MPV будет запускаться на каждые пять — шесть слов. Каждый раз будет отыскиваться файл с программой, затем содержимое файла (а он не маленький) будет копироваться в оперативную память компьютера. По окончании проигрывания память будет освобождаться, а затем всё повторится снова и снова. Не слишком ли это громоздко? Не приведёт ли это к ненужным паузам между группами слов при воспроизведении?
MPV умеет при одном запуске проигрывать по нескольку документов, нужно лишь указать имена файлов или адреса один за другим в командной строке. Этим можно воспользоваться, помещая в командную строку много-много адресов. Однако операционная система не допускает слишком длинных списков параметров при запуске команд. И, хотя этот лимит довольно большой, программа наверняка с ним столкнётся, если ей придётся читать вслух «Войну и мир». Что же делать? Группировать адреса по сотне, по тысяче, запуская MPV для каждой такой группы? Слишком громоздко.
На наше счастье, MPV позволяет передавать имена
файлов или адреса и другим способом. Нужно имена или адреса записать в файл, по
одному в строке (такой файл принято называть
плейлист), а в командной строке
указать имя плейлиста после параметра --playlist
. Сам же
плейлист может содержать сколько угодно элементов. Вот так:
%
mpv --really-quiet --playlist=playlist.txt
Мы предпочли более изящное решение. Если вместо имени плейлиста указать дефис, список адресов программа mpv будет считывать со стандартного ввода. Можно открыть команду
mpv --really-quiet --playlist=-
для записи, затем выводить в полученный дескриптор адреса по одному в строке (открытие команд мы обсуждали в разделе «Команды как файлы»). Когда адреса закончатся, не забудем закрыть дескриптор, иначе MPV не узнает, что плейлист исчерпан, и будет ждать дальнейшего ввода.
Выше мы упоминали об проблеме пауз при воспроизведении между элементами
плейлиста. Для загрузки очередного документа требуется время, сильно зависящее
от скорости соединения компьютера с интернетом. Оказывается,
MPV умеет справляться с этой напастью. Мы покопались
в документации к плееру, и обнаружили опцию с говорящим названием
--gapless-audio
, которая устраняет паузы.