惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Vulnerabilities – Threatpost
L
LINUX DO - 热门话题
H
Hacker News: Front Page
Hacker News - Newest:
Hacker News - Newest: "LLM"
L
Lohrmann on Cybersecurity
Cisco Talos Blog
Cisco Talos Blog
O
OpenAI News
S
Securelist
Security Latest
Security Latest
T
Threat Research - Cisco Blogs
H
Heimdal Security Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Recorded Future
Recorded Future
Microsoft Azure Blog
Microsoft Azure Blog
MyScale Blog
MyScale Blog
Webroot Blog
Webroot Blog
The Hacker News
The Hacker News
Google Online Security Blog
Google Online Security Blog
Latest news
Latest news
N
Netflix TechBlog - Medium
N
News and Events Feed by Topic
D
Docker
D
DataBreaches.Net
A
About on SuperTechFans
T
Tor Project blog
V
V2EX
G
Google Developers Blog
博客园 - Franky
N
News | PayPal Newsroom
T
The Blog of Author Tim Ferriss
I
InfoQ
H
Help Net Security
V2EX - 技术
V2EX - 技术
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
S
Security Affairs
SecWiki News
SecWiki News
The Register - Security
The Register - Security
人人都是产品经理
人人都是产品经理
NISL@THU
NISL@THU
小众软件
小众软件
B
Blog
T
Threatpost
P
Palo Alto Networks Blog
博客园 - 【当耐特】
L
LangChain Blog
AWS News Blog
AWS News Blog
月光博客
月光博客
宝玉的分享
宝玉的分享

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Назирокодил утилиту на Kotlin и JavaScript для создания аккордов в любой тональности
Дмитрий Исанин · 2026-05-27 · via Все публикации подряд на Хабре

Средний

13 мин

6.7K

Пробовали когда-нибудь сочинять свои аккорды на гитаре, укулеле или на клавишных?
Не всякий аккорд подходить к любому другому, так? Например, ре-мажорный аккорд (D) не очень-то ладит с фа-минорным (Fm). То есть просто "слепить" любые аккорды друг с другом в одну последовательность не получится. Тем более, что хочется, чтобы их было больше 2-х. И повторять до бесконечности Am Dm Em не хочется, так?
Для решения этой проблемки написал, используя ИИ, утилиту на JavaScript, доступную по URL:
https://a-kordey.netlify.app

Генератор аккордов. Текстовый веб-интерфейс.

Генератор аккордов. Текстовый веб-интерфейс.

Но сначала по незнанию я сделал это в простом виде на Kotlin :D
Утилиту сможете применить в вашем браузере. Если чешутся руки погенерить аккорды в более хардкорном режиме, мотайте сразу вниз на раздел "Как запустить утилиту Kotlin?"! В три простых действия получите аккорды.

Про путь создания этой утилиты на Kotlin с ИИ и поведаю в этой статье.

Как я озадачился этим?

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

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

Почему именно ИИ+Kotlin?
Дело в том, что по работе у меня в road map (это, кто пока не в курсе, дорожная карта дел на год):

  • освоение ИИ в целях оптимизации тестирования;

  • написание автотестов в проекте на Kotlin.

Надо бы в этом всём прокачаться. Надо-надо... Надо подкачаться, вот и решил написать код по интересующей меня теме муз. гармонии на Kotlin с консультированием в текстовом чате с ИИ.

Как алгоритмизовать муз. гармонию?

Любая музыка - суть последовательность аккордов. Если глубже, то последовательность функциональных ступеней в разных комбинациях, подчинённых законам муз.гармонии, описанных уже давно Хуго Риманом и уточнённых его последователями, но мы не будем в этот раз слишком глубоко нырять в гармонию).

Нам пока достаточно того, что упрощённо музыка - последовательность аккордов, причём неслучайных. Стало быть, чтобы создать музыку нужно эту последовательность создать = сгенерировать.

