...

суббота, 3 февраля 2018 г.

[Из песочницы] Новый облегчённый язык разметки текста на основе парных кавычек (pq)

Причины (впрочем, достаточно субъективные), почему я выбрал именно символы ‘’ (а не `´ или, скажем, «» или “”):
  1. Они достаточно приятно выглядят (в соответствующем шрифте [например, Courier New]). На мой взгляд, лучше, чем «» или ‹›.
  2. Они идут подряд друг за другом в любой кодировке (в отличие от `´ или «» или ‹›) [также как и “”].
  3. Почему не “” — в русских текстах парным символу является (Экранная типографика. Кавычки).
  4. А ещё мне коды[/годы] этих символов нравятся...
    201? и 201¿

Один существенный минус символов кавычек и в том, что символ закрывающей одиночной кавычки согласно текущему стандарту Юникод является предпочтительным символом в качестве знака апострофа, и во многих документах и веб-страницах на английском языке символ действительно используется в качестве апострофа (например так: Don’t), в том числе — в сгенерированной документации к Python, впрочем в HTML-коде там используетсяещё недавно использовался HTML entity ’, а в исходных rst-файлах документации используется обычный апостроф ' — так что и html (при использовании ’) и rst-файлы можно заключать в такие кавычки без проблем [и, к слову, наиболее известные [мне]типографы (за редким исключением) вставляют HTML-entity ’[или ’ или ' или ’], а не непосредственно сам символ закрывающей кавычки , а если посмотреть на крупнейшие англоязычные новостные ресурсы, то можно заметить, что на многих из них в качестве апострофа используется просто символ апострофа ' (вопреки рекомендации Unicode Consortium)], но если есть такая необходимость, то пк-разметка позволяет вставить непарную открывающую и закрывающую кавычку: так `‘` или так '‘’ (смотри Дополнительные возможности форматирования. "Сырой"\Raw HTML).

Ещё один минус [символов кавычек и ]неоднозначность отображения символов и во многих популярных шрифтах. В частности, в Consolas, используемом в GitHub и Bitbucket. И вообще, символы парных кавычек в разных шрифтах выглядят по-разному (в том числе и в моноширных шрифтах для программирования):

[На мой взгляд, это просто показывает, что в целом отсутствует чёткое/однозначное представление о том, как должны выглядеть эти символы.]

(Кстати, на Хабре символы одиночных парных кавычек отображались вполне приемлимо (по крайней мере, в Windows) за счёт использования Verdana для основного текста и Courier New для кода, но несколько месяцев назад шрифт для основного текста статей поменяли на Arial, из-за чего пришлось немного подправлять статью — заменять ‘ и ’ на и (в pq: `‘` и `’`).)

Let's block ads! (Why?)

Интервью. Как создать качественную базу знаний: выбор технологий, поиск и дальнейшая поддержка

Базы знаний — популярный инструмент, который компании используют для решения разных задач вроде снижения нагрузки на службу поддержки. Идея звучит красиво: зачем постоянно отвечать на одни и те же вопросы, когда можно просто описать эти решения и ссылаться на них. Однако на практике всё не так просто, и каждый сталкивался с ситуациями, когда база знаний висит мертвым грузом и не приносит никому никакой пользы.

О том, как этого избежать, говорим сегодня с Дмитрием Плотниковым, консультантом по SharePoint и Office 365, Microsoft MVP, который создавал базы знаний для многих крупных компаний.

Какими вообще бывают базы знаний?


Вообще, в разных случаях понятие «база знаний» может различаться, но вариантов технической реализации такой базы не так уж и много: от простого документа до более сложных иерархических структур. Они могут быть полностью автоматизированными, поддерживаться сообществом или конкретными сотрудниками компании. Но суть одна: там хранится информация, к которой нужен доступ для решения определенных задач.

О чем нужно подумать, прежде чем начинать работу по созданию такой базы?


В разработке базы знаний необходимо прежде всего определиться с тем, какая информация будет в базе (о продуктах, услугах и т. д.), кто будет ее использовать (сотрудники или, наоборот, клиенты и пользователи), будет ли она передаваться и если да, то каким образом.

Важно понимать: мало просто создать базу знаний и начать вносить в нее информацию. В последние годы в разных сферах отмечается реальный переизбыток информации, эта же проблема есть и в бизнесе. Если компания достаточно большая или ее продукты пользуются популярностью, то в короткое время база знаний значительно разрастется, в ней появится множество статей, и ориентироваться во всём этом разнообразии будет очень трудно. Поэтому еще на этапе проектирования базы знаний нужно думать о том, как в ней будут реализованы поиск и классификация контента.

Какие ошибки часто допускают при создании таких продуктов?


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

Создавать ли инструмент самостоятельно или на базе существующих? Это сложный вопрос, однозначно тут ответить трудно. Многие из таких решений не подразумевают какой-то серьезной кастомизации — тот же Confluence можно только взять и начать пользоваться, по сути, как есть. На практике такие базы очень быстро становятся бесполезными, поскольку сильно разрастаются и проявляются все связанные с этим проблемы.

Другая крайность — создание собственных систем с нуля или на основе технологий, не очень подходящих для базы знаний. В ходе одного из проектов, в котором я участвовал, мы брали за основу SharePoint и уже поверх встроенного туда вики-движка «докручивали» системы распознавания и поиска. Изначально SharePoint не особенно предназначен для создания баз знаний, но корпоративная инфраструктура заказчиков была сильно завязана на эту технологию. В итоге решение получилось действительно сложным, хоть и эффективным.

К сожалению, в моей практике почти не встречалось случаев, когда удалось бы соблюсти баланс: базы знаний всегда получаются либо слишком простыми и вскоре становятся бесполезными, либо они становятся очень сложными — и, хоть и несут пользу, создавать и поддерживать их нелегко.

И как минимизировать возможные проблемы?


Пожалуй, главный тезис: база знаний — это продукт, который должны поддерживать реальные люди. Надеяться на то, что удастся создать базу, которая потом будет жить сама, — неверный подход.

К примеру, ресурс MSDN от Microsoft — это, по сути, мощная база знаний, содержащая техническую документацию. Ее главная проблема: из-за огромных объемов хранимого контента найти что-то конкретное там почти невозможно. Этот факт стал одним из плюсов ресурса Stack Overflow: в свое время там были очень распространены треды, в которых люди делились ссылками на конкретные статьи с MSDN, — по сути, это был единственный способ найти там то, что нужно.

У Microsoft были и другие попытки создать базу знаний. Еще один проект получил название TechNet Wiki, и его идея состояла в том, чтобы привлечь к ведению базы сообщество пользователей. Предполагалось, что люди сами будут писать статьи, расставлять в них теги и так далее. Этот подход оказался куда более эффективным, чем полностью автоматизированная база MSDN.

Можешь рассказать о каком-то интересном проекте по созданию базы знаний, в котором удалось поучаствовать?


В одном из наших проектов мы разрабатывали базу знаний для крупного заказчика, которому нужно было оптимизировать работу службы поддержки. Вики-движок самой базы был интегрирован с корпоративной АТС. Для ускорения обслуживания клиентов в этой системе была реализована связка механизма распознавания речи и поиска информации в базе по ключевым словам. Если в ходе разговора оператора и пользователя произносились определенные «ключевики», база должна была автоматически выдавать релевантные статьи.

Звучит серьезно, но в современных базах знаний применяются и более продвинутые подходы — например, Ontology Search, который позволяет создавать целые иерархии вложенных, зависящих друг от друга тегов и соответствующим образом получать более точную выдачу при поиске по статьям.

В заключение можешь сформулировать три главных совета по разработке качественных баз знаний?


Да, конечно, вот мои советы:
  1. За базу знаний должен кто-то отвечать, иначе она быстро умрет.
  2. Для небольших проектов можно обойтись коробочными решениями, но нужно понимать, что на больших объемах это не будет работать.
  3. Самое главное в базе знаний — возможность поиска. В самом начале, может быть, она и не понадобится, но с течением времени работоспособность системы будет определяться именно качеством поиска.

Полезные хабрастатьи по теме:


Let's block ads! (Why?)

[Перевод] Разработка игр под NES на C. Главы 1-3. От введения до Hello World

Впервые я задумался о том, как разрабатывают игры под приставки где-то через 20 минут после того, как в самый первый раз увидел Turbo Pascal. На глаза иногда попадался Subor с клавиатурой, и появилась мысль: "Наверное можно набрать какую-то программу, а потом в нее поиграть". Но интерес быстро затух, потому что абсолютно никакой информации по этой теме тогда не было доступно. Следующий раз эта же идея всплыла, когда увидел вполне играбельные эмуляторы старых консолей. Тогда стало ясно, что вбивать листинг в саму консоль и необязательно. Где-то очень потом появился Хабр с благожелательной аудиторией для таких вещей. В какой-то момент даже начал собирать разрозненную инфу чтобы написать мануал самому, и вот сегодня наткнулся на готовый учебник, который явно надо перевести.

Разработка под старые консоли документирована вдоль и поперек, но именно по NES 99% информации относятся к разработке на Ассемблере. Меня почему-то зарубило, что надо освоить именно работу с С.
image


Оглавление
  1. С чего начать
  2. Как работает cc65
  3. Первая программа
  4. Что такое V-blank
  5. Немного цвета
  6. Спрайты
  7. Ввод
  8. Коллизии спрайтов
  9. Работа с фоном
  10. Коллизии фона
  11. Прокрутка
  12. Простой платформер
  13. Дебаг и Sprite 0
  14. Простейшие звуки
  15. Добавляем музыку
  16. Добавляем звуковые эффекты
  17. Планирование игры
  18. Кодинг 1
  19. Кодинг 2
  20. Кодинг 3
  21. Благодарности
  22. Что дальше?
  23. Звуки DMC
  24. MMC3, переключение банков, прерывания
  25. Импорт MIDI в Famitracker
  26. Ассемблер 1
  27. Ассемблер 2
  28. Ассемблер 3
  29. Ассемблер 4
  30. Ассемблер 5
  31. Обновления

Всем привет.
image

Меня зовут Дуг. Я пишу игры для NES вот уже год, и решил начать этот блог. Я намерен написать туториал по разработке игр под NES, чтобы вдохновить других людей делать собственные игры.

Особенностью блога будет использование чистого С, так что другие программисты смогут начать писать быстро и не особо вникая в ассемблер для процессора 6502. Насколько мне известно, других туториалов такого формата по компилятору cc65 пока нет, кроме нескольких примеров игр на сайте Shiru.

