1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ボイパでAIとジャムセッションしてみたかったけどできなかった

Posted at

はじめに

VisasQ Inc. Advent Calendar 2025 19日目の記事です。よろしくお願いいたします。


この記事は私の趣味の1つであるボイパ≒ビートボックス1を使ってAIと遊んだ記録です。
タイトルの通り、想像通りにうまくはできませんでした。
私のテック、ビートボックスの技術力をもっと磨く必要がありそうです。

:bulb:

いつも通りビートボックスの練習をしていた時に、このビートに音楽生成AIがメロディーとかつけてくれたら面白くないか?という天啓を得ました。

例えばピアノ単体の楽曲を与え、元のピアノは一切改変せずそこにAIがドラムやベース、サックスなどの楽器を重ね合わせてジャズミュージックを生成するといった感じです。
つまり、AIとジャムセッションできる機能を持つサービス、あるいはツールはないのかのと。

ローカル音楽生成AIを使う

手軽に音楽を生成できるサービスであるSuno2, Udio, Stable Audio, Kits AI, Beatoven.aiなど、いろいろ触ってみましたがなかなか思うようにはいきませんでした。
Audio2Audio機能は与えた楽曲をかなり自在に改変してしまい、一方でtemperatureを下げるとただドラムビートをミキシングして音質をよくしただけみたいな感じになりました。

調べたところによると、MIDIやDAWを使うことで半自動的に実現が可能のようで、それ相応の専門知識が必要そうだということがわかりました。

ということで、提供されるWebサービスでは限界がありそうなことがわかりました。
もしかしたらドンピシャなサービスがあるのかもですが見つけられませんでした。
そこで、ローカルで使える音楽生成AIモデルをうまく使うことで、なんとかそれっぽくAIとジャムセッションする方法はないかと考えました。

ACE-Step

いくつか見つかった中でACE-Stepを使ってみることにしました。
このオープンソースモデルも2025年6月末以降更新がない状態で、若干ほこりを被り始めているところですが、それでもちゃんと使えそうという理由で選択しました。

目標

私自身が口から出力したドラムビートに対して、それに合うメロディー等をAIに作ってもらい、それを合成してあたかもジャムセッションをしているかのようにすることを目指します。

フロー(仕様)

以下のフローのスクリプトを組ませました。

  1. ドラムビートのBPM解析
  2. 特定したBPMに重ねるためのメロディー生成プロンプト作成 (Gemini API)
  3. メロディー音源の作成(ACE-Step)
  4. ミックスと出力

環境

一昔前のミドルスペックです。

  • Windows
  • CPU: AMD Ryzen7 2700
  • GPU: NVIDIA GeForce RTX 3060
  • メモリ: 32GB

言語, ツール

  • Python
  • Conda
  • Gemini API
  • ACE-Step

ACE-Stepローカル起動まで

とりあえずACE-Stepをローカル起動させます。
GithubからリポジトリをCloneしてきます

git clone https://github.com/ace-step/ACE-Step.git

Installation

READMEをもとにInstall DependenciesのPyTorchのインストールまで進めていきます。
このあと注意点があります。

requirements.txt

requirements.txtを修正します。

- gradio
+ gradio==5.49.1
- peft
+ peft==0.17.1

以上の変更を行ったあとにpip install -e .を実行します。

gradio
TypeError: Audio.__init__() got an unexpected keyword argument 'show_download_button'

gradioは最新バージョン6.x以降show_download_buttonというパラメーターがサポートされなくなったらしいです。
ACE-Step起動時にエラーで起動しなくなるため、5.xのバージョンを指定することで回避しました。

peft
ModuleNotFoundError: No module named 'transformers.modeling_layers'

起動時にエラーが出るため、既存のissueを参考にバージョンを固定しました。

torchcodec

ModuleNotFoundError: No module named 'torchcodec'

これも既存issueをもとに以下をインストールして解決しました。

pip install torchcodec  
conda install "ffmpeg<8"

起動

書かれている通りのコマンドで起動します。

SnapCrab_NoName_2025-12-17_23-19-59_No-00.png

試しに何もいじらずGenerateボタンを押してみます。
最初はモデルの読み込みに時間がかかります。
出力されたのは↓

...絶妙なつんのめり具合です。早くも雲行きが怪しいです。

