В предыдущей части мы занимались общими вопросами построения данных часов. В этой части займемся звуковыми семплами, включая которые в нужной последовательности можно получить любое значение текущего времени от 0:00 до 23:00 в голосовом формате.
Итак, преступим.
Для того, чтобы озвучить значения времени от 0 часов до 20, нужно 21 слово («ноль», «один», «два» … «двадцать»). Но, чтобы сказать 21, 22 и 23, новые слова нам уже нужны. Можно использовать уже имеющиеся в распоряжении: «двадцать» + «один», «двадцать» + «два» и «двадцать» + «три». Идея, надеюсь, понятна.
Чтобы текущее время звучало более-менее вменяемо, после значений 1 и 21 необходимо говорить «час» (двадцать один час), после 2, 3, 4, 22, 23 подходит слово «часа» (четыре часа, двадцать два часа), и после остальных значений, а именно 0, 5-20 слово «часов» (ноль часов, десять часов). Все сказанное попробую представить в виде таблицы:

Таким образом, нам необходимо три варианта слова: «час», «часа», «часов».
Перейдем к минутам. Проделывая все те же операции, получаем следующую таблицу:

Здесь так же три варианта завершающего слова, а именно «минут», «минута» и «минуты». Если текущее время что-то типа 21:00, то хочу, чтоб часы говорили «двадцать один час, ровно», а не «двадцать один час, ноль минут».
Займемся теперь самими цифрами. Для экономии места во FLASH-памяти хотелось бы использовать те же слова для озвучивания и значений часа, и минут. Но тут не совсем все гладко. Для случая 20:20 «двадцать часов, двадцать минут» и подобных все отлично, но для 21:21 видна проблема, нельзя сказать «двадцать один час, двадцать один минута». Таким образом, для некоторых значений цифр необходимо два варианта, в мужском и женском роде. Этих «особых» цифр не так и много, а именно «1» и «2», что не может не радовать.
В итоге нам необходимо записать следующие слова:
"ноль", "один", "одна", "два", "две", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять", "десять", "одиннадцать", "двенадцать", "тринадцать", "четырнадцать", "пятнадцать", "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать", "двадцать", "тридцать", "сорок", "пятьдесят", "ровно", "час", "часов", "часа", "минут", "минута", "минуты".
Ну и так как у нас есть внешний термодатчик, то почему бы не озвучивать и его? В итоге добавим еще несколько слов:
"минус", "градус", "градуса", "градусов", "температура на улице".
Итак, все подготовительные операции завершены, можно приступать к озвучиванию. Вариант с записью своего голоса отпадает по эстетическим соображениям, поэтому на ум сразу приходят разные речевые синтезаторы. Гугл.Тетка и ей подобные мне не понравились. В результате наткнулся на этот синтезатор, насколько понял называется RHVoice. Доступно несколько голосов и больше всего понравился Anna, его и решил использовать. Устанавливается все это хозяйство как-то мутно, поэтому будет не лишним написать небольшую инструкцию. Операция проводилась на Windows 7 x64. На XP наблюдались проблемы.
Для успешной установки необходимо скачать 3 файла: основной пакет, языковой пакет и голос, причем везде выбираем «Версия, совместимая с SAPI5». После скачивания у нас должно образоваться файла:
- RHVoice-v0.5-setup.exe
- RHVoice-language-Russian-v2.0-setup.exe
- RHVoice-voice-Russian-Anna-v1.0-setup.exe
Устанавливаем их в указанном порядке. Весь процесс установки занимает считанные секунды. Затем можно пойти в Панель управления => Специальные возможности => Распознавание речи => Преобразование текста в речь, выбрать пункт «Anna» и проверить, как она говорит (там может быть пункт «Microsoft Anna», это не то).
Далее, нам нужна программа, которая будет на вход получать наш текст, и, используя установленный синтезатор, сохранять его в wav-файл. Для этого подойдет программа, которая называется Balabolka. Она умеет много чего, в том числе воспроизводить текстовый файл с помощью установленных на компе речевых синтезаторов и сохранять текст в wav-файл, то, что нам и нужно! Собственно, записываем наши слова, при необходимости обрабатываем их в каком-нибудь аудиоредакторе и получаем готовые семплы.
Тут есть небольшая хитрость, некоторые слова синтезатор произносит криво, и чтоб это хоть как-то это исправить слово нужно писать «не совсем правильно». Например, «пятьдесят» ну ни как не хотела говорить нормально, а когда написал «пядесят» все стало норм. Некоторые слова исправляются пробелом посреди слова, некоторые приходилось писать в составе предложения, чтобы нормально звучала интонация. Так же есть специальные управляющие символы, которыми можно указать ударение и интонацию, но я с ними не разбирался. В конце статьи будет размещена ссылка на архив с моими wav-семплами.
Фух, половина дела завершено. Поговорим теперь о том, как предоставить доступ к нашей коллекции семплов AVR-ке. Да легко, повесим внешнюю spi флешку и все. Для этого нам нужно разработать формат хранения семплов на флешке. На этом этапе нам надо определиться с частотой дискретизации и разрядностью ЦАП-а. С разрядностью особо не поиграешься, в моем случае для R-2R делителя 8 бит будет оптимальным значением. А вот частоту дискретизации выбрал ~22 КГц. Теперь аудиоредактором конвертируем наши семплы в соответствующий вид: Моно, 22 КГц, 8 бит.
Теперь нам нужна еще одна программа, которая из полученных семплов будет собирать дамп. И ее придется писать самим. В ходе долгих (или не очень долгих, давно было, уже не помню) размышлений появилась вот такая структура:
/* структура, хранящая информацию об одном семпле */
struct SAMPLE_STRUCT
{
uint32_t start; //адрес начала семпла
uint32_t lenght; //длина семпла
};
/* структура, которая хранит информацию о всех семплах */
struct FLASH_HEADER
{
SAMPLE_STRUCT sample_0; //"ноль"
SAMPLE_STRUCT sample_1m; //"один"
SAMPLE_STRUCT sample_1w; //"одна"
SAMPLE_STRUCT sample_2m; //"два"
SAMPLE_STRUCT sample_2w; //"две"
SAMPLE_STRUCT sample_3; //"три"
SAMPLE_STRUCT sample_4; //"четыре"
SAMPLE_STRUCT sample_5; //"пять" и т.д.
SAMPLE_STRUCT sample_6;
SAMPLE_STRUCT sample_7;
SAMPLE_STRUCT sample_8;
SAMPLE_STRUCT sample_9;
SAMPLE_STRUCT sample_10;
SAMPLE_STRUCT sample_11;
SAMPLE_STRUCT sample_12;
SAMPLE_STRUCT sample_13;
SAMPLE_STRUCT sample_14;
SAMPLE_STRUCT sample_15;
SAMPLE_STRUCT sample_16;
SAMPLE_STRUCT sample_17;
SAMPLE_STRUCT sample_18;
SAMPLE_STRUCT sample_19;
SAMPLE_STRUCT sample_20;
SAMPLE_STRUCT sample_30;
SAMPLE_STRUCT sample_40;
SAMPLE_STRUCT sample_50;
SAMPLE_STRUCT sample_rovno; //"ровно"
SAMPLE_STRUCT sample_chas; //"час"
SAMPLE_STRUCT sample_chasov; //"часов"
SAMPLE_STRUCT sample_chasa; //"часа"
SAMPLE_STRUCT sample_minut; //"минут"
SAMPLE_STRUCT sample_minuta; //"минута"
SAMPLE_STRUCT sample_minuty; //"минуты"
SAMPLE_STRUCT sample_bell; //звук, который звучит перед сообщением
SAMPLE_STRUCT sample_alarm; //звук будильника (будильник так и не сделал)
SAMPLE_STRUCT sample_minus; //"минус"
SAMPLE_STRUCT sample_gradus; //"градус"
SAMPLE_STRUCT sample_gradusa; //"градуса"
SAMPLE_STRUCT sample_gradusov; //"градусов"
SAMPLE_STRUCT sample_temperatura_na_ulitse; //"температура на улице"
};
/* Собственно, дамп флешки */
struct FLASH_DUMP
{
//заголовок
struct FLASH_HEADER header;
//сами семплы, друг за другом,
// в порядке, представленном в FLASH_HEADER
char pcm_data[FLASH_SIZE - sizeof(FLASH_HEADER)];
} flash_dump_s;
В чем состоит идея: дамп разбит на две части: заголовок и сами данные. Заголовок состоит из 40-ка записей по sizeof(SAMPLE_STRUCT) = 2*sizeof(uint32_t) = 64 байта. Каждая запись хранит информацию о адресе начала текущего семпла, и о его длине. После заголовка идут сами семплы, склееные вместе друг за другом. Если мы хотим проиграть слово «три», то нам нужно обратиться к 5-ой записи (счет с нуля), которая будет находиться по адресу 5*sizeof(SAMPLE_STRUCT) = 320, прочитать sizeof(SAMPLE_STRUCT) = 64 байта, узнать адрес начала start и длину семпла lenght, в котором записано слово «три», перейти по адресу start и прочитать lenght байт. Затем прочитанные данные выплюнуть в ЦАП и наслаждаться звучанием слова «три». Осталось дело за малым, реализовать все это)) Описывать программу я не буду, так как отрыв ее, я испытал тихий ужас от своего старого кода. Кину в конце статьи как есть. В планах конечно же все переделать и сделать все правильно)) Но это когда-нибудь потом.
На этом пока все, в следующей части возьмемся за железо наших часов.
программа для создания дампа https://yadi.sk/d/9aVGuQHY3NXhjN семплы расположены в VoiceBuilder\bin\Debug\
Читать продолжение.