Amazon Bedrock でゲームの BGM を作ってみました !

2024-08-01
デベロッパーのためのクラウド活用方法

Author : Sheng Hsia Leng

Amazon Bedrock でゲームの BGM を作ってみました !

2024-08-01
デベロッパーのためのクラウド活用方法

Author : Sheng Hsia Leng

ゲームなみなさんこんにちは ! Game Solutions Architect の Leng です。
この記事では、生成 AI をゲームサウンドの制作分野に活用できるかどうかを試しながら可能性を探っていきます。

ゲームサウンドの制作は、多様なシーンに応じた様々な音楽を用意する必要があります。例えば RPG のゲームでいえば、フィールド、バトル、町、ダンジョンなど、シーンごとに異なる雰囲気の音楽が求められます。このような音楽制作には高い専門性が必要とされると思います。

プロトタイプ制作の過程で、仮のサウンドが必要になったり、他のニュアンスでメロディーのアイディアを出したくなる場合があります。そのような状況で、生成 AI は強力なツールとなり得ます。生成 AI ならシナリオに応じた細かい指示を出すことで、例えば、「この場面は主人公が危機的状況に陥っているので緊迫感のあるメロディーを」など、ゲームのコンテクストを加味してプロンプトを設定すれば、その状況に合った BGM を生成してくれます。

このように、サウンド制作の専門知識がなくてもメロディーや BGM のアイディアを生み出すことができるので、サウンドクリエイターを支援する役割を果たせると考えられています。

このクラウドレシピ (ハンズオン記事) を無料でお試しいただけます »

毎月提供されるクラウドレシピのアップデート情報とともに、クレジットコードを受け取ることができます。 


AWS for Games

AWS for Games はより早い開発、よりスマートな運営、そしてより楽しいゲームへの成長という BuildRunGrow の 3 つの柱に沿ってサポートします。今回は Build の柱 クラウドゲーム開発 のお話になります。


構成

まずは Amazon Bedrock を用いた基本的な構成を紹介します。下の図で、① Amazon API Gateway がユーザーが入力したプロンプトを受け取り、 AWS Lambda がそのプロンプトを整形します。その後、② Lambda は Bedrock がホストする生成 AI モデルにプロンプトを投げて、③ Bedrock からレスポンスが返ってきます。④ 生成された結果を Amazon S3 に保存しておきます。

今回のサンプルでは、Anthropic 社の言語モデル「Claude 3 Sonnet」を使用し、ユーザーのプロンプトに応じて適切なピッチ (音符の高さ) とデュレーション (長さ) といった音価の情報を出力します。下記のように、音符や休符のイベントを指定できます。ピッチには None (休符) か音高を表す文字列、デュレーションには四分音符を 1 とした相対的な長さの値を入れます。

# ピッチ, デュレーション
'C5', 0.5

# None は休符を表します
None, 0.5

そしてオープンソースの music21 という音楽理論ライブラリを利用し、instrumentFromMidiProgram 関数に基づいて楽器の情報を出力していきます。music21 は多機能な音楽理論ライブラリで、楽譜ファイル形式の入出力、楽譜の編集・解析、表示・変換、音楽理論計算など幅広い機能を備えています。オープンソースで無料利用できるため、音楽制作、教育、研究でよく活用されています。

instrumentFromMidiProgram 関数は、MIDI のプログラム番号から対応する楽器を特定するために使用します。MIDI の一般的な規格である General MIDI では、以下の楽器に対応しています。

'Acoustic Grand Piano': 0,
'Bright Acoustic Piano': 1,
'Electric Grand Piano': 2,
'Honky-tonk Piano': 3,
'Electric Piano 1': 4,
'Electric Piano 2': 5,
'Harpsichord': 6,
'Clavi': 7,
'Celesta': 8,
...

最終的に Claude 3 Sonnet が下記のように楽器と音価を出力します。

# 8 番の楽器(Celesta), 音価データ
8,[('C4', 0.5), ('E4', 0.5), ('G4', 0.5), ('C5', 0.5),
('E5', 1.0), ('D5', 1.0), ('C5', 1.0), ('G4', 1.0),
('F4', 0.5), ('A4', 0.5), ('C5', 0.5), ('F5', 0.5),
('E5', 1.0), ('D5', 1.0), ('C5', 1.0), ('G4', 1.0)]

1 つの楽器だけで演奏するのは寂しいかもしれません。下記のようにプロンプトをうまく工夫すれば、Claude 3 Sonnet は自動的に複数の楽器とパートを織り交ぜた重奏演奏を生成することができるのです。Lambda ハンドラで event オブジェクトを受け取り、その中から必要な user_prompt の値を抽出し、ユーザーが入力した user_prompt の内容に基づいて適切なメロディを作成します。