Первый блин.com

Сперва я составил промпт для текстового ИИ-ассистента (Qwen):

Помоги в простом формате написать программу на Kotlin, которая:

  1. Запрашивает и принимает у пользователя набор аккордов (string-слов) через пробел.

    1. Если пользователь не задаёт, по дефолту используем базовую белоклавишную тональность ля-минор/до-мажор (именно в ней те самые "блатные аккорды"):
      Am Bdim С Dm Em F G.

  2. Уточняет сколько аккордов должно быть в последовательности: 2, 3, 4 или больше (по дефолту - 3)

  3. Выдаёт как финальный результат рандомную последовательность из предложенных пользователем аккордов.

  4. Предлагает повторить сценарий.

ИИ сгенерил код:

import kotlin.random.Random

fun main() {
    while (true) {
        // 1. Получаем аккорды от пользователя
        println("Введите аккорды через пробел (или нажмите Enter для использования по умолчанию):")
        val input = readLine()
        val chords = if (input.isNullOrBlank()) {
            listOf("Am", "Bdim", "C", "Dm", "Em", "F", "G")
        } else {
            input.split(" ").filter { it.isNotEmpty() }
        }

        if (chords.isEmpty()) {
            println("Нет аккордов для обработки. Попробуйте снова.")
            continue
        }

        // 2. Получаем количество аккордов в последовательности
        println("Сколько аккордов в последовательности? (2, 3, 4 или больше, по умолчанию 3):")
        val sequenceLength = try {
            val lengthInput = readLine()
            when {
                lengthInput.isNullOrBlank() -> 3
                else -> lengthInput.toIntOrNull() ?: 3
            }
        } catch (e: NumberFormatException) {
            3
        }

        // 3. Генерируем случайную последовательность
        val randomSequence = (1..sequenceLength).map {
            chords[Random.nextInt(chords.size)]
        }

        // 4. Выводим результат
        println("Случайная последовательность аккордов: ${randomSequence.joinToString(" ")}")

        // 5. Спрашиваем о повторении
        println("Хотите повторить? (y/n):")
        val repeat = readLine()?.lowercase()
        if (repeat != "y" && repeat != "yes") {
            break
        }
    }

    println("Спасибо за интерес к созданию музыки! Впереди всегда много интересного.")
}

Поскольку сначала хотелось прогнать код в браузере онлайн, а не в IDE (среде разработки), попробовал исполнить его в https://play.kotlinlang.org/. Взаимодействия через текстовый диалог, как я задумал, со мной не произошло: этот онлайн-компилятор не рассчитан на вводы пользователя. Зато сработал прописанный в коде механизм предварительно заданных (=дефолтных) значений. Выполнена цель генерации рандомных аккордов из "умолчательного" набора тональности ля-минор.

Вывод онлайн-компилятора. Диалога не получилось, но последовательность аккордов есть!

Вывод онлайн-компилятора. Диалога не получилось, но последовательность аккордов есть!

При уточнении в чате с ИИ, стало ясно, что дело в ограниченности исполнения кода в "песочнице" (play.kotlinlang.org/). ИИ предложил в этом онлайн-компиляторе исполнить упрощённый код:

import kotlin.random.Random

fun main() {
    // Используем стандартный набор аккордов
    val defaultChords = listOf("Am", "Bdim", "C", "Dm", "Em", "F", "G")
    val chords = defaultChords
    
    // Задаем длину последовательности
    val sequenceLength = 3
    
    // Генерируем случайную последовательность
    val randomSequence = (1..sequenceLength).map { 
        chords[Random.nextInt(chords.size)] 
    }
    
    // Выводим результат
    println("Случайная последовательность аккордов: ${randomSequence.joinToString(" ")}")
    
    println("Спасибо за неподдельное любопытство в создании музыки!")
}

