Gamedev
chelovekbeznika
1964

Как я делал максимально аутентичную восьмибитную музыку в передаче "Объясняю каждую строчку"

В закладки
Аудио

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

Идея

Когда я изучал возможности Monogame чуть подробнее, я наткнулся на возможность самому генерировать нужные звуковые сигналы. То есть, можно было написать свой синтезатор, выводящий звук в формате 16-bit PCM (это просто сэмплы звуковой волны без всякого сжатия).

На свою беду (это было ещё задолго до старта проекта) я наткнулся в интернете на пару видосов, где объяснялось, какие именно звуки издаёт старенькая Денди. Выглядело это как нечто, что вполне можно реализовать. Пять каналов. Два генерируют квадратики, один треугольники, один вообще рандомом шпарит, и есть ещё какая-то дельта-модуляция, но с этим можно разобраться. В общем, ни разу не Rocket Science.

Когда я взялся за этот проект, я начал по возможности эксплуатировать эстетику NES. В основном от безысходности, конечно. Когда дело дошло до музыки, я понял, что я обязан сделать МАКСИМАЛЬНО АУТЕНТИЧНУЮ ВОСЬМИБИТНУЮ МУЗЫКУ (говорить голосом персонажа Jojo's Bizarre Adventures, пока за спиной летают угловатые иероглифы).

Короче говоря, я решил написать свой эмулятор звукового чипа NES.

Реализация. Часть первая. Кости.

Стартовой точкой для меня стал сайт wiki.nesdev.com. Я вечерами копался в этой документации, пытаясь сопоставить то, что там написано с какой-то абстракцией в своей программе, алгоритмами работы, здравым смыслом и логикой инженеров Nintendo.

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

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

Там, была не Rocket Science, но стримов восемь я над этим просношался. В итоге получилась сравнительно правдоподбная эмуляция, которой можно было управлять с помощью команд, записываемых через JSON (вообще-то через объекты си-шарпа, которые легко сериализуются, а местный редактор абсолютно конченное говно, но это так, лирика).

Ты выбираешь секунду и номер сэмпла в сгенерённой мелодии (47100 сэмплов в секунду), канал, параметр, который надо изменить, и его значение. И так ты переключаешь то, что в настоящем чипе существует в виде 22 байт write-only регистров процессора Ricoh 2A03. И в нужный момент, кратный 38 тактам этого самого процессора (38 * 47100 = число близкое к тактовой частоте этой штуки), внутри моего типа эмулятора меняется какой-то флажок, или частота звука на одном из каналов.

Таким образом, можно было создавать музыку, используя те же ограничения, что и безымянные японцы в геймдевовских конторах тех суровых времён. Да, безымянные, потому что все ссались, что хранителей тайных знаний и запредельных умений переманят, и запрещали разработчикам указывать свои имена в игре. Право на своё имя в титрах была одной из плюшек, которой Юдзи Наку, отца Соника, уговорили работать над второй частью. Причём уламывала Sega of America, потому что японцам такое кощунство тогда в голову бы не пришло.

Реализация. Часть вторая. Мясо.

Была одна проблема. Как с помощью этого создавать музыку? Чтоб вы понимали, в регистрах NES не было частоты звука. Там были, например, 11 бит, определяющих, сколько тактов процессора должно пройти до следующей смены цифры в ряду 0-1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-15-14-13-12-11-10-9... для треугольных шумов, например.

Пришлось наворачивать ещё один уровень абстракции. Вместо команд я стал записывать в JSON объекты, которые умели превращаться в список команд. И первый класс, который я наделил таким умением, была сама команда. Да, она выдавала список из одной команды, самой себя. Ну и некоторые штуки чуть посложнее, типа "полсекунды белого шума с такими параметрами". Таким образом, я мог перемешивать "передвинуть этот бит" с "включи первый треугольный канал" или "нота ля малой октавы длительностью одна восьмая на треугольном канале". Да, ноты...

В общем, я не мог просто так наколхозить музыку из регистров. Если на то пошло, я её вообще не мог наколхозить. Какой у меня был выход? Перерабатывать уже существующую музыку. Где мне было её брать в виде "исходников"? Ох, просто попробуйте представить, как охуел мой брат с начальным музобразованием, когда узнал, что я, демонстративно проигнорировав в детстве музыкальную школу, сел учить нотную грамоту через несколько лет после окончания универа.

До этого я знал только то, что нот семь. Как и многие из вас. Поэтому я поделюсь с вами одним маленьким секретом.ИХ НИХЕРА НЕ СЕМЬ! ИХ ДВЕНАДЦАТЬ! Хочешь попасть в промежуток, где таится одна из безымянных нот, цепляй диез или бемоль, чтобы сделать плюс-минус полтона. Да, октава делится на шесть тонов. А между нотами полтона-тон. А ещё не забудь подумать, как реализовать тот факт, что некоторые ноты акцентированы. Некоторые не акцентированы. А некоторые вообще вторые доли в размере 3/4. И это мне ещё не надо было реализовывать триоли.

На кости из эмуляции писков NES было навёрнуто мясо из макросов, реализующих этакую пародию на нотную запись. Оставалось самое главное:

Реализация. Часть третья. Душа.

В музыке для ледяных уровней мне должен был помочь замечательный композитор Леонид Карлович Бекман. У него много достоинств, но самым главным из них является то, что он уже 80 лет как мёртв. А значит, можно было взять на пробу вот эту мелодию:

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

Можно почитать, как это выглядит у меня вот здесь:

Я бы скинул вам послушать MP3 с результатом, но его не существует в природе. Мелодия воссоздаётся из файла выше по мере надобности, а потому уходит в небытие.

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

Вариант номер два - открыть один из выпусков в промежутке 114-116, где я это мелодию колхожу. Там в процессе просмотра стрима, наслушаетесь.

А потом я узнал о том, что существует famitracker...

Но пить боржоми было уже поздно. Приглашаю всех неравнодушных понаблюдать за моим героическим велосипедостроением.

Присоединиться к нашему движу можно тут:

Или тут:

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

Да поможет мне господь...

А сейчас три билборда на границе между постом и комментариями.

https://www.youtube.com/c/explainingeverystring Impossible to insert youtube channel link without workarounds and # always turns into hashtag https://www.youtube.com/c/explainingeverystring
https://www.youtube.com/c/explainingeverystring Editor is still broken https://www.youtube.com/c/explainingeverystring
https://www.youtube.com/c/explainingeverystring How comes, Denis Shiryaev? https://www.youtube.com/c/explainingeverystring

Материал опубликован пользователем.
Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "chelovekbeznika", "author_type": "self", "tags": [], "comments": 8, "likes": 75, "favorites": 147, "is_advertisement": false, "subsite_label": "gamedev", "id": 90175, "is_wide": false, "is_ugc": true, "date": "Wed, 12 Feb 2020 04:12:11 +0300", "is_special": false }
0
{ "id": 90175, "author_id": 105160, "diff_limit": 1000, "urls": {"diff":"\/comments\/90175\/get","add":"\/comments\/90175\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/90175"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 64954, "last_count_and_date": null }
8 комментариев
Популярные
По порядку
Написать комментарий...
15
Ответить
1

Благородное занятие, охватывающее множество тем и набивающее руку.
Удачи в велосипедостроении :)

