БлогNot. Javascript: как сделать голосовые часы или Когда Дима Ложится?

Javascript: как сделать голосовые часы или Когда Дима Ложится?

Дима, как известно, делает это в 21:30, а Злата тоже хочет приходить в садик первой, причинно-следственная связь между временем укладывания и пробуждения ей давно уже ясна, а вот со счётом времени пока туго. Поэтому самый популярный вопрос каждого нашего вечера с воскресенья по четверг включительно - "А через сколько (когда) Дима ложится (спать)?"

Я грозился сделать для этого доставшего вопроса робота-автоответчика, вот, собственно, и сделал. Удобнее всего, если это будет просто веб-страничка, после загрузки голосом отвечающая на вопрос.

Проблем, по сути, всего две.

1. Как вычислить время, оставшееся до нужного времени укладывания? Для простоты не будем учитывать возможный переход через полночь (хотя можно и учесть) и примем, что сегодня для работы с промежутками времени уже не стоит писать собственных решений, а есть почти стандартная библиотека moment.

Скачаем её и подключим обычным образом к странице, тогда задача становится совсем несложной:

<script type="text/javascript" src="js/moment.js"></script>
<script type="text/javascript">
//листинг 1 ...
 var sleepTime = '21:30'; //Время, когда он всё же ложится
 var t1 = moment (sleepTime, 'H:mm');
 var t2 = moment (moment().subtract(1,'minute'), 'H:mm');
 var diff = moment.duration (t2.diff(t1)); //Получить разницу в часах и минутах
 var h = diff.hours();
 var m = diff.minutes();
 //...
</script>

Если бы нужно было только текущее время в часах и минутах, moment.js не понадобился бы совсем, а этот код сократился бы до 3 строчек:

<script type="text/javascript">
 var d = new Date();
 var h = d.getHours();
 var m = d.getMinutes();
 //...
</script>

Дальше с часами h и минутами m можно делать то, что нам хочется, учтите только, что если полученные от moment.js величины неотрицательны, то нужное время уже прошло:

if (h>=0 && m>=0) { //Есть неотрицательные  значения - время уже прошло!
 //говорим что-то
}
else { //Есть часы и/или минуты со знаком "-" - время ещё не настало
 //говорим то, что надо, здесь будет листинг 2
}

Нас будет интересовать именно вторая ветвь, по ней потребуется сформировать нужную цепочку слов и заставить компьютер по порядку их произнести.

2. В HTML5 уже есть тег для аудио и заставить страницу сказать нужное слово несложно. Проблема в том, что у нас довольно много файлов с числительными и разными вспомогательными словами. Поместим каждое слово в свой элемент <audio>:

<audio id="sfx_0" src="sfx/0.wav" preload="auto"></audio>
<audio id="sfx_1" src="sfx/1.wav" preload="auto"></audio>
<audio id="sfx_1f" src="sfx/1f.wav" preload="auto"></audio>
<audio id="sfx_2" src="sfx/2.wav" preload="auto"></audio>
<audio id="sfx_2f" src="sfx/2f.wav" preload="auto"></audio>
<audio id="sfx_3" src="sfx/3.wav" preload="auto"></audio>
<audio id="sfx_4" src="sfx/4.wav" preload="auto"></audio>
<audio id="sfx_5" src="sfx/5.wav" preload="auto"></audio>
<audio id="sfx_6" src="sfx/6.wav" preload="auto"></audio>
<audio id="sfx_7" src="sfx/7.wav" preload="auto"></audio>
<audio id="sfx_8" src="sfx/8.wav" preload="auto"></audio>
<audio id="sfx_9" src="sfx/9.wav" preload="auto"></audio>
<audio id="sfx_10" src="sfx/10.wav" preload="auto"></audio>
<audio id="sfx_11" src="sfx/11.wav" preload="auto"></audio>
<audio id="sfx_12" src="sfx/12.wav" preload="auto"></audio>
<audio id="sfx_13" src="sfx/13.wav" preload="auto"></audio>
<audio id="sfx_14" src="sfx/14.wav" preload="auto"></audio>
<audio id="sfx_15" src="sfx/15.wav" preload="auto"></audio>
<audio id="sfx_16" src="sfx/16.wav" preload="auto"></audio>
<audio id="sfx_17" src="sfx/17.wav" preload="auto"></audio>
<audio id="sfx_18" src="sfx/18.wav" preload="auto"></audio>
<audio id="sfx_19" src="sfx/19.wav" preload="auto"></audio>
<audio id="sfx_20" src="sfx/20.wav" preload="auto"></audio>
<audio id="sfx_30" src="sfx/30.wav" preload="auto"></audio>
<audio id="sfx_40" src="sfx/40.wav" preload="auto"></audio>
<audio id="sfx_50" src="sfx/50.wav" preload="auto"></audio>
<audio id="sfx_hour1" src="sfx/hour1.wav" preload="auto"></audio>
<audio id="sfx_hour2" src="sfx/hour2.wav" preload="auto"></audio>
<audio id="sfx_hour5" src="sfx/hour5.wav" preload="auto"></audio>
<audio id="sfx_minute1" src="sfx/minute1.wav" preload="auto"></audio>
<audio id="sfx_minute2" src="sfx/minute2.wav" preload="auto"></audio>
<audio id="sfx_minute5" src="sfx/minute5.wav" preload="auto"></audio>
<audio id="sfx_am" src="sfx/am.wav" preload="auto"></audio>
<audio id="sfx_pm" src="sfx/pm.wav" preload="auto"></audio>
<audio id="sfx_off" src="sfx/off.wav" preload="auto">
 <p>Извините, Ваш браузер не сможет воспроизвести звук</p>