ACE-Stepの機能のAPI化

↑の記事を参考にしてtext2musicの機能をAPIにしました。
SnapCrab_NoName_2025-12-14_21-38-52_No-00.png

ジャムセッション用スクリプト

requreiments.txt

ACE-Stepのrequreiments.txtにスクリプトで使うモジュールも追加し、condaの仮想環境にインストールします。

requests
google-generativeai
pydub
python-dotenv
pip install -e. --upgrade

.env

.envファイルをACE-Stepのルートディレクトに追加し、Gemini APIのキーを記載します。

GEMINI_API_KEY=ここにAPIキーを記載する

スクリプト

gemini-3-proで最初のスクリプトを生成してみて、以降は手直し or gemini-2.5-flashを利用しました。
print文の最初に絵文字をつけてくれるのは生成AIのクセなんですかね?

このスクリプトファイルもACE-Stepのルートディレクトリに配置します。
ACE-Stepのプロンプト用の音楽ジャンルの文字列は、ACE-Stepのモジュールの中から拝借してくるようにしています。

import os
from dotenv import load_dotenv

load_dotenv()

import time
import librosa
import numpy as np
import google.generativeai as genai
from pydub import AudioSegment
import shutil
from gradio_client import Client
from acestep.ui.components import GENRE_PRESETS

# ==========================================
# 設定エリア
# ==========================================
INPUT_DRUM_FILE = "session/drums.mp3"  # 入力ドラムファイル名
OUTPUT_FILENAME = "session_complete.mp3"

# ==========================================
# 1. ドラムのBPM解析 (Librosa)
# ==========================================
def analyze_bpm(file_path):
    print(f"🥁 {file_path} の解析を開始...")
    try:
        y, sr = librosa.load(file_path, duration=30)
        tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
        bpm = round(tempo) if np.isscalar(tempo) else round(tempo[0])
        print(f"✅ 解析完了: 推定BPM = {bpm}")
        return bpm
    except Exception as e:
        print(f"❌ 解析エラー: {e}")
        return 120 

# ==========================================
# 2. メロディー生成プロンプト作成 (Gemini API)
# ==========================================
def get_music_prompt(bpm):
    print("🤖 Geminiにプロンプト作成を依頼中...")
    api_key = os.getenv("GEMINI_API_KEY")
    genai.configure(api_key=api_key)
    model = genai.GenerativeModel('gemini-2.5-flash')

    genere = GENRE_PRESETS.values()
    prompt_instruction = f"""
    あなたは音楽プロデューサーです。BPM {bpm} のドラムビートに重ねるための、適切なmusic genereを{genere}から選び、タグののみで構成された音楽プロンプトを生成してください。出力は一行で、他の説明は不要です。プロンプトの最後に必ず「NO lyrics, NO vocals, NO DRUMS, NO PERCUSSION, {bpm} bpm」を追加してください。
    """
    
    try:
        response = model.generate_content(prompt_instruction)
        music_prompt = response.text.strip()
        print(f"✅ 生成されたプロンプト: \"{music_prompt}\"")
        return music_prompt
    except Exception as e:
        print(f"❌ Geminiエラー: {e}")
        return f"Funky bass and electric piano melody, {bpm} bpm, NO DRUMS, NO PERCUSSION"