Таким образом, вручную меняя значения в функции listOf из инициатора для значения defaultChords, можно без проблем получить желаемую случайную последовательность аккордов средствами Kotlin. Число аккордов в последовательности можно регулировать через значение sequenceLength.

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

Я захотел сделать что-то более универсальное, гибкое и дружелюбное.
И кстати... откуда я взял изначальные аккорды? Почему именно такие? Гитаристы знают, что есть ещё и другие ...

Можно масштабировать этот мой первый опыт для всех тональностей.

Утилита для любой тональности

Ах да, тональностей :D Упоминаю в седьмой раз тональность, но не расшифровываю, что ж...

Тональность - это (опять же упрощённо) набор из 7 нот. Наш с вами друг Иоган Себастьян Бах выбирал определённые 7 нот из 12 нот хроматического ряда. Да-да, нот далеко не 7! 😉 В каждой из тональностей он написал лютейшую красоту, разобранную музыкантами-наследниками на цитаты (см. и слушай "Хорошо темперированный клавир").

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

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

За 2 простых действия выберите любую тональность, известную современной музыке

За 2 простых действия выберите любую тональность, известную современной музыке

Те, кто прошёл музыкальную школу жизни отметят, что таблица упорядочена по родственным тональностям, имеющим одинаковое число знаков при ключе (♯/♭), что достаточно удобненько само по себе.

Как это получилось?

В чате у ИИ я попросил дать возможность пошагово сгенерировать последовательность аккордов:

  1. Составить таблицу тональностей, и словарь их аккордов.

  2. Дать пользователю возможность выбирать тональность и получить набор её базовых аккордов.

  3. Выбрать аккорды и задать число их случайных появлений в последовательности.

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

Тут я включу skip и перемотаю мои мытарства в диалоге с юным ИИ (я, кстати, сменил Qwen на Perplexity), не особо грамотным в муз.теории, а также в тестировании и отладке программы. Покажу вам сразу самую мякотку - готовую программу, которую можно успешно запустить, задав все нужные параметры на https://www.codechef.com/kotlin-online-compiler:

fun main() {
  val app = ChordSequenceGenerator()
  app.run()
}

class ChordSequenceGenerator {

  private enum class RepeatMode {
    SAME_CHORDS,
    SAME_KEY_NEW_CHORDS,
    NEW_KEY,
    EXIT
  }

  fun run() {
    println(
      "=== Генератор последовательностей аккордов ===\n" +
        "В каждой тональности есть по 7 аккордов, связанных музыкльной гармонией.\n" +
        "Давайте сгенерируем случайную последовательность аккордов в любой из тональностей!" +
        "*-dur - мажорные тональности, *-moll - минорные\n"
    )

    while (true) {
      try {
        val key = step1_chooseKey()
        var selectedChords = step2_chooseChords(key)
        var count = step3_askCount()

        while (true) {
          val sequence = generateSequence(selectedChords, count)
          displaySequence(sequence,key)

          when (step4_askRepeatMode()) {
            RepeatMode.SAME_CHORDS -> {
              continue
            }
            RepeatMode.SAME_KEY_NEW_CHORDS -> {
              selectedChords = step2_chooseChords(key)
              count = step3_askCount()
            }
            RepeatMode.NEW_KEY -> {
              break
            }
            RepeatMode.EXIT -> {
              println("Спасибо за использование!")
              return
            }
          }
        }
      } catch (e: Exception) {
        println("Ошибка: ${e.message}")
        if (!step5_askRepeatAfterError()) break
        println()
      }
    }

    println("Спасибо за использование!")
  }

