テキストから音楽を作成するMubert-Text-to-Musicのインストール

テキストから音楽を作成するMubert-Text-to-Musicのインストール 機械学習

「テキストから音楽を作成してみたい」
「Mubert APIの使い方を知りたい」

このような場合には、Mubert-Text-to-Musicがオススメです。
この記事では、Mubert-Text-to-Musicをローカル環境で動かす方法を解説しています。

本記事の内容

  • Mubert-Text-to-Musicとは?
  • Mubert-Text-to-Musicのシステム要件
  • Mubert-Text-to-Musicのインストール
  • Mubert-Text-to-Musicの動作確認

それでは、上記に沿って解説していきます。

Mubert-Text-to-Musicとは?

Mubert-Text-to-Musicとは、Mubert APIを使ったデモのことです。
そのデモでは、テキストから音楽(MP3)を生成できます。

GitHub – MubertAI/Mubert-Text-to-Music
https://github.com/MubertAI/Mubert-Text-to-Music

上記ページでそのデモのプログラムが案内されています。
あくまで、プログラムであることに注意してください。

動きをWebアプリで気軽に試せるわけではありません。
そして、プログラムはColabで動くことが前提のコードです。

Colabを利用する場合は、そのままそのプログラムを利用してください。
この記事では、そのプログラムをローカル環境で動くように改変しています。

よって、この記事で言うMubert-Text-to-Musicはローカル環境で動くデモのことになります。
デモと言っても、実際にテキストから音楽を生成できます。

しかし、本当に肝となる部分は公開されていません。
肝となる部分は、APIとして提供されています。

APIとして無料で使わせて、普及させる企業戦略なのかもしれません。
https://mubert.com/

よって、テキストから音楽を生成するAIを獲得できるというわけではないです。
利用制限は、そのうち出てくるかもしれません。

あと、生成した音楽の権利はこちらにはありません。
権利を得たいなら、有償サービスを利用する必要があるかもしれません。

情報が少ないので、どうしても推測ばかりになってしまいます。

以上、Mubert-Text-to-Musicについて説明しました。
次は、Mubert-Text-to-Musicのシステム要件を説明します。

Mubert-Text-to-Musicのシステム要件

Mubert-Text-to-Musicのシステム要件は、PyTorchが最大のポイントです。
インストールするのは、GPU版のPyTorchです。

なるべく新しいPyTochをインストールすることをオススメします。
基本的には、これでシステム要件は終わったようなモノです。

OSは、GPU版PyTorchが動くなら以下の何でも構いません。

  • Windows
  • macOS
  • Linux

あと、PythonについてもGPU版PyTorchに対応するなら何でもOK。
と言っても、以下のPython公式開発サイクルには従いましょう。

バージョンリリース日サポート期限
3.62016年12月23日2021年12月23日
3.72018年6月27日2023年6月27日
3.82019年10月14日2024年10月
3.92020年10月5日2025年10月
3.102021年10月4日2026年10月

PyTorchに関しても、基本的にはPython公式開発サイクルに従っているはずです。

以上、Mubert-Text-to-Musicのシステム要件を説明しました。
次は、Mubert-Text-to-Musicのインストールを説明します。

Mubert-Text-to-Musicのインストール

Mubert-Text-to-Musicのインストールは、Python仮想環境の利用をオススメします。
Python仮想環境は、次の記事で解説しています。

検証は、次のバージョンのPythonで行います。

> python -V    
Python 3.10.4

そして、システム要件としてはGPU版PyTorchをインストール済という状況です。
このような状況において、Mubert-Text-to-Musicのインストールを進めます。

システム要件さえ満たしていれば、あとは簡単です。
次のコマンドを実行するだけになります。

pip install sentence-transformers
pip install httpx

sentence-transformersについては、次の記事で説明しています。

httpxは、Python用のHTTPクライアントです。
これを用いて、MubertのAPIにアクセスしています。

この時点でのインストール状況は、以下となります。

> pip list 
Package               Version 
--------------------- ------------ 
anyio                 3.6.2 
certifi               2022.9.24 
charset-normalizer    2.1.1 
click                 8.1.3 
colorama              0.4.5 
filelock              3.8.0 
h11                   0.12.0 
httpcore              0.15.0 
httpx                 0.23.0 
huggingface-hub       0.10.1 
idna                  3.4 
joblib                1.2.0 
nltk                  3.7 
numpy                 1.23.4 
packaging             21.3 
setuptools            65.5.0 
sniffio               1.3.0 
threadpoolctl         3.1.0 
tokenizers            0.13.1 
torch                 1.12.1+cu116 
torchaudio            0.12.1+cu116 
torchvision           0.13.1+cu116 
tqdm                  4.64.1 
transformers          4.23.1 
typing_extensions     4.4.0 
urllib3               1.26.12 
wheel                 0.37.1

