概要
背景
めざましい進化を遂げるAI技術。
話題に挙がるのはコーディングや画像・動画生成が主だが、作曲の分野においても高い水準に達しつつある。
今回は令和8年5月に公開された音楽生成AIシリーズ「Stable Audio 3.0」のうち、オープンウェイトの最高水準モデルMediumを高火力 DOKで動かして、音楽生成を試してみた。
成果物
構成はいたって単純。
Hugging FaceからAIモデルを取得して、スクリプトを含めてコンテナイメージ化し、コンテナレジストリに格納する。
高火力 DOKでそのコンテナイメージを動かしてAI音楽生成を実行し、成果物はオブジェクトストレージに格納する。

Dockerイメージ作成用サーバー構築についてはこちらの記事で解説しているので、今回は割愛する。モデルが重いので、4コア16GB・ディスク100GBのサーバーを用いた。
構築
コンテナレジストリ・オブジェクトストレージの準備
あらかじめ、コンテナレジストリを作成しておく。
さくらのクラウドのコントロールパネルから作成し、ユーザ登録も済ませておく。

コンテナレジストリのユーザ登録が完了した時点で、高火力 DOKのレジストリ認証情報も登録できる。

成果物を格納するオブジェクトストレージのバケットとそのアクセスキーIDも作成しておく。詳しくはマニュアルを参照されたし。