  private fun step1_chooseKey(): String {
    println("Шаг 1: Выбор тональности")
    println()

    println("    № | Мажор (M)       | минор (m)       | Знаки альтерации")
    println("------|-----------------|-----------------|-----------------")

    val majors = mutableListOf<String>()
    val minors = mutableListOf<String>()

    Vocabulary.keyPairs.forEachIndexed { idx, (maj, min) ->
      majors.add(maj)
      minors.add(min)

      val sigMaj = Vocabulary.keySignatures[maj] ?: "?"
      val numMaj = idx + 1

      println(
        "${numMaj.toString().padStart(5)} | ${maj.padEnd(15)} | " +
          "${min.padEnd(15)} | $sigMaj"
      )
    }

    print("\nВведите номер строки: ")
    val choice = readLine()?.toIntOrNull()

    if (choice != null && choice in 1..majors.size) {
      val index = choice - 1
      print("Мажор (M) или минор (m): ")
      val type = readLine()?.lowercase()?.trim()

      return if (type == "m" || type == "минор" || type == "moll") {
        minors[index]
      } else {
        majors[index]
      }
    }

    println("Неверный выбор, используется C-dur")
    return "C-dur"
  }

  private fun step2_chooseChords(key: String): List<String> {
    val availableChords = Vocabulary.allChords[key]
      ?: error("Неизвестная тональность: $key")

    println("\nШаг 2: Доступные аккорды выбранной тональности: $key")
    availableChords.forEachIndexed { index, chord ->
      println("${index + 1}. $chord")
    }

    print("Введите номера аккордов через пробел (Enter для всех): ")
    val input = readLine()?.trim()

    if (input.isNullOrBlank()) {
      return availableChords
    }

    val selected = mutableListOf<String>()
    input.split("\\s+".toRegex())
      .forEach { num ->
      val index = num.toIntOrNull()
      if (index != null && index in 1..availableChords.size) {
        selected.add(availableChords[index - 1])
      }
    }

    return if (selected.isEmpty()) availableChords else selected.distinct()
  }

  private fun step3_askCount(): Int {
    println("\nШаг 3: количество аккордов в последовательности")
    print("Сколько аккордов в последовательности? (по умолчанию 4): ")
    val input = readLine()?.trim()
    return input?.toIntOrNull() ?: 4
  }

  private fun generateSequence(chords: List<String>, count: Int): List<String> {
    return (1..count).map { chords.random() }
  }

  private fun displaySequence(sequence: List<String>, key: String) {
    println("\n=== Последовательность в $key, которую мы заслужили ===")
    sequence.chunked(4).forEach { chunk ->
      println(chunk.joinToString(" "))
    }
  }

  private fun step4_askRepeatMode(): RepeatMode {
    println("\nЕщё?")
    println("1 — сгенерировать ещё раз с теми же аккордами")
    println("2 — выбрать другие аккорды в той же тональности")
    println("3 — выбрать новую тональность")
    println("4 — выйти")
    print("Ваш выбор: ")

    return when (readLine()?.trim()) {
      "1" -> RepeatMode.SAME_CHORDS
      "2" -> RepeatMode.SAME_KEY_NEW_CHORDS
      "3" -> RepeatMode.NEW_KEY
      "4" -> RepeatMode.EXIT
      else -> RepeatMode.EXIT
    }
  }

  private fun step5_askRepeatAfterError(): Boolean {
    println("\nХотите попробовать снова? (y/n): ")
    val input = readLine()?.lowercase()?.trim()
    return input == "y" || input == "yes" || input == "д" || input == "да"
  }
}

object Vocabulary {

  val keySignatures = mapOf(
    // мажоры
    "C-dur"     to "0",
    "G-dur"     to "1♯",
    "D-dur"     to "2♯",
    "A-dur"     to "3♯",
    "E-dur"     to "4♯",
    "B-dur"     to "5♯",
    "C♭-dur"    to "7♭",
    "F♯-dur"    to "6♯",
    "G♭-dur"    to "6♭",
    "D♭-dur"    to "5♭",
    "C♯-dur"    to "7♯",
    "A♭-dur"    to "4♭",
    "E♭-dur"    to "3♭",
    "B♭-dur"    to "2♭",
    "F-dur"     to "1♭",

    // миноры
    "A-moll"    to "0",
    "E-moll"    to "1♯",
    "B-moll"    to "2♯",
    "F♯-moll"   to "3♯",
    "C♯-moll"   to "4♯",
    "G♯-moll"   to "5♯",
    "A♭-moll"   to "7♭",
    "E♭-moll"   to "6♭",
    "D♯-moll"   to "6♯",
    "B♭-moll"   to "5♭",
    "A♯-moll"   to "7♯",
    "F-moll"    to "4♭",
    "C-moll"    to "3♭",
    "G-moll"    to "2♭",
    "D-moll"    to "1♭"
  )

