Пользователь
0,1
рейтинг
4 июня 2013 в 02:25

Разработка → Пишем музыку с помощью PHP перевод

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

Например, такую:

сгенерированная мелодия

Вы видите мелодию, сгенерированную на PHP. Да, конечно, эта мелодия не из самых запоминающихся, но она не безобразна. И что удивительно, код, генерирующий эту последовательность нот довольно короткий.

Итак, что происходит? Если коротко, музыкальные данные анализируются скриптом чтобы «научиться» понимать, какие интервалы составляют приятные мелодии. Затем скрипт создаёт новую композицию, выбирая высоты, основываясь на своих знаниях. Говоря технически, скрипт вычисляет карту вероятностей мелодичных интервалов и применяет Марковский процесс для генерации новой последовательности.

Стоя на плечах гигантов


Сочинение музыки не происходит из ничего. Бах был впечатлён Букстехуде и Вивальди. Шопен был под влиянием Листа и Вагнера. Моцарт и Гайдн учили Бетховена. Одна и та же фраза мелодии может быть найдена в различных частях работы. Орфей и Эвридика Глюка и мотив гимна Non Dignus содержат общие музыкальные фразы.

общие музыкальные фразы в Орфее и Эвридике Глюка и мотиве гимна Non Dignus

Если вы попросите PHP сочинять вслепую, результаты получатся не очень. В качестве доказательства приведу мелодию, составленную путём сопоставления случайных значений успешных вызовов функции rand() с нотами.

случайная мелодия

Если вы не владеете техникой додекафонии, то для вдохновения и понимания лучше отталкивайтесь от классических композиций.

Для этого я сперва переписал мелодию нескольких частей музыкальных произведений с помощью научной системы обозначения высоты звука. Я не заморачивался с длительностью нот. Вместо этого я сосредоточился на самой высоте звука. «До» первой октавы из привычных нот вводилось как C4 (где C — название ноты, 4 — октава), нота на пол тона выше 'C#4', ещё на пол тона выше D4, и так далее, в результате мелодия (здесь показаны первые 8 тактов Tantum Ergo) записывалась так:

первые 8 тактов из Tantum Ergo, Bottazzo

A4 C5 G4 A4 G4 A#4 D5 A4 B4 A4 C5 D5 C5 G4 B4 B4 C5

Теперь у меня появилась последовательность нот, которая легко парсилась и которую я мог также легко использовать для анализа. Например, какая нота вероятнее всего следует за A4?

A4 C5 G4 A4 G4 A#4 D5 A4 B4 A4 C5 D5 C5 G4 B4 B4 C5

что в итоге даёт:

C5 G4 B4 C5

Видим, что в 50% случаев следом идёт нота C5, в 25% случаев это C4, и ещё в 25% случаев это B4.

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

Взывая к Доктору Маркову


Мы знакомы с детерминистическими системами — системами, где одни и те же входные данные всегда возвращают неизменный результат. Сложение — это детерминистическая функция, в которой 2 и 4 на входе всегда дадут 6. Стохастические системы, напротив, ведут себя с определенным уровнем случайности. Одни и те же входные данные могут привести к совершенно разным результатам, например с функцией array_rand(). В сочинении музыки присутствует элемент случайности. Будь это не так, любой композитор, встретив ноту F4 одинаково её продолжит, создавая очередной хит для ненасытной династии эстрадных певцов. Но случайность всё же вносит свои коррективы, несмотря на то что на подсознательном уровне композитор понимает, какие сочетания приятны слуху.

Ярким примером стохастической системы, которая также имеет отношение к скрипту, сочиняющему музыку — Марковский процесс (названный в честь математика Андрея Маркова который не только изучил его, но и отростил замечательную бороду). Вот как поясняет Ник Дидковский:

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

Традиционный пример использующийся для иллюстрации концепции — граф прогнозирования погоды. Предположим, следующий день после солнечного имеет 90% вероятность также быть солнечным, а тот, который следует после дождливого, имеет 50% вероятность быть дождливым. Граф будет выглядеть следующим образом:

Граф Маркова Солнечно/дождливо

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

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

Граф ноты A4

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

