написать впереди
Мне всегда нравилось играть в музыкальные игры, от самого раннего ритм-мастера до cytus2, malody, lanota и так далее. Но музыкальную диаграмму музыкальной игры создать сложно, и ее нужно сопоставлять, настраивать и т. д., что очень хлопотно, поэтому я подумал об использовании ИИ для ее создания.
Поскольку объем данных относительно невелик и не считается помеченными данными, это всего лишь попытка.
(Кстати, обратите внимание на волну на станции B)
Есть много видов музыкальных игр, вот режим падения 4K от Malody, который можно попробовать.
О Малоди
Malody – это аудиосимулятор со множеством режимов. Если вы играли в Rhythm Master, то знаете, что в Rhythm Master есть три режима: 4K, 5K и 6K. Здесь я попробую только режим 4K или режим 4K Mania.
По сравнению с ритм-мастером, Malody не меняет трек, то есть слайдера нет. Но у Malody будет три ставки и четыре ставки, а мастер ритма обычно может играть только двумя пальцами, а режим 4k Malody должен использовать четыре пальца.
В malody есть много ключевых форм, следующие примеры:
Многоуровневый ключ
ключевой разрез
Тип вертикального ключа
Тип ключа лапши
Есть и другие типы ключей, которые здесь не перечислены.
Анализ файла спектра
Карта битов Malody заканчивается суффиксом mcz, что на самом деле является zip-архивом.
После выбора карты ударов и распаковки файла mcz с помощью zip-файла Python вы можете увидеть, что есть три файла.
jpg
фоновое изображение карты битов,mc
Формат - это диаграмма, иogg
это музыка.
Здесь мы в основном смотрим на файл mc.
Этот файл на самом деле в формате json. Преобразуйте его в словарь Python. Вы можете видеть, что этот словарь имеет всего 4 ключа.
mc_data["meta"]
Основная информация о песне
mc_data["time"]
В основном это информация об изменении BPM во времени, потому что у malody переменная скорость, поэтому мы не рассматриваем здесь переменную скорость, просто посмотрите на первую, вы можете видеть, что BPM этой песни равен 180.
mc_data["note"]
Нота — это наш ключ, есть два типа: одна — нота, и длинная нота — длинная полоса.
Вы можете видеть, что если внизу есть эндбит, то есть длинный такт.Давайте посмотрим на этот бит, состоящий из трех чисел, в качестве примера возьмем [27, 2, 4]. 27 представляет собой 28-ю долю (считая с 0), эта доля должна быть рассчитана по BPM, BMP этой песни 180, то есть 180 ударов в минуту, т.е.Секунды в такт, то это 27 отсекунд дов этот раз.
Давайте посмотрим на значение 2 и 4 в этом [27, 2, 4]: последние 4 на самом деле представляют маленькие доли в доле, здесь всего 4 маленьких доли, так что доля делится на четыре части, а 2 — третий тик (считая с 0).
Таким образом, это [27, 2, 4] представляетсекунд в этот момент времени.
mc_data["note"][-1]
В последнем элементе заметки он особенный, он загружает музыку и устанавливает смещение.
offset представляет собой минимальное значение опережения в миллисекундах, чтобы выровнять линию ритма ритма с ритмом музыки. На самом деле это значение выравнивания. Для получения подробной информации см.:воооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооо
Смещение здесь составляет 315 мс, так что предыдущий [27, 2, 4] фактически представляетсекунд этой точки.
mc_data["extra"]
Кажется, это не работает, так что мне все равно.
построение набора данных
Поскольку ИИ должен использоваться в качестве партитуры, часть данных должна быть подготовлена в первую очередь.Здесь в качестве обучающих данных выбраны таблицы партитуры Lv20-Lv25 в более чем 20 malodys (все они очень простые песни).
определение проблемы
Здесь наш вход представляет собой непрерывный звук, а выход — нота из четырех дорожек, так что на самом деле это случай последовательного ввода для последовательного вывода.
построение входных признаков
Основные сведения об извлечении признаков из музыки см. в моей предыдущей статье:Извлечение признаков аудио с помощью Python
Здесь разделение последовательности будет основано на времени, и каждый удар можно разделить на четыре временные точки. То есть в каждом такте не более 4 хитов.
Взяв в качестве примера BMP 180, один удар равен 60/180 = 0,333 секунды. Интервал времени между каждой точкой попадания составляет 0,333/4=0,08333 секунды.
Тогда характеристикой точки попадания является характеристика 0,08333 секунды, которая здесь извлекается с помощью mfcc, а 0,08333 секунды делится на две части, характеристика mfcc извлекается отдельно, а затем объединяется как характеристика звука.
# x为音乐的时域信息,也就是一个列表
# sr为音频的采样频率
# position为第几个打击点
# offset为谱面的偏移
def get_audio_features(x, sr, bpm, position, offset):
one_beat = 60 / bpm
beat = position * one_beat / 4 - offset/1000
start = beat
end = start + one_beat / 8
end2 = start + one_beat / 4
if start < 0:
start = 0
start_index = int(sr * start)
end_index = int(sr * end)
end_index2 = int(sr * end2)
features = []
mfcc1 = librosa.feature.mfcc(y=x[start_index:end_index], sr=sr, n_mfcc=32)
mfcc2 = librosa.feature.mfcc(y=x[end_index:end_index2], sr=sr, n_mfcc=32)
features += [float(np.mean(e)) for e in mfcc1]
features += [float(np.mean(e)) for e in mfcc2]
return features
разделение входной последовательности
Здесь, поскольку оценка будет относительно длинной, будут тысячи очков жизни для суждения, поэтому очки суждения должны быть разделены на 50 очков в каждом раунде.
Выходной формат
Выход можно разделить на 4 типа:
-
- пустой, без удара
-
- обратите внимание, что является точкой атаки
-
- начало длинной ноты, начало длинной ноты
-
- длинная нота продолжать, непрерывная длинная нота
В функции эти три случая могут быть представлены 0, 1, 2, 3.
Чтобы упростить здесь, мы рассматриваем начало длинной ноты и продолжение как ключ, так что выходные результаты равны 0, 1 и 2.
ключевое кодирование
Поскольку это аудиоигра с 4 клавишами, для каждой позиции есть 3 ситуации: пустая, забастовка и полоса.
Таким образом, в общей сложностиВ среднем случае это может быть представлено 4-значным 3-значным числом, которое составляет от 0 до 80 при преобразовании в десятичное число.
Например, эта ключевая форма:
В троичном представлении это:Другой пример следующий:
В троичном представлении это:Формат данных
Таким образом, требуется много комплексного анализа данных.
Тогда каждый вход представляет собой матрицу 40 * 64. (40 — длина последовательности, 64 — размер функции)
Каждый вывод представляет собой список 40*1.
дизайн модели
Приведенные выше данные, очевидно, являются проблемой seq2seq, и можно использовать модель кодер-декодер. Можно найти код с машинным переводом. Но одно отличие состоит в том, что ввод нашего кодировщика здесь не нужно встраивать, потому что мы уже извлекли функции с помощью mfcc.
Encoder
class EncoderRNN(nn.Module):
def __init__(self, hidden_size):
super(EncoderRNN, self).__init__()
self.hidden_size = hidden_size
self.gru = nn.GRU(Feature_DIM, hidden_size)
def forward(self, input_, hidden):
input_ = input_.view(1, 1, -1)
output, hidden = self.gru(input_, hidden)
return output, hidden
def initHidden(self):
return torch.zeros(1, 1, self.hidden_size, device=device)
hidden_size = 128
encoder = EncoderRNN(hidden_size).to(device)
Кодировщик здесь представляет собой простой уровень GRU, потому что входные данные — это функция, поэтому кодировщик seq2seq не нуждается во встраивании по сравнению с машинным переводом.
процесс распространения энкодера
Для энкодера нужно один раз запустить цикл, а потом получить скрытый параметр последнего элемента.
x1 = torch.from_numpy(np.array(X1[index])).to(device).float() # 输入特征
y1 = torch.from_numpy(np.array(Y1[index])).to(device).long() # label
encoder_hidden = encoder.initHidden()
for ei in range(max_length):
_, encoder_hidden = encoder(
x1[ei], encoder_hidden)
Decoder
class DecoderRNN(nn.Module):
def __init__(self, embedding, hidden_size, output_size):
super(DecoderRNN, self).__init__()
hidden_size = hidden_size
# self.embedding = nn.Embedding(output_size, hidden_size)
self.embedding = embedding
self.gru = nn.GRU(50, hidden_size)
self.out = nn.Linear(hidden_size, output_size)
self.softmax = nn.LogSoftmax(dim=1)
self.dropout = nn.Dropout(0.2)
def forward(self, input_, hidden):
output = self.embedding(input_).view(1, 1, -1)
output = self.dropout(output)
# output = F.relu(output)
output, hidden = self.gru(output, hidden)
output = self.softmax(self.out(output[0]))
return output, hidden
hidden_size = 128
decoder = DecoderRNN(embedding, hidden_size, 81).to(device)
Здесь декодер тоже рнн, для каждого выхода будет вывод результата, то есть тип ключа.
«Ключевая» языковая модель
Здесь отмечается, что в декодере есть встраивание, которое на самом деле очень похоже на встраивание НЛП. Вектор слова на самом деле выражает, какое слово ближе всего к слову.
Тип ключа Embedded здесь одинаков, какой тип ключа, скорее всего, появится вместе. В сочетании с RNN, что фактически изучается, так это то, каким, скорее всего, будет следующий тип ключа, когда известны предыдущий тип ключа и текущие характеристики ноты.
Это также языковая модель в НЛП.
декодер процесс декодирования
Процесс декодирования декодера на самом деле является циклом, но первый скрытый вход ввода не 0, а скрытый скрытый кодировщика.
decoder_input = torch.tensor([[0]], device=device)
decoder_hidden = encoder_hidden
for di in range(max_length):
decoder_output, decoder_hidden = decoder(
decoder_input, decoder_hidden)
target = y1[di].view(-1)
# print(decoder_output)
# print(target)
loss += F.nll_loss(decoder_output, target)
decoder_input = target # Teacher forcing
Модель 2.0
После моего тестирования вышеуказанное не работает, а сгенерированные ключи очень нестабильны. Причина, о которой я думаю, заключается в том, что данных слишком мало, всего 24 песни, и шум данных будет относительно большим.
Не буду здесь вдаваться в подробности, код тот же, что и выше, но есть еще несколько копий. Ниже приводится краткое изложение того, что я сделал.
-
- В целях снижения сложности я отделил полоски от хитпойнтов, чтобы от предыдущегокласс становитсяклассов, что значительно снижает сложность.
-
- От одной модели к двум моделям. Раньше напрямую оценивался тип ключа, но во многих случаях нулей будет больше, поэтому эффект нехороший. Теперь я добавил классификатор, чтобы сначала определить, есть ли полоса или точка попадания, а затем определить тип ключа. Таким образом, если есть хитпойнты или полоски, модель типа ключа обязательно сгенерирует тип ключа, а не будет напрямую пустой.
-
- Упростите операцию, сначала создайте очки жизни, а затем регенерируйте длинные полоски. Если они перекрываются, полоса перекрывает точку попадания. Однако вероятность, конечно, невелика, и на большой эффект это не влияет.
Конечный эффект такой же, как на видео моей станции B. Всего сгенерировано три песни:China-P,Цзинчжэ,весеннее равноденствие. Общий эффект вроде нормальный.
Код для этой статьи:GitHub.com/yourado/AI_no…
(Этот код написан очень беспорядочно. Я чувствую, что большинство людей не будут его изучать, поэтому нет сортировки и оптимизации. Базовый код — это seq2seq, а затем есть куча данных для построения и разбора кода .)