  val keyPairs = listOf(
    "C-dur" to "A-moll",
    "G-dur" to "E-moll",
    "D-dur" to "B-moll",
    "A-dur" to "F♯-moll",
    "E-dur" to "C♯-moll",
    "B-dur" to "G♯-moll",
    "C♭-dur" to "A♭-moll",
    "G♭-dur" to "E♭-moll",
    "F♯-dur" to "D♯-moll",
    "C♯-dur" to "A♯-moll",
    "D♭-dur" to "B♭-moll",
    "A♭-dur" to "F-moll",
    "E♭-dur" to "C-moll",
    "B♭-dur" to "G-moll",
    "F-dur" to "D-moll"
  )

  val allChords = mapOf(
    "C-dur" to listOf("C", "Dm", "Em", "F", "G", "Am", "B°"),
    "G-dur" to listOf("G", "Am", "Bm", "C", "D", "Em", "F♯°"),
    "D-dur" to listOf("D", "Em", "F♯m", "G", "A", "Bm", "C♯°"),
    "A-dur" to listOf("A", "Bm", "C♯m", "D", "E", "F♯m", "G♯°"),
    "E-dur" to listOf("E", "F♯m", "G♯m", "A", "B", "C♯m", "D♯°"),
    "B-dur" to listOf("B", "C♯m", "D♯m", "E", "F♯", "G♯m", "A♯°"),
    "F♯-dur" to listOf("F♯", "G♯m", "A♯m", "B", "C♯", "D♯m", "E♯°"),
    "C♯-dur" to listOf("C♯", "D♯m", "E♯m", "F♯", "G♯", "A♯m", "B♯°"),
    "C♭-dur" to listOf("C♭", "D♭m", "E♭m", "F♭", "G♭", "A♭m", "B♭°"),
    "G♭-dur" to listOf("G♭", "A♭m", "B♭m", "C♭", "D♭", "E♭m", "F°"),
    "D♭-dur" to listOf("D♭", "E♭m", "Fm", "G♭", "A♭", "B♭m", "C°"),
    "A♭-dur" to listOf("A♭", "B♭m", "Cm", "D♭", "E♭", "Fm", "G°"),
    "E♭-dur" to listOf("E♭", "Fm", "Gm", "A♭", "B♭", "Cm", "D°"),
    "B♭-dur" to listOf("B♭", "Cm", "Dm", "E♭", "F", "Gm", "A°"),
    "F-dur" to listOf("F", "Gm", "Am", "B♭", "C", "Dm", "E°"),

    "A-moll" to listOf("Am", "B°", "C", "Dm", "Em", "F", "G"),
    "E-moll" to listOf("Em", "F♯°", "G", "Am", "Bm", "C", "D"),
    "B-moll" to listOf("Bm", "C♯°", "D", "Em", "F♯m", "G", "A"),
    "F♯-moll" to listOf("F♯m", "G♯°", "A", "Bm", "C♯m", "D", "E"),
    "C♯-moll" to listOf("C♯m", "D♯°", "E", "F♯m", "G♯m", "A", "B"),
    "G♭-moll" to listOf("G♭m", "A♭°", "B♭", "C♭m", "D♭m", "E♭", "F"),
    "D♭-moll" to listOf("D♭m", "E♭°", "F", "G♭m", "A♭m", "B♭", "C"),
    "A♭-moll" to listOf("A♭m", "B♭°", "C♭", "D♭m", "E♭m", "F", "G"),
    "E♭-moll" to listOf("E♭m", "F°", "G♭", "A♭m", "B♭m", "C", "D"),
    "D♯-moll" to listOf("D♯m", "E♯°", "F♯", "G♯m", "A♯m", "B♭", "C♯"),
    "B♭-moll" to listOf("B♭m", "C°", "D♭", "E♭m", "Fm", "G", "A"),
    "A♯-moll" to listOf("A♯m", "B♯°", "C♯", "D♯m", "E♯m", "F♯", "G♯"),
    "F-moll" to listOf("Fm", "G°", "A♭", "B♭m", "Cm", "D", "E"),
    "C-moll" to listOf("Cm", "D°", "E♭", "Fm", "Gm", "A♭", "B♭"),
    "G-moll" to listOf("Gm", "A°", "B♭", "Cm", "Dm", "E♭", "F"),
    "D-moll" to listOf("Dm", "E°", "F", "Gm", "Am", "B♭", "C")
  )
}