# ==========================================
# 3. メロディー音源の生成
# ==========================================
def generate_melody_ace_step(prompt, length_seconds=30):
    """
    Gradio APIを呼び出して、テキストプロンプトからメロディーを生成します。
    """
    print(f"🎼 Gradio API経由でメロディーを生成中... (約{length_seconds:.1f}秒)")

    try:
        # ローカルで起動しているGradioサーバーに接続
        client = Client("http://127.0.0.1:7865/")
    except Exception as e:
        print("❌ サーバーへの接続に失敗しました: http://127.0.0.1:7865/")
        print("Gradioサーバーが `python gui.py` などで起動していることを確認してください。")
        print(f"エラー詳細: {e}")
        return None

    # ACE-Stepの出力先ディレクトリ
    output_dir = "session/generated_melodies"
    os.makedirs(output_dir, exist_ok=True)

    try:
        # client.predictは (filepath, params_json) のタプルを返すため、最初の要素のみを取得
        temp_path, _ = client.predict(
            format="wav",
            audio_duration=length_seconds,
            prompt=prompt,
            lyrics="",  # 歌詞は指定しない
            infer_step=60,
            guidance_scale=15,
            scheduler_type="euler",
            cfg_type="apg",
            omega_scale=10,
            manual_seeds=None,
            guidance_interval=0.5,
            guidance_interval_decay=0,
            min_guidance_scale=3,
            use_erg_tag=True,
            use_erg_lyric=False,
            use_erg_diffusion=True,
            oss_steps=None,
            guidance_scale_text=0,
            guidance_scale_lyric=0,
            audio2audio_enable=False,
            ref_audio_strength=0.5,
            ref_audio_input=None,
            lora_name_or_path="none",
            lora_weight=1,
            api_name="/text2music"
        )
        
        print(f"✅ APIから音楽が生成されました。一時ファイルパス: {temp_path}")

        # 一意のファイル名を作成して、ファイルを永続的な場所へコピーする
        output_filename = f"melody_{int(time.time())}.wav"
        final_path = os.path.join(output_dir, output_filename)
        shutil.copy(temp_path, final_path)
        
        print(f"✅ メロディーを保存しました: {final_path}")
        return final_path

    except Exception as e:
        print(f"❌ Gradio API経由での音楽生成中にエラーが発生しました: {e}")
        print("Gradioサーバーのコンソールでエラーが出ていないか確認してください。")
        import traceback
        traceback.print_exc()
        return None


# ==========================================
# 4. ミキシングと出力 (Pydub)
# ==========================================
def mix_tracks(drum_file, melody_file, output_file):
    print("🎛️ トラックをミックス中...")
    try:
        drums = AudioSegment.from_file(drum_file)
        melody = AudioSegment.from_file(melody_file)

        # ドラムの長さに合わせてメロディーをカットまたはループ
        if len(drums) < len(melody):
            melody = melody[:len(drums)]
        elif len(drums) > len(melody):
            melody_length = len(melody)
            num_repeats = len(drums) // melody_length
            remaining_time = len(drums) % melody_length
            melody = melody * num_repeats + melody[:remaining_time]

        # 音量調整とオーバーレイ
        melody = melody - 2 
        mixed = drums.overlay(melody, position=0)

        mixed.export(output_file, format="mp3")
        print(f"🎉 完成!ファイルが出力されました: {output_file}")
        
    except Exception as e:
        print(f"❌ ミックスエラー: {e}")

# ==========================================
# メイン実行処理
# ==========================================
if __name__ == "__main__":
    # 処理全体の開始時間を記録
    start_time = time.time()

    # `session` ディレクトリがなければ作成
    os.makedirs("session", exist_ok=True)
    
    if not os.path.exists(INPUT_DRUM_FILE):
        print(f"🥁 エラー: 入力ドラムファイル {INPUT_DRUM_FILE} が見つかりません。")
        sample_drum_file = "data/test_track_001.mp3"
        if os.path.exists(sample_drum_file):
            print(f"💡 ヒント: プロジェクト内のサンプルファイル '{sample_drum_file}' をコピーして利用できます。")
            try:
                import shutil
                shutil.copy(sample_drum_file, INPUT_DRUM_FILE)
                print(f"✅ サンプルファイルを {INPUT_DRUM_FILE} にコピーしました。処理を続行します。")
            except Exception as e:
                print(f"自動コピーに失敗しました: {e}")
                exit() 
        else:
            print(f"👉 '{os.path.dirname(INPUT_DRUM_FILE)}' ディレクトリに 'drums.mp3' という名前でドラムのMP3ファイルを配置してください。")
            exit()
            
    # これ以降の処理はファイルが存在する場合にのみ実行される
    # 1. 解析
    bpm = analyze_bpm(INPUT_DRUM_FILE)

    # 2. プロンプト作成
    prompt = get_music_prompt(bpm)
    
    # 3. メロディー生成
    try:
        melody_path = generate_melody_ace_step(prompt, length_seconds=drum_duration_seconds)
        
        # 4. 合成
        if melody_path:
            mix_tracks(INPUT_DRUM_FILE, melody_path, OUTPUT_FILENAME)
    except Exception as e:
        print(f"処理中にエラーが発生しました: {e}")
        print("入力ファイルが有効なオーディオファイルであることを確認してください。")
    
    # 処理全体の終了時間を記録し、総実行時間を出力
    end_time = time.time()
    print("-" * 40)
    print(f"⏱️  総実行時間: {end_time - start_time:.2f}")
    print("-" * 40)