Также помните, что я не профессионал ни в разработке, ни в ведении блога. Если возникнут вопросы по NES, скорее всего ответы найдутся в Вики:
http://wiki.nesdev.com/

Я постараюсь максимально упростить обучение, и использовать самые простые примеры. Также рекомендую начать с простейшей идеи игры. Читателю явно захочется сделать новую Зелду, но это не получится. Простейшая игра потребует 2-3 месяца на разработку, Зелда — 2-3 года. Такой проект скорее всего будет заброшен. Ориентируйтесь на Пакман, хотя бы первое время.


Память консоли

Поговорим о структуре памяти. У NES два независимых адресных пространства — память процессора с диапазоном $0-$FFFF и память PPU — видеочипа.

Начнем с памяти процессора.


  • Первые $800 это RAM.
  • Диапазон $6000-$7FFF некоторые игры используют для работы с SRAM (сохранение на картрирдж с батарейкой), или как дополнительный Work RAM.
  • На пространство $8000-$FFFF отображается ROM. Некоторые мапперы (дополнительный процессор в картридже) могут использовать более 32k ROM, но они все равно обычно работают через $8000-$FFFF.
  • Адрес $FFFC-$FFFD это вектор reset, который указывает на начало программы.

Здесь более подробная информация:
http://wiki.nesdev.com/w/index.php/CPU_memory_map

У PPU свое, независимое адресное пространство. Оно имеет размер $3FFF, но местами зеркалируется. Доступ к нему идет через регистры в памяти процессора. Видеопамяти хватает на 4 экранных буфера, но в подавляющем большинстве игр используется только 2 — для реализации прокрутки.


  • $0-$1FFF = здесь хранятся спрайты
  • $2000-$23FF = Таблица имен 0
  • $2400-$27FF = Таблица имен 1
  • $2800-$2BFF = Таблица имен 2
  • $2C00-$2FFF = Таблица имен 3
    При этом таблицы 2 и 3 это зеркало таблиц 0 и 1
  • $3F00-$3F1F = палитра

Таблица имен, nametable, связывает тайлы фона и их позицию на экране.

Зеркалирование позволяет управлять горизонтальной или вертикальной прокруткой, но всему свое время.
image

Еще в PPU есть отдельная область памяти OAM, Object Attribute Memory, размером 256 байт. Доступ к ней реализован через регистры в адресном пространстве процессора, и она позволяет управлять отображением спрайтов.

Вот подробная информация по памяти PPU:
http://wiki.nesdev.com/w/index.php/PPU_memory_map

Еще один момент. Есть два типа картриджей. В некоторых два ROM чипа — PRG-ROM с исполняемым кодом и CHR-ROM с графикой. В таком случае графика автоматически отображается в адреса $0-1FFF PPU. Это позволяет очень просто сделать отрисовку — просто записать номер тайла в таблицу. Мы будем использовать этот формат.

Другой тип картриджа использует CHR-RAM вместо CHR-ROM. Это позволяет подгрузить часть графики в эту дополнительную оперативную память. Это сложная техника, и в этом туториале не рассматривается.

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


  • Компилятор
  • Редактор тайлов
  • Графический редактор
  • Notepad++
  • Хороший эмулятор
  • Упаковщик тайлов

В этом туториале рассматривается только cc65. Это один из лучших компиляторов для 6502, процессора NES.
http://cc65.github.io/cc65/

Я использую версию 2.15 (для проверки введите ‘cc65 --version’ в консоли). Файлы из разных версий несовместимы, поэтому при необходимости используйте nes.lib из комплекта вашего компилятора.

Во-вторых, надо создать графику. Я использую YY-CHR

Для предобработки графики нужен любой графический редактор: Photoshop или GIMP, по вкусу.

Код удобно писать в Notepad++. У него есть подсветка сишного синтаксиса и нумерация строк — это облегчает отладку.
image

А теперь эмулятор. Я использую FCEUX 90% времени, потому что в нем есть крутой дебаггер и инструменты для работы с памятью, просмотрщики спрайтов и все такое. Но он не самый точный в эмуляции. Игры надо будет тестировать где-то еще. Судя по отзывам, самые точные эмуляторы это Nintendulator, Nestopia, и puNES. Еще желательно подгрузить более точную палитру — лежит здесь.
Есть две версии FCEUX — SDL и Win32. Первая работает почти везде, вторая только в Windows. Так вот, отладчик есть только во второй. Так что в случае альтернативной ОС придется воспользоваться виртуалкой или Wine.

И наконец расстановщик тайлов. Мы можем сделать игру без него, но он точно поможет. Я рекомендую NES Screen Tool. Он отлично показывает ограничения консоли по цветам и отлично подходит для одноэкранных игр. Для игр с прокруткой лучше подойдет Tiled map editor.

Как же всем этим пользоваться?
image
Надо сжать изображение до адекватного размера, например 128 пикселей в ширину. Потом преобразовать в 4 цвета и подправить при необходимости огрехи. Теперь можно копипастить в YY-CHR.

В YY-CHR надо проверить, чтобы цвет был двухбитный.
image

Палитра сейчас не имеет значения, потому что она все равно задается в другом месте.


Как работает сс65

Все компиляторы для NES работают через консоль, без графического интерфейса. То есть пишем программу в Блокноте, а потом вызываем компилятор с нужными параметрами.

Дл упрощения работы будем использовать .bat-скрипты и Makefile. Это позволит автоматизировать процесс и собирать образ картриджа в одно касание.

Процесс примерно такой. cc65 компилирует файл с кодом на С в ассемблерный код. ca65 собирает объектный файл. ld65 линкует его в образ картриджа .nes, который можно запустить в эмуляторе. Настройки хранятся в .cfg файле.

В приставке используется 8-битный процессор MOS 6502. Он не умеет просто обращаться к переменным больше 8 бит. Адресация 16-битная, из математики есть только сложение, вычитание и битовые сдвиги. Так что код придется писать с учетом этих факторов.


  • Большая часть переменных должна быть типа unsigned char — 8 бит, значения 0-255
  • Лучше не передавать значения в функции, или делать это через директиву fastcall, которая передает аргументы через 3 регистра — A,X,Y
  • Массивы не должны быть длинее 256 байт
  • printf отсутствует
  • ++g заметно быстрее, чем g++
  • cc65 не может ни передавать структуры по значению, ни возвращать их из функции
  • Глобальные переменные намного быстрее локальных, даже структуры

Испольуйте опцию -O для оптимизации. Есть еще опции i,r,s, которые иногда комбинируют в -Oirs, но они, например, могут удалить чтение из регистра процессора, значение которого не используется. А это фатально.

Здесь еще немного рекомендаций по использованию компилятора:
http://www.cc65.org/doc/coding.html

Оптимизация жизненно необходима, потому что у 8-битного процессора очень мало ресурсов, и в некоторых случаях надо следить за временем выполнения кода. А обычный код на С этим требованиям не соответствует.

Поддерживается импорт переменных из других файлов. cc65 умеет импортировать переменные и массивы из ассемблерных модулей командой

extern unsigned char foo;

а если это символ из нулевой страницы памяти, то добавьте директиву

#pragma zpsym (“foo”);

В дальнейшем курсе эти конструкции будут использоваться редко. Единственное исключение — импорт большого бинарного файла. В этом случае оптимально будет завернуть его в ассемблерный файл:

.export _foo
_foo:
.incbin "foo.bin"

а потом импортировать в С как

extern unsigned char foo[];

Знак _ здесь критичен, потому что при компиляции в ассемблерный код cc65 добавляет _ перед каждым именем переменной. Нам надо этому соответствовать.

Можно вызывать функции, написанные на ассемблере, через __fastcall__. В этом случае аргументы передадутся в функцию через регистры, а не стек — экономит время. В некоторых случаях без ассемблерного кода не обойтись, например при инициализации приставки. В любом случае, чем меньше аргументов передается в функцию, тем лучше. Сравним две функции, причем переменные test и A глобальные:

void Test (char A) {
 test = A;
}
// функция с одним аргументом компилируется в 19 команд ассемблера

_Test
jsr pusha
 ldy #$00
 lda (sp),y
 sta _test  ; test = A;

 jmp incsp1

pusha: ldy sp 
 beq @L1 
 dec sp
 ldy #0 
 sta (sp),y 
 rts 

@L1: dec sp+1 
 dec sp 
 sta (sp),y 
 rts 

incsp1:

 inc sp
 bne @L1
 inc sp+1
@L1: rts

void Test (void) {
 test = A;
}
// аргумент не передается, компилируется в 3 команды

_Test
 lda _A
 sta _test
 rts

Еще можно вставлять ассемблерный код прямо в сишный. Я так почти никогда не делаю, но наверное иногда это необходимо. Выглядит примерно так:


 asm ("Z: bit $2002") ;
 asm ("bpl Z") ;

Кроме того, я заменил громоздкий код инициализации crt0.s на компактный reset.s, и подправил конфигурацию для всего этого. Эти файлы иногда будут меняться. nes.lib используется стандартный, из состава компилятора. Проект собирается с опцией –add-source, которая не удаляет промежуточные ассемблерные файлы — можно порассматривать сгенерированный код.

Удобней определить переменные в сишном коде, а потом импортировать в ассемблерный через

.import _Foo

Но это вопрос вкуса, на мой взгляд, такой код наглядней.


Hello World

Эта программа будет просто печатать текст на экране. Надо помнить, что приставка вообще не знает про кодировку ASCII и работу с текстом в любом виде. Но зато есть возможность вывести картинки размером 8х8 поверх фона.

Так что делаем массив спрайтов-букв, чтобы адреса букв в нем соответсвовали их ASCII-кодам. Потом их можно будет дернуть из кода на С.
image

Код инициализации приставки пока берем как есть, после его выполнения происходит переход на main().

Нам надо сделать такие операции:


  • Выключить экран
  • Настроить палитру
  • Вывести заветные слова
  • Отключить прокрутку
  • Включить экран
  • Повторить

Выключение экрана нужно, потому что работа с видеопамятью вызывает мусор на экране. Надо или выключить экран, или ждать кадровый гасящий импульс (V-Blank). Детально этот вопрос мы рассмотрим в следующий раз.

Код инициализации заполняет память нулями, так что весь экран будет залит нулевым тайлом — в нашем случае, он пустой. А вся палитра заполнена серым цветом.