Как запустить утилиту?

Программу выше можно скопировать и вставить в левое поле интерфейса codechef (1), ...

Собственно запуск происходит по нажатии на синюю кнопку Run.

Параметры для запуска можно задать в правом окне (2): каждой параметр (13, m и т.д.) на отдельной строке представляют собой ответы на вопросы из программы, где строки пронумерованы по порядку ввода:

Шаг 1: Выбор тональности:

  1. Введите номер строки:
    в примере 13

  2. Мажор (M) или минор (m):
    в примере m
    (если ничего не задать, оставив строку пустой будет Мажор)

    Шаг 2: Доступные аккорды выбранной тональности:
  3. Введите номера аккордов через пробел (Enter для всех):
    в примере 1 2 3 4 5 6 7 (но лучше для минора 2-ую ступень не выбирать ;) )
    (если ничего не задать, оставив строку пустой будут все 7 аккордов)

    Шаг 3: количество аккордов в последовательности
  4. Сколько аккордов в последовательности? (по умолчанию 4):
    в примере ничего не задано, просто по Enter оставил строку пустой.

  5. Дальше блок повторных вводов: там пока имеет смысл вводить только повторные генерации в той же тональности через ввод "1". Я 3 раза ввёл "1" в своём примере и получил 3 последовательности.

    Входные параметры (Input) для взаимодействия с программой.

    Входные параметры (Input) для взаимодействия с программой.

Результат смотреть в нижней правой части экрана. Там можно промотать.

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

=== Последовательность в C-moll, которую мы заслужили ===
E♭ Fm E♭ Cm

Ещё?
1 — сгенерировать ещё раз с теми же аккордами
2 — выбрать другие аккорды в той же тональности
3 — выбрать новую тональность
4 — выйти

...
=== Последовательность в C-moll, которую мы заслужили ===
B♭ Cm Gm E♭

...

=== Последовательность в C-moll, которую мы заслужили ===
E♭ A♭ E♭ Gm

Умудрённые музыканты увидят, что первую из последовательностей утилита даже кононично закончила на тоническом трезвучии (T). Совпадение? В случае с утилитой Kotlin точно - да!

Но для веб-версии я прописал базовую логику - функциональную теорию из муз. гармонии.

Как результат генерации выглядит в UI веб-приложения на javaScript

Как результат генерации выглядит в UI веб-приложения на javaScript

Протестировал: попробовал сыграть все последовательности на гитаре.
Всё звучит классно, даже уменьшённые (dim) аккорды! 🤘 🤘 🤘
Можно развивать последовательности, меняя ритмику, длительности игры аккорда и даже сам аккорд: был Xm - а стал Xm7 или Xm maj7 или Xsus2/4 или Xm9 или Xaug и т.д.).

