0,0
рейтинг
21 января в 15:37

Разработка → go-скрипт который делает аудиокнигу из текстового файла используя один из лучших синтезаторов речи — Ivona от Amazon

Указываем в скрипте путь на книгу в txt — на выходе получаем папку с озвучкой хорошим синтезом.

Люблю потреблять контент ушами — в это время можно заниматься спортом или давать отдых глазам. Я привык что любая нормальная книга имеет аудио-версию, в худшем случае — любительскую, однако это не всегда так. Сегодня я несколько часов перебирал разные онлайн-сервисы и Андроид-программы для проговаривания русского текста — из приглянувшегося только Pocket который использует Google TTS и Google Books с тем же движком но отчего то другим качеством голоса. Также интересен SpeechKit от Яндекс. Но вроде лучший синтез у Ivona — эту компанию в 2013 году купил Амазон. Гитхаб нашел с десяток скриптов для дергания готового звука, но полностью готового решения для озвучки целой книги не оказалось. Используя неофициальную Go-библиотеку для IVONA Speech Cloud API за несколько часов написал скрипт — впервые использую Go, удобно что все зависимости в одном файле, даже библиотеку с Гитхаба подтягивает автоматически.

Скрипт создает один ogg-файл на абзац — у меня была книга в plaintext, конечно логичнее было бы разбивать по главам, а целиком книгу скормить не получилось — есть лимит на входное количество символов, около десяти минут озвучки на нормальной скорости. Выходной формат можно поменять на mp3. Имена файлов нормально отсортированы по порядку. При работе скрипта в консоли показывается текущий абзац отправленный на озвучку.



Для работы скрипта нужны access key и secret key — я оставил тут свои, но если перестанет работать — вы можете бесплатно получить новые ключи тут.

package main
import (
	"log"
	"fmt"
	"io/ioutil"
	"strings"
	ivona "github.com/jpadilla/ivona-go"
)
func main() {
	client := ivona.New("GDNAICTDMLSLU5426OAA", "2qUFTF8ZF9wqy7xoGBY+YXLEu+M2Qqalf/pSrd9m")
	text, err := ioutil.ReadFile("/home/vitaly/Desktop/test.txt")
	if err != nil {
		log.Fatal(err)
	}

	arrayOfParagraphs := strings.Split(string(text), "\n\n")
	i := 0
	for _,paragraph := range arrayOfParagraphs {
                paragraph = strings.TrimSpace(paragraph)
		if (len(paragraph) < 1) { // against empty lines
			continue
		}
		log.Printf("%v\n", paragraph)
		options := ivona.NewSpeechOptions(paragraph)
		options.Voice.Language = "ru-RU"
		options.Voice.Name = "Maxim"
		options.Voice.Gender = "Male"
		options.OutputFormat.Codec = "OGG"
		r, err := client.CreateSpeech(options)
		if err != nil {
			log.Fatal(err)
		}

		i++
		file := fmt.Sprintf("/home/vitaly/Desktop/ivona/tts%04d.ogg", i) // files like 0001.ogg
		ioutil.WriteFile(file, r.Audio, 0644)
	}
}


После замены пути к книге и пути к выходной папке (а возможно и заменив символ по которому делается сплит файла а также указав английский вместо русского) запускаем скрипт — минут за десять получаем около сотни страниц готового tts:
go run ivona-tts.go

Это мой первый go код, приветствую вашу критику.