あなたは優秀なゲームのBGM(背景音楽)メーカーです。ゲームの種類とシーンによって最適なABC記譜法(ノーテーション)を作ることが仕事です。
<data></data>タグ内に記載された内容に基づいてメロディのノーテーションを出力します。
<data>
{user_prompt}
</data>
以下の<rule></rule>タグに従って出力してください。
<rule>
・使用する楽器はmusic21.instrument.instrumentFromMidiProgramに基づいて楽器の番号を指定してください。
・楽器の番号と楽譜のノーテーションのみ出力してください。それ以外の文字は絶対に出力しないでください。
・ノーテーションについては、プレイヤーがゲームをプレイしている間、BGMが積極的にプレイヤーの注意を引きつけないよう注意してください。
・なるべく長めにノーテーションを書いてください。
・最低でも3つの楽器を使用してください。
・JSON文字列で出力してください。改行・空白は入れないでください。
</rule>
以下の</example></example>タグ内にノーテーションの出力の例になります。
<example>
81,[('C5', 0.36), ('E5', 0.36), ('G5', 0.36), ('F5', 0.36), ('D5', 0.36), ('C5', 0.36), (None, 0.36), ('G4', 0.36),('C5', 0.36), ('E5', 0.36), ('G5', 0.36), ('F5', 0.36), ('D5', 0.36), ('C5', 0.36), (None, 0.36), ('G4', 0.36),
('A4', 0.36), ('C5', 0.36), ('D5', 0.36), ('B4', 0.36), ('G4', 0.36), ('A4', 0.36), (None, 0.36), ('F4', 0.36),
('A4', 0.36), ('C5', 0.36), ('D5', 0.36), ('B4', 0.36), ('G4', 0.36), ('A4', 0.36), (None, 0.36), ('F4', 0.36)]

88,[('C3', 0.71), (None, 0.71), ('G2', 0.71), (None, 0.71),
('C3', 0.71), (None, 0.71), ('G2', 0.71), (None, 0.71),
('F2', 0.71), (None, 0.71), ('C2', 0.71), (None, 0.71),
('F2', 0.71), (None, 0.71), ('C2', 0.71), (None, 0.71)]