Диезы и бемоли не пугают прошаренных гитаристов, ведь последние знают, что:

  • аппликатуры гитарного аккорда с бемолем (♭) левее на 1 лад своего "обычного" представления на грифе:
    например, E♭ ← E.

  • аппликатуры с диезом (♯) - правее на 1 лад:
    например, Em → E♯m.

Пути развития утилиты

  1. Чтобы сделать утилиту user-friendly, переписать её с Kotlin на JavaScript ✅
    https://a-kordey.netlify.app/

  2. Разместить в GitHub ✅
    https://github.com/VedaIsanin/A-kordey/

  3. Добавить строгие ограничения по законам классической муз. гармонии ✅
    В отдельной статье напишу подробней про трудности и реализацию логики в JavaScript с ассистированием ИИ (Perplexity. GigaChat).

  4. Можно добавить и табулатуры аккордов, и иные представления аккордов

  5. Хотел подарить вам эту утилиту в виде Telegram-бота
    Научился разворачивать, подтягиваю зависимости либы com.github.kotlintelegrambot% через gradle и даже запустил бота с токеном от @BotFather из Телеграм, но, увы, сейчас Телега движется туго, надо как-то через прокси или не с российским IP разворачивать удалённо на сервере, поэтому реализация через ТГ-бот отложена.
    Бот в Макс только через ЮЛ, у меня только самозанятость оформлена пока.

Как использовать

  1. Индивидуальная практика в музыке. На вашем музыкальном инструменте сыграйте все(!) возможные аккорды - получите опыт и навык и хвастайтесь друзьям ачивкой!

    1. Можно просто потыкать, погенерить цепочки аккордов и поиграть. Аккорды, что пока не знаете, прогуглите и сможете играть. Это такой универсальный тренажёр в том числе: задаёт задачки по уровню возрастания: 1-8, 9-15 уже будет легко двигаться.

  2. Практика в коллективе. Сыграйте с вашим рок-бэндом, докрутите последовательность, как вам надо - и играйте новые песни с вашими аккордами, ведь мне они не принадлежат, музыка - общее достояние.

  3. Можете "докрутить" сам код утилиты и сделать вашу утилиту, которая, например,

    1. будет генерировать последовательности по строгим законам гармонии (T-S-D) или допускать, как в блюзе нарушения строгости (T-D-S), ✅

    2. будет давать некие отклонения или модуляции из тональности в тональность.

    3. Будет транспонировать цепочку сгенерированных аккордов (=переводить в другую тональность)

    4. ... Ещё масса вариантов. Делитесь вашими идеями, и успехов в их реализации!

Желаю интересных генераций,
а также хорошей, неслучайной и - главное - качественной музыки!


Дмитрий Исанин

Тестировщик ПО, сонграйтер и исполнитель, изучаю и применяю ИТ и ИИ для создания песен и музыки, а также озвучки

Про то, как делаю музыку в последние 2 года читайте статьи на Хабре:
https://habr.com/ru/users/Dmitry_Isanin/articles/,-
среди которых "Почему я выбрал Suno AI для создания мемного альбома «Вася Тестировщик»?"

Слушайте на всех популярных площадках мой альбом в духе хип-хопа
"Лоскутное одеяло": https://band.link/penchwork

А также альбом с утяжелённым звучанием гитар
"Хлад металлов": https://band.link/hlad_metallov

Также я публикуюсь и в других местах:
ВК: https://vk.com/dmisanin
Max: https://max.ru/join/eplS_t3xAWUrdsb52ojWxP3_gc1mbE3pE6c-Jv2-eE8
Telegram: https://t.me/shine_sean (Дзен прицепом)
Suno: https://suno.com/@dmisanin3
Sferoom: https://lk.sferoom.space/artists/117182/feed
Boosty: https://boosty.to/dmisanin (тут можно задонатить)
YouTube: https://www.youtube.com/@dmisanin (не забудьте про VPN)