これでさくらのクラウド側の準備は完了だ。
コンテナイメージの準備
以下のようなDockerfileを記述した。
Pythonバージョンの要件からUbuntu22.04、依存パッケージの要件からnumpyを2未満のバージョンで採用している。
文献が少なく、依存ライブラリを洗い出すのに何度か試行錯誤を迫られた。
個人閉域利用なので、モデル取得時に使うHugging Faceのアクセストークンはビルド時に--build-argで渡す方式としている(Hugging Faceのアカウントがない方は作成されたし)。
FROM nvidia/cuda:13.0.0-runtime-ubuntu22.04
LABEL jp.sakuracr.t-shirotani-airepo.version="1.0.0"
LABEL jp.sakuracr.t-shirotani-airepo.release-date="2026-07-28"
ENV DEBIAN_FRONTEND=noninteractive \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8
ENV PIP_BREAK_SYSTEM_PACKAGES=1
# 依存関係のインストール。
RUN apt-get update && \
apt-get install -y \
ca-certificates \
python3 \
python3-pip \
libsndfile1 && \
rm -rf /var/lib/apt/lists/* && \
pip install torch torchaudio --index-url https://download.pytorch.org/whl/cu130 && \
pip install \
"numpy<2.0.0" \
boto3 \
einops \
huggingface_hub \
pytorch-lightning \
soundfile \
stable_audio_tools \
&& \
pip cache purge && \
mkdir /stable-audio-3-medium /opt/artifact
# モデルの取得。
WORKDIR /stable-audio-3-medium
ARG access_token
RUN hf auth login --token $access_token && \
hf download stabilityai/stable-audio-3-medium --local-dir . && \
rm -rf ~/.cache/huggingface
# 実行用スクリプト等の配置
COPY runner*.py /stable-audio-3-medium/
COPY docker-entrypoint*.sh /
RUN chmod +x /docker-entrypoint*.sh
ENTRYPOINT [ "/docker-entrypoint.sh" ]
エントリーポイントとなるシェルスクリプトとPythonスクリプトは以下の通り。
#!/bin/bash
set -ue
shopt -s nullglob
export TZ=${TZ:-Asia/Tokyo}
if [ -z "${SAKURA_ARTIFACT_DIR:-}" ]; then
echo "Environment variable SAKURA_ARTIFACT_DIR is not set" >&2
exit 1
fi
if [ -z "${SAKURA_TASK_ID:-}" ]; then
echo "Environment variable SAKURA_TASK_ID is not set" >&2
exit 1
fi
if [ -z "${PROMPT:-}" ]; then
echo "Environment variable PROMPT is not set" >&2
exit 1
fi
if [ -z "${DURATION_SECONDS:-}" ]; then
echo "Environment variable DURATION_SECONDS is not set" >&2
exit 1
fi
pushd /stable-audio-3-medium
python3 runner.py \
--output="${SAKURA_ARTIFACT_DIR}" \
--prompt="${PROMPT}" \
--seconds="${DURATION_SECONDS:-380}" \
--steps="${STEPS:-8}" \
--objst-key="${OBJST_KEY:-stable_audio_output.wav}" \
--objst-bucket="${OBJST_BUCKET:-}" \
--objst-endpoint="${OBJST_ENDPOINT:-}" \
--objst-secret="${OBJST_SECRET:-}" \
--objst-token="${OBJST_TOKEN:-}"
popd
import argparse
import boto3
import json
import logging
import torch
import torchaudio
from einops import rearrange
from huggingface_hub import login
from stable_audio_tools.models.factory import create_model_from_config
from stable_audio_tools.models.utils import load_ckpt_state_dict
from stable_audio_tools.inference.generation import generate_diffusion_cond_inpaint
from pathlib import Path
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s')
# 環境変数からパラメータを取得
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument('--output', default='/opt/artifact', help='出力先ディレクトリを指定します。')
arg_parser.add_argument('--prompt', type=str, default="A dream-like Synthpop instrumental that would accompany, a dream-sequence in a surrealist movie 120 BPM", help='プロンプトを指定します。')
arg_parser.add_argument('--seconds', type=int, default=380, help='生成する音楽の長さ(秒)を指定します。')
arg_parser.add_argument('--steps', type=int, default=8, help='拡散ステップ数を指定します。')
arg_parser.add_argument('--objst-key', type=str, default="stable_audio_output.wav", help='オブジェクトストレージのオブジェクトキーを指定します。')
arg_parser.add_argument('--objst-bucket', help='オブジェクトストレージのバケットを指定します。')
arg_parser.add_argument('--objst-endpoint', help='オブジェクトストレージ互換エンドポイントのURLを指定します。')
arg_parser.add_argument('--objst-secret', help='オブジェクトストレージのシークレットアクセスキーを指定します。')
arg_parser.add_argument('--objst-token', help='オブジェクトストレージのアクセスキーIDを指定します。')
args = arg_parser.parse_args()
device = "cuda"
logging.info(f"{device}にモデルをロードします。")
# model, model_config = get_pretrained_model("/stable-audio-3-medium")
config_path = "/stable-audio-3-medium/model_config.json"
weights_path = "/stable-audio-3-medium/model.safetensors"
# 設定ファイルを読み込む
with open(config_path, "r") as f:
model_config = json.load(f)
# 設定からモデルを初期化し、重みをロードする
model = create_model_from_config(model_config)
model.load_state_dict(load_ckpt_state_dict(weights_path))
sample_rate = model_config["sample_rate"]
sample_size = model_config["sample_size"]
model = model.to(device)
model = model.to(torch.float16)
logging.info(f"{args.seconds}秒の音楽を生成します。")
conditioning = [{
"prompt": args.prompt,
"seconds_total": args.seconds
}]
output = generate_diffusion_cond_inpaint(
model,
steps=args.steps,
cfg_scale=1.0,
conditioning=conditioning,
sample_size=sample_size,
sampler_type="pingpong",
device=device
)
output = rearrange(output, "b d n -> d (b n)")
logging.info("生成が完了しました。ローカルに保存します。")
output = output.to(torch.float32).div(torch.max(torch.abs(output))).clamp(-1, 1).mul(32767).to(torch.int16).cpu()
output_path = Path(args.output) / Path(args.objst_key).name
torchaudio.save(output_path, output, sample_rate)
logging.info(f"生成した音楽を{output_path}に保存しました。")
# ==========================================
# さくらのオブジェクトストレージへのアップロード処理
# ==========================================
if all([args.objst_key, args.objst_bucket, args.objst_endpoint, args.objst_secret,args.objst_token]):
logging.info(f"オブジェクトストレージに成果物をアップロードします: {args.objst_bucket}/{args.objst_key}")
# さくらのオブジェクトストレージ(S3互換)向けのBoto3クライアント設定
objst_client = boto3.client(
's3',
endpoint_url=args.objst_endpoint,
aws_access_key_id=args.objst_token,
aws_secret_access_key=args.objst_secret,
)
try:
objst_client.upload_file(
Filename=str(output_path),
Bucket=args.objst_bucket,
Key=args.objst_key,
ExtraArgs={
"ContentType": "audio/wav",
})
logging.info("アップロードが完了しました。")
except Exception as e:
logging.error(f"アップロードに失敗しました: {e}")
else:
logging.warning("ストレージの認証情報が環境変数に完全に提供されていません。アップロードを省略しました。")
動作時に必要な環境変数は後述する。
上記3ファイルを同ディレクトリ配下に配置し、Dockerイメージをbuild、pushする。
sudo docker login -t <作成したレジストリのホスト名>.sakuracr.jp -u <作成したユーザ> -p <作成したユーザのパスワード>
sudo docker build --build-arg access_token=Hugging Faceのアクセストークン -t <作成したレジストリのホスト名>/stable-audio-3-medium:latest .
sudo docker image push <作成したレジストリのホスト名>.sakuracr.jp/stable-audio-3-medium:latest
大容量のモデルなので少し時間がかかる。作曲でもしながら待とう。
実行
pushできたら、高火力 DOKでタスクを実行していく。

イメージは
<作成したレジストリのホスト名>.sakuracr.jp/stable-audio-3-medium:latest
レジストリ認証情報は先ほど登録したものを選択する。
プロンプトやファイルの保存先等は環境変数で受けるように設計している。以下の設定が必要である。
| 環境変数 | 役割 |
|---|---|
| PROMPT | 音楽生成を支持するプロンプト。英語で記述することが望ましい。 |
| DURATION_SECONDS | 音楽の長さ(秒)。 |
| STEPS | ステップ数。高ければ品質が上がるが処理時間も増える。 |
| OBJST_KEY | オブジェクトストレージに格納する成果物のキー。 |
| OBJST_ENDPOINT | オブジェクトストレージのエンドポイント。「https://」が必要。 |
| OBJST_BUCKET | オブジェクトストレージのバケット名。 |
| OBJST_TOKEN | オブジェクトストレージのアクセスキーID。 |
| OBJST_SECRET | オブジェクトストレージのシークレットアクセスキー。 |
| HF_TOKEN | Hugging Faceのトークン。 |
Stable Audio 3.0 Mediumのモデルはコンテナ内部に格納されているが、テキストエンコーダの初期化処理時にHugging Faceと通信するため、Hugging Faceのトークンも必要である。
これらを登録して、タスク登録を行う。
今回は以下のような内容で2分のピアノ曲を生成させてみた。
A cinematic piano solo, sentimental and emotional soundtrack, nostalgic mood, slow tempo, delicate and expressive melody, soft dynamics, atmospheric reverb, high quality, stereo.
1分ほどで処理が完了し、オブジェクトストレージに成果物が格納された。


出力された音源は、素材として使うには申し分ない品質だった。
まとめ
今回は高火力 DOK上で音楽生成AI「Stable Audio 3.0 Medium」を動かしてみた。
この記事では2分の楽曲を1曲だけ生成したが、H100のスペックであれば大量の音楽生成を走らせることが可能だ。より重量級のモデルにも挑戦できる。
ぜひ試してみて欲しい。