Для вывода на экран надо записать координаты начала заливки начиная со старшего байта по адресу $2006, а потом записывать номера тайлов в $2007. PPU будет выводить тайлы с соответствующими номерами один за другим, с переходом на новую строку. Можно перенастроить PPU на шаг вывода, равный 32 — тайлы будут выводиться один под другим. Нам же надо выставить шаг 1, через регистр $2000. Пересчитать координаты экрана в адрес можно через NES screen tool.

Нам также надо заполнить первые 4 цвета палитры — они отвечают за фон. Они записываются по адресу $3F00.

Запись в регистры PPU ломает положение прокрутки, так что ее тоже надо сбросить. Иначе картинка может уехать за экран. Мы делаем это через регистры $2006 и $2005.


lesson1.c

#define PPU_CTRL  *((unsigned char*)0x2000)
#define PPU_MASK  *((unsigned char*)0x2001)
#define PPU_STATUS  *((unsigned char*)0x2002)
#define SCROLL   *((unsigned char*)0x2005)
#define PPU_ADDRESS  *((unsigned char*)0x2006)
#define PPU_DATA  *((unsigned char*)0x2007)

unsigned char index;
const unsigned char TEXT[]={
"Hello World!"};

const unsigned char PALETTE[]={
0x1f, 0x00, 0x10, 0x20
}; //black, gray, lt gray, white

void main (void) {
 // turn off the screen
 PPU_CTRL = 0;
 PPU_MASK = 0;

 // load the palette
 PPU_ADDRESS = 0x3f; // set an address in the PPU of 0x3f00
 PPU_ADDRESS = 0x00;
 for(index = 0; index < sizeof(PALETTE); ++index){
  PPU_DATA = PALETTE[index];
  }

 // load the text
 PPU_ADDRESS = 0x21; // set an address in the PPU of 0x21ca
 PPU_ADDRESS = 0xca;  // about the middle of the screen
 for( index = 0; index < sizeof(TEXT); ++index ){
  PPU_DATA = TEXT[index];
  }

 // reset the scroll position 
 PPU_ADDRESS = 0;
 PPU_ADDRESS = 0;
 SCROLL = 0;
 SCROLL = 0;

 // turn on screen
 PPU_CTRL = 0x90; // NMI on
 PPU_MASK = 0x1e; // screen on

 // infinite loop
 while (1); 
}

image

Ссылка на код:
http://dl.dropboxusercontent.com/s/1oxcgi4t1m4ifzj/lesson1.zip
https://github.com/BubaVV/nesdoug
На Гитхабе чуть исправил Makefile, чтобы корректно работал под Windows.

Строка
ONCE: load = PRG, type = ro, optional = yes;
внутри секции segments{} в файлах .cfg нужна для совместимости со свежей версией cc65.

Включение экрана через “PPUMASK = 0x1e” описано в вики:
http://wiki.nesdev.com/w/index.php/PPU_registers

Все файлы здесь размером 0х4000. Это самый маленький возможный размер PRG ROM. 90% игр сюда не влезут, и будут отображаться на адреса $8000-$FFFF. У нас же игра загружается в адреса $C000-$FFFF и зеркалируется в $8000-$BFFF. Для разработки большей игры надо будет перенастроить адрес начала ROM на $8000, и выставить размер тоже $8000. А еще включить второй банк PRG ROM в секции header.

Let's block ads! (Why?)

Банки Клайдсдейла и Йоркшира внедрят ServiceNow для управления ИТ-инфраструктурой

Холдинговая компания Clydesdale and Yorkshire Bank Group (CYBG) — куда входят банки Клайдсдейла и Йоркшира — решила использовать платформу ServiceNow для управления и организации ИТ-услуг (ITSM). Далее расскажем, чем обоснован такой выбор и какие планы строят компании на будущее.


/ Flickr / Giuseppe Milo / CC

Yorkshire Bank (его первое название West Riding Penny Savings Bank) появился в 1859 году в городе Галифаксе благодаря полковнику Эдуарду Эйкройду (Edward Akroyd). В свое время этот банк стал первым, что основал так называемые школьные банки с целью популяризации идеи накопления денег с ранних лет. Что касается Clydesdale Bank, то он основан в 1838 году в Глазго и считается самым маленьким из трех банков Шотландии, которые выпускают банкноты.

Оба эти банка принадлежали Национальному банку Австралии, однако с февраля 2016 года они работают независимо, но в единой команде – в составе CYBG. Представители холдинга отмечают, что они регулярно совершенствуют свои бизнес-процессы и развивают технологии, чтобы предоставить клиентам более качественный сервис.

Например, внедрение цифровой банковской платформы «B» для управления финансами и счетами увеличило частоту использования банковских сервисов на 27%. Компания намерена и дальше развивать ИТ-инфраструктуру. Для этого было решено внедрить систему автоматизации управления и обработки информации. За этим представители CYBG обратились к ServiceNow.

Почему выбрали ServiceNow


До ServiceNow компания использовала платформу Remedy от BMC, однако, по словам Скотта МакГарви (Scott McGarvey), главы ITSM-направления в CYBG, решение не отвечало потребностям бизнеса: ему не хватало гибкости, пользовательский интерфейс был неудобным, а интегрировать системные компоненты было нельзя.

В CYBG искали облачную платформу с широким функционалом «из коробки». И остановили выбор на ServiceNow. Как отмечает МакГарви, новое ITSM-решение является модульным и позволит оценить скорость поставки решений на рынок за счет измерения KPI для времени и финансовых затрат. При этом система упростит работу с уже готовыми решениями, автоматизировав рутинные задачи техподдержки (например, сброс паролей пользователей) и переведя часть услуг на модель self-service.

Уже завершено трехмесячное тестирование новой платформы, а первый этап реализации решения на корпоративном уровне запланирован на 2018 год. Для внедрения ServiceNow банки используют итеративный подход и будут добавлять новые функции по мере расширения проекта. Такая стратегия, по словам МакГарви, поможет ИТ-сотрудникам и пользователям постепенно привыкнуть к нововведениям, а чередование «затратных» и «менее дорогих» этапов внедрения решения позволит сбалансировать бюджет.


/ Flickr / Pavlo Petrenko / CC

В последние годы ServiceNow расширила число сфер применения своей платформы. Партнерские отношения с компаниями Cisco, Google, Okta и др. позволили использовать сервис для внедрения процессов по управлению HR и обеспечением безопасности. Поэтому, по словам МакГарви, после запуска ServiceNow в CYBG планируют постепенно начать использовать её возможности в других подразделениях банков, помимо ИТ-службы.

Кто еще работает с SeriveNow


CYBG не единственная финансовая организация, которая использует возможности ServiceNow. Королевский банк Шотландии также внедрил у себя эту платформу. Раньше в организации любой запрос на изменение вносился с помощью 5 инструментов, и уходило на это 7 часов рабочего времени (подробнее об управлении изменениями можно почитать в нашем блоге).

Внедрение ServiceNow позволило сократить это время на 76%. В итоге Королевский банк Шотландии сэкономил 46 тысяч часов рабочего времени в месяц.

Еще один кейс — Банк Ирландии (Bank of Ireland). Руководство банка решилось на внедрение гибкой платформы для автоматизированного управления услугами и выбрало ServiceNow. В результате время обработки изменений сократилось с 20 до 5 минут, а работа ИТ-подразделения банка стала более слаженной.

Несколько практических кейсов и материалов по теме из блога «ИТ Гильдии»:


Пара свежих постов о ServiceNow из нашего блога на Хабре:

Let's block ads! (Why?)

Алгоритм выбора location в Nginx

Алгоритм выбора location обязателен к знанию при настройке nginx. Тем не менее, на официальном сайте nginx (на 2018 год) не сказано ни слова про алгоритм выбора в случаях, когда какие-то location'ы вложены друг в друга, а в статьях в интернете приводятся в корне неверные алгоритмы. Поэтому решил написать свою небольшую заметку.

Если Вы не знали о том, что кроме спуска по дереву вложенных location nginx также делает и подъём по дереву, статья обязательна к прочтению. В статье также будет дан пример уязвимого конфига.


Если Вы новичок в nginx, то следует рассмотреть вначале частный случай без использования вложненных location, т. к. алгоритм для частного случая значительно проще:
  1. Вначале будет искаться равенство. Оно имеет высший приоритет.
  2. Потом будет искаться максимальный префиксный location, после чего будет проверено, есть ли на нём модификатор приоритета (^~), и если он есть, то будет возвращён этот location.
  3. Потом будут проверяться регулярные выражения сверху вниз. При первом же совпадении будет возвращён совпавший location.
  4. Потом вернётся тот префиксный location, который мы нашли до этого.

Обратите внимание, что этот алгоритм неприменим при наличии вложенных location.
  1. Стартуем с верхнего уровня.
  2. Если на текущем уровне выполняется равенство (=), поиск прекращается — это и будет результат, т. к. такой location не может иметь никаких других вложенных location.
  3. В противном случае ищем на текущем уровне самый большой префиксный location ((   ) или (^~)).
    • Если такой префиксный location существует, то делаем его текущим уровнем и переходим к п. 2.
    • В противном случае выходим из цикла.
  4. Мы вышли из цикла. На данный момент мы нашли «самый большой» префиксный location, но не думайте, что это самый большой из всех. Пример:
    location /abc {
            location /abcdefghi {
                    …
            }
    }
    
    location /abcdef {
            …
    }
    
    

    В данном примере мы перейдём в /abcdef, т. к. на его уровне он переборол более короткий /abc. Но по факту существуют location и больше него.
  5. Теперь в найденном location мы ищем первый верный regexp. При нахождении поиск полностью прекращается. Обратите внимание: в этом пункте мы по факту ищем regexp на самом нижнем уровне, а не на верхнем, как многие могли бы подумать. Т. е. поиск regexp идёт снизу, а не сверху (но внутри одного уровня идёт сверху, а не снизу).
    • Далее, если ничего не найдено, поднимаемся на один уровень вверх и аналогично ищем первый regexp, но в этот раз уже только при условии, что location, в котором мы были до этого, не имел метки (^~). Повторяем этот пункт до тех пор, пока подниматься будет некуда.
    • При этом нужно иметь ввиду:
      • Даже если какой-то из уровней имеет метку (^~), это не значит, что мы не осуществляем подъём. Подъём осуществляется всегда, но если более нижний уровень имел метку (^~), то на текущем уровне поиск regexp'ов не проводится.
      • Возможности запретить проверку regexp в самом нижнем уровне нет — для этого нужно создать ещё один вложенный уровень. А вот запретить проверку regexp на нулевом уровне можно — для этого location первого уровня (который находится на нулевом уровне) должен иметь метку (^~).
  6. Мы сделали подъём по дереву, но так и не нашли ни одного regexp. Раз regexp не найден, возвращаем «почти самый большой» префиксным location, который был найден ранее. Готово.

