こんにちはhamasan05です。
この記事は
KLab Engineer Advent Calendar 2025の6日目の記事です。
今回は5分のBGMが必要になったので学習がてら手元のマシンで生成AIを使って作ってみようと思いました。
ローエンドGPU環境でAI音楽生成を試したい人向けの記事になっています。
マシンスペック
CPU:ryzen2700
GPU:T600 4GB
OS:Windows11
こんな感じの環境でGPUを使ってどこまでいけるか試してみました。
商用利用について(重要)
AudioCraft のコード自体は MIT ライセンスで商用利用可能ですが、
MusicGen‑Small(facebook/musicgen-small)のモデル重みは CC‑BY‑NC 4.0(非営利)です。
そのため、MusicGen‑Small を使って生成した音源は商用利用できません。
広告付きサイト・販売・商用プロジェクトでの利用は不可となります。
誤った記述があったため、ここに訂正しお詫びいたします。
商用利用を前提とする場合は、以下のような 商用利用可能なモデル を使用する必要があります。
- Stable Audio Open 1.0(Stability AI)
- 商用利用OKと明記されたサードパーティ製 MusicGen 互換モデル
本記事では「技術検証」として MusicGen‑Small を使用していますが、
商用利用を行う場合は、必ず上記のような商用OKモデルをご利用ください。
環境構築
生成AIに聞いたところによるとpython3.10が安定とのことでこちらからインストールする
https://www.python.org/downloads/release/python-31011/
CUDAもセットアップ
118がいいらしいです。
https://developer.nvidia.com/cuda-11-8-0-download-archive
こんなrequirements.txtを作っておいて
--extra-index-url https://download.pytorch.org/whl/cu118
annotated-types==0.7.0
antlr4-python3-runtime==4.9.3
audioread==3.1.0
blis==0.7.11
catalogue==2.0.10
certifi==2025.11.12
cffi==2.0.0
charset-normalizer==3.4.4
click==8.3.1
cloudpathlib==0.23.0
cloudpickle==3.1.2
colorama==0.4.6
colorlog==6.10.1
confection==0.1.5
cymem==2.0.13
decorator==5.2.1
docopt==0.6.2
dora_search==0.1.12
einops==0.8.1
en-core-web-sm @ https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.7.1/en_core_web_sm-3.7.1-py3-none-any.whl#sha256=86cc141f63942d4b2c5fcee06630fd6f904788d2f0ab005cce45aadb8fb73889
ffmpeg-python==0.2.0
filelock==3.20.0
flashy==0.0.2
fsspec==2025.12.0
future==1.0.0
huggingface-hub==0.36.0
idna==3.11
intel-openmp==2021.4.0
Jinja2==3.1.6
joblib==1.5.2
julius==0.2.7
langcodes==3.5.1
lazy_loader==0.4
librosa==0.11.0
lightning-utilities==0.15.2
llvmlite==0.45.1
markdown-it-py==4.0.0
MarkupSafe==3.0.3
mdurl==0.1.2
mkl==2021.4.0
mpmath==1.3.0
msgpack==1.1.2
murmurhash==1.0.15
networkx==3.4.2
ninja==1.13.0
num2words==0.5.14
numba==0.62.1
numpy==1.26.4
omegaconf==2.3.0
packaging==25.0
pillow==12.0.0
platformdirs==4.5.0
pooch==1.8.2
preshed==3.0.12
pycparser==2.23
pydantic==2.12.5
pydantic_core==2.41.5
pydub==0.25.1
Pygments==2.19.2
PyYAML==6.0.3
regex==2025.11.3
requests==2.32.5
retrying==1.4.2
rich==14.2.0
safetensors==0.7.0
scikit-learn==1.7.2
scipy==1.15.3
sentencepiece==0.2.1
shellingham==1.5.4
smart_open==7.5.0
soundfile==0.13.1
soxr==1.0.0
spacy==3.7.6
spacy-legacy==3.0.12
spacy-loggers==1.0.5
srsly==2.5.2
submitit==1.5.3
sympy==1.14.0
tbb==2021.13.1
thinc==8.2.5
threadpoolctl==3.6.0
tokenizers==0.22.1
torch==2.2.2+cu118
torchaudio==2.2.2+cu118
torchdiffeq==0.2.5
torchmetrics==1.8.2
torchvision==0.17.2+cu118
tqdm==4.67.1
transformers==4.57.3
treetable==0.2.6
typer==0.20.0
typer-slim==0.20.0
typing-inspection==0.4.2
typing_extensions==4.15.0
urllib3==2.5.0
wasabi==1.1.3
weasel==0.4.3
wrapt==2.0.1
xformers==0.0.25.post1 --extra-index-url https://download.pytorch.org/whl/cu118
git+https://github.com/facebookresearch/audiocraft.git --no-deps
gitbashからいつものようにvenvを作ってpipでインストール
$ /c/Users/$USERNAME/AppData/Local/Programs/Python/Python310/python.exe -m venv venv
$ source venv/Scripts/activate
$ pip install -r requirements.txt
いくつかWindowsだとインストールできないパッケージがいたので
audiocraftは --no-deps で強引にインストールしていき、足りないパッケージをコマンドラインで地道に追加していくという作業の結果↑の requirements.txt になりました。
import avをコメントアウト
$ vi venv/lib/site-packages/audiocraft/data/audio.py
avをインストールするのが手間かかりそうだったのでコメントアウト
試行錯誤
いろいろ試行錯誤した結果自分で使いやすいのはこんな仕様になりました。
- 低VRAM対策でmusicgen-smallを使う
- 64秒単位で生成。
- クロスフェード結合で5分BGMを構築。
- シード管理 (固定/ランダム)。
- WAV/MP3保存。
非力なマシンなので
「10秒程度で試し生成 → 気に入ったらシード固定 → 長め生成」
という使い方をする想定です。
生成用のコード
ここは完全に生成AIに書かせてレビューアーをしました。
案外書いてもらうだけでは動かなくていくつか直してもらいました。
import argparse
import datetime
import random
import numpy as np
import soundfile as sf
import torch
from audiocraft.models import MusicGen
from pydub import AudioSegment
def set_seed(seed: int | None):
"""seed が指定されたときだけ固定。それ以外はランダム。"""
if seed is None:
s = random.randint(0, 2**32 - 1)
torch.manual_seed(s)
return s, True # 実際に使った乱数シード/ランダム生成フラグ
else:
torch.manual_seed(seed)
return seed, False
def ensure_stereo_float32(audio: np.ndarray) -> np.ndarray:
"""
audiocraft の出力を [samples, channels] float32 に整形。
入力: [channels, samples] or [samples] or [samples, channels]
出力: [samples, channels] float32
"""
if isinstance(audio, torch.Tensor):
audio = audio.cpu().numpy()
if audio.ndim == 1:
audio = audio.reshape(-1, 1)
elif audio.ndim == 2:
C, S = audio.shape[0], audio.shape[1]
if C in (1, 2) and S > C:
audio = audio.T
else:
raise ValueError(f"Unsupported audio shape: {audio.shape}")
if audio.dtype != np.float32:
audio = audio.astype(np.float32)
# 正規化(-1.0〜1.0に収める)
max_val = np.max(np.abs(audio))
if max_val > 1.0:
audio = audio / max_val
audio = np.clip(audio, -1.0, 1.0)
return audio
def generate_segment(model: MusicGen, prompt: str, duration: int) -> np.ndarray:
"""1セグメント生成。返り値は [samples, channels] float32"""
model.set_generation_params(duration=duration)
wav_list = model.generate([prompt], progress=True)
audio = wav_list[0]
return ensure_stereo_float32(audio)
def save_wav(path: str, audio_sc: np.ndarray, sample_rate: int, subtype: str = "PCM_16"):
"""soundfile で WAV 保存(PCM16で耳障り軽減)。"""
sf.write(path, audio_sc, sample_rate, format="WAV", subtype=subtype)
def build_names(prefix: str, seed_used: int, is_random: bool) -> tuple[str, str, str]:
"""ファイル名を生成(タイムスタンプ+シード)。"""
ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
seed_tag = f"rand{seed_used}" if is_random else f"seed{seed_used}"
segment = f"{prefix}_segment_{ts}_{seed_tag}.wav"
loop_wav = f"{prefix}_loop_{ts}_{seed_tag}.wav"
loop_mp3 = f"{prefix}_loop_{ts}_{seed_tag}.mp3"
return segment, loop_wav, loop_mp3
def make_loop_with_crossfade(segment_path: str, times: int = 5, crossfade_ms: int = 2000) -> AudioSegment:
"""生成済みセグメントを読み込み、指定回数ぶん結合してループを作る。"""
seg = AudioSegment.from_wav(segment_path)
loop = seg
for _ in range(times - 1):
loop = loop.append(seg, crossfade=crossfade_ms)
# ローパスフィルタで高音を抑制(若干高音がしんどかったので)
loop = loop.low_pass_filter(16000)
return loop
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--prompt", type=str, required=True, help="音楽生成のプロンプト")
parser.add_argument("--duration", type=int, default=10, help="セグメントの長さ(秒)")
parser.add_argument("--seed", type=int, default=None, help="乱数シード。未指定ならランダム")
parser.add_argument("--prefix", type=str, default="music", help="出力ファイル名の接頭辞")
parser.add_argument("--crossfade_ms", type=int, default=2000, help="結合時クロスフェード長(ms)")
parser.add_argument("--loop_times", type=int, default=5, help="ループ結合の回数")
args = parser.parse_args()
seed_used, is_random = set_seed(args.seed)
model = MusicGen.get_pretrained("facebook/musicgen-small")
audio_sc = generate_segment(model, args.prompt, args.duration)
segment_path, loop_wav_path, loop_mp3_path = build_names(args.prefix, seed_used, is_random)
save_wav(segment_path, audio_sc, model.sample_rate, subtype="PCM_16")
loop = make_loop_with_crossfade(segment_path, times=args.loop_times, crossfade_ms=args.crossfade_ms)
loop.export(loop_wav_path, format="wav")
loop.export(loop_mp3_path, format="mp3")
print(f"segment: {segment_path}")
print(f"loop(wav): {loop_wav_path}")
print(f"loop(mp3): {loop_mp3_path}")
print(f"seed_used: {seed_used} ({'random' if is_random else 'fixed'})")
if __name__ == "__main__":
main()
動かしてみた結果
- GPUは85℃あたりで安定
- VRAMは3GB使う
- 120秒だとVRAMが足りなくて落ちる
- BGMとして使うならプロンプトにloopをいれてクロスフェードさせればよさそう
サンプル
-
https://youtu.be/U_CeS79pAsU
- Dark German techno track, 133 BPM, heavy kick drum, minimal synth loop, industrial atmosphere, cold and mechanical mood
-
https://youtu.be/74Ea9KmsC6c
- Japanese pop-inspired jungle track, energetic drum and bass rhythm, airy synths, cheerful and emotional mood loop
loopにすると1分×5で5分生成してもそこまで違和感がなく仕上がりました。
こだわりだすとモデルを大きくしたりとかあるのでしょうけど、ある程度しあがったらCPU使ってじっくり生成するのもいいかもしれませんね
感想
生成AIに教えてもらいながら直接誰かのトレースではなく、勉強しながらやってみました。
世の中の生成AIがVRAM8GBなところからスタートということで、VRAM少ない非力なGPUでもなんとかなるのか?
と思って試してみたら、制限こそあるものの何とかなりました。
知らないこともたくさん教えてもらって目的も達成できていい時代になったなーと思います。
生成AIと戯れていると無限にやりたいことが出てくる今日この頃です。