以上、Mubert-Text-to-Musicのインストールを説明しました。
次は、Mubert-Text-to-Musicの動作確認を説明します。

Mubert-Text-to-Musicの動作確認

GitHub上の公式ページでは、Colab用プログラムが公開されています。
それを参考にして、ローカル環境で動くようにカスタマイズしました。

あと、音声ファイル(MP3)をローカル環境に保存するようにも変更しています。
それ以外にも、コードを少し整理しています。

そうやって出来たのが、次のコードです。

import numpy as np
from sentence_transformers import SentenceTransformer
import httpx
import json
import time
import datetime
import requests

EMAIL_ADDRESS = "あなたのメールアドレス"
PROMPT = "samurai"

minilm = SentenceTransformer('all-MiniLM-L6-v2')

mubert_tags_string = 'tribal,action,kids,neo-classic,run 130,pumped,jazz / funk,ethnic,dubtechno,reggae,acid jazz,liquidfunk,funk,witch house,tech house,underground,artists,mystical,disco,sensorium,r&b,agender,psychedelic trance / psytrance,peaceful,run 140,piano,run 160,setting,meditation,christmas,ambient,horror,cinematic,electro house,idm,bass,minimal,underscore,drums,glitchy,beautiful,technology,tribal house,country pop,jazz & funk,documentary,space,classical,valentines,chillstep,experimental,trap,new jack swing,drama,post-rock,tense,corporate,neutral,happy,analog,funky,spiritual,sberzvuk special,chill hop,dramatic,catchy,holidays,fitness 90,optimistic,orchestra,acid techno,energizing,romantic,minimal house,breaks,hyper pop,warm up,dreamy,dark,urban,microfunk,dub,nu disco,vogue,keys,hardcore,aggressive,indie,electro funk,beauty,relaxing,trance,pop,hiphop,soft,acoustic,chillrave / ethno-house,deep techno,angry,dance,fun,dubstep,tropical,latin pop,heroic,world music,inspirational,uplifting,atmosphere,art,epic,advertising,chillout,scary,spooky,slow ballad,saxophone,summer,erotic,jazzy,energy 100,kara mar,xmas,atmospheric,indie pop,hip-hop,yoga,reggaeton,lounge,travel,running,folk,chillrave & ethno-house,detective,darkambient,chill,fantasy,minimal techno,special,night,tropical house,downtempo,lullaby,meditative,upbeat,glitch hop,fitness,neurofunk,sexual,indie rock,future pop,jazz,cyberpunk,melancholic,happy hardcore,family / kids,synths,electric guitar,comedy,psychedelic trance & psytrance,edm,psychedelic rock,calm,zen,bells,podcast,melodic house,ethnic percussion,nature,heavy,bassline,indie dance,techno,drumnbass,synth pop,vaporwave,sad,8-bit,chillgressive,deep,orchestral,futuristic,hardtechno,nostalgic,big room,sci-fi,tutorial,joyful,pads,minimal 170,drill,ethnic 108,amusing,sleepy ambient,psychill,italo disco,lofi,house,acoustic guitar,bassline house,rock,k-pop,synthwave,deep house,electronica,gabber,nightlife,sport & fitness,road trip,celebration,electro,disco house,electronic'
mubert_tags = np.array(mubert_tags_string.split(','))
mubert_tags_embeddings = minilm.encode(mubert_tags)


def get_track_by_tags(tags, pat, duration, maxit=20, autoplay=False, loop=False):
    if loop:
        mode = "loop"
    else:
        mode = "track"
    r = httpx.post('https://api-b2b.mubert.com/v2/RecordTrackTTM',
                   json={
                       "method": "RecordTrackTTM",
                       "params": {
                           "pat": pat,
                           "duration": duration,
                           "tags": tags,
                           "mode": mode
                       }
                   })

    rdata = json.loads(r.text)
    assert rdata['status'] == 1, rdata['error']['text']
    trackurl = rdata['data']['tasks'][0]['download_link']

    print('Generating track ', end='')
    for i in range(maxit):
        r = httpx.get(trackurl)
        if r.status_code == 200:
            # display(Audio(trackurl, autoplay=autoplay))
            save_file(trackurl)
            break
        time.sleep(1)
        print('.', end='')