Также при этом:

  • В версиях 0.7.1–0.8.41 префиксный location (   ) при точном совпадении действует как =
location ~ \.php$ {
        deny all; # Здесь должно быть проксирование на php-fpm
}

location /posts/ {
        location ~ (.*)_2x(\.[a-z]+)$ {
                try_files $uri $1$2 =404;
        }
}


В данном конфиге мы настроили игнорирование "_2x", если файл не найден. Например, nginx попробует найти файл /posts/img/a_2x.png как по указанному пути, так и по пути /posts/img/a.png. Но в реальности, если мы запросим /posts/authData_2x.php, то мы получим исходный текст скрипта authData.php в голом виде. Чтобы избежать таких ошибок, нужно знать, как обрабатывается location в nginx.

Также дополнительной защитой может являться хранение скриптов в отдельной директории, недоступной из под обычных location. В этом случае, если наш location на php по каким-то причинам не сработает, пользователь получит ошибку 404, а не исходный текст скрипта.


  1. Если try_files не содержит кода ошибки последним параметром, то будет сделано перенаправление в другой location, т. к. последний параметр всегда делает перенаправление. Обратите внимание: код ошибки в try_files должен писаться через равно (=).
  2. index и error_page при срабатывании всегда делают перенаправление в другой location. Также перенаправление делает rewrite, если добавить в него флаг last.


  1. При выборе location не учитывается строка запроса, которая начинается со знака "?".


Алгоритм в статье составлен мной на основе моих личных наблюдений, и не факт, что он является правильным. К сожалению, официальной документации нет, а исходники я не читал. Если кто-то найдёт ошибки в алгоритме, просьба написать личным сообщением или в комментариях.

Let's block ads! (Why?)

[Из песочницы] GObject: основы

[Перевод] Дорога на войну: ИИ игр серии Total War

image

С самого основания моей серии видео «AI and Games» на YouTube я исследовал различные реализации ИИ, решающие определённые задачи в дизайне видеоигр, требующие немедленного, реактивного поведения в сочетании с долговременным и стратегическим принятием решений. Популярным жанром, в котором ИИ может создавать быстрый, реактивный и захватывающий геймплей, являются шутеры от первого лица (FPS). Но если мы хотим рассмотреть потребность в эффективных системах, учитывающих баланс принятия краткосрочных и долгосрочных решений, то идеальным предметом для изучения являются игры в жанре стратегии реального времени (Real-Time Strategy, RTS).

И RTS, и FPS продолжают вносить инновации в современные видеоигры, стремясь соответствовать их дизайну. Но играм в жанре RTS нужно оценивать гораздо более обширное и длительное влияние быстрых и реактивных решений. Кроме того, им требуется управлять ресурсами на разных уровнях абстракции и сложности в многочасовых игровых сессиях. При обсуждении сложностей и инноваций, с которыми приходится столкнуться ИИ стратегических игр, ни в коем случае нельзя не упомянуть одну франшизу — Total War компании Creative Assembly.
С момента выпуска первой части Total War в 2000 году франшиза постоянно росла и набирала мощь. Каждая игра становилась всё более амбициозной, а проблемы, с которыми приходилось встречаться системе ИИ, продолжали увеличиваться в масштабах и сложности. В результате компания собрала команду разработки, которая как никто иной в истории видеоигр развивает новые тенденции в создании искусственного интеллекта. Однако путь к последним частям серии, таким как Total War: Attila и Warhammer, не был гладким. Поэтому в этой статье я рассмотрю не только фундаментальные принципы ИИ серии Total War, но и то, как каждая новая часть серии усовершенствовала, исправляла или полностью переписывала части этих систем. Кроме того, я расскажу о реакции поклонников серии на эти изменения, о сообществе моддеров, пышным цветом разросшемся в рамках Total War, а также о том, как Creative Assembly с самой первой игры серии и по сей день применяет передовые научные открытия для создания инноваций.


Но сначала давайте рассмотрим сам жанр Real-Time Strategy, чтобы понять, какое место в нём занимает Total War. Основной задачей в стратегиях реального времени обычно является контроль над территориями и ресурсами: две или несколько противоборствующих сторон появляются на одной карте, чтобы захватить доминирование, охраняя ресурсы или локации на ограниченной области. Часто для достижения этих целей каждая из сторон строит здания и юниты, которые или улучшают сбор ресурсов в заданной области, или защищают уже имеющиеся здания и земли, или исследуют и захватывают силой другие области карты. В процессе решения последней задачи требуется уничтожение юнитов противника. Для этого процесса игрок обычно должен исследовать ранее скрытые туманом войны области, чтобы обнаружить местонахождение врагов и ресурсов. Кроме того, со временем каждая из сторон должна модернизировать здания и юниты, усовершенствуя их характеристики и объём собираемых ресурсов.

Хотя выпущенная в 1992 году компанией Westwood Dune II: The Building of a Dynasty и не стала первой RTS-игрой, она стала архетипом для будущих игр этого жанра, таких как серия Command and Conquer той же Westwood, Age of Empires, Total Annihilation, Dungeon Keeper, Homeworld, а также франшиз Warcraft и Starcraft компании Blizzard. Жанр продолжал эволюционировать и создавать ответвления с конца 1990-х. Возникали его подобия, например, жанр тактики реального времени в лице игр Dawn of War и Star Wars: Empire at War, в которых упор делался на тактику, а не управление ресурсами. Тем временем, такие игры, как UFO: Enemy Unknown (1994 год) исследовали баланс между принятием решений в реальном времени и в пошаговом режиме. Её подход оказался популярным, что доказывает успешный перезапуск франшизы под названием XCOM: Enemy Unknown в 2012 году. Одним из самых далеко отклонившихся от RTS игр стал жанр Multiplayer Online Battle Arena, или MOBA, популяризированный модом Defence of the Ancients для Warcraft III и упрочившим успех благодаря Dota 2 и League of Legends. В них игроки берут на себя роль наземных юнитов-героев, определяющих масштабные стратегии и реализующие их в течение матча.

ИИ использовался в RTS ещё с самого зарождения жанра: игроки, управлявшие движением юнитов, просто указывали им, куда нужно идти, но не прокладывали путь самостоятельно. Поэтому игрок должны были взаимодействовать с такими системами, чтобы получить желаемые результаты. Кроме того, ИИ критически важен для однопользовательских режимов игры, в которых игрок сражается против вражеских командиров, управляющих собственными юнитами и стратегиями производства. Роль ИИ в таких играх чрезвычайно сложна и считается одной из самых огромных проблем, с которыми приходилось сталкиваться в научных исследованиях. Это привело к созданию множества научных инструментов оценки и проектов, в том числе соревнований Starcraft AI Competition, проводимых на конференции AAAI, посвящённой искусственному интеллекту и интерактивным цифровым развлечениям. Эта тема интересна сама по себе, но о ней мы поговорим в других выпусках AI and Games.
Так что же делает Total War такой особой и сложной для систем ИИ? Серия стратегических игр Creative Assembly интересна тем, что сочетает в себе несколько режимов стратегического геймплея: игроки в пошаговом режиме управляют ресурсами и политикой, а также участвуют в крупномасштабных боях реального времени. Кроме того, бой ведётся на микро- и макроуровнях, требуя от ИИ не только управления отдельными юнитами, но и более абстрактного контроля за видами войск, управления боевыми построениями и размещением отрядов на поле боя в реальном времени.

Первая часть франшизы, Shogun: Total War, балансирует между симуляцией боя, стремящейся к реализму и убедительности, и политической стратегией, придающей каждому конфликту контекст и понимание того, что поставлено на карту. Действие первой Total War происходит в 1530 году, в период Сэнгоку феодальной Японии: это время популяризировано в современной культуре работами японского кинорежиссёра Акиры Куросавы. Такие фильмы, как «Тень воина», «Семь самураев» и «Ран», оказали влияние на дизайн и разработку проекта. Отрывки из «Ран» даже использовались в видеороликах игры. Игрок и вражеский ИИ берут на себя роль даймё — феодальных правителей, управляющих провинциями Японии. Им приходится вести и дипломатические игры, и военные действия. Когда противники втягиваются в конфликт, игроки берут управление «тайсё» (генералами) и перемещают по полю боя сотни, если не тысячи воинов. Период Сэнгоку стал идеальной исторической эпохой для игры, потому что политика и даже экономика того времени были основаны на логистике армий, защищавших даймё и его амбиции.


Total War отличается от многих традиционных стратегий реального времени тем, что она отказалась в боевой части от механики тумана войны, а также от управления ресурсами. Из-за структуры самой игры управляющий противниками ИИ был разбит на две отдельные системы ИИ:
  • ИИ кампаний и дипломатии управляет стратегией игры в пошаговом режиме и несёт ответственность за перемещение армий по карте, занимается дипломатией (отправляет убийц или переговорщиков для устранения противников или заключения альянсов), а также заботится о сельском хозяйстве и инфраструктуре собственных провинций.
  • Боевой ИИ командует боевыми юнитами, стратегией и паттернами атак. Как и живые игроки, он несёт ответственность только за управление стратегией отдельных групп юнитов, потому что сами отдельные юниты тоже управляются собственным ИИ.

Поэтому чтобы разобраться с ИИ Total War, я буду исследовать системы ИИ с самого низкого до самого высокого уровней: от систем управления отдельными отрядами до стратегических систем, задача которых — завоевать всю феодальную Японию.

Поведение ИИ юнитов


Бой в Total ведётся юнитами: группами определённых видов войск, которым можно задавать боевые построения, от отрядов ближнего боя до лучников и кавалерии. Такие боевые юниты не только должны сохранять построение, но и перемещаться, а также вести бой как единый отряд. Движение часто оказывается сложной задачей, особенно когда юниту приказывают пройти через разные типы местности, такие как горы и леса.

Стандартная схема нейросети

Нейросети работают как взвешенная функция суммирования входящих данных. Изменяя веса каждого отдельного вычислительного узла, или «нейрона», мы можем менять результат данных, передаваемых слоям справа. [Источник изображения]

