- BrainTools - https://www.braintools.ru -

Назирокодил утилиту на Kotlin для создания аккордов в любой тональности

Хотел бы показать, как быстра может быть эволюция [1] от идеи до готового кода с небездумным применением искусственного интеллекта [2] (ИИ).

По работе у меня в road map (это, кто пока не в курсе, дорожная карта дел на год)

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

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

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

Давно я чаял идею написать утилиту для создания музыки, т.к. уж 20 лет как ею занимаюсь и ей обучаюсь любительски то тут, то там.

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

Нам пока достаточно того, что упрощённо музыка – последовательность аккордов. Стало быть, чтобы создать музыку нужно эту последовательность создать = сгенерировать.
Спойлер:
Всё получилось. Утилиту сможете легко применить в вашем браузере. В статье описал как. Если чешутся руки погенерить аккорды мотайте сразу вниз на раздел “Как запустить утилиту?! В три простых действия получите аккорды и удовольствие :)

Первый блин.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/ [3]. Взаимодействия через текстовый диалог, как я задумал, со мной не произошло: этот онлайн-компилятор не рассчитан на вводы пользователя. Зато сработал прописанный в коде механизм предварительно заданных (=дефолтных) значений. Выполнена цель генерации рандомных аккордов из “умолчательного” набора тональности ля-минор.

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

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

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

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. Однако в целом заставлять вчитываться в код – это не очень-то дружелюбно к пользователю, если он не знаком с программированием или алгоритмами. Не всем своим приятелям-музыкантам я могу дать в пользование такую утилиту. Вместе с ней нужно высылать пояснительные бригады, а их не напасёшься.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 [5] (1), …

Назирокодил утилиту на Kotlin для создания аккордов в любой тональности - 3

Параметры для запуска можно задать в правом окне (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). Совпадение? В этом случае точно – да!

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

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

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

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

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

  1. Надо в github такое оформлять, знаю. Пока ещё не сделал, ибо новичок в создании кода с нуля. Ещё бы и красиво портянку кода на классы разделить. Когда сделаю, обновлю эту статью.

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

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

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

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

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

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

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

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

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

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

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


Назирокодил утилиту на Kotlin для создания аккордов в любой тональности - 5

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

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

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

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

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

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

Автор: Dmitry_Isanin

Источник [19]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/30859

URLs in this post:

[1] эволюция: http://www.braintools.ru/article/7702

[2] интеллекта: http://www.braintools.ru/article/7605

[3] https://play.kotlinlang.org/: https://play.kotlinlang.org/

[4] опыт: http://www.braintools.ru/article/6952

[5] https://www.codechef.com/kotlin-online-compiler: https://www.codechef.com/kotlin-online-compiler

[6] @BotFather: https://www.braintools.ru/users/BotFather

[7] https://habr.com/ru/users/Dmitry_Isanin/articles/: https://habr.com/ru/users/Dmitry_Isanin/articles/

[8] “Почему я выбрал Suno AI для создания мемного альбома «Вася Тестировщик»?”: https://habr.com/ru/articles/984790/

[9] https://band.link/penchwork: https://band.link/penchwork

[10] https://band.link/hlad_metallov: https://band.link/hlad_metallov

[11] https://vk.com/dmisanin
Max: https://vk.com/dmisaninMax

[12] https://max.ru/join/eplS_t3xAWUrdsb52ojWxP3_gc1mbE3pE6c-Jv2-eE8
Telegram: https://max.ru/join/eplS_t3xAWUrdsb52ojWxP3_gc1mbE3pE6c-Jv2-eE8Telegram

[13] https://t.me/shine_sean : https://t.me/shine_seanSuno

[14] https://suno.com/@dmisanin3
Sferoom: https://suno.com/@dmisanin3Sferoom

[15] https://lk.sferoom.space/artists/117182/feed
: https://lk.sferoom.space/artists/117182/feedYouTube

[16] Boosty: https://www.youtube.com/@dmisaninBoosty

[17] https://boosty.to/dmisanin: https://boosty.to/dmisanin

[18] задонатить: https://boosty.to/dmisanin/donate

[19] Источник: https://habr.com/ru/articles/1037586/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1037586

www.BrainTools.ru

Rambler's Top100