Доброго времени всем пользователям Suno и тем, кто интересуется технологиями ИИ в музыке. Как и ранее не претендую на полноту освещения вопроса, но надеюсь, что информация будет полезной.
Под аранжировкой обычно понимают создание инструментального трека (минусовки, backing track) на основе мелодии или мелодии + гармонии, с определенным стилем, темпом, формой/структурой. Suno генерирует песни (и инструментал) целиком с задаваемыми (относительно) стилем и формой. Показалось интересным протестировать систему на возможность создания нужной аранжировки. Особенно после своих экспериментов с Audio Input (см. статью), когда эта функция Suno стала очевидной. Да и читатели обращались с подобным вопросом. Так я затеял серию экспериментов, результаты которых представил в пяти тестах. (Все звуковые примеры и детали у меня на сайте).
За последние пару месяцев я сделал более 80 DS (dataset, исходников), как с вокалом, так и инструментальных. Причем, чаще это была не просто отрезка фрагмента до 60 сек. из готового трека, а его препарация или сборка фрагмента c нужным содержанием (вокал, аккорды, Hi-Hat Loop и т.п.).
Тест #1 / от спетой мелодии, текста… («По-Гру-Гла-Но …») *.
Отталкиваясь от удачного опыта с генерацией аранжировок в разных стилях на а капеллу Воробей я решил провести более детальный эксперимент: на входе спетая мелодия + текст (Lyrics) и задание Style. Классно же спеть свою мелодию, скормить Suno и получить готовую песню в нужном стиле…
Написал простую мелодию, сочинил шуточный текст, включающий Bridge, Verse и Chorus и пропел все это на простую гармонию. Сначала в темпе 90 bpm — тогда пришлось сделать 2 разных DS: Bridge+Verse и Bridge+Chorus, иначе нельзя было уложиться в ограничение в 60 сек. Затем — в темпе 102 bpm: Bridge+Chorus, здесь Bridge уже не пропевался.
* Все детали этого теста — DS, ноты, результат генерации (21) в разных стилях с тональностями и темпом приведены на сайте.
Для этой песни сделал 10 DS, результаты для 8 приведены. Все DS, естественно, включали мелодию (голос) и Hi-Hat по 1/4, в некоторых была полностью проведена гармония (аккорд фортепиано на каждый такт или на слабые доли), в некоторых ОДИН аккорд вначале. Где-то добавлены бас и DrumLoop, где-то голос был смягчен (легкий завал ВЧ + Reverb). Все эти вариации с целью понять, как надежнее передать Suno мелодию, гармонию.
Что в итоге? Вполне рабочий вариант, если нужна готовая песня, а не минусовка. Конечно, инструментальный трек можно получить разделением (vocalremover), но качество его будет не очень хорошим. Основные моменты:
1.1 Разбивать одну песню на 2 DS нежелательно — различия в генерациях/моделях (из-за определение тональности, гармонизации) при одном и том же Style могут быть существенными, что сильно усложнит или сделает невозможной правильную сборку песни.
1.2 При простой гармонии и тексте, среднем темпе Suno в целом воспринимает и отрабатывает все основные параметры, внося в них изменения, видимо, согласно заданному Style и выбранной модели. В большинстве генераций, аранжировка делается правильно (в музыкальном смысле ), примерно так, как это мог бы сделать человек.
1.3 Достаточно интересны регенерации вокала: есть расширение диапазона, изменения мелодии и по ступеням, и по ритмике/длительностям/штрихам, опевания/мелизмы в соответствии со стилем, также разные «подачи». Где-то и на октаву выше исходника. В целом мне результат нравится — Suno поет моим голосом лучше, правильнее (в вокальном смысле), чем я сам.
1.4 Стоит обратить внимание на генерации, где Suno гармония не передавалась, а задавалась лишь самой мелодией (v9.n).
1.5 Интересным представляется v9.1, где и стиль не задавался. Скорее всего, здесь Suno делает выбор исключительно из ритмической организации и содержания текста.
Тест #2 / от спетой мелодии, текста… («Красота в глазах …») **
Тест, по сути, аналогичный #1, но с испытанием кроме мужского и женского DS. Опять сочинил мелодию, скажем там, слегка сконструированную. Написал текст, чтобы не издеваться над чужим. Подобрал гармонию (а-ля джазовую, хотя настоящие джазисты, сделали бы это лучше.) В общем, мелодия и гармония сложнее, чем в Тест #1 — как это отработает Suno?
Для этой песни сделал 6 DS, для 3-х приведены результаты (16 генераций). Также, как и в Тест #1 DS отличались по содержанию — к голосу были добавлены (или не) аккорды, бас и пр.
В целом выводы такие же как по Тесту #1, но есть особенности:
2.1 Как можно было догадаться, гармония в данном случае была Suno отработана хуже, но все-таки все оставалось соответствующим заданному стилю.
2.2 Женский DS не во всех генерациях был отработан, где-то явно не сохранился и был «морфирован» тембр (v5.7), а где-то сменился и гендер (v5.2, v5.3, v5.6, v5.8). Связано это, скорее всего, с заданным Style и выбранной Suno моделью.
Важный момент по Тестам #1 и #2: повторяя подобный эксперимент, загрузите вначале ваш DS с вокалом и не задавайте Style. Suno выберет что-то, что посчитает подходящим, исходя из анализа текста — размер/ритмика, смысл, образы. Обратится к своей базе и решит: «На русском? Нет, батенька, все-таки шансон … , да». (Шучу…) Скорее всего, при задании стиля, похожего на тот, который выбрал(о) Suno, будет меньше внесено изменений в мелодию, а также будет меньше сбоев в форме — несоответствий фраз в тексте фразам в музыке и т.п.
** Все детали этого теста — DS, ноты, результат генерации (16) в разных стилях с тональностями и темпом приведены на сайте.
Тест #3 / от реальной песни и текста...
Цель этого теста — проверить, сможет ли Suno воссоздать тембр, манеру певца и стиль исходной песни, при генерации новой. А также, можно ли сменить язык, скажем, будет ли Кети Топурия (A’Studio) петь на французском, а Шакира (Shakira) — на русском… «Дорогая, слова я сочинил, но ты определись, кто споёт на твой день рождения — А.Малинин, Мумий Тролль или С.Михайлов?»… В общем, может ли Suno сгенерить песню с голосом известного исполнителя? Да! Но, давайте по-порядку.
Suno не пропускает в Audio Input известные композиции — подгрузив подобный фрагмент, вы увидите сообщение «Uploaded audio matches existing work of art»… Я сделал около 20 разных DS из реальных песен, как мужских, так и женских — три из своих прошлых проектов, часть — из хитов известных исполнителей, как наших, так и зарубежных. Соответственно, сгенерил новые песни (см. примеры). Для известных, привел (исключительно в ознакомительных целях!) нарезку из коротких фраз, где голос, манера, стиль исходной композиции узнаваемы. Для своих — полные треки.
Да, в режиме Audio Input сама по себе система может генерить, синтезировать голоса известных артистов и тут два важных момента — технический, как преодолеть ограничение и юридический — можно ли вообще это делать. Мои соображения об этом ниже.
В целом Тест #3 показал, что генерация новой песни из старой — вполне рабочий вариант создания аранжировки, точнее готовой песни. Вы сохраняете и стиль песни, и (в большинстве случаев) голос, манеру исполнителя. Причем, работает и смена языка. Во всех генерациях, которые представлены, я оставлял поле Style пустым (т.е. no style), полагая, что все необходимое для генерации есть в DS. Единственное, что я попробовал для отхода от исходника, это заставить мужчину петь женским голосом и наоборот — т.е. использовал команды [Female vocals] и [Male vocals], но гендер практически нигде не менялся.
Тест #4 / backing track от гармонии…
Этот тест — попытка проверить, пожалуй, самый распространенный подход к созданию аранжировки в инструментах и приложениях типа Auto-Arranger: выбираем стиль, вводим аккорды, управляем формой (Verse, Chorus, Fill/Break).
Я собрал 5 DS, результаты применения 2-х представлены в примерах. Везде была простая гармония Am,Am,G,Am, Am,Am,G,E7, самая употребимая тональность (A минор) и средний темп — 102 bpm. Пробовал 8 и 16 тактов, аккорды — фортепиано (т.е. затухающий) и Synth (тянущийся Sinus), конечно Hi-Hat по 1/4, играл уровнями и панорамированием отдельных компонентов…
Хотелось для Verse и Chorus получить аккомпанемент без сольных партий, Intro, Bridge, Outro/Ending можно было бы дописать потом. Поэтому пробовал в Style и Lyrics прописывать разные команды, типа: [sololess BGM], [backing track, no any solo, sololess track]. Также пробовал DS с одним аккордом вначале (Am), а секвенцию задавать в Lyrics:
[Verse 1, 8 bars] // вар 1.
[Am chord, 1 bar]
[Am chord, 1 bar]
[G chord, 1 bar]
[Am chord, 1 bar]
[Am chord, 1 bar]
[Am chord, 1 bar]
[G chord, 1 bar]
[E7 chord, 1 bar] или
[Verse 1, 16 bars] // вар 2
|Am|Am|G|Am|Am|Am|G|E7|
|Am|Am|G|Am|Am|Am|G|E7|
Ни 1-й, ни 2-й способ не привели к отработке гармонии, а во 2-м появился мужской вокал в стиле Scat.
Конечно, что-то из генераций можно было бы использовать, как части для аранжировки — тональность и темп сохраняются. Гармония — не строго. Система как бы развивает ее, согласно заданного стиля. Плюс, точнее минус — почти везде есть сольные вставки… Может кто-то и умеет управлять генерацией для решения подобной задачи, я примеров не видел. Правда, я не могу сказать, что просмотрел все — Suno слишком объемная вещь.
Тест #5 / инструментальные вставки от исходника…
Тут все просто и правильно — Suno легко дописывает DS с указанным в Style сольным инструментом, сохраняя темп, тональность, стилистку. Это позволяет сгенерить вполне убедительные инструментальные части для создаваемой песни — Intro, Bridge, Outro/Ending — см. примеры. В качестве DS я использовал фрагмент своей композиции, скажем так, в джазовом стиле. Попробовал сгенерить соло с акустическими инструментами (Saxophone, Trumpet, Violin, Flute, Guitar и др.), которые не так просты в имитации, если аранжировка делается в компьютере без привлечения живых исполнителей.
Хотя и не все команды в этом тесте были исполнены (v5,v6 — [no bass, no drums], v6 — [String quartet]), в целом мне он представляется успешным — если система «генерит джаз», то уж точно справится с более простыми стилями и гармониями.
К слову, Suno может обыгрывать, развивать и вашу мелодию, если она присутствует в DS — вы играете простое соло, пусть Distortion Guitar, а на выходе получаете импровизацию на вашу тему (может и виртуозную).
Важное о ГОЛОСЕ и его синтезе в Suno
Это мои догадки, которым я нахожу подтверждение в процессе тестирования в последнее время. Сколько слов, секунд речи вам нужно услышать, чтобы распознать человека? Два слова, пару секунд?
Голос условно можно разделить на 3 компонента:
ТЕМБР — «окраска», определяемая в первую очередь строением/акустикой голосового аппарата — трахея, гортань, связки/складки, резонаторы (пазухи носа, глотка, рот) — т.е. физической конструкцией, геометрией и свойствами материалов. Впрочем, сам человек может влиять на геометрию и, в итоге, говорить «в нос», визгливо или глухо.
ИНТОНАЦИЯ — особенности звуковысотного движения, формируемые связками: взятие тона («подъезды»), удержание тона (стабильно/нестабильно, случайные девиации или отработанное вибрато), отпускание тона — т.е. характерная для речи и пения «кривая тона» (Pitch Curve).
АРТИКУЛЯЦИЯ — особенности формирование речи (действия языка, нёба, челюсти, губ).
Первые синтетические голоса (TTS) требовали, кажется, 25 часов речи диктора, для тренировки алгоритма, в RVC — 10-20 мин, а сейчас Suno обрабатывает 1 минуту музыки с вокалом за 30 сек. и этого достаточно, чтобы затем ре-синтезировать голос — и тембр, и манеру.
Возникает вопрос, как выполняется синтез речи без полного набора фонем, ведь абсолютно точно их нет в 60-секундном фрагменте песни? Об этом я задумался еще в первых экспериментах, когда заставлял Suno петь своим голосом — тогда из спетого 1-го куплета волшебным образом синтезировался 2-й и 3-й.
Скорее всего, тут как раз работает приведенное выше условное разделение голоса на 3 компонента. Я думаю, 60 сек. вполне достаточно, чтобы снять тембр, т.е. физические характеристики голосового аппарата. Интонацию в песни в любом случае надо формировать независимо, как и артикуляцию, отвечающую в первую очередь за язык.
Если эта догадка верна, то понятно, как в сгенерированном вокале появляются отсутствующие в исходнике, но характерные для заданного стиля интонации — опевания, мелизмы, вибрато, также подача — крик или шепот. И как с исходным тембром синтезируется вокал на другом языке. Складываются 3 компонента, в которых 1-й (тембр) остается неизменным, а 2-й и 3-й (интонация и артикуляция) — меняются.
Этот подход должен обладать очень большой гибкостью, однако, по идее, он не будет регенерировать такие особенности в речи, как картавость, шепелявость, неверное произношение отдельных звуков — т.е. все, что сильно отличается от среднестатистической модели конкретного языка. Мне кажется, Suno исправит такие дефекты также, как в этих тестах подправил(о) мое пение.
Также, как и с RVC тембр и манера определяют узнаваемость (в cтатье я приводил аналогию: тембр = одежда, манера = походка). Если и тембр яркий, и манера заметная, то мы будем слышать исполнителя в генерации. Если голос и манера, по большому счету обыкновенные, и исполнителя мы узнаем, скорее, по песне, то в генерации он будет не так уж узнаваем.
Сборка DS
DS для задания темпа и гармонии. Здесь задача передать Suno темп, тональность, гармонию, при этом минимизировать присутствие исходника в генерации.
Выбираю темп, целое число тактов в пределах 60 сек. Причем, заканчиваю DS на тактовой черте — мне показалось это важным, особенно после многих генераций с ускорением на 5-10 и более % — иногда Suno сам(о) ставит таким результатам метку Bug. Прописываю Hi-Hat по 1/4, иногда легкую DrumLoop в соответствующем стиле. Аккорды и бас — обычно фортепиано, в начале такта или плотнее, если аккорды меняются чаще. Hi-Hat и Piano — самые безобидные звуки, которые легко вписываются практически в любой стиль. Пробовал аккорды записывать Synth с чистой, синусоидальной волной, полагая, что так Suno легче распознавать, но … особой разницы с Piano не заметил. Можно прописать и фактуру/ритмический рисунок аккордами, если вас не будет смущать их присутствие в генерации.
Пробовал разные уровни и панорамирование (+- 20%) компонентов в DS. Suno читает Hi-Hat и при -18…-24 dB от нормализованного уровня. Аккорды при таком ослаблении отрабатываются хуже, но строгую статистику тут привести затруднительно, т.к. каждая новая генерация — это новая модель.
Да, как отмечал ранее, для задания тональности можно в начале прописать всего лишь одну ноту. Но не тонику (в A minor это Ля), а доминанту (Ми). Правда, надежнее, все-таки задавать аккорд.
DS из реальной песни. Не утверждаю, что мой способ единственно правильный, но мне он кажется логичным.
Я делал DS из очень разных источников, включая записи начала прошлого века и джаза, где темп нестабилен, да и качество фонограмм невысокое. В таком случае я сначала выравнивал все по сетке и приводил к среднему темпу.
В некоторых случаях разделял вокал и инструментал, менял баланс и снова смешивал. Достаточно часто делал корректировку общего спектра, иногда компрессию, если перепады уровня в частях были большими.
Просматривал всю композицию и выбирал наиболее важные и отличающиеся по звучанию части. Как правило это Verse, Chorus, Bridge. Задача — включить в DS максимум из данной композиции, т.е. и спокойные и энергичные части — как кульминацию, так и «яму» (разрежение, breakdown). Иногда это вступление или, наоборот, окончание.
Когда эти важные части определены, я вырезал из них куски по тактам и компоновал вместе так, чтобы не превысить ограничение в 60 сек. При этом было важно не сломать логику в гармонии. Если в исходной композиции была смена тональности, то какой-то кусок транспонировал. Естественно, делал небольшие cross-fade’ы на стыках.
В общем, надо было 3-4 минуты впихнуть в 60 сек — сделать короткую версию, в которой есть все характерное из исходной песни. На образцах современной популярной музыки все эти манипуляции делать намного проще.
В зависимости от качества DS, а также задания в Style, Suno может подмешивать похожую модель. (Несколько раз я получал генерацию без присутствия использованного DS из-за его низкого качества — например, из зальника the Beatles / Hey Jude — Suno просто применял(о) похожий стиль).
Подобные добавления иногда удачные, порой — не очень. Разные ударные неплохо уживаются, а вот некоторые сольные инструменты приобретают «двойственной» характер — меняется регистр, тембр, интонирование. И мы слышим Rhodes Piano с повадками Distortion Guitar, или пассаж Nylon Guitar, переходящий в верхнем регистре в клавесин. Мне это чем-то напоминает опцию в ИИ алгоритмах генерации изображений — «столько-то % от Reference Image», т.е. смешивание стилей.
Ограничение Audio Input относительно известных композиций. Я полагаю, что первые алгоритмы идентификации музыки работали на сравнении лишь амплитудного профиля. Сейчас Suno анализирует мелодию и текст вокала, а также мелодии Intro, Bridge и др. частей, возможно что-то еще. Я заметил, что есть некоторое количество слов, которое является критичным. Т.е. длительность критичного фрагмента может зависеть от темпа песни и длительностей в вокальной партии. По этой причине вам не удастся «скормить» Suno заметную часть куплета или припева. Выход, как часто у хирурга, один — резать. При этом, как уже отмечал, желательно не сломать гармонию. На самом деле, в Audio Input Suno можно вставить все что угодно, но чем сильнее мы ломаем исходную композицию, тем меньше на нее будут похожи генерации.
Некоторые DS, созданные из реальных песен с хорошим звуком, сведением, дают гарантированно хороший результат — я сделал несколько песен на их основе. И теперь мне иногда проще взять такой DS, чем экспериментировать с заданием в Style, т.к. я точно знаю, что получу.
Опять Copyright!
Так вот, можно (точнее, законно) ли создавать песни с голосами известных исполнителей? Не знаю, есть ли у юристов законы, регулирующих эту новую область авторского права (кстати, 16.09.24 в ГД внесен «законопроект об охране голоса как объекта личных неимущественных прав»). Скорее всего, для использования чужого голоса нужно согласие его обладателя или правопреемника… Но, история показывает, что если что-то представляет ценность, то это или покупают, или крадут…
У людей нет единого мнения относительно подобных технологий — «хорошо» это или «плохо». Скорее, большинство склоняется к «плохо», имея в виду мошенничество и злоупотребления.
Мой старинный приятель Рик Пол (Rick Paul, CA), хотя и поучаствовал с моей подачи своим материалом в моих экспериментах, достаточно консервативен и в глубине души, похоже, против всех этих новаций. Но недавно прислал заметку (… и комментарий, что, наверное, это хорошо.): «Популярный американский кантри-певец Рэнди Трэвис (Randy Travis, 65-лет), продавший более 25 млн. копий, обладатель 7 премий «Грэмми», почти потерял речь в 2013 после инсульта, недавно воспользовался ИИ, чтобы выпустить новый за 10 лет трек».
Артист А.Михайлов, категорически против «оживления» ушедших артистов с помощью ИИ в новых фильмах, о съемках которых объявил К.Эрнст (1-й канал ТВ). Вообще-то ученые могут синтезировать (или уже) голос Тутанхамона или другого исторического лица. Могут смоделировать (или уже) походку, мимику А.Македонского или Наполеона. Это как, «хорошо» или «плохо»?
У меня нет однозначного ответа. Правда, я бы с интересом слушал «новый» альбом Дж.Бенсона (G.Benson) и смотрел «новый» фильм с Ю.Никулиным… А почему нет однозначного мнения — потому, что неизвестно, кто и как будет использовать образы/модели этих артистов, насколько бережно, тактично отнесутся продюсеры к их творчеству и памяти о них.
… Да, можно было бы посоветовать поэтам новый «путь к звездам». Теперь им не надо сильно тратиться, чтобы записать песню для «звезды» — берется существующий хит, генерируется в Suno точно в такой же манере и с голосом «звезды» и отправляется ей(ему)… Правда, … кто их знает, могут и в суд подать на автора своей новой песни 🙂
Чтобы в этой сфере достичь какого-то порядка, мне кажется надо решать два вопроса: а) регистрация артистами прав на свои образы; б) исключение анонимности в сети. Понятно, что ничего абсолютного нет, но все-таки.
Детские голоса в Suno
Эта задача оказалось самой трудной. Мне кажется, в генерации голоса в Suno изначально заложена идентификация на мужской/женский (хотя есть и подкласс — Vocaloid, Chiptune, Miku Voice).
Я собрал, наверное, до 30 DS, пытаясь заставить Suno спеть детским голосом (см. Примеры). Сделал 4 DS на основе речи (панграммы), но не один из них не работал. Сработало лишь несколько DS (3-4), собранных из реальных песен, и один из детского пения а капелла (девочка 12 лет). Общий % удачных генераций вряд ли более 4-5%. Да, DS из детских хоров (советских времен) тоже не работали.
Причем, DS из реальных песен (которые работали) были не очень чистыми. Вполне возможно, что Suno не идентифицирует голос по М/Ж, если фонограмма грязная. В итоге генерирует из того, что есть.
Выводы
Да, Suno можно использовать как аранжировщик. Для бюджетных студий и самодеятельных авторов — точно. Для профессионалов — это скорее инструмент для проверки и генерации идей.
Если вы захотите сделать аранжировку (минусовку) в Suno, то без монтажа/редакции в DAW вам это вряд ли удастся***. Хотите петь сами, чтобы все было честно, тогда схема такая:
-
Спели (лучше под метроном) куплет и припев, укладываясь в 60 сек.
-
Загрузили в Audio Input, задали Style, вбили текст в Lyrics.
-
Сгенерировали несколько версий.
-
Смонтировали (возможно без Intro, Bridge, Ending) из них песню (без мастеринга).
-
Отрезали кусочек (до 60 сек), после которого вам нужно сыграть инструментальную часть (Bridge, Ending).
-
Снова занялись монтажом уже целой песни — добавили инструментальные части (без мастеринга).
-
Разделили вокал и инструментал.
-
Спели под минус или плюс.
-
Собрали свой вокал с инструменталом и отмастерили.
*** Suno в режиме загрузки части песни в Audio Input довольно часто путает структуру — дважды повторяет 2-й куплет, меняет/смешивает слова Verse и Chorus и т.д. Чтобы Suno не зажевывал первый куплет, я почти всегда в Lyrics перед 1-м куплетом вставлял:
[Instrumental solo]
[Bridge]
Мммммм мммммм
Хотя и это не всегда помогало.
P.S.
-
Пора обновлять теорию слуха? Известно 2-3, Гельмгольц и др.
… В улитке ~20 тыс. волокон, они, покрывая 16Гц- 20кГц, резонируют на своих частотах и передают сигналы в мозг. Слуховые навыки развиваются с взрослением ребенка, а также тренируются сознательно. Человек различает очень тонкие изменения в звуковых сигналах — это исключительно работа мозга, т.е. «нейронки». (… когда М и Ж засыпают рядом, то один может услышать (когда другому лень говорить), что выдох был «на улыбке» — т.е. связки/складки НЕ РАБОТАЛИ и лишь окраска шумового спектра переносила информацию).
Как человек слышит отдельный голос, объект, муз. инструмент в общем звуковом потоке? К примеру, скрипку? Нам не удастся собрать коллекцию всех штрихов, во всех регистрах, тем более, технические параметры, которые могли бы быть измерены, идентифицированный чем-то иным, чем алгоритм ИИ.
Мне кажется, что распознавание звуковых объектов ТОЧНО ТАКОЕ ЖЕ, как и зрительных. (Ученые утверждают, что качество картинки, которое передает сетчатка неважное, хуже современных камер в смартфонах. Картинку ДОРИСОВЫВАЕТ мозг на базе ранее увиденных образов).
Если так, то мозг, фактически, ищет в спектральной картинке, точнее, в «видео ряде», графические образы, на которые был ранее натренирован. И когда находит, то «их усиливает, повышает яркость». Происходит как-бы фокусировка — искомый объект становится «громче», а остальные — «тише». Плюс к этому и «предсказание», «предслышание» на основе прошлого опыта — звук только начинается, а мозг уже знает, как он закончится.
В общем, мне кажется, что благодаря развитию ИИ алгоритмов пришло время разработки новой теории слуха. Юрий Исаков, мой старинный приятель, однокурсник (НГТУ/НЭТИ), член AES, говорит, что что-то подобное уже начинается.
ссылка на оригинал статьи https://habr.com/ru/articles/850156/
Добавить комментарий