Чтобы добиться этого, Total War использует для управления отдельными юнитами искусственные нейронные сети. Нейронные сети — это быстрый и эффективный инструмент достижения быстрых и реактивных откликов на заранее заданную задачу. Нейросеть обрабатывает данные в «нейронах»: простых обрабатывающих узлах, получающих входящие данные через имеющие вес «синапсы». Меняя их веса, мы изменяем результаты данных, передаваемых по нейросети. Обычно эти веса подбираются с помощью методов машинного обучения, но их можно настраивать и вручную, если сеть достаточно мала. Они эффективны потому, что после обучения время обработки для принятия решения невероятно мало. Кроме того, хорошо обученная нейронная сеть способна обобщать свой процесс принятия решений, то есть она может распознавать схожесть во множестве отдельных условий и в каждом случае давать схожие ответы.

Несмотря на это, нейросети обычно не очень хороши, когда от них требуют одновременного выполнения нескольких задач, особенно когда эти задачи противоречат друг другу. На этот случай отряды имеют разные нейросети, которые они могут включать в зависимости от решаемой задачи: выбора построения, избегания вражеского огня или подбора позиции для атаки вблизи или издалека. Каждая из этих сетей создана заранее, она не настраивается и не оптимизируется в процессе игры, то есть не учится, чтобы стать лучше по ходу игры. Такие системы ИИ юнитов работают вне зависимости от того, кто управляет армией — человек или ИИ, потому что игроку в любом случае нужны такие системы нейросетей для управления поведением отдельных отрядов.

Поведение боевого ИИ


Уровень боя — это система ИИ, управляющая противниками игрока. Этот ИИ принимает решения о выборе построения групп и действиях юнитов. Он взаимно перемещает части армии, чтобы достичь определённой тактического или микроменеджментного движения юнитов в зависимости от текущего рельефа или погоды.

Чтобы достичь этого, в Shogun применяется логическая система, позволяющая принимать определённые решения на основе текущего состояния мира. Такие решения взяты из «Искусства войны»: сборника стратегий китайского военного стратега Сунь Цзы, датированного примерно 500 годом до нашей эры. Использование логической системы хорошо подходит для адаптации текстов, во многих случаях используются заданные условия для получения определённого отклика от системы. В этих правилах учитывается тип, состояние и размер собственных юнитов и противников, текущий рельеф, погода и моральное состояние войск. Вот некоторые из примеров, взятые из «Искусства войны»:
  • Если окружаешь войско противника, оставь открытой одну сторону.
  • На открытой местности не пытайся перекрыть путь противника.
  • Если у тебя сил в десять раз больше, чем у противника, окружи его со всех сторон.
  • Если у тебя сил в пять раз больше, нападай на него.
  • Если у тебя сил вдвое больше, раздели его на части.

Разработчики говорят, что в системе боевого ИИ реализовано 220 правил в трёх различных версиях, у каждой из которых есть слегка отличающиеся наборы поведений. Важнейшей причиной этого было создание как можно более реалистичного боя, непохожего на тактику, управлявшую большинством стратегий реального времени той эры. Вот, что говорит директор по разработке Creative Assembly Майк Симпсон:
«В большинстве игр используются простые скрипты шаблонного поведения, которые срабатывают при контакте и очень простые поведения толп юнитов. Они очень ограничены и кажутся „игрушечными“, а не реальными. Я имею в виду, что используемую для победы над ними тактику нужно изучать в каждой новой игре, а иногда и в каждом сценарии
или уровне, и она не имеет никакого отношения к реальности. Мы стремились создать игру, в которой будет работать настоящая тактика из реального мира. Мы выбрали не самый лёгкий путь, но он принёс большую награду.»

[Battle AI, Майк Симпсон, блог Total War, 10 ноября 2009 года]


Поведение ИИ кампании


Последний слой систем ИИ Shogun — это система кампаний и дипломатии. Она управляет всеми решениями на основной карте кампании: управление движением армий, ведение дипломатии между сторонами и строительство инфраструктуры провинций.

В ней используется сочетание классического принятия решений на конечных автоматах с генетическими алгоритмами для создания индивидуальной тактики каждого даймё. Простая система «состояние-действие» просто указывает, что нужно делать в текущих условиях и обеспечивает мгновенную реакцию ИИ. Она не хранит никакой памяти о существующем поведении и не имеет сложного процесса принятия решений. Она просто реагирует на происходящие вокруг события. Затем используется генетический алгоритм, позволяющий обеспечить некоторую индивидуальность.

Генетические алгоритмы — это вид машинного обучения, при котором мы создаём огромные количества возможных решений определённой задачи, которые учатся решать её путём выращивания лучших. Эти возможные решения не являются программным кодом, это просто кодировка того, как обычно ведёт себя ИИ в виде строки чисел, которая затем передаётся в сам ИИ, чтобы задавать его поведение. Они позволяют нам создавать наборы сложных решений задачи: все они не действуют аналогично друг другу и имеют собственные немного отличающиеся характеристики. В результате каждый даймё получил собственное уникальное поведение в бою. С точки зрения игроков это придаёт им некую индивидуальность: некоторые даймё больше стремятся к дипломатии, другие более агрессивны и расширяют свою территорию, атакуя других ИИ-правителей и игрока.


Все эти три системы являются фундаментальными принципами ИИ Total War: они управляют отдельными юнитами, обеспечивая убедительное поведение, группируют отряды войск логичным, реактивным и сложным для игрокам образом, а также создают стратегических противников с собственной индивидуальностью. Несмотря на то, что первая игра создана ещё в 2000 году, основные принципы боя и стратегии, в сочетании с системами, созданными в Shogun, были перенесены франшизой Total War на новые разнообразные поля битв множества последовавших частей игры. Несмотря на все эти закулисные инновации, ИИ всё-таки нельзя назвать непогрешимым, и в каждой новой версии игры он совершенствуется и взрослеет.

Первые изменения были внесены, когда серия перенеслась из феодальной Японии в европейское Средневековье. Выпущенная в 2002 году Medieval: Total War отправляет игрока ещё глубже назад во времени, в конец восьмого века, и проводит его до века четырнадцатого. Игроки управляют различными сторонами в разные исторические периоды: от эры викингов в Британии, когда бОльшая часть страны была раздроблена из-за покинувшей её Римской империи, до времени позднего Средневековья, когда оформились границы стран большей части Европы.

Medieval: Total War во многих отношениях стала улучшенной вариацией первой игры. Она построена на усовершенствованной версии движка Shogun, позволившей проводить ещё более масштабные битвы. Крупных отличий в технологии ИИ между Shogun и Medieval не было, только множество оптимизаций и исправлений. Системы ИИ юнитов научились лучше справляться со скоплениями в узких местах, например, на мостах. Кроме того, набор систем боевого ИИ расширился с трёх до девяти, обеспечивая бОльшую гибкость. Всё это в результате привело к созданию игры, которую Rock Paper Shotgun назвал «почти идеальной». Роб Закни писал:

«Я по-прежнему не уверен, что в серии Total War можно будет создать ещё более сбалансированную игру. ИИ легко управляет войсками на карте, а разные стартовые позиции каждого королевства и империи позволяют сформироваться настоящим сверхдержавам ИИ, которые позже доставят игроку много хлопот.

Medieval — это триумф простоты, и Total War потребовалось ещё десять лет, чтобы снова приблизиться к нему».

[The Best And Worst Total War Games, Роб Закни, 4 января 2016 года]



Shogun и Medieval стали самым началом франшизы и можно без сомнений сказать, что дорога на войну не была гладкой. С каждой последующей версией франшизы сложности росли в масштабах, а объём самих игр значительно увеличивался. Во второй части статьи я расскажу о сообществе моддеров, которое сложилось после выпуска в 2004 году Rome: Total War, о том, как выпуск Medieval II: Total War стимулировал разработчиков к переписыванию и перестройке в Empire: Total War, и о том, как это масштабное изменение влияло на всю франшизу вплоть до выпуска Total War: Shogun 2.

Let's block ads! (Why?)

[Перевод] Шон Пирс: настоящий лидер

Понимание lvalue и rvalue в C и С++

Отслеживаем действия пользователя с помощью CSS

В этом посте вы сможете узнать, почему даже с выключенным JavaScript и без плагинов, вы все равно можете отсылать поведенческие данные на сторонний сервер.

Также здесь мы рассмотрим метод, как получить поведенческую информацию от пользователей используя только HTML и CSS.

Возможно, после прочтения поста, вам покажется что я "изобрел колесо". Так и есть, методы описанные в этом посте не новы, и используют спецификации которые поддерживают практически все браузеры.

Так или иначе, эта информация поможет вам понять один нестандартный метод отслеживания поведения пользователей, который на данный момент нельзя "отключить" (в настройках) или заблокировать (плагинами вроде AdBlock или Ghostery).


Предыстория

Представьте на минуту, что у вас есть:


  • Аудитория с выключенным JavaScript, или установлены плагины вроде Ghostery
  • Желание отслеживать поведение пользователей

Прежде чем пытаться найти решение для этой задачи, давайте рассмотрим какими методами отслеживания мы располагаем на данный момент:


  • JavaScript жучки, такие как Яндекс.Метрика, Google Analytics — действуют на стороне клиента.
  • Анализаторы логов приложения, такие как logstash, awstat — оперируют логами приложения на сервере.
  • Статичные счетчик — как правило, загружают скрытую картинку, или другой ресурс, не требуют выполнения JavaScript кода.

JavaScript жучки не подходят исходя из требований. За исключением таких, которые идут в комплекте с статичным счетчиком. К примеру, жучок для Яндекс.Метрики загружает изображение следующего вида:


В случае, если у клиента не выполняется JavaScript, этот подход позволит получить такую информацию, как:


  • хиты
  • хосты
  • ip-адрес
  • время визита
  • … другие данные

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


Решение — используем CSS

В CSS мы можем загрузить внешний ресурс через инструкцию url(адрес-ресурса). Обычно этот ресурс загружается только тогда, когда он становиться необходим для рендеринга страницы. Почему бы не использовать эту особенность, для того чтобы собрать информацию о поведении пользователя? Мы вполне можем написать специальный CSS, который будет:


  • собирать данные о поведении пользователя
  • определять версии/особенности браузеров с помощью CSS-хаков

Итак, наша задача сводиться к формированию HTML + CSS кода, который вынудит браузер, при взаимодействии с пользователем, сделать get запрос на наш сервер.

К примеру, мы желаем отслеживать клики по ссылкам. Для этого, мы можем использовать псевдокласс :active, а именно такой шаблон (jsfiddle):

/* spycss*/
.spycss:active::after {
    content: url("/backend/click-google");
}

