[Work/Class/IPythonでアルゴリズム作曲とAI作曲/ml]

非対話型遺伝的アルゴリズムでフレーズを生成

コード例

1つ目のセル.配列からSMFを生成する関数を定義している.

# 必要なライブラリの読み込み.これは以前と同じ.
from mido import MidiFile, MidiTrack, MetaMessage, Message, bpm2tempo
from google.colab import files

def generate_smf_drum(hat, snare, bassdrum, file_name, index):
  # ドラムパターンをMIDIにする部分を関数定義したもの
  from mido import MidiFile, MidiTrack, MetaMessage, Message, bpm2tempo
  
  midi_file = MidiFile()
  
  drum_track = MidiTrack()
  
  drum_track.append(MetaMessage('midi_port', port=1))
  drum_track.append(MetaMessage('set_tempo', tempo=bpm2tempo(120)))
  
  time_from_previous_message = 0
  for i in range(16):
    # Closed Hi-hat
    drum_track.append(Message('note_off', note=42, time=time_from_previous_message))
    # Snare
    drum_track.append(Message('note_off', note=38, time=0))
    # Bass Drum
    drum_track.append(Message('note_off', note=35, time=0))
    
    if(hat[i] == 1):
      drum_track.append(Message('note_on', note=42, velocity=80, time=0, channel=9))
    if(snare[i] == 1):
      drum_track.append(Message('note_on', note=38, velocity=80, time=0, channel=9))
    if(bassdrum[i] == 1):
      drum_track.append(Message('note_on', note=35, velocity=80, time=0, channel=9))
    time_from_previous_message = int(0.25 * 480)

  #MIDI file (Standard MIDI File)の作成
  midi_file.tracks.append(drum_track)
  midi_file.save(file_name + str(index) + '.mid')
  files.download(file_name + str(index) + '.mid')

2つ目のセル.非対話型遺伝的アルゴリズムを回す.

# GA
import numpy as np

tournament_unit_size = 4
generation_max = 10
population_size = 200
beat_base = 16 # 1小節をいくつに区切るか.16だと16分音符
num_of_instrument = 3 # 楽器の数.ドラムの場合

# 集団の初期化
population = np.array([[np.random.randint(0, 2) for i in np.arange(beat_base * num_of_instrument)] for j in np.arange(population_size)])
print(population.shape)

# MainLoop
for generation in range(generation_max):
  new_generation = np.zeros((population_size, beat_base * num_of_instrument), dtype=np.uint8)
  print(new_generation.shape)
  
  # 点数付け(正解からの距離)
  # ここではフルビットを正解とする
  sum_array = np.sum(population, axis=1)
  
  # 現在の点数を表示
  print('Max Value in current generation:', np.max(sum_array))
  best_index = np.argmax(sum_array)
  
  # ドラムパターンを作って書き出す
  print(population[best_index][0:16])
  hat = list(population[best_index][0:16])
  snare = list(population[best_index][16:32])
  bassdrum = list(population[best_index][32:48])
  generate_smf_drum(hat, snare, bassdrum, 'ga_generating_drum', generation)
  
  
  # 子世代を作る
  for i in range(int(population_size / 2)):
    
    # 2つの子につき2つの親を選ぶ
    parent_indexes = np.zeros(2, dtype=np.uint8)
    for j in range(2):
      tournament_max_value = 0
      
      # tournament_unit_size分だけ個体を持ってきて
      # 一番高い点数のものを親とする
      # 親2つ分
      for k in range(tournament_unit_size):
        index = np.random.randint(population_size)
        if sum_array[index] > tournament_max_value:
          tournament_max_value = sum_array[index]
          parent_indexes[j] = index

    # 一点交叉
    # 交叉ポイントをランダムに決めて
    cutting_point = np.random.randint(beat_base * num_of_instrument)
    for j in range(beat_base * num_of_instrument):
      # 単純に入れ替えた子供を作る.2つの親から2つの子.
      if j < cutting_point:
        new_generation[i][j] = population[parent_indexes[0]][j]
        new_generation[i+1][j] = population[parent_indexes[1]][j]
      else:
        new_generation[i+1][j] = population[parent_indexes[0]][j]
        new_generation[i][j] = population[parent_indexes[1]][j]

  # 集団サイズ分子供ができたら
  # 新しく作った子世代で親世代を置き換え 
  population = new_generation