はじめに
先週のライブ会場で紹介した、AI作曲ツール「SUNO」で作ったアルバム「Hiking Song」。
収録曲「新道と古道」は、自分でも気に入っていたのですが、案の定、女性二名から絶賛の声が届きました!
https://suno.com/playlist/c35fdc89-85e4-4553-8cc4-585bfc553b73
https://www.youtube.com/watch?v=QYrHWe_eLZA
やはりAI作曲のポイントは作詞で、どこまで人の心に響く歌詞を作れるかにかかっているのだと改めて実感しました。
そこで、AIに曲の歌詞をブラッシュアップさせるボットを作ってみることに!
GoogleのAI Studioが無料で使えて便利なので、今回はその開発ダイジェストと、なぜか完成してしまったジョーク作品をご紹介します。
SUNOの歌詞を洗練させるつもりが、なぜかお笑いの道に進んでしまったという…
そんな開発の舞台裏を覗いてみてください!
SUNOAIの歌詞を洗練させるボットとは?
「SUNOAIの歌詞を洗練させるボット」は、その名の通り、SUNOで作成した歌詞をより洗練されたものにすることを目的としたAIボットです。
例えば、
- より自然な日本語表現にする
- 表現を豊かにする
- 歌詞に深みを与える
- 特定のテーマに沿った歌詞にする
といったことをAIの力を借りて行います。
開発ダイジェスト
Google AI Studioを使って開発を進めています。
2つのAIエージェントにロールを与えて会話をしてもらうだけの機能です。
{knowledge_source_1}の部分は将来的には、RAGでロールにあったスキルプロンプトをデータベースから引っ張る予定です。
import tkinter as tk
from tkinter import scrolledtext
import requests
import json
from dotenv import load_dotenv
import os
import threading
import time
load_dotenv()
API_TOKEN_1 = os.getenv("API_TOKEN_1")
API_TOKEN_2 = os.getenv("API_TOKEN_2")
conversation_history = []
def log_request(data, api_token):
"""Log the API request data to a file."""
with open("api_requests.log", "a", encoding="utf-8") as log_file:
log_file.write(f"API Token: {api_token}\n")
log_file.write(f"Request Data: {json.dumps(data, ensure_ascii=False, indent=2)}\n")
log_file.write("\n")
def generate_response(prompt, api_token):
url = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent'
headers = {'Content-Type': 'application/json'}
data = {
"contents": [{"parts": [{"text": prompt}]}]
}
params = {'key': api_token}
log_request(data, api_token)
try:
response = requests.post(url, headers=headers, params=params, data=json.dumps(data))
response.raise_for_status()
result = response.json()
if 'candidates' in result and result['candidates']:
return result['candidates'][0]['content']['parts'][0]['text']
else:
return "Error: Unable to generate response"
except requests.RequestException as e:
return f"Error: {str(e)}"
def continue_conversation():
global conversation_history
repeat_count = int(repeat_entry.get())
for _ in range(repeat_count):
for agent_num, api_token, agent_prompt in [(1, API_TOKEN_1, agent_1_prompt_entry.get("1.0", tk.END).strip()), (2, API_TOKEN_2, agent_2_prompt_entry.get("1.0", tk.END).strip())]:
update_chat_history(f"エージェント{agent_num}が考え中...\n")
full_prompt = agent_prompt
truncated_history = conversation_history[-5:]
for message in truncated_history:
full_prompt += f"{message['speaker']}: {message['content']}\n"
agent_response = generate_response(full_prompt, api_token)
conversation_history.append({"speaker": f"agent_{agent_num}", "content": agent_response})
update_chat_history(f"エージェント{agent_num}: {agent_response}\n")
time.sleep(2)
update_chat_history("会話は終了しました。\n\n")
input_field.config(state="normal")
send_button.config(state="normal")
def send_message():
user_message = input_field.get()
input_field.delete(0, tk.END)
input_field.config(state="disabled")
send_button.config(state="disabled")
conversation_history.append({"speaker": "user", "content": user_message})
update_chat_history(f"あなた: {user_message}\n")
threading.Thread(target=continue_conversation, daemon=True).start()
def update_chat_history(message):
chat_history.config(state="normal")
chat_history.insert(tk.END, message)
chat_history.see(tk.END)
chat_history.config(state="disabled")
def maximize_window():
window.state('zoomed')
def clear_chat_history():
global conversation_history
conversation_history = []
chat_history.config(state="normal")
chat_history.delete(1.0, tk.END)
chat_history.config(state="disabled")
window = tk.Tk()
window.title("チャットアプリ")
chat_history = scrolledtext.ScrolledText(window, state="disabled", height=20, width=50)
chat_history.pack(padx=10, pady=10)
input_field = tk.Entry(window, width=50)
input_field.pack(padx=10, pady=(0, 10))
send_button = tk.Button(window, text="送信", command=send_message)
send_button.pack(pady=(0, 10))
repeat_label = tk.Label(window, text="リピート回数:")
repeat_label.pack()
repeat_entry = tk.Entry(window, width=5)
repeat_entry.insert(0, "10")
repeat_entry.pack()
agent_1_prompt_label = tk.Label(window, text="エージェント1のプロンプト:")
agent_1_prompt_label.pack()
agent_1_prompt_entry = tk.Text(window, height=5, width=50)
agent_1_prompt_entry.insert(tk.END, "あなたは放送作家です。\n{knowledge_source_1}")
agent_1_prompt_entry.pack()
agent_2_prompt_label = tk.Label(window, text="エージェント2のプロンプト:")
agent_2_prompt_label.pack()
agent_2_prompt_entry = tk.Text(window, height=5, width=50)
agent_2_prompt_entry.insert(tk.END, "あなたはプロデューサーです。\n{knowledge_source_2}")
agent_2_prompt_entry.pack()
clear_button = tk.Button(window, text="チャット履歴をクリア", command=clear_chat_history)
clear_button.pack(pady=(10, 0))
maximize_button = tk.Button(window, text="最大化", command=maximize_window)
maximize_button.pack(pady=(10, 10))
window.mainloop()
まとめ
SUNOAIの歌詞を洗練させるボットは、現在も開発中です。
将来的には、
- より高度な歌詞生成機能
- 様々な音楽ジャンルへの対応
- ユーザーインターフェースの改善
などを行い、より使いやすいボットを目指していきます。
今回の開発を通して、AIは単なるツールではなく、人間の創造性を刺激するパートナーになり得ると感じました。
今後もAIの可能性を探求し、音楽制作の可能性を広げていきたいと思います。
最後に
今回の開発は、まだまだ始まったばかりです。
SUNOAIの歌詞を洗練させるボットが、どのように進化していくのか、楽しみにしていてください!
また、この記事を読んで、AI作曲に興味を持った方は、ぜひSUNOを試してみてください。
きっと、あなただけのオリジナルソングが作れるはずです。
追伸
開発を進めるうちに、SUNOが生成する歌詞は、時に予想外の面白さやユーモアを持っていることに気づきました。
そこで、方向転換!
歌詞を洗練させるだけでなく、その面白さをさらに引き出すようなボットを作ることにしました。
どんなプロンプトを使ったかは、ご想像にお任せします。