def find_similar(em, embeddings, method='cosine'):
    scores = []
    for ref in embeddings:
        if method == 'cosine':
            scores.append(1 - np.dot(ref, em) / (np.linalg.norm(ref) * np.linalg.norm(em)))
        if method == 'norm':
            scores.append(np.linalg.norm(ref - em))
    return np.array(scores), np.argsort(scores)


def get_tags_for_prompts(prompts, top_n=3, debug=False):
    prompts_embeddings = minilm.encode(prompts)
    ret = []
    for i, pe in enumerate(prompts_embeddings):
        scores, idxs = find_similar(pe, mubert_tags_embeddings)
        top_tags = mubert_tags[idxs[:top_n]]
        top_prob = 1 - scores[idxs[:top_n]]
        if debug:
            print(f"Prompt: {prompts[i]}\nTags: {', '.join(top_tags)}\nScores: {top_prob}\n\n\n")
        ret.append((prompts[i], list(top_tags)))
    return ret


# @markdown **Get personal access token in Mubert and define API methods**
email = EMAIL_ADDRESS  # @param {type:"string"}

r = httpx.post('https://api-b2b.mubert.com/v2/GetServiceAccess',
               json={
                   "method": "GetServiceAccess",
                   "params": {
                       "email": email,
                       "license": "ttmmubertlicense#f0acYBenRcfeFpNT4wpYGaTQIyDI4mJGv5MfIhBFz97NXDwDNFHmMRsBSzmGsJwbTpP1A6i07AXcIeAHo5",
                       "token": "4951f6428e83172a4f39de05d5b3ab10d58560b8",
                       "mode": "loop"
                   }
               })

rdata = json.loads(r.text)
assert rdata['status'] == 1, "probably incorrect e-mail"
pat = rdata['data']['pat']
print(f'Got token: {pat}')

# @title **Generate some music 🎵**

prompt = PROMPT  # @param {type:"string"}
duration = 30  # @param {type:"number"}
loop = False  # @param {type:"boolean"}


def generate_track_by_prompt(prompt, duration, loop=False):
    _, tags = get_tags_for_prompts([prompt, ])[0]
    try:
        get_track_by_tags(tags, pat, duration, autoplay=True, loop=loop)
    except Exception as e:
        print(str(e))
    print('\n')


def save_file(url):
    file_name = get_file_name() + ".mp3"
    url_data = requests.get(url).content

    with open(file_name, mode='wb') as f:
        f.write(url_data)


def get_file_name():
    t_delta = datetime.timedelta(hours=9)
    JST = datetime.timezone(t_delta, 'JST')
    now = datetime.datetime.now(JST)
    d = now.strftime('%Y%m%d%H%M%S')

    return d


generate_track_by_prompt(prompt, duration, loop)

上記コードを動かす際に、最低限で変更するのは以下の箇所になります。

EMAIL_ADDRESS = "あなたのメールアドレス" 
PROMPT = "samurai"

APIを利用する際に、メールアドレスが必要のようです。
と言っても、何かアカウント登録などをするわけではありません。

あくまで、APIの呼び元を判別するために利用しているだけだと思います。
ただし、今後はアカウント登録が必要になるかもしれません。
今は、お試し期間に過ぎない可能性が多いにあります。

そして、「PROMT」に音楽にしたいテキストを入力します。
今回は、「samurai」と入れました。
それで出来上がった音楽は、以下。

samurai_all_tag

正直、まったく「samurai」を連想できません。
でも、せめて音楽のジャンルを選びたいです。

このような場合は、「mubert_tags_string」を変更します。
上記コードでは、多くのキーワードが詰まっています。

音楽のジャンル、楽器などを表すキーワードが記載されています。
ここを調整すれば、望むジャンルの音楽になるようです。

「samurai」のままで、次のように変更してみましょう。

mubert_tags_string = 'reggae'

上記条件で作成された音楽は、以下となります。

samurai_reggae

確かに、レゲエですね。
でも、相変わらず「samurai」はイメージできません。

以上、Mubert-Text-to-Musicの動作確認を説明しました。

タイトルとURLをコピーしました