</audio>

По путям к файлам в атрибутах src Вы можете, при желании, скачать их, дописав содержимое атрибутов к базовому адресу http://scripts.kislenko.net/dimaClock/, по которому я планирую разместить скрипт. Например, для первого файла из списка получится адрес http://scripts.kislenko.net/dimaClock/sfx/0.wav

Пара из этих файлов нам, возможно, не пригодится для Диминой задачи, но их будет использовать второй скрипт, говорящий текущее время.

К сожалению, на сегодняшний день не существует звукового формата "для всех браузеров". Я использовал обычные файлы .wav, так что звука не будет в Internet Explorer, который... не поддерживает этот родной для Windows формат :) Можно, конечно, сделать копии всех файлов в MP3 и подключать их аналогичными тегами, но скрипт и так достаточно велик, а IE почти никем не используется, просто пренебрежём поддержкой "ослика".

Итак, мы создадим пустой массив sounds и натолкаем туда идентификаторов тех звуковых файлов, которые должны произноситься скриптом в данный момент времени, в h часов и m минут:

//листинг 2
  h = Math.abs(h); m = Math.abs(m);
  var s = (h>0 ? h + ' ' + goodWordForm(h,'час',  '','а','ов').word : '') + ' ' +
          (m>0 ? m + ' ' + goodWordForm(m,'минут','а','ы','' ).word : '');
  document.getElementById ('time_content').innerHTML = s;
  document.getElementById ('time_image').innerHTML = '<img src="img/on.png" alt="">';
  //сформировать цепочку слов:
  var sounds = [];
  if (h > 0) { //часы
   if (h < 20) sounds.push ('sfx_'+h);
   else {
    sounds.push ('sfx_20');
    var n = h % 20;
    if (n) sounds.push ('sfx_'+n);
   }
   sounds.push ('sfx_hour'+goodWordForm(h,'час','','а','ов').type);
  }
  if (m > 0) { //минуты
   if (m < 3) sounds.push ('sfx_'+m+'f');
   else if (m < 20) sounds.push ('sfx_'+(m%20));
   else {
    var n = Math.floor (m/10);
    if (n) sounds.push ('sfx_'+n+'0');
    n = m % 10;
    if (n) sounds.push ('sfx_'+n+(n<3?'f':''));
   }
   sounds.push ('sfx_minute'+goodWordForm(m,'минут','а','ы','' ).type);
  }
  //...

Здесь метод goodWordForm просто дописывает в конец слова нужное окончание ("час", "часа" или "часов"):

function goodWordForm (k, w, o1, o2, o5) { //окончания и типы слов
 var t = 0;
 if ( (k%100>10 && k%100<20) || k%10>4 || k%10==0) { w += o5; t = 5; }
 else if (k%10==1) { w += o1; t = 1; }
 else { w += o2; t = 2; }
 return { word: w, type: t };
}

Остаётся организовать последовательный вывод аудиофайлов, идентификаторы которых собраны в массиве sounds. Вывести один звуковой файл из Javascript-кода совсем несложно:

var sfx = document.getElementById ('id');
sfx.play();

Если файлов два и выводятся они по порядку, второй должен подождать завершения первого:

var sfx1 = document.getElementById ('id1');
sfx1.play();
sfx1.onended = function() {
 var sfx2 = document.getElementById ('id2');
 sfx2.play();
}

Как же вывести несколько файлов? Простейший путь - организовать цикл и в нём запускать файлы по очереди - выглядит весьма сомнительным. Дело в том, что яваскрипт выполняется асинхронно, по-моему, ожидать при этом последовательного выполнения процессов, запускаемых из цикла, не стоит. Поступим уродливо, но надёжно - у нас всё равно говорится не больше 6 слов (типа "двадцать три часа пятьдесят девять минут") и достаточно будет соответствующего количества вложенных ветвей "if":

//листинг 2, продолжение
  //и сказать это:
  var len = sounds.length;
  var sfx = document.getElementById (sounds[0]);
  var index = 1;
  sfx.play();
  if (len > 1) {
   sfx.onended = function() {
    var sfx1 = document.getElementById (sounds[1]);
    sfx1.play();
    if (len > 2) {
     sfx1.onended = function() {
      var sfx2 = document.getElementById (sounds[2]);
      sfx2.play();
      if (len > 3) {
       sfx2.onended = function() {
        var sfx3 = document.getElementById (sounds[3]);
        sfx3.play();
        if (len > 4) {
         sfx3.onended = function() {
          var sfx4 = document.getElementById (sounds[4]);
          sfx4.play();
          if (len > 5) {
           sfx4.onended = function() {
            var sfx5 = document.getElementById (sounds[5]);
            sfx5.play();
           }
          }
         }
        }
       }
      }
     }
    }
   }
  }

Как работает всё вместе легко увидеть в исходнике страницы сервиса, а вот и она:

 Открыть сервис в новом окне/вкладке

Для вас практичнее может оказаться простая "говорилка текущего времени" почти с тем же кодом и лежащая в той же папке:

 Открыть говорилку текущего времени в новом окне/вкладке (кроме IE, нужен включённый звук)

Кроме обновления щелчком по ссылке со страницы скрипта, его всегда можно перезагрузить обычным нажатием клавиши F5.

06.01.2018, 13:45 [2914 просмотров]


теги: javascript html личное время форматы детское сервис wav

К этой статье пока нет комментариев, Ваш будет первым