ジャムセッション実施

初トライ

まずビートボックスをします。最初は15秒ほどのシンプルなビートで試してみます。風呂場リバーブです。

ACE-Stepを起動し、別ターミナルでcondaの仮想環境を立ち上げスクリプトファイルを実行します。

Python jam-session.py
  • 生成されたプロンプト
    • metal, electric guitar, bass, aggressive, intense, heavy, NO lyrics, NO vocals, NO DRUMS, NO PERCUSSION, 152 bpm
  • 合計処理時間
    • 45.63秒

プロンプトの時点で察した通り全然合わない...

ドラムビートを長くする

もうちょっとビートを長くして試してみます。
今度はマイクで録音してみました。ただ、激安コンデンサマイクなのでむしろ悪化音質が悪化しているのかも。

  • 生成されたプロンプト
    edm, synth, bass, euphoric, pulsating, energetic, NO lyrics, NO vocals, NO DRUMS, NO PERCUSSION, 129 bpm
  • 合計処理時間
    • 69.67秒

EDMにはなりましたが、テンポが合わないです。
あと、NO vocalsとしているのにボーカルも生成されてしまいました。

もう一度別なビートで試してみます。

  • 生成されたプロンプト
    country, acoustic guitar, steel guitar, fiddle, heartfelt, rustic, warm, NO lyrics, NO vocals, NO DRUMS, NO PERCUSSION, 99 bpm
  • 合計処理時間
    • 53.50秒

カントリーミュージックになったのは面白いですが、やっぱ合わないですね。

プロンプトを生成しない

BPMという情報だけからそれに合うような音楽ジャンルを選ばせるのはさすがに無理だったかと考え、ビートに合わせたプロンプトをACE-Stepに与えるようにベタ書きして試してみます。

  • 使用したプロンプト
    dramnbase, baseline, NO lyrics, NO vocals, NO DRUMS, NO PERCUSSION, {bpm} bpm
  • 合計処理時間
    • 53.50秒

...何語?
よくわからないものが生まれました。

別なジャンルでも試してみます。

  • 使用したプロンプト
    dubstep, dark, NO lyrics, NO vocals, NO DRUMS, NO PERCUSSION, {bpm} bpm
  • 合計処理時間
    • 45.37秒

最後のほうだけ、ちょっと可能性を感じられる程度の何かにはなりました。

あとは音源の時間を指定したり、プロンプトを少しずついじっていろいろ試してみましたが、結局うまくいきませんでした。

これジャムセッション(=即興)か??

音楽生成AIに限らず、生成AIは多くの場合一度の出力で完成を目指すものではなく、複数回の出力や対話を通じて微調整していく使い方が一般的です。
一発でめっちゃいい感じのものが出力されることは現実的ではなく、結果として人間同士のジャムセッションのような即興性からは程遠い体験になってしまいました。
無限の猿定理のように、与えたドラムビートに合う楽曲が偶然生成されるまで試す必要があります。

まとめ

アイディアはよかったのではないかと思うのですが、私の技術力が足りなかったです。
(いい感じの方法を知っている方がいましたら、ぜひ教えていただけると嬉しいです:pray:

あるいはまだ時期じゃないのかもしれません。
生成AIともっと気軽に遊べるような時代が楽しみです。

また、ACE-Step自体は非常におもしろいオープンソースプロジェクトだと思います。
興味がある方はぜひお試しください。

最後に一言。
Beatbox IKIGAI!!💥

  1. ボイパとビートボックスはどう違うの?と聞かれた時に、同じ派と違う派で派閥が分かれます。今回はボイパのほうがより広く単語として浸透しているだろうという感覚から、タイトルにはボイパを使用しました。認知向上のため、記事内ではビートボックスを使用しました。

  2. SunoにはStudio機能というDAWのような機能があり、これを使うことで実現できそうに見えましたが、プレミアムプランのみ解放されている機能でそれなりのお値段であり、かつそこまでガチでトラックメイキングをしたいわけでもなかったので試しませんでした。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?