アイドルソングを自動生成してみた❷コード進行生成
〈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()
こちらも歌詞と同じ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)を自動生成" になります。
そちらもぜひどうぞ!
参考
この記事が気に入ったらサポートをしてみませんか?