При клике на такую ссылку, мы получим GET на /backend/click-google.

Похожим образом мы можем использовать и другие псевдоклассы:

.spycss1:hover::after {
    content: url("/backend/hover");
}
.spycss2:focus::after {
    content: url("/backend/hover");
}

В обоих случаях мы принимаем GET на нашем сервере.


Ускоряемся вместе с SpyCss

Писать такой CSS вручную и отслеживать каждую ссылку — довольно проблемно и непродуктивно. Именно для этих целей я написал небольшую библиотеку SpyCss. С помощью нее можно генерировать отслеживающий HTML + CSS без особых проблем. К примеру, можно использовать такой код для генерации отслеживаемой ссылки:

hcbogdan.com</a>
echo $s->builder()
    ->tag('a')
    ->content('hcbogdan.com')
    ->attribute('href', 'https://hcbogdan.com')
    ->interactions([
        new SpyCss\Interaction\Active('click_on_hcbogdan_com')
    ])
    ->get();

// Библиотека генерирует CSS с необходимыми инструкциями
echo '';

Теперь мы можем отслеживать клики по ссылкам и наведения мышки на DOM элементы. Давайте посмотрим на HTML5 формы. А именно на валидацию (jsfiddle):

Получается точно таким же образом мы можем использовать псевдокласс :valid для отслеживания заполнения формы:

// Создаем поле 
echo $s->builder()
->tag('input')
->attributes([
    'name' => 'you_name',
    'value' => '',
    'required' => true,
    'placeholder' => 'Напишите текст',
])
->interactions([
    new \SpyCss\Interaction\Valid('you_fill_input'),
])
->get();

echo '';

Когда пользователь заполнит поле — мы получим свой GET запрос.

Мы также можем отследить как долго пользователь держал курсор над DOM элементом (который получил состояние hover) с помощью css-анимаций. Например:

@keyframes spycss {
  0% {background-image: url("/backend/0")}
  100% {background-image: url("/backend/100")}
}
.spycss:hover::after {
  animation: spycss 20s infinite;
}

Аналогичный пример с помощью библиотеки SpyCss (она добавит префиксы -webkit, -moz):

echo $s->builder()
->tag('a')
->content('hcbogdan.com')
->attributes([
    'href' => 'https://hcbogdan.com',
    'target' => '_blank'
])
->interactions([
    new \SpyCss\Interaction\Online('view_on_hcbogdan_com'),
])
->get();

echo '';

Итоги

Даже с выключенным или недоступным JavaScript жучком, у нас есть с помощью CSS:


  • отслеживать поведение пользователей,
  • определять некоторые версии браузера
  • определять примерные размеры окна и PPI
  • определить ориентацию и тип устройства

В этом посте были рассмотрены некоторые, самые необходимые механизмы отслеживания. Полноценный пример, где используется все выше описанные подходы вы сможете найти тут — https://github.com/Bogdaan/spycss-demo

Let's block ads! (Why?)

DDD на практике

пятница, 2 февраля 2018 г.

Программный синтез звука на ранних персональных компьютерах. Часть 2

Это статья о первых программных синтезаторах, которые были когда-то созданы на самых обычных персональных компьютерах. Я даю несколько практических примеров по реализации простых методов звукового синтеза в историческом контексте.

Перейти к первой части

Однобитный синтез


Моим первым персональным компьютером был клон ZX Spectrum 48K. В данной модели отсутствовал «музыкальный сопроцессор» и звуковые возможности ограничивались встроенным динамиком, которым можно было управлять с помощью вывода нулей или единиц в специальный однобитный порт. В руководстве к ZX-Бейсику существовала единственная команда для работы со звуком — BEEP, для которой требовалось задать высоту ноты и длительность. Этого хватало для простых одноголосных мелодий в духе популярных когда-то электронных музыкальных открыток.

Впрочем, как я быстро уловил, качество компьютерного звука, выдаваемого через динамик, сильно зависело от таланта программиста. Особенно это было заметно по играм: во многих из них звучание было примитивным, но существовали и удивительно хорошо звучащие музыкальные вещи. Самые качественные произведения воспроизводились во время статичных заставок. Громкость динамика при этом ощущалась ниже обычного (настолько, что иной раз приходилось ухо прикладывать к клавиатуре, чтобы уловить нюансы композиции). Зачастую, слышны были свист и искажения, как при плохой записи. Но, при всем этом, явственно можно было различить несколько одновременно звучащих голосов, присутствовали смена тембров, работа с амплитудной огибающей, пространственные эффекты, звучание ударных. К ZX Spectrum я еще вернусь после некоторого отступления.


Пример однобитного синтеза для ZX Spectrum, автор Tim Follin

Однобитная музыка является старейшей среди прочей компьютерной музыки. Еще в начале 50-х годов на первых компьютерах в Австралии и Англии проводились эксперименты с воспроизведением мелодий. Разумеется, в те времена создатели компьютеров не думали о каких-то задачах из области музыки. Динамик предназначался исключительно для целей отладки и диагностики работы вычислительной машины. Тем не менее, в свободное от работы время и потратив на это массу сил, хакеры начала 50-х заставили свои компьютеры проигрывать простейшие мелодии. В арсенале тогдашних компьютерных музыкантов было только два уровня сигнала, подаваемого на динамик.

Появление динамика на одном из первых микрокомпьютеров, Apple II, было связано с желанием Стива Возняка портировать на свой компьютер игру Breakout. В оригинальном игровом автомате от Atari имелись звуки, поэтому Возняк и добавил динамик в Apple II. Серьезной проблемой для программиста при работе с динамиком являлась необходимость совмещать вывод звука с другими задачами. Для корректного формирования меандра приходилось тщательно выверять такты процессора. В этом отношении несколько проще дела обстояли в IBM PC, поскольку на нем задача генерирования меандра могла быть возложена на отдельную микросхему таймера. Но даже эта микросхема не слишком помогала в использовании широтно-импульсной модуляции (ШИМ, PWM) — способа для вывода многоразрядных звуковых данных на динамик.

Вывод многоразрядного звука на динамик с помощью ШИМ заключается в передаче аудиоданных с использованием прямоугольного сигнала-несущей, ширина импульса которой модулируется (управляется) значениями уровня амплитуды. При получении очередного значения амплитуды компьютер тратит дополнительное время на генерирование несущей с жестко заданным периодом, который зависит от желаемой разрядности звука. В случае ШИМ за наличие всего двух уровней квантования сигнала приходится расплачиваться значительно возросшим разрешением во временной области при передаче данных. Неудивительно поэтому, что наиболее эффективно ШИМ использовалась не на микрокомпьютерах, а на PC. Причины связаны как с быстродействием, так и с объемом памяти, ведь с помощью ШИМ чаще всего воспроизводили оцифрованную музыку. Особенно симпатично такая музыка звучала в заставках PC-игр второй половины 80-х от французской компании Loriciel.

Чтобы завершить тему PC, упомяну об одном распространенном подходе к имитации полифонического звучания для динамика. В большинстве случаев на стадии игрового процесса было слишком накладно выводить многоразрядный звук с помощью ШИМ. Вместо этого проигрываемые голоса попросту чередовались с заданным временным интервалом. Чем короче период чередования, тем более качественное «полифоническое» звучание можно получить, но это потребует и большей активности со стороны процессора. Любопытно, что такого рода прием давно известен в классической музыке под названием скрытого многоголосия и использовался, например, в баховских сюитах для виолончели соло. Ощущение полифонии слушателем при скрытом многоголосии является, в большей степени, психоакустическим эффектом. Феномен схожего рода возникает в однобитной музыке и во время исполнения звуков ударных. Все остальные голоса при этом просто замолкают, ведь реальное микширование каналов не используется, но мозг слушателя автоматически достраивает несуществующее в данный момент звучание.


Пример однобитного синтеза для ZX Spectrum, авторы Jonathan Smith и Keith Tinman

Вернемся, наконец, к ZX Spectrum. Однобитная музыка второй половины 80-х для этого микрокомпьютера отличалась особенным своеобразием. У ZX Spectrum, разумеется, не было возможностей PC для эффективной поддержки подходов к выводу звука, о которых говорилось выше. Но при этом, популярность Speccy стимулировала интерес разработчиков к поиску нетривиальных приемов воспроизведения звука с помощью динамика.

Простейший однобитный синтез к середине 80-х был хорошо известен разработчикам ZX Spectrum. Основа его в том, что при генерировании прямоугольного сигнала (последовательно отправляя нули и единицы в порт динамика) есть возможность управлять двумя параметрами: частотой сигнала и шириной импульса. В результате, модулируя по некоторому закону частоту, можно получить разновидность FM-синтеза, о котором говорилось в одном из предыдущих разделов. Кроме того, достижимы и эффекты на основе ШИМ. Здесь важно отличать подход ШИМ для вывода многоразрядного звука, где частота несущей может составлять мегагерцы, от ШИМ, влияющей на тембр, в духе звучания знаменитых звуковых микросхем SID (Commodore C64) и 2A03 (NES). Управляя данными параметрами можно получить достаточно широкую палитру интересных тембров. Единственный недостаток — все это работает в режиме одного голоса.

Одним из первых на ZX Spectrum многоканальный однобитный синтез начал применять Тим Фоллин (Tim Follin). Юный музыкант и программист из Англии (на момент создания своих знаменитых однобитных синтезаторов ему было 15-16 лет) в первых своих композициях для игр использовал 2-3 канала, но уже к 1987 году его музыка насчитывала более 5 каналов с использованием нескольких тембров, амплитудных огибающих, пространственных эффектов и ударных.

Вместо меандра, у которого ширина импульса равна половине периода, Фоллин использовал форму волны c весьма узкой шириной импульса. В результате получался характерный «игольчатый» тембр, отдаленно напоминающий звучание язычковых инструментов, таких, как губная гармоника. При этом, в заданных узких пределах ширину импульса можно было регулировать, что отражалось на громкости, и, дополнительно, на тембре. Микширование нескольких виртуальных каналов Фоллин реализовал с помощью последовательного вывода значений в порт в духе скрытого многоголосия, но на значительно более высокой частоте, чем в случае PC.

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

К слову сказать, на некоторых микрокомпьютерах 80-х, включающих в себя звуковые микросхемы, тоже использовались некоторые элементы программного синтеза. Для получения большего разнообразия тембров находчивые музыканты использовали ряд трюков, связанных с модуляцией параметров звуковых чипов. Например, на Commodore C64 сложные тембры компоновались из нескольких базовых форм волны путем их быстрого переключения, а на Atari ST имитация звучания ШИМ в духе чипа SID была реализована путем управления одним из аппаратных каналов микросхемы AY-3-8910 с помощью прерывания таймера.