Роботы композиторы (сингулярность уже близко)


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

<?php
namespace Zaemis;

class Composer
{
    private $pitchProb;

    public function __construct() {
        $this->pitchProb = [];
    }

    public function train($noteData) {
       $numNotes = count($noteData);
       for ($i = 0; $i < $numNotes - 1; $i++) {
           $current = $noteData[$i];
           $next = $noteData[$i + 1];
           $this->pitchProb[$current][] = $next;
       }
   }

   public function compose($note, $numNotes) {
       $melody = [$note];
       while (--$numNotes) {
           $i = array_rand($this->pitchProb[$note], 1);
           $note = $this->pitchProb[$note][$i];
           $melody[] = $note;
       }
       return $melody;
   }
}


<?php
require_once '../include/Zaemis/Composer.php';
use Zaemis\Composer;

$noteData = trim(file_get_contents('../data.txt'));
$noteData = explode(' ', $noteData);

$c = new Composer();
$c->train($noteData);
$melody = $c->compose($_GET['note'], $_GET['count']);

echo '<img src="img/notes/clef.png" alt="Treble Clef">';
foreach ($melody as $note) {
    echo '<img src="img/notes/' . urlencode($note) . '.png" alt="' .
        $note . '">';
}


Процесс обучения описан в методе train(), принимающем на вход массив нот для обучения (закодированная мелодия разделена пробелами). Код прост, быстр и выглядит топорно; Ноты хранятся в двумерном массиве с вероятностью косвенно подразумевающейся самим количеством элементов.

В заполненном состоянии, массив будет выглядеть примерно так:

array(9) {
  ["A4"]=> array(13) {
    [0]=> string(2) "C5"
    [1]=> string(2) "G4"
    [2]=> string(3) "A#4"
    [3]=> string(2) "C5"
    [4]=> string(2) "B4"
    [5]=> string(3) "A#4"
    [6]=> string(2) "G4"
    [7]=> string(2) "A4"
    [8]=> string(2) "D5"
    [9]=> string(2) "G4"
    [10]=> string(2) "C5"
    [11]=> string(2) "C5"
    [12]=> string(2) "G4"
  }
  ["C5"]=> array(11) {
...


Глядя на данные, мы видим, что случайно выбранная нота, следующая после A4 имеет примерно 31% вероятность стать С5 т.к. 4 из 13 элементов списка содержат это значение. (Да, я знаю, лучше оперировать вероятностями, чем держать в памяти список всех возможных нот в композиции, но я сделал это намеренно, чтобы лучше проиллюстрировать идею.)

Метод compose() инкапсклирует логику генерации мелодичной последовательности. Получая на входе желаемую продолжительность последовательности нот, следующих за начальной, метод случайно выбирает значение нот из массива, пока не получит их столько, сколько нужно.

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

Быстрее, Выше, Сильнее!


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

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

Помимо очевидных изменений, таких как изменения высоты матрицы для поддержания вероятностей, какие бы вы внесли улучшения? Может быть, заменить этот наивный подход на совершенно другой механизм для анализа музыки? Анализировать входные данные из файлов MIDI? Что необходимо для выявления гармонии? Как насчёт аккордов? Длительности нот? «Почерка» композиторов? Может ли ваш скрипт обучаться на основе анализа собственных хороших мелодий и использовать их для пополнения своей базы знаний? Каким образом вы могли бы перераспределить сэмплы для создания новых работ?

Мне было бы интересно читать ваши комментарии о результатах экспериментов в области искусственного интеллекта управляемого сочинения музыки.



От переводчика: Очень хотелось послушать мелодии, которые получаются в результате. savostin предложил выводить в MIDI используя библиотеку на PHP, идея мне понравилась. А сами мелодии, как мне показалось, проще всего брать из интернета в формате RTTTL. На скорую руку набросал пример из статьи, но заточенный под работу с RTTTL и подключил вывод в MIDI. Теперь каждый сможет оценить работу, проделанную автором статьи.

Демо
Перевод: Timothy Boronczyk
Желнин Евгений @pewpew
карма
8,5
рейтинг 0,1
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (40)

  • +24
    А послушать-то как? :(
  • +3
    Статья интересная, но не хватает ссылок на сгенерированные аудиозаписи.
    И не совсем понятно, зачем анализировать последовательность нот в произведениях, если все уже давным-давно проанализировано.
    • –1
      > анализировать последовательность нот в произведениях, если все уже давным-давно проанализировано.

      Вы дали ссылку на гамму. Если вы проанализируете гамму, то цепь Маркова будет вам генерировать только ту же самую гамму.
      • 0
        Да-да, гамма — она такая.

        В гамме До-мажор после До идет Ре и никак иначе. Посему вероятность перехода из ноты До в Ре в цепи Маркова будет равна единице. Тут уж ничего не поделаешь. Поэтому анализ гаммы через цепь Маркова даст ту же самую гамму.
  • +3
    Классическая гармония, кстати, может быть описана математическими формулами. Попробуйте написать гармонизатор для существующей мелодии — будет круто.
    • 0
      А потом и транспозицию в разные тональности можно прикрутить!
    • +4
      3 года назад у меня был диплом «Автоматизация Гармонизации Мелодии» — я его защищал и в у универе (информатика), и в консерватории. В универ нес документ, что диплом логичен с музыкальной точки зрения, в консерваторию — что с математической. Весьма удобно было защищать 1 диплом в 2 вузах :) просто с двух разных сторон. Получалось гармонизировать весьма неплохо — сделал интерфейс на канвасе, закодил правила из учебника (Бригадного) и получал несколько вариантов классического решения. Ну а для прослушивания того что получилось генерировал midi.

      Классная статья, спасибо
      • 0
        Программой из диплома не поделитесь? ;)

        Бригадный учебник рулит! На кого учились в консе, и в какой?
        • 0
          Учился в филиале Донецкой консы в Симферополе — скрипка. Учился заочно, чтобы учиться на информатике в ТНУ.

          вот прога, должна работать — bit.ly/1e3KzLf.
          p.s. За код строго не ругайте — первое знакомство с php+js+canvas ;)
  • +3
    Естественно, мы люди и хотим видеть результат в привычной нам нотной записи.

    Я бы сказал, что люди хотят послушать музыку. Честно говоря, не представляю как это сделать на PHP, который мой основной язык за последние 10+ лет, кроме как вникать в подробности музыкальной теории, преобразующей ноты в частоты, и математики, преобразующей частоты в амплитуды, а амплитуды в конкретный звуковой файл.

    И то, мои ранние эксперименты (не на PHP, а на ассемблере) на этом поприще приводили к тому, что математически и алгоритмически преобразование безупречно (не считая погрешности дискретизации на уровне сотых долей Герца и АЧХ тракта), но одноклассники, серьезно музыкой занимающиеся, говорили и о «фальши» и о «неправильном темпе» даже на простейших мелодиях типа «Во поле березка стояла». При этом я не программировал динамик напрямую через порты, а пользовался какой-то (С?)БИС, которая синхронизировалась от кварца.

    P.S. Прошу счесть коммент за конструктивную критику — может вас это натолкнет на мысли как не попасть в тот тупик, в который попал я, и забил на него из-за критики результатов.
    • –1
      > но одноклассники, серьезно музыкой занимающиеся, говорили и о «фальши» и о «неправильном темпе»

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

      Попробуйте в малых пределах рандомизировать частоту, авось, что и выйдет ;)
      • 0
        Под хардварными вы имеете в виду «устаревшие» или различные навороченные звуковые карты, хранящие в ПЗУ сэмплы?

        Да поздно уже пробовать. Я разочаровался в музыкантах — их даже резонанс камертона не убеждал :) А если серьезно, то мои эксперименты в этой области окончились в прошлом тысячелетие, столкнувшись с субъективной критикой, которую объективно не подтверждается.
        • 0
          > Под хардварными вы имеете в виду «устаревшие» или различные навороченные звуковые карты, хранящие в ПЗУ сэмплы?

          под хардварными я имею в виду «живые» инструменты с живым исполнителем, играющим на них.

          > Я разочаровался в музыкантах — их даже резонанс камертона не убеждал :)

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

          > субъективной критикой, которую объективно не подтверждается

          человечество не привыкло к точным звукам. С этим ничего не поделаешь, это как темперированная система, или строение мажора и минора — к ним тоже все привыкли, и их изменение воспринимается с удивлением (и подчас с негативом).
          • 0
            Ну я под «устаревшими» имел в виду «живые» :)
        • +1
          Да очень много тонкостей. Что вы знаете про mel-scale? А, ну да, увеличение частоты в два раза на слух не всегда воспринимается как повышение тона на октаву. А от чего ещё зависит тон, кроме частоты? А от амплитуды зависит. Более интенсивный звук кажется (то есть, воспринимается) более высоким. И вот опа, вы можете подстроить частоту точно, но человеком будет восприниматься как фальшивая нота.

          Я ещё напомню про различные коммы, в том числе, то, что двенадцать квинт != семь октав (как должно было бы быть). В равномерно темперированном строе чуток «смяли» квинту и сделали её слегка не чистой, что вполне можно расслышать, И слышно это как «небольшая фальшь». Хотя частоты точно вычислены по формуле, да.
    • +1
      Для преобразования нот в частоты есть такая формула: .

      Звуковые файлы можно теоретически генерировать на PHP при помощи класса WavFile из Securimage. Но проще всего будет «проигрывать» музыку при помощи очень простого приложения на C, использующего функцию WinAPI Beep(), которая подает сигнал определенной частоты на спикер.
      • 0
        Вот именно функцию аналогичную Beep (правда о Windows тогда речи не было, по крайней мере в моем распоряжении) я использовал. Правда для нот использовал табличные значения для основной октавы и деление/умножение для остальных.
      • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Вот именно эта формула и есть фальшивая.

        Всем известно, что октава — соотношение частот 1:2, а квинта (чистая натуральная) — 3:2. А теперь внимание, найдите проблему.
    • +4
      Тогда уже Midi (PHP или даже JavaScript)
      • 0
        Очень кстати. Я тут набросал пример по мотивам кода из поста.
        Только оперирует с RTTTL — мелодиями.
        Онлайн конвертер RTTTL->MIDI.

        Сами мелодии можно найти в интернете. Их ещё много осталось со времён Nokia 3310.
        Примеры мелодий
        Pacman:d=4,o=5,b=112:32b,32p,32b6,32p,32f#6,32p,32d#6,32p,32b6,32f#6,16p,16d#6,16p,32c6,32p,32c7,32p,32g6,32p,32e6,32p,32c7,32g6,16p,16e6,16p,32b,32p,32b6,32p,32f#6,32p,32d#6,32p,32b6,32f#6,16p,16d#6,16p,32d#6,32e6,32f6,32p,32f6,32f#6,32g6,32p,32g6,32g#6,32a6,32p,32b.6
        LeisureSuit:d=16,o=6,b=56:f.5,f#.5,g.5,g#5,32a#5,f5,g#.5,a#.5,32f5,g#5,32a#5,g#5,8c#.,a#5,32c#,a5,a#.5,c#.,32a5,a#5,32c#,d#,8e,c#.,f.,f.,f.,f.,f,32e,d#,8d,a#.5,e,32f,e,32f,c#,d#.,c#
        Bond:d=4,o=5,b=320:c,8d,8d,d,2d,c,c,c,c,8d#,8d#,2d#,d,d,d,c,8d,8d,d,2d,c,c,c,c,8d#,8d#,d#,2d#,d,c#,c,c6,1b.,g,f,1g.
        Terminator II:d=4,o=5,b=90:32d6,16e6,2f6,8e6,8c6,2f,16p,32d6,16e6,2f6,8e6,8c6,2a6,2g6
        Toccata:d=4,o=5,b=160:16a4,16g4,1a4,16g4,16f4,16d4,16e4,2c#4,16p,d.4,2p,16a4,16g4,1a4,8e.4,8f.4,8c#.4,2d4
        fifth:d=4,o=5,b=63:8P,8G5,8G5,8G5,2D#5
        Flinstones:d=4,o=5,b=40:32p,16f6,16a#,16a#6,32g6,16f6,16a#.,16f6,32d#6,32d6,32d6,32d#6,32f6,16a#,16c6,d6
        Tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,2a,8p,d6,8f6,a6,8g6,8f6,e6,8e6,8c6,e6,8d6,8c6,b,8b,8c6,d6,e6,c6,a,a
        MahnaMahna:d=16,o=6,b=125:c#,c.,b5,8a#.5,8f.,4g#,a#,g.,4d#,8p,c#,c.,b5,8a#.5,8f.,g#.,8a#.,4g,8p,c#,c.,b5,8a#.5,8f.,4g#,f,g.,8d#.,f,g.,8d#.,f,8g,8d#.,f,8g,d#,8c,a#5,8d#.,8d#.,4d#,8d#.

        • +1
          Онлайн конвертер у меня выдает ошибку при попытке воспроизведения мелодий из вашего примера.
          Нашел причину — в конвертере ноты нужно указывать через маленькую латинскую букву(8f6) а у вас в примере — через большую(8F6).
          • 0
            Спасибо. Не только поправил, но и использовал библиотеку, предложенную . Теперь из генерируется MIDI-файл, который можно скачать и послушать.
    • +1
      Зачем делать это на PHP? Можно же взять какую-нибудь MIDI игралку. А Midi — это ноты и есть.

      Я всегда буду читать комментарии, прежде, чем писать свой!
  • +4
    я думаю, прежде чем продолжать, вам надо покурить тональности (мажор / минор) и гаммы… если не забуду вечером кину хорошую книжку, где музыка разбирается с точки зрения физики/математики.
    и попробуйте обращать внимание не на то что «после А4 идет С5» а то что после А4 следующий звук на 3 полутона выше (терция)
    • +3
      кидайте книжку не в личку только, пожалуйста.
      • 0
        С. Попов «Музыкальное и аппликатурное мышление гитариста» Отличная книга по теории музыки.
    • 0
      Кидайте сюда.
    • 0
      мне тоже книжку пожалуйста :)
      • +2
        книжка называется занимательная теория музыки rghost.ru/46507670
    • +2
      Лучше даже так: надо при анализе смотреть не то, какой высоты нота (A4, если примеры выше), а то, какой ступенью она является в используемой тональности — и статистику считать уже относительно ступеней. Например, первая ступень часто встречается после седьмой, второй и пятой.

      Либо, если лень считать ступенями, надо всё приводить к до-мажору/ля-минору (там нет знаков при ключе) и сбор статистики с сочинением вести именно в этой паре беззнаковых тональностей.
  • +1
    А, кстати, почему отбросили («не стали заморачиваться») с длительностью нот?
    Думаю по тому же принципу можно и длительность получить.
    • 0
      Только заметил, что перевод.
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Странно то что в моем анализе большого кол-ва произведений наиболее вероятная нота это — та же самая нота
      • 0
        не странно

        но нужно выбирать не самую вероятную ноту, а случайную, но по неравномерному распределению

        Марковский процесс действительно не подходит. Нота же зависит не только от предыдущих, а ещё и, например, от положения в такте, в квадрате (или другом сегменте, если музыка «неквадратная») и так далее.
  • +1
    Выглядит как обычный n-gram, сомневаюсь, что на выходе будет что-то не раздражающее слух сколько на чем не тренируй.
    Если не тащить PHP, а сразу взять что-то вроде Overtone или athenaCL, то можно сразу работать в предметной области.
  • +1
    Зря вы сделали отсылку к Маркову, и забыли сделать отсылку к Моцарту. У него же есть книжка о том, как сочинять музыку с помощью двух игральных костей. И он даже примеры сочиненных таким образом приводит :)
    • +1
      Вот тут можно сочинить музыку по методу «Музыкальная игра в кости» Моцарта
      sunsite.univie.ac.at/Mozart/dice/#options (очень неплохой результат получается 1 1 если ввести, а если 7 8 мне уже не очень)
  • 0
    Р. Х. Зарипов. Кибернетика и музыка. 1971 год, рассмотрены не только способы генерации мелодии, но и ритма. Электронный вариант найти не могу, в далеком детстве чудом удалось почитать в читальном зале библиотеки.

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