116,[('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36),
('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36),
('C2', 0.71), (None, 0.71), ('C2', 0.71), (None, 0.71),
('C2', 0.71), (None, 0.71), ('C2', 0.71), (None, 0.71)]

115,[(None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18),
(None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18),
(None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18),
(None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18),
(None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18),
(None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18)]
</example>

その後に AWS Lambda 上で music21 ライブラリを使って、この音価情報を MIDI ファイルに変換しています。作成された MIDI ファイルは、演奏情報がデジタルデータとして記録されているため、デジタル録音済みの波形データの wav や mp3 と違って、編集の自由度が高いです。そのままデジタル音源に入力して演奏を確認したり、DAW (Digital Audio Workstation) ソフトに取り込んで編集を加えたりと、様々な用途に活用できます。つまり、生成 AI と音楽理論ライブラリの連携により、アイディア出しから実際の作曲フローまでのプロセスを大幅に効率化できる可能性があります。


Amazon Bedrock の利用開始方法

ここでは AWS マネジメントコンソール上での構築方法を紹介します。以下の流れで 構築していきますが、始まる前に必ず次の注意点を確認してください。

  • Bedrock のモデルアクセス設定 : Bedrock のモデルとして、今回は Anthropic Claude 3 Sonnet を利用します。
  • S3 バケットの準備 : Lambda 関数に対するリクエストとレスポンスを扱うために、必要なライブラリを S3 バケットにアップロードしておきます。
  • Lambda の設定 : ノーテーションの生成と MIDI ファイルへ変換する Lambda 関数を作成します。
  • Lambda レイヤーの設定 : MIDI ファイルへ変換するに必要なライブラリを Lambda レイヤーとしてアタッチします。

注意点

  • 本章の作業は、すべて北バージニアリージョンで行います。もし作業の途中で、作ったリソースが存在しないなどの問題が発生した場合は、リージョンが正しいか確認してください。Bedrock 上の Anthropic Claude 3 Sonnet が利用可能なリージョンは今後追加される可能性もありますので、詳しくは こちらのドキュメント をご覧ください。
  • 本章の流れに沿ってリソースを構築する場合は、AWS のサービス料金がかかる場合のあることにご注意ください。リソースを削除する場合は、「リソースの削除」の章をご確認ください。

Amazon Bedrock のモデルアクセス設定

Amazon Bedrock を利用を開始するには、利用するモデルのアクセス設定が必要です。

マネジメントコンソールで Amazon Bedrock のページに遷移し、左側のペインで「モデルアクセス」を選択します。

それから、右上の「モデルアクセスを管理」をクリックしてください。ここで、利用したいモデルにアクセスできるようにします。今回はプロンプトに回答するモデルとして「Anthropic Claude 3 Sonnet」を利用します。

画像をクリックすると拡大します

Lambda レイヤーで利用するライブラリの zip ファイル作成

ローカル PC で以下のコマンドで music21 と依存するライブラリをインストールした後、zip ファイルとして Lambda Layer で利用します。

mkdir combined-layer
python3 -m venv env
source env/bin/activate #Windowsの場合はenv\Scripts\activate
pip install music21 requests more_itertools webcolors

mkdir python
cp -r env/lib/python3.12/site-packages/music21 python/
cp -r env/lib/python3.12/site-packages/requests python/
cp -r env/lib/python3.12/site-packages/charset_normalizer python/
cp -r env/lib/python3.12/site-packages/idna python/
cp -r env/lib/python3.12/site-packages/certifi python/
cp -r env/lib/python3.12/site-packages/more_itertools python/
cp -r env/lib/python3.12/site-packages/webcolors python/

zip -r music21-layer.zip python

Amazon S3 バケットの準備

その後は生成された MIDI ファイルおよび Lambda レイヤーのパッケージを格納する S3 バケットを作成します。

画像をクリックすると拡大します

次に、 Lambda Layer の必要な依存パッケージをアップロードしていきます。Step.1 で作成した .zip ファイルを S3 バケットにアップロードします。

画像をクリックすると拡大します

Lambda の設定

ここからは、BGM 生成や変換処理用に Lambda 関数 (Python 3.12) を作成します。

画像をクリックすると拡大します

import json
import boto3
import music21
from pathlib import Path
from datetime import datetime
import ast

s3 = boto3.client('s3')
bedrock_client = boto3.client(service_name='bedrock-runtime', region_name="us-east-1")

def generate_message(bedrock_runtime, model_id, messages, max_tokens, temp, top_p, top_k):
    body=json.dumps(
        {
            "anthropic_version": "bedrock-2023-05-31",
            "max_tokens": max_tokens,
            "messages": messages,
            "temperature": temp,
            "top_p": top_p,
            "top_k": top_k
        }  
    ) 
    response = bedrock_runtime.invoke_model(body=body, modelId=model_id)
    result = json.loads(response.get('body').read())
    input_tokens = result["usage"]["input_tokens"]
    output_tokens = result["usage"]["output_tokens"]
    output_list = result.get("content", [])

    print("Invocation details:")
    print(f"- The input length is {input_tokens} tokens.")
    print(f"- The output length is {output_tokens} tokens.")

    print(f"- The model returned {len(output_list)} response(s):")
    for output in output_list:
        print(output["text"])

    return result
    
def lambda_handler(event, context):
    # ユーザのプロンプト取得
    user_prompt = event.get('user_prompt')
    
    prompt = f"""\n\nHuman:
    あなたは優秀なゲームのBGM(背景音楽)メーカーです。ゲームの種類とシーンによって最適なABC記譜法(ノーテーション)を作ることが仕事です。
    <data></data>タグ内に記載された内容に基づいてメロディのノーテーションを出力します。
    <data>
    {user_prompt}
    </data>
    以下の<rule></rule>タグに従って出力してください。
    <rule>
    ・使用する楽器はmusic21.instrument.instrumentFromMidiProgramに基づいて楽器の番号を指定してください。
    ・楽器の番号と楽譜のノーテーションのみ出力してください。それ以外の文字は絶対に出力しないでください。
    ・ノーテーションについては、プレイヤーがゲームをプレイしている間、BGMが積極的にプレイヤーの注意を引きつけないよう注意してください。
    ・なるべく長めにノーテーションを書いてください。
    ・最低でも3つの楽器を使用してください。
    ・JSON文字列で出力してください。改行・空白は入れないでください。
    </rule>
    以下の</example></example>タグ内にノーテーションの出力の例になります。
    <example>
    81,[('C5', 0.36), ('E5', 0.36), ('G5', 0.36), ('F5', 0.36), ('D5', 0.36), ('C5', 0.36), (None, 0.36), ('G4', 0.36),('C5', 0.36), ('E5', 0.36), ('G5', 0.36), ('F5', 0.36), ('D5', 0.36), ('C5', 0.36), (None, 0.36), ('G4', 0.36),
                        ('A4', 0.36), ('C5', 0.36), ('D5', 0.36), ('B4', 0.36), ('G4', 0.36), ('A4', 0.36), (None, 0.36), ('F4', 0.36),
                        ('A4', 0.36), ('C5', 0.36), ('D5', 0.36), ('B4', 0.36), ('G4', 0.36), ('A4', 0.36), (None, 0.36), ('F4', 0.36)]

    88,[('C3', 0.71), (None, 0.71), ('G2', 0.71), (None, 0.71),
                  ('C3', 0.71), (None, 0.71), ('G2', 0.71), (None, 0.71),
                  ('F2', 0.71), (None, 0.71), ('C2', 0.71), (None, 0.71),
                  ('F2', 0.71), (None, 0.71), ('C2', 0.71), (None, 0.71)]

    116,[('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36),
                  ('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36), ('C2', 0.36), (None, 0.36),
                  ('C2', 0.71), (None, 0.71), ('C2', 0.71), (None, 0.71),
                  ('C2', 0.71), (None, 0.71), ('C2', 0.71), (None, 0.71)]

    115,[(None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18),
                   (None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18),
                   (None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18),
                   (None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18),
                   (None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18),
                   (None, 0.71), ('D4', 0.18), (None, 0.18), ('D4', 0.18), (None, 0.18)]
    </example>

    Assistant:
    """
    
    ### Defining the messages and invoking the model
    messages=[{ "role":'user', "content":[{'type':'text','text': prompt}]}]
    result = generate_message(
        bedrock_client, model_id = "anthropic.claude-3-sonnet-20240229-v1:0",
        messages=messages,
        max_tokens=4096,
        temp=1,
        top_p=1,
        top_k=100)
        
    response_text = ""
    for chunk in result.get("content", []):
        response_text += chunk.get("text", "")
    
    response_json = ast.literal_eval(response_text)
    
    bgm_score = music21.stream.Score()

    # 数字キーと対応する値を辞書に格納
    numeric_keys = {}
    for key, value in response_json.items():
       if key.isdigit():
           numeric_keys[int(key)] = value

    # 楽器と音価データを抽出
    instruments = []
    for key, value in numeric_keys.items():
        key
        value
        instrument = music21.instrument.instrumentFromMidiProgram(key)
        instruments.append((value, 0.0, instrument))
    
    for notes, offset, instrument in instruments:
        instrument_part = music21.stream.Part()
        instrument_part.insert(0, instrument)
        for pitch, duration in notes:
            if pitch is not None:
                note = music21.note.Note(pitch, quarterLength=duration)
                instrument_part.append(note)
            else:
                rest = music21.note.Rest(quarterLength=duration)
                instrument_part.append(rest)
        bgm_score.insert(offset, instrument_part)
    
    # 音価データをMIDIファイルに書き込む
    midi_file = bgm_score.write('midi')
    midi_data = open(midi_file, 'rb').read()
    
    # タイムスタンプ付きのMIDIファイル名を定義する
    current_datetime = datetime.now().strftime('%Y%m%d-%H%M%S')
    midi_file_name = f'bgm_{current_datetime}.mid'
    bucket_name = 'artifacts-bgm-generator'
    
    s3.put_object(
    Bucket=bucket_name,
    Key=midi_file_name,
    Body=midi_data
    )
    
    return {
        'statusCode': 200,
        'body': json.dumps(f'BGM MIDI file uploaded to S3://{bucket_name}/{midi_file_name}')
    }

上記のコードでは、イベントからユーザー入力を取得し、music21 ライブラリを使って music21 ライブラリを呼び出し、ABC ノーテーションから MIDI ファイルを変換していきます。作成した MIDI ファイルは S3 バケットに保存します。次のステップに進む前に一度 Lambda をデプロイしてください。

画像をクリックすると拡大します

次に、Lambda への Bedrock と S3 バケットへのアクセスを許可するためにリソースポリシーを適用します。そのためには、上部タブを「コード」から「設定」に切り替え、実行ロール名をクリックすると IAM ロールの管理画面に遷移します。

画像をクリックすると拡大します

IAM ロールの管理画面に遷移した後に、許可ポリシーを追加します。

ポリシーの一覧から「AmazonBedrockFullAccess」と「AmazonS3FullAccess」を検索して、ポリシーをアタッチします。

画像をクリックすると拡大します

次に、Lambda がリクエストを処理するのに十分な時間を持つように設定を調整します。

Lambda の関数画面に戻り、「設定」トップタブから「一般設定」を選択し、「編集」をクリックし、タイムアウトを 1 分に更新します。他の設定はデフォルトのままにして、「保存」を選択します。これで Lambda 関数のセットアップが完了しました。

画像をクリックすると拡大します

Lambda レイヤーの設定

Lambda レイヤーを作成してアタッチするために、Lambda ネイティブで提供されていない依存関係の .zip ファイルが必要となります。MIDI ファイルに変換するために music21 ライブラリを使用します。

AWS Lambda コンソールに移動し、左側のパネルから「レイヤー」を選択し、「レイヤーの作成」を選択します。

Lambda レイヤーの名前を music21-layer とします。

Amazon S3 からファイルをアップロードする」を選択し、事前に S3 バケットにアップロードした .zip ファイルの URL を貼り付けてください。互換性のあるアーキテクチャに x86_64 を、ランタイムに Python 3.12 を選択します。 

図の例のように選択してください。

画像をクリックすると拡大します

Lambda レイヤーの作成が完了したら、Lambda 関数 bgm-generator に戻り、「コード」タブを選択します。レイヤーセクションまでスクロールし、「レイヤーの追加」を選択します。

ラジオボタンから「カスタムレイヤー」のオプションを選び、作成した music21-layer のレイヤーとバージョン 1 を選んで「追加」をクリックします。Lambda 関数に戻り、レイヤーが正しく追加されたことを確認してください。

画像をクリックすると拡大します

これで bgm-generator 関数に必要な依存関係を Lambda レイヤーとして作成し、追加する作業が完了しました。


bgm-generator を使ってみる

このステップを実行する前に必ず Lambda をデプロイしてください。「Test」ボタンをクリックし、テストイベントを作成します。

好きなシーンをプロンプトとして入力して、保存して「呼び出し」ボタンを押下することによってテストイベントを実行します。生成された MIDI ファイルは artifacts-bgm-generator の S3 バケットから確認できます。

画像をクリックすると拡大します

こちらは user_prompt の内容に基づいて生成したメロディの例です。(サイトで再生できるよう、事前に mp3 ファイルに変換しています)

{
"user_prompt": "昔懐かしいドット絵RPGゲームの主人公が森を歩いていると、ぷるんぷるんと可愛らしいスライムが現れた。主人公は剣を構え戦闘に備える。攻撃を受けたスライムはゼリー飛沫を浴びせてくるが、主人公はかわしてスライムを倒した。経験値を得て、さらなる冒険に向かって前進する。"
}

{
"user_prompt": "朝日が窓からこぼれ込む中、ジェインは新鮮なモーニングコーヒーの香りに包まれながら目を覚ました。彼女の一日はいつも都会の喧騒から離れた小さな都市農園から始まります。庭の花々に水をあげ、自転車に乗って通勤する準備を整えました。ペダルを踏む度に、穏やかな風が彼女の髪をなびかせる。"
}


リソースの削除

最後に、構築のために作成したリソースを削除する流れを説明します。以下の手順に従って、AWS 環境をクリーンアップし、不要な課金を避けましょう。

S3 バケットの削除

S3 コンソールに移動します。artifacts-bgm-generator バケットを選択します。ファイルを削除して、このバケットが空になっていることを確認します。

削除」を選択し、バケット名を入力して削除します。

Lambda 関数とレイヤーの削除

Lambda コンソールに移動します。 bgm-generator を選択します。 「削除」をクリックし、アクションを確認します。

Lambda コンソールのレイヤータブに移動し、music21-layer を削除します。


まとめ

今回は Bedrock を利用したゲームの BGM 生成する手順を紹介しました。生成 AI を活用すれば、ゲームのあらゆるシーンに合わせて 、メロディーや BGM のアイディアを生み出すことができます。また、プロンプトエンジニアリングによって工夫すれば、複数の楽器が重なり合った重奏演奏を実現し、より豊かで多彩な作曲支援ができます。

この記事が生成 AI の活用により、ゲーム開発の効率化と、より面白いゲームづくりのきっかけになれば幸いです。


builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

Sheng Hsia Leng
アマゾン ウェブ サービス ジャパン合同会社 ソリューションアーキテクト

ゲーム業界に特化したソリューションアーキテクトとしてお客様を支援しております。
昔懐かしいドット絵ゲームが好きです。2024 年はあと半分しか残っていませんが、生成 AI の力を借りて動画制作にもチャレンジしようと思っています。

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する