Где сегодня используется однобитный звук? Судя по всему, тема синтеза для динамика оказалась неисчерпаемой, поскольку энтузиасты ZX Spectrum и поныне продолжают совершенствовать свои движки с количеством каналов, которое уже перевалило за десяток. На смену ШИМ в качестве метода для вывода многоразрядных данных пришла плотностно-импульсная модуляция, которая применяется в высококачественных аудиосистемах. В академической сфере об эстетике однобитной музыки пишут научные статьи и даже используют однобитные генераторы в концертах камерной музыки.

Практика


Давайте рассмотрим устройство музыкальной программы Тима Фоллина, опубликованной в августовском номере журнала Your Sinclair за 1987 год. В заметке, озаглавленной Star Tip 2, говорится лишь, что в приведенном коде используется 3 канала звучания.


Заметка Star Tip 2 из журнала Your Sinclair

К счастью, результат дизассемблирования HEX-дампа, напечатанного в журнале, можно найти в интернете. Видно, что программа состоит из небольшого плеера, за которым следует некоторое число фреймов музыкальных данных.

Как же устроен синтез из Star Tip 2? Каждый голос определяется периодом и шириной импульса. Вместо реального микширования голоса выводятся на динамик последовательно. Имеется 16 возможных значений ширины импульса. Данный параметр задается разом для всех 3 выводимых голосов и соответствует ощущаемой в данный момент их общей громкости. Остальная часть кода для исполнения фрейма связана с организацией «амплитудной» огибающей, которая динамически управляет общим для голосов параметром ширины импульса.

Синтез организован таким образом, что после последовательной обработки счетчиков голосов и возможного вывода данных на динамик используется еще некоторое число тактов для организации работы огибающих. Более того, после завершения проигрывания очередного фрейма, некоторое количество тактов затрачивается на чтение параметров следующего фрейма. Все эти задержки не оказывают такого разрушительного влияния на звук, как это можно было бы ожидать.

При моделировании кода Star Tip 2 на Питоне возникает проблема с частотой дискретизации. 44100 Гц не хватает для организации вывода голосов с учетом необходимого контроля ширины узких импульсов. Наилучшим вариантом было бы выполнение кода плеера на частоте процессора Z80 (3.5 МГц) с дальнейшим понижением частоты результата до 44100 Гц. Передискретизацию, или, если говорить конкретнее, понижение частоты дискретизации (прореживание или децимацию) необходимо производить аккуратно, с предварительным удалением частот, лежащих вне целевого диапазона, а не просто отбрасывая лишние сэмплы. Простым вариантом реализации фильтра для децимации является использование скользящего среднего (см. функцию decimate). Обратите внимание, что в приведенном далее коде я использую коэффициент децимации равный лишь 32 для ускорения работы программы. Константы T1 и T2 были выбраны на слух и с учетом известной длины мелодии.


Фрагмент композиции Star Tip 2, в увеличенном масштабе представлена «игольчатая» форма сигнала

def decimate(samples, m):
  x_pos = 0
  x_delay = [0] * m
  y = 0
  for i, x in enumerate(samples):
    y += x - x_delay[x_pos]
    x_delay[x_pos] = x
    x_pos = (x_pos + 1) % len(x_delay)
    samples[i] = y / m
  return samples[::m]

# Коэффициент децимации
M = 32

# Уровни громкости динамика
ON = 0.5
OFF = 0

# Задержки при отправке данных в порт динамика
T1 = 6
T2 = 54

def out(samples, x, t):
  samples += [x] * t

# Синтез 3 голосов на основе параметров фрейма
def engine(samples, dur, t1, t2, vol, periods):
  counters = periods[:]
  t1_counter = t1
  t2_counter = t2
  update_counter = t1
  width = 1
  is_slide_up = True
  for d in range(dur):
    for i in range(3):
      counters[i] -= 1
      if counters[i] == 0:
        out(samples, ON, width * T1)
        out(samples, OFF, (16 - width) * T1)
        counters[i] = periods[i]
    # Далее происходит обработка огибающей в двух режимах: нарастание и спад
    update_counter -= 1
    if update_counter == 0:
      update_counter = 10
      if is_slide_up:
        t1_counter -= 1
        if t1_counter == 0:
          t1_counter = t1
          width += 1
          if width == 15:
            width = 14
            is_slide_up = False
      else:
        t2_counter -= 1
        if t2_counter == 0:
          t2_counter = t2
          if width - 1 != vol:
            width -= 1
    out(samples, samples[-1], T2)

# Чтение и проигрывание фреймов
def play(samples, frames):
  dur = 0
  vol = 0
  t1 = 0
  t2 = 0
  i = 0
  while frames[i] != 0:
    if frames[i] == 0xff:
      dur = frames[i + 2] << 8 | frames[i + 1]
      t1, t2, vol = frames[i + 3:i + 6]
      i += 6
    else:
      engine(samples, dur, t1, t2, vol, frames[i: i + 3])
      i += 3

samples = [0]

with open("startip2.txt") as f:
  frames = [int(x, 16) for x in f.read().split()]
  play(samples, frames)

write_wave("startip2.wav", decimate(samples, M))

Исходный код
Фреймы

Звучание

Необычное звучание на 26 секунде в wav-файле создается с помощью эффекта флэнжер, путем введения очень короткой задержки между идентичными голосами.

Алгоритмы передискретизации широко используются в реализациях современных программных синтезаторов. В частности, использование подобных алгоритмов является еще одним, помимо уже известного нам FM-синтеза с обратной связью, способом борьбы с явлением наложения частот при воспроизведении волновых форм. Кроме того, как показывает данный пример, без подобных алгоритмов трудно обойтись при моделировании звучания старинных звуковых чипов (AY-3-8910, SID и т.д.).

Sonix


В 1985 году на свет появился персональный компьютер Commodore Amiga 1000, у которого для работы со звуком использовался специальный чип Paula. Возможности Paula разительно отличались от типичных звуковых микросхем, которые использовались в домашних компьютерах того времени. В распоряжении пользователя на Amiga находились 4 аппаратных аудиоканала с разрядностью по 8 бит каждый и настраиваемой для каждого канала частотой дискретизации до 28.8 кГц. Схожими характеристиками обладал популярный в среде музыкантов сэмплер E-MU Emulator. С точки зрения программиста особенно удобной была организация в данном компьютере вывода многоканального звука практически без участия центрального процессора.

Хотя Amiga и позиционировалась в качестве компьютера для творческих людей, она, все же, потерпела поражение в схватке с Atari ST за внимание со стороны профессиональных музыкантов. В Atari ST была встроенная поддержка MIDI-интерфейса, а у Amiga ее не было. Более того, на первых Amiga существовали определенные проблемы с использованием последовательного порта для соединения по MIDI. В результате Amiga стала отличной системой для компьютерных музыкантов-любителей, которые сами написали для себя нужные программы — музыкальные трекеры с их своеобразной нотацией нотных событий и сэмплами в качестве инструментов.

Программные синтезаторы для Amiga были редкостью, что объяснимо, в том числе, нехваткой быстродействия процессора. Тем не менее, программный синтез на Amiga существовал, например, в виде FM-синтезаторов (FMsynth). Самой же первой реализацией программного синтеза для Amiga стало приложение MusiCraft, которое использовалась компанией Commodore для демонстрации возможностей нового компьютера. В 1986 году MusiCraft был продан компании Aegis и, после некоторых доработок, вышел на рынок под именем Sonix.


Окно параметров Sonix

Sonix представляет собой модель аналогового синтезатора, работающую в реальном времени с полифонией до 4 голосов. Возможности этого старинного программного синтезатора не так уж сильно отличаются от современных VSTi-плагинов: есть осциллятор с выбором формы волны, фильтр с настраиваемой частотой среза (но без резонанса), ADSR-огибающая, LFO (генератор сигнала низкой частоты). Форму волны для осциллятора и LFO можно изобразить с помощью мыши. В Sonix использовано остроумное решение для имитации звучания резонанса: по нажатию на экранную кнопку изображение формы волны попросту заменяется несколькими ее повторениями в пределах того же периода, что приводит к характерному звучанию. Также в наличии имеется эффект флэнжер с настраиваемыми параметрами. Хотя возможности Sonix кажутся по нынешним временем весьма скромными, синтезатор отличает достаточно широкая палитра звучаний. Единственное, несколько странным на сегодняшний взгляд кажется решение разработчиков использовать классический нотный стан для написания музыки в Sonix.

Практика


Теперь давайте попробуем создать, в духе Sonix, модель осциллятора и фильтра аналогового синтезатора. В качестве осциллятора будет использоваться генератор пилообразного сигнала. Для избавления от явно слышимых проявлений эффекта наложения частот воспользуемся FM-синтезом с обратной связью. Любопытно отметить, что с помощью регулирования уровня обратной связи можно сразу получить некоторый эффект НЧ-фильтра, но это будет работать только для конкретного осциллятора, а не для любого входного сигнала.

Как и в случае Sonix, реализовываться будет фильтр нижних частот без управления резонансом. Обратите внимание на реализацию НЧ-фильтра 1 порядка (см. определение класса Lp1 в коде ниже). Хотя такой фильтр не отличается высокой крутизной среза частот, он требует лишь всего одного умножения и может использоваться, например, для сглаживания значений управляющих параметров. Кроме того, данный фильтр может применяться и в качестве строительного блока для построения более сложных фильтров в каскадной форме. В частности, знаменитый фильтр Муга содержит 4 последовательно соединенных НЧ-фильтра 1 порядка. Такая каскадная схема реализована и в данном примере.

Дополнительный осциллятор lfo1 (генератор синусоиды) используется в автоматизации управления частотой среза фильтра. Для демонстрации я взял знаменитую нотную последовательность из композиции On the Run группы Пинк Флойд.

# Реализация генератора пилообразного сигнала
# с помощью FM-синтеза с обратной связью,
# используется ранее определенный класс Sine
class Saw:
  def __init__(self):
    self.o = Sine()
    self.fb = 0

  def next(self, freq, cutoff=2):
    o = self.o.next(freq, cutoff * self.fb)
    # Используется усреднение для уменьшения эффекта наложения частот
    self.fb = (o + self.fb) * 0.5
    return self.fb