P.S.: Первым делом лучше искать уже озвученную человеком версию книги.
Виталий Зданевич @zdanevich-vitaly
карма
6,0
рейтинг 0,0
Junior Software Developer
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (35)

  • +1
    Использую Vocalizer и для русского и для английского языка, очень качественный синтез, рекомендую
    4pda.ru/forum/index.php?showtopic=200728
    • 0
      А можно демо русского и английского?
      • 0
        Да, как замена Google TTS
        • +2
          Я имел в виду можете ли вы дать линк на Ютуб например который говорит именно тем голосом который вам больше всего нравится?
          • –3
            www.youtube.com/watch?v=KHxAap53Hd8
            Похоже на это, но у меня Milena Premium-High и звучит на телефоне получше, а для английских книг Ava Premium-High использую
            Профит в том, что не надо ничего кодировать, форматы переводить, стоит только открыть читалку(FBReader к примеру) и есть возможность слушать любые тексты
            • +1
              Попробуйте ту же Татьяну из Ivona, можете приятно удивиться. Когда-то сравнивали как раз на книгах, и голоса от Nuance заметно им проигрывали, особенно в витиеватой художественной литературе. В т.ч. на телефоне неплохо работает.
              • +1
                Спасибо, стоит изучить вопрос, я пользуюсь вокалайзером уже около двух лет, звучит хорошо, поэтому не смотрел за обновлениями
            • +2
              Мне мужские голоса в любом синтезе кажутся гораздо лучше…
              • +1
                Попробовал Ивону. Таки да, мужской голос лучше.

                Причём что забавно: тестировал на «Белой берёзе», и Максим читает «стоИт берёза», а Татьяна читает «стОит берёза». Это заговор! :D Впрочем, без «ё» они одинаково ошибаются в определении слова.

                Тестовый текст
                Белая берёза
                Под моим окном
                Принакрылась снегом,
                Точно серебром.

                На пушистых ветках
                Снежною каймой
                Распустились кисти
                Белой бахромой.

                И стоит берёза
                В сонной тишине,
                И горят снежинки
                В золотом огне.
  • +3
    Это мой первый go код, приветствую вашу критику.

    Не стоит хардкорить пути и ключи прямо в исходники. Для этого есть flag и path/filepath (не стоит обходиться обычной конкатерацией). А в bufio есть сканнер для чтения по словам/строкам/символам, хотя и не принципиально для файлов среднего размера. io/ioutil.WriteFile может возвратить ошибку (например место кончилось или ещё чего) и её стоит обработать.

    Парочка усовершенствований
    package main
    
    import (
    	ivona "github.com/jpadilla/ivona-go"
    
    	"bufio"
    	"flag"
    	"fmt"
    	"io/ioutil"
    	"log"
    	"os"
    	"path/filepath"
    	"strings"
    )
    
    const format = "tts%04d.ogg" // files like 0001.ogg
    
    var dir = flag.String("dir", ".", "like /home/vitaly/Desktop/")
    var textFile = flag.String("text", "", "path to text file, like /home/vitaly/Desktop/test.txt")
    var key = flag.String("key", "GDNAICTDMLSLU5426OAA", "access key for ivona api")
    var secret = flag.String("secret", "2qUFTF8ZF9wqy7xoGBY+YXLEu+M2Qqalf/pSrd9m", "secret key for ivona api")
    
    func main() {
    	flag.Parse()
    
    	conf := os.Stdin // если нет конфига, но читаем стандартный ввод
    	if *textFile != "" {
    		conf, err := os.Open(*textFile)
    		if err != nil {
    			log.Fatal(err)
    		}
    		defer conf.Close()
    	}
    
    	client := ivona.New(*key, *secret)
    	scanner := bufio.NewScanner(conf)
    	for i := 1; scanner.Scan(); i++ {
    		paragraph := strings.TrimSpace(scanner.Text())
    		if paragraph == "" { // пропускаем пустые строки
    			continue
    		}
    		log.Printf("process: %v\n", paragraph)
    
    		options := ivona.NewSpeechOptions(paragraph)
    		options.Voice.Language = "ru-RU"
    		options.Voice.Name = "Maxim"
    		options.Voice.Gender = "Male"
    		options.OutputFormat.Codec = "OGG"
    
    		r, err := client.CreateSpeech(options)
    		if err != nil {
    			log.Fatal(err)
    		}
    
    		file := filepath.Join(*dir, fmt.Sprintf(format, i))
    		err = ioutil.WriteFile(file, r.Audio, 0644)
    		if err != nil {
    			log.Fatalln("writing result file:", err)
    		}
    	}
    
    	if err := scanner.Err(); err != nil {
    		log.Fatalln("reading text file:", err)
    	}
    }
    


    diff
    4d3
    < 	"fmt"
    5a5,8
    > 
    > 	"bufio"
    > 	"flag"
    > 	"fmt"
    7a11,12
    > 	"os"
    > 	"path/filepath"
    10a16,22
    > const format = "tts%04d.ogg" // files like 0001.ogg
    > 
    > var dir = flag.String("dir", ".", "like /home/vitaly/Desktop/")
    > var textFile = flag.String("text", "", "path to text file, like /home/vitaly/Desktop/test.txt")
    > var key = flag.String("key", "GDNAICTDMLSLU5426OAA", "access key for ivona api")
    > var secret = flag.String("secret", "2qUFTF8ZF9wqy7xoGBY+YXLEu+M2Qqalf/pSrd9m", "secret key for ivona api")
    > 
    12,15c24,32
    < 	client := ivona.New("GDNAICTDMLSLU5426OAA", "2qUFTF8ZF9wqy7xoGBY+YXLEu+M2Qqalf/pSrd9m")
    < 	text, err := ioutil.ReadFile("/home/vitaly/Desktop/test.txt")
    < 	if err != nil {
    < 		log.Fatal(err)
    ---
    > 	flag.Parse()
    > 
    > 	conf := os.Stdin // если нет конфига, но читаем стандартный ввод
    > 	if *textFile != "" {
    > 		conf, err := os.Open(*textFile)
    > 		if err != nil {
    > 			log.Fatal(err)
    > 		}
    > 		defer conf.Close()
    18,21c35,39
    < 	arrayOfParagraphs := strings.Split(string(text), "\n")
    < 	i := 0
    < 	for _, paragraph := range arrayOfParagraphs {
    < 		if len(paragraph) < 4 { // against empty lines
    ---
    > 	client := ivona.New(*key, *secret)
    > 	scanner := bufio.NewScanner(conf)
    > 	for i := 1; scanner.Scan(); i++ {
    > 		paragraph := strings.TrimSpace(scanner.Text())
    > 		if paragraph == "" { // пропускаем пустые строки
    24c42,43
    < 		log.Printf("%v\n", paragraph)
    ---
    > 		log.Printf("process: %v\n", paragraph)
    > 
    29a49
    > 
    35,37c55,63
    < 		i++
    < 		file := fmt.Sprintf("/home/vitaly/Desktop/ivona/tts%04d.ogg", i) // files like 0001.ogg
    < 		ioutil.WriteFile(file, r.Audio, 0644)
    ---
    > 		file := filepath.Join(*dir, fmt.Sprintf(format, i))
    > 		err = ioutil.WriteFile(file, r.Audio, 0644)
    > 		if err != nil {
    > 			log.Fatalln("writing result file:", err)
    > 		}
    > 	}
    > 
    > 	if err := scanner.Err(); err != nil {
    > 		log.Fatalln("reading text file:", err)
    

    • +1
      у вас только конфиг из файла никогда читаться не будет
      • 0
        И правда. Вот фикс:
        	var err error
        	if *textFile != "" {
        		conf, err = os.Open(*textFile)
        
    • +2
      Спасибо.
  • –3
    Только Go всё-таки не скриптовый язык.
    • 0
      Сам сейчас периодически пользую go
      и как ни странно, как скриптовый, это позволяет наличие большого количества либ. Да и запуск не такой уж и долгий. Хотя конечно лучше собирать. Но для изучения подойдет и просто go run
      • –1
        Тогда ждём статьи про C-скрипты, чего уж…
        • +5
          Вы удивитесь — bellard.org/tcc
          C script supported: just add '#!/usr/local/bin/tcc -run' at the first line of your C source, and execute it directly from the command line.
          • 0
            С Go можно даже так
            //usr/bin/go run $0 $@; exit $?
            package main
            
            import "fmt"
            
            func main() {
            	fmt.Println("Hello world!")
            }
            
            

            Однако, по моему разумению, слово скрипт указывает на скриптовый язык.
  • +4
    Зачем конвертировать если можно сразу читать голосом? На андроиде это умеют многие приложения, например fbreader. На iOS и того проще — чтение текста встроенная системная функция, можно активировать в настройках чтение (в любой программе) по двойному свайпу сверху вниз.
  • 0
    Голос так себе, не знаю почему автор его выбрал, есть получше (имею в виду русский)
  • +1
    Спасибо за IVONA, не слышал. Наличие Java API радует, заберу к себе в умный дом
  • +1
    Умеют ли современные синтезаторы речи решать проблему с е/ё?
    • +2
      Попробовал скрипт автора, и могу сказать что в опробованной книге кусочек из фразы: «Я и осел здесь, чтобы отвязаться от вас.» слово осел было прочитано как осёл. Т.е. проблему вроде как решают, но местами неправильно
  • +1
    У меня возник вопрос, а если в тексте встречается предложение в котором есть и русский текст и английский?
    Один будет в пролете?
    • +1
      Будет прочитан весь текст, но не факт, что произношение будет правильным. У API Ivona есть возможность задавать лексиконы, которые позволяют подстроить произношение слова/фразы или произнести сокращения полностью. Я думаю, этим механизмом можно воспользоваться для подобных случаев.
    • +1
      Не знаю, как в Ivona, а в RHVoice, который я использовала с «Балаболкой», есть возмощность XML-тегами переключать язык произношения и другие параметры.
    • +1
      У них на сайте тестовый текст содержит фразы со смесью языков, вроде:
      Я один из голосов программы преобразования текста в речь IVONA. Введите фразу здесь и нажмите Play.
      Произносит без проблем.
  • +1
    Спасибо! Две недели назад как раз искала максимально качественный TTS. Пока лучшее из того, что нашла для русского языка — «Балаболка». Попробую поэкспериментировать с Ivona :)
  • +1
    if (len(paragraph) < 1) {
    

    Скобки в Go не нужны.
    log.Printf("%v\n", paragraph)
    

    В принципе ок, особенно для скрипта. Но т.к. точно известно, что у вас строка, то быстрее будет с %s.
  • +1
    Наступил на грабли) Для пробы скачал с lib.ru наугад стихотворение Бунина, оказалось в старинной транскрипции, с ятями. Очень неожиданное прочтение получилось. А так — действительно впечатляет, хорошее звучание и интонации естественные (я специально стихи для пробы выбрал). Пожалуй попробую себе в машину что-нибудь сделать послушать.
    Только вот извините, но хардкодить входные/выходные файлы — жуткий моветон. Я тоже go первый раз в жизни вижу, однако если вы разобрались с остальным, то узнать как передать параметры в командной строке уж совсем не проблема. Еще я не понял как оно работает с импортом модулей с гитхаба, мне пришлось руками ставить.
  • +1
    Интересное решение, но не проще ли использовать уже готовое решение с лучшим синтезом для русского языка chitatel.pro
    Попробовать можно синтез от ЦРТ на: voicefabric.ru
    Небольшой отрывок из Достоевского: «в начале июля, в чрезвычайно жаркое время, под вечер, один молодой человек вышел из своей каморки, которую нанимал от жильцов в С — м переулке»
    Ивона: пОд вечер
    Читатель: под вЕчер

    И, ксати, к вопросу о женских голосах, рекомендую попробовать голос Юлия.
    • 0
      Я видел эти сайты и пробовал их до написания своего скрипта — на мой вкус голос Ivona лучше. Хотя логично было бы предположить что русский синтез должен быть лучше у русских компаний вроде Яндекса или Центра Речевых Технологий, наверняка в будущем и те и те улучшат свои продукты.
      • +1
        было бы интересно провести Mos-оценку. Синтезировать один и тот же текст Ivona и ЦРТ и выложить на голосование.
      • 0
        Статья, в которой приведено сравнение синтеза ЦРТ и Яндекса: habrahabr.ru/post/272655
  • 0
    https://www.ivona.com/us/about-us/voice-portfolio/

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.