Ответить
1

Очень интересно не почитать, а послушать то где?

Ответить
0

Я бы скинул вам послушать MP3 с результатом, но его не существует в природе. 

Вариант номер два - открыть один из выпусков в промежутке 114-116, где я это мелодию колхожу. Там в процессе просмотра стрима, наслушаетесь.  

Вы путаетесь в показаниях

Ответить
1

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

Ответить
0

Чей замок?)

Ответить
0

Вот простая мелодия ещё:

Ответить
0

По просьбам трудящихся:

https://youtu.be/Be7c1BP2bQk?t=8279

Ответить

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fizc" } } }, { "id": 4, "label": "Article Branding", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "cfovz", "p2": "glug" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "clmf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-250597-0", "render_to": "inpage_VI-250597-0-1134314964", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=clmf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Баннер в ленте на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudo", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "chvjx", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fzvc" } } }, { "id": 20, "label": "Кнопка в сайдбаре", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "chfbl", "p2": "gnwc" } } } ]
{ "jsPath": "/static/build/dtf.ru/specials/DeliveryCheats/js/all.min.js?v=05.02.2020", "cssPath": "/static/build/dtf.ru/specials/DeliveryCheats/styles/all.min.css?v=05.02.2020", "fontsPath": "https://fonts.googleapis.com/css?family=Roboto+Mono:400,700,700i&subset=cyrillic" }