# НЧ-фильтр 1 порядка
class Lp1:
  def __init__(self):
    self.y = 0

  # Частота среза cutoff в пределах 0..1
  def next(self, x, cutoff):
    self.y += cutoff * (x - self.y)
    return self.y

# E3, G3, A3, G3, D4, C4, D4, E4, 165 BPM
ON_THE_RUN = [82.41, 98, 110, 98, 146.83, 130.81, 146.83, 164.81]

osc1 = Saw()
lfo1 = Sine()
flt1 = Lp1()
flt2 = Lp1()
flt3 = Lp1()
flt4 = Lp1()

samples = []

for bars in range(16):
  for freq in ON_THE_RUN:
    for t in range(int(sec(0.09))):
      x = osc1.next(freq)
      cutoff = 0.5 + lfo1.next(0.2) * 0.4
      x = flt1.next(x, cutoff)
      x = flt2.next(x, cutoff)
      x = flt3.next(x, cutoff)
      x = flt4.next(x, cutoff)
      samples.append(0.5 * x)

write_wave("on_the_run.wav", samples)

Исходный код

Звучание

Моделирование фильтров аналоговых синтезаторов — обширная тема, по которой можно рекомендовать хорошую современную книгу The Art of VA Filter Design. Автор этой книги, Вадим Завалишин, в конце 90-х создал программный синтезатор Sync Modular и, далее, стал одним из ключевых разработчиков знаменитого синтезатора NI Reaktor. Технология Core, разработанная Завалишиным, имеет отношение к тем самым блокам и проводам, пример работы с которыми был приведен в одном из предыдущих разделов. В случае Core графы транслируются в эффективный машинный код каждый раз, когда меняется их структура и происходит это прямо в процессе работы с синтезатором.


Интерфейс Sync Modular

Music Kit


1988 год. На сцене концертного зала играет необычный дуэт, состоящий из профессионального скрипача и компьютера, который генерирует в реальном времени звучание струнных c помощью синтеза на основе физического моделирования. Именно таким образом Стив Джобс решил завершить презентацию своего нового детища — персонального компьютера NeXT. Джобс сделал все от него зависящее, чтобы звуковые возможности нового компьютера не имели равных среди конкурентов. Сложный звуковой синтез на NeXT производился с использованием основного 32-битного процессора, математического сопроцессора, а также специально выделенного для задач, связанных со звуком, DSP-процессора. Вывод аудио в NeXT осуществлялся в режиме стерео, 44100 Гц, 16 бит.

Джобс понимал, что для того, чтобы задействовать в полном объеме мощную аппаратную часть, ему понадобится команда профессионалов компьютерного звука. По этой причине он обратился Джулиусу Смиту (Julius Smith), одному из лучших специалистов в области музыкальной цифровой обработки сигналов из Стэнфордского университета.

Смит и еще один выходец из Стэнфорда, Дэвид Яффе (David Jaffe), создали систему Music Kit для NeXT. Эта система объединяла MIDI-подход для работы с нотными событиями с подходом описания звуковых модулей и связей между ними из MUSIC N. Music Kit брала на себя управление DSP-процессором, занималась распределением ресурсов и предоставляла разработчику высокоуровневый интерфейс в виде набора объектов, которые могли быть использованы в создании конечных GUI-приложений, связанных со звуком.

В рамках Music Kit были реализованы различные виды синтеза звука. Вариант, прозвучавший на презентации 1988 года, основан на алгоритме Карплуса-Стронга (Karplus-Strong). Это удивительно простой алгоритм, придуманный еще в конце 70-х, который позволяет моделировать звучание затухающих колебаний струны. В первоначальной версии данный алгоритм использует циклический буфер (линию задержки), длина которого соответствует периоду колебаний моделируемой струны. Для создания начальных условий для затухающих колебаний в буфер помещаются случайные значения. Далее последовательное чтение из буфера сопровождается усреднением очередной пары соседних отсчетов, то есть фильтрацией, которая может быть реализована без операций умножения. Подход Карплуса-Стронга Смит развил до синтеза на основе цифровых волноводов (digital waveguide), который сейчас является общим подходом к физическому моделированию различных акустических инструментов. Данная технология применялась компаниями Yamaha и Korg в своих синтезаторах на основе физического моделирования.

Приложения для Music Kit, как и сам компьютер NeXT, были известны, в основном, в университетской среде. Среди разработок можно выделить несколько визуальных сред музыкального программирования (SynthEdit, SynthBuilder), а также синтезатор поющего голоса (SPASM). Кроме того, с помощью Music Kit были созданы физические модели самых различных акустических инструментов.


Среда SynthBuilder на базе Music Kit

С начала 90-х система Music Kit развивалась в стенах Стэнфордского университета, а Стив Джобс принял заслуживающее особого уважения решение открыть ее код. Появилась версия Music Kit для PC, но, к сожалению, распространения она так и не получила.

Практика


В данном примере мы рассмотрим реализацию простой модели акустической бас-гитары методом Карплуса-Стронга. Модель будет состоять из следующих элементов: генератор шума, линия задержки, НЧ-фильтр. Ключевым элементом является линия задержки, которая имитирует колебания струны. В момент очередного «удара по струне» в буфер линии задержки попадают значения из генератора шума и количество этих значений зависит от силы «удара». Случайные значения создают эффект более естественного звучания, когда каждый удар по струне несколько отличается от предыдущего. Длина буфера линии задержки соответствует периоду колебания и чтение очередного сэмпла результата происходит по индексу, который перемещается в пределах размера буфера. Для создания эффекта колебаний струны, при которых высокие гармоники затухают быстрее, чем низкие, используется НЧ-фильтр, который применяется к значению по текущему индексу в буфере линии задержки.

В оригинальном варианте алгоритма НЧ-фильтр представляет собой простое усреднение значений двух сэмплов, которое можно реализовать без операции умножения, но при такой реализации проявляется нежелательный эффект: высокие ноты затухают слишком быстро, а низкие, соответственно, слишком медленно. В представленном ниже коде используется НЧ-фильтр 1 порядка, реализованный в предыдущем разделе. Этот фильтр, с помощью соответствующего параметра, позволяет установить для конкретной ноты нужное значение частоты среза.

# "Щипок" струны (используется класс Lp1 из предыдущего раздела),
# amp: амплитуда ноты,
# freq: частота ноты,
# dur: длительность ноты
def pluck(samples, amp, freq, dur):
  flt = Lp1()
  delay_buf = [0] * int(SR / freq)
  delay_pos = 0
  for i in range(int(len(delay_buf) * amp)):
    delay_buf[i] = random.random()
  for t in range(int(sec(dur))):
    delay_buf[delay_pos] = flt.next(delay_buf[delay_pos], 220 / len(delay_buf))
    samples.append(amp * delay_buf[delay_pos])
    delay_pos = (delay_pos + 1) % len(delay_buf)

# Пауза в звучании длительностью dur,
# Обратите внимание, что здесь повторяется последнее значение
# из буфера сэмплов, это позволяет избежать сильных скачков амплитуды
def rest(samples, dur):
  for t in range(int(sec(dur))):
    samples.append(samples[-1])

samples = []

for i in range(4):
  pluck(samples, 0.7, 58, 0.27)
  pluck(samples, 0.4, 62, 0.27)
  pluck(samples, 0.4, 66, 0.27)
  pluck(samples, 0.6, 69, 0.27)
  pluck(samples, 0.5, 138, 0.01)
  rest(samples, 0.13)
  pluck(samples, 0.7, 123, 0.13)
  rest(samples, 0.27)
  pluck(samples, 0.7, 139, 0.47)
  rest(samples, 0.07)
  pluck(samples, 0.7, 78, 0.27)
  pluck(samples, 0.4, 82, 0.27)
  pluck(samples, 0.4, 87, 0.27)
  pluck(samples, 0.6, 92, 0.27)
  pluck(samples, 0.5, 184, 0.01)
  rest(samples, 0.13)
  pluck(samples, 0.7, 139, 0.13)
  rest(samples, 0.27)
  pluck(samples, 0.7, 165, 0.47)
  rest(samples, 0.07)

write_wave("chameleon.wav", samples)

Исходный код

Звучание

Остается добавить, что в качестве демонстрации звучания я выбрал басовый рифф из композиции Chameleon Херби Хэнкока.

Заключение


В 90-е годы программные синтезаторы для PC и Mac пережили бурное развитие. Среди наиболее необычных программ можно упомянуть MetaSynth, с помощью которой, в духе советского синтезатора «АНС» Е.А. Мурзина, можно было генерировать звук на основе изображения. Одним из пользователей MetaSynth был известный электронный музыкант Эфекс Твин (Aphex Twin). В 1999 году компания Steinberg выпустила вторую версию своего формата аудиоплагинов VST, в котором поддерживались входные MIDI-данные. Так появились столь популярные сегодня VST-синтезаторы.

В последние годы основные исследования по части синтеза звука сосредоточены на детальном моделировании элементов аналоговых синтезаторов. Синтез на основе физического моделирования акустических инструментов и более экзотические подходы к созданию тембров оказались не слишком востребованы музыкантами. Как мне думается, в настоящий момент у большинства музыкантов просто нет художественных задач, требующих использования сложных и абстрактных схем синтеза звука.

Однако, сложный звуковой дизайн все еще востребован в кино. Одним из исторических и вдохновляющих меня примеров компьютерного звукового дизайна является композиция Deep Note, созданная Джеймсом Мурером (James A. Moorer) из Стэнфордского университета. Эта короткая композиция звучала в американских кинотеатрах каждый раз, когда на экране появлялся логотип технологии THX. Создание DeepNote потребовало от ее автора знаний в области музыкального программирования. Такого рода композицию, где практически не используются типовые музыкальные элементы, сложно сочинить в современных редакторах, рассчитанных на устоявшийся процесс работы пользователя. Еще одним примером из области кино является система визуального музыкального программирования Kyma, которая и в наше время используется в озвучивании голливудских фильмов.

Оптимизм вызывает также недавний (2010 год) выход серьезного учебника Энди Фарнелла (Andy Farnell) Designing Sound. Эта книга полностью посвящена компьютерному звуковому синтезу с примерами на визуальном аудиоязыке Pure Data.

Наконец, сложные схемы алгоритмического звукового дизайна могут применяться в компьютерных играх. В частности, это касается моделирования природных явлений или звуков различных механизмов, шума двигателя. Соответствующие методы синтеза звука уже некоторое время используются в играх и в будущем, следует надеяться, будут использоваться более широко.

Let's block ads! (Why?)