見出し画像

アイドルソングを自動生成してみた❷コード進行生成

〈AI×JapaneseIDOL = AiDolipper〉

コード進行を自動生成してみよう

続いてはコード進行の生成です。
基本的には歌詞と同じような流れで進めていきます。

1. コード進行データを集める (スクレイピング)
2. コードデータを整理する (前処理)

chords = browser.find_elements_by_class_name('cd_fontpos')

コード進行も同じサイトを利用しました。データは同じBiSHの58曲分から取得します。歌詞の時と違う部分は、class_name のみです。


3. 歌詞データを学習させる (LSTM)

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation, LSTM
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.utils import get_file
import numpy as np
import random
import sys

path = '/content/drive/My Drive/AIDOL/chord_list.txt'
text = open(path, "r").read()
#text = text.split()

chars = sorted(list(set(text)))
print('Total chars:', len(chars))

char_indices = dict((c, i) for i, c in enumerate(chars))
indices_char = dict((i, c) for i, c in enumerate(chars))

maxlen = 10
step = 1
sentences = []
next_chars = []

for i in range(0, len(text)-maxlen, step):
   sentences.append(text[i : i+maxlen])
   next_chars.append(text[i+maxlen])

#sentences = sum(sentences, [])

# テキストのベクトル化
X = np.zeros((len(sentences), maxlen, len(chars)), dtype=np.bool)
y = np.zeros((len(sentences), len(chars)), dtype=np.bool)

for i, sentence in enumerate(sentences):
   for t, char in enumerate(sentence):
     X[i, t, char_indices[char]] = 1
   y[i, char_indices[next_chars[i]]] = 1


# モデルを定義する
model = Sequential()
model.add(LSTM(128, input_shape=(maxlen, len(chars))))
model.add(Dense(len(chars)))
model.add(Activation('softmax'))
optimizer = RMSprop(lr=0.01)
model.compile(loss='categorical_crossentropy', optimizer=optimizer)

def sample(preds, temperature=1.0):
   preds = np.asarray(preds).astype('float64')
   preds = np.log(preds) / temperature
   exp_preds = np.exp(preds)
   preds = exp_preds / np.sum(exp_preds)
   probas = np.random.multinomial(1, preds, 1)
   return np.argmax(probas)

for iteration in range(1, 20):
   print()
   print('-' *50)
   print('繰り返し回数: ', iteration)
   model.fit(X, y, batch_size=128, epochs=1)

   random.seed(1)
   start_index = random.randint(0, len(text)-maxlen-1)

   for diversity in [0.2]:
       print()
       print('-----diveristy', diversity)

       generated = ''
       sentence = text[start_index: start_index + maxlen]
       generated += sentence
       print('----- Seedを生成しました: "' + sentence + '"')
       sys.stdout.write(generated)

       for i in range(60):
           x = np.zeros((1, maxlen, len(chars)))
           for t, char in enumerate(sentence):
               x[0, t, char_indices[char]] = 1.

           preds = model.predict(x, verbose=0)[0]
           next_index = sample(preds, diversity)
           next_char = indices_char[next_index]

           generated += next_char
           sentence = sentence[1:] + next_char

           sys.stdout.write(next_char)
           sys.stdout.flush()
       print()

画像1

こちらも歌詞と同じLSTMで生成していきます。
先頭コードを C, G, D の3パターンで生成してみます。
さてコード進行は...


C →

C / G / F / G / F / G / Em / Am / F / G / C / F / G / Em / Am / F / G / C / Am
C / G / F / G / Am / F / G / C / Am / F / G / C / Am / F / G / C / G / Am / G
C / G / F / FonG / FonG / Em / EonG# / A / A / EonG# / A / B / C#m

G →

G / A / D / F# / G / F# / Bm / A / Bm / G / F#m / Bm / E / D / E / C#m
G / A / D / AonC# / D / E / F#m / AonC# / D / E / F#m / A / Bm / G / D
G / A / D / A / Bm / F#m / Bm / G / D / A / Bm / G / D / A / Bm / G / D / A

D →

D / G / C / D / G / F#m / G / F#m / E / F#m / A / Bm / G / A / Bm / A
D / G / C / D / G / F / C / D / G / C / D / G / F / C / D / G / F / C / D / G / C 
D / G / C / D / G / F#m / Bm / A / Bm / G / D / A / Bm / G / D / A / Bm / A


、、、全くわかりません
しかしそれなりに、繰り返しになりすぎることもなく色々なパターンが生成されているように見えます。


4. コード進行を再生させてみよう
次に再生させてみます。

freq_list = {"A" : 442,
           "A#" : 468.2826877,
           "B" : 496.1282254,
           "C"	: 525.6295448,
           "C#" : 556.8851041,
           "D"	: 589.9992155,
           "D#" : 625.0823946,
           "E" : 662.251728,
           "F" : 701.631265,
           "F#" : 743.3524311,
           "G" : 787.5544668,
           "G#" : 834.3848924,
}
import numpy as np
from pychord import Chord
from scipy.io import wavfile
import IPython.display

seconds = 1.2
rate = 44100

def oscillator(frequency, seconds, rate):
   phases = np.cumsum(2.0 * np.pi * frequency / rate * np.ones(int(rate * seconds)))
   return np.sin(phases)

def chord2wave(chordname):
 chord_comps = Chord(chordname).components()

 wave = 0
 for comp in chord_comps:
   freq = freq_list[comp]
   _wave = oscillator(freq, seconds, rate)
   wave += _wave*0.1
 
 wave = (wave * float(2 ** 15 - 1)).astype(np.int16)
 return wave

def chord2melody(chord_list):
 wave_list = np.empty(0).astype(np.int16) #dtype=int16に変更(np.arrayのデフォルトはdtype=int64)

 for chord in chord_list:
   wave_n = chord2wave(chord)
   wave_list = np.hstack([wave_list, wave_n])
   
 filename = chord_list[0]
 wavfile.write("/content/drive/My Drive/AIDOL/chord/{}_melody.wav".format(filename) , rate, wave_list)

音の周波数からコード(和音)をつくります。
コード一つあたり1.2秒にして繋ぎ合わせます。(1.2秒はなんとなくです..)

chord_listC = ["C", "G", "F", "G", "F", "G", "Em", "Am"]
chord_listG = ["G", "A", "D", "F#", "G", "F#", "Bm", "A"]
chord_listD = ["D", "G", "C", "D", "G", "F", "C", "D"]
#Cスタートのコード進行の場合
chord2melody(chord_listC)
filename = chord_listC[0]

IPython.display.Audio("/content/drive/My Drive/AIDOL/chord/{}_melody.wav".format(filename), autoplay=True)

先ほど生成されたコード進行から先頭が C, G, D の3パターンで8コード分をピックアップしました。結果はどうでしょう...


 C コード

Gコード

Dコード


どうでしょうか??けっこういい感じな気がします!
アイドルっぽいかは分かりませんが、それなりに自然なコード進行のように感じます。色んなパターンを量産することで、コレは!というものが見つかるかもしれません。。

ただもしかすると、これどんな組合せでもそれっぽく聞こえるのでは...
今はひとまずそれはヨコに置いておきます...


次回の記事は "メロディデータ(MIDI)を自動生成" になります。
そちらもぜひどうぞ!


参考


この記事が気に入ったらサポートをしてみませんか?