関数呼び出しによる音楽再生の実装
- LLM(大規模言語モデル)に基づき、音声対話による音楽再生を実現します。
技術ポイント
- Function Calling:大規模言語モデルのツール呼び出しの仕組みを学びます。
- 非同期プログラミング:Python の asyncio と FastAPI の非同期フレームワークを体験します。
- Web 開発:FastAPI フレームワークの高度な使い方と WebSocket によるリアルタイム通信
- システム統合:複雑なシステムのモジュール設計と統合を習得します。
応用シーン
スマートホームの音声操作、パーソナライズされた音楽コンパニオン、車載音声アシスタント、子ども向けの教育・エンターテインメントのお供など、さまざまなシーンに活用できます。
機能要件
- シナリオ説明:運転者や同乗者が手を使わずに、音声で車内の音楽を安全に操作できます。
- 典型的な対話:
- 運転者:「ジェイ・チョウの曲を流して。」
- LLM:「ジェイ・チョウの『Mojito』を再生しています。道路状況が複雑なので、運転に集中してください。」
ハードウェアの準備
ディスプレイ * 1、Bluetooth キーボード・マウス * 1、マイク * 1、メインコンソール * 1

回路接続
ハードウェアモジュール
ヒント:ハードウェアモジュールの使用説明
ハードウェアシステムの全体組み立て
組み込み AI 開発ボード RDK_X5
Uni mega2560 拡張ボード【V1.1】使用説明
電子モジュールの紹介
物理配線
具体的な配線・取り付け方法については、ハードウェアシステムの全体組み立てを参照してください。
ソフトウェア設計
音楽再生のワークフロー
音声による音楽再生の事例として、「xx の曲を再生する」場合のワークフローを示します。
- ユーザー入力:「『稲香』を再生して」
- LLM による解析:
- 関数呼び出しが必要であることを識別
- パラメータを抽出:曲名 =「稲香」
3. Function Calling で音楽ツールを呼び出し
4. 実行:
音楽プレーヤーツールが「稲香」の再生を開始します
5. 結果の返却:
「『稲香』の再生を開始しました。」
ソースコードのダウンロード
(HumansX サポートより提供)
プログラムコード
"""
音乐播放器功能函数
为LLM提供音乐播放的Function Calling接口
"""
import logging
from typing import Dict, Any
from .music_player import get_music_player
from .function_registry import FunctionRegistry
logger = logging.getLogger(__name__)
@FunctionRegistry.register_function('music_list', '获取可播放的音乐列表')
def music_list_func() -> str:
"""获取音乐列表"""
try:
player = get_music_player()
music_list = player.list_music()
if not music_list:
return "很抱歉,当前没有可播放的音乐。请将音乐文件放入music目录中,并确保已安装pygame库:pip install pygame==2.5.2"
# 生成友好的回复
music_names = []
for music_file in music_list:
# 去掉文件扩展名
name = music_file.rsplit('.', 1)[0]
music_names.append(name)
return f"当前可播放的音乐有:{', '.join(music_names)},共{len(music_names)}首歌曲。"
except Exception as e:
if "pygame库未安装" in str(e):
return "音乐播放器需要pygame库支持。请先安装:pip install pygame==2.5.2"
logger.error(f"获取音乐列表失败: {e}")
return "获取音乐列表时出现了问题,请稍后重试。"
@FunctionRegistry.register_function('music_play', '播放指定的歌曲')
def music_play_func(music_name: str) -> str:
"""播放指定歌曲"""
try:
player = get_music_player()
result = player.play_music(music_name)
return result
except Exception as e:
logger.error(f"播放音乐失败: {e}")
return f"播放音乐时出现了问题:{str(e)}"
@FunctionRegistry.register_function('music_play_random', '随机播放一首歌曲')
def music_play_random_func() -> str:
"""随机播放歌曲"""
try:
player = get_music_player()
result = player.play_random_music()
return result
except Exception as e:
if "pygame库未安装" in str(e):
return "音乐播放器需要pygame库支持。请先安装:pip install pygame==2.5.2"
logger.error(f"随机播放失败: {e}")
return f"随机播放时出现了问题:{str(e)}"
@FunctionRegistry.register_function('music_stop', '停止当前播放的音乐')
def music_stop_func() -> str:
"""停止播放音乐"""
try:
player = get_music_player()
result = player.stop_music()
return result
except Exception as e:
logger.error(f"停止音乐失败: {e}")
return f"停止音乐时出现了问题:{str(e)}"
@FunctionRegistry.register_function('music_volume_set', '设置音乐播放音量')
def music_volume_set_func(volume: float) -> str:
"""设置音量"""
try:
# 验证音量参数
if not isinstance(volume, (int, float)):
return "音量参数必须是数字"
if volume < 0 or volume > 1:
return "音量范围应在0.0到1.0之间"
player = get_music_player()
result = player.set_volume(volume)
return result
except Exception as e:
if "pygame库未安装" in str(e):
return "音乐播放器需要pygame库支持。请先安装:pip install pygame==2.5.2"
logger.error(f"设置音量失败: {e}")
return f"设置音量时出现了问题:{str(e)}"
@FunctionRegistry.register_function('music_volume_up', '增加音乐播放音量')
def music_volume_up_func(step: float = 0.1) -> str:
"""增加音量"""
try:
# 验证步长参数
if not isinstance(step, (int, float)):
return "音量步长必须是数字"
if step <= 0 or step > 1:
return "音量步长应在0到1之间"
player = get_music_player()
result = player.increase_volume(step)
return result
except Exception as e:
if "pygame库未安装" in str(e):
return "音乐播放器需要pygame库支持。请先安装:pip install pygame==2.5.2"
logger.error(f"增加音量失败: {e}")
return f"增加音量时出现了问题:{str(e)}"
@FunctionRegistry.register_function('music_volume_down', '减少音乐播放音量')
def music_volume_down_func(step: float = 0.1) -> str:
"""减少音量"""
try:
# 验证步长参数
if not isinstance(step, (int, float)):
return "音量步长必须是数字"
if step <= 0 or step > 1:
return "音量步长应在0到1之间"
player = get_music_player()
result = player.decrease_volume(step)
return result
except Exception as e:
if "pygame库未安装" in str(e):
return "音乐播放器需要pygame库支持。请先安装:pip install pygame==2.5.2"
logger.error(f"减少音量失败: {e}")
return f"减少音量时出现了问题:{str(e)}"
@FunctionRegistry.register_function('music_status', '获取当前音乐播放状态')
def music_status_func() -> str:
"""获取播放状态"""
try:
player = get_music_player()
status = player.get_current_status()
if status["is_playing"] and status["current_music"]:
music_name = status["current_music"].rsplit('.', 1)[0]
volume_percent = int(status["volume"] * 100)
return f"🎵 正在播放《{music_name}》,音量:{volume_percent}%"
else:
volume_percent = int(status["volume"] * 100)
return f"⏸ 当前没有播放音乐,音量设置为{volume_percent}%,共有{status['music_count']}首歌曲可供播放。"
except Exception as e:
if "pygame库未安装" in str(e):
return "音乐播放器需要pygame库支持。请先安装:pip install pygame==2.5.2"
logger.error(f"获取播放状态失败: {e}")
return "获取播放状态时出现了问题。"
def parse_volume_value(value) -> tuple:
"""解析音量值,支持多种格式"""
if isinstance(value, (int, float)):
v = float(value)
if v < 0:
return 0.0, 'mute'
elif v > 1:
return 1.0, 'max'
else:
return v, 'normal'
if isinstance(value, str):
v = value.strip().lower()
# 支持中文描述
if v in ['静音', 'mute']:
return 0.0, 'mute'
elif v in ['最大', '最大音量', '全开']:
return 1.0, 'max'
elif v in ['最小', '最小音量']:
return 0.0, 'mute'
elif v in ['一半', '中等', '50%']:
return 0.5, 'normal'
# 支持百分比
if v.endswith('%'):
try:
percent = float(v[:-1])
if percent <= 0:
return 0.0, 'mute'
elif percent >= 100:
return 1.0, 'max'
else:
return percent / 100.0, 'normal'
except ValueError:
pass
# 尝试直接转换为数字
try:
f = float(v)
if f < 0:
return 0.0, 'mute'
elif f > 1:
return 1.0, 'max'
else:
return f, 'normal'
except ValueError:
pass
# 默认值
return 0.5, 'normal'
@FunctionRegistry.register_function('music_volume_set_smart', '智能设置音乐音量(支持数字、百分比、中文描述)')
def music_volume_set_smart_func(value) -> str:
"""智能设置音量,支持多种输入格式"""
try:
volume, vtype = parse_volume_value(value)
player = get_music_player()
result = player.set_volume(volume)
# 根据输入类型优化回复
if vtype == 'mute':
return "音乐已为您静音。"
elif vtype == 'max':
return "音乐音量已调到最大。"
else:
volume_percent = int(volume * 100)
return f"音乐音量已设置为{volume_percent}%。"
except Exception as e:
logger.error(f"智能设置音量失败: {e}")
return f"设置音量时出现了问题:{str(e)}"
"""
音乐播放器工具实现
只支持实际音频播放的完整版本
"""
import os
import logging
import random
import threading
import time
from typing import List, Optional, Dict, Any
from pathlib import Path
# 尝试导入pygame,如果不可用则抛出错误
try:
import pygame
import pygame.mixer
_pygame_available = True
logger = logging.getLogger(__name__)
logger.info("pygame音频播放库可用")
except ImportError:
pygame = None
pygame.mixer = None
_pygame_available = False
logger = logging.getLogger(__name__)
logger.error("pygame不可用,音乐播放器无法工作。请运行: pip install pygame==2.5.2")
class SimpleMusicPlayer:
"""完整的音乐播放器类,只支持实际音频播放"""
def __init__(self, music_dir: str = "music"):
if not _pygame_available:
raise RuntimeError("pygame库未安装,音乐播放器无法工作。请安装: pip install pygame==2.5.2")
self.music_dir = Path(music_dir)
self.current_music = None
self.is_playing = False
self.volume = 0.7
self._music_list = []
self._playback_thread = None
self._stop_event = threading.Event()
# 创建音乐目录
self.music_dir.mkdir(exist_ok=True)
# 初始化pygame mixer
self._init_audio()
# 初始化音乐列表
self._load_music_list()
logger.info(f"音乐播放器初始化完成,目录: {self.music_dir}")
def _init_audio(self):
"""初始化音频系统"""
try:
pygame.mixer.init(frequency=22050, size=-16, channels=2, buffer=512)
pygame.mixer.music.set_volume(self.volume)
logger.info("pygame音频系统初始化成功")
except Exception as e:
logger.error(f"pygame音频系统初始化失败: {e}")
raise RuntimeError(f"音频系统初始化失败: {e}")
def _load_music_list(self) -> List[str]:
"""加载音乐文件列表"""
try:
if not self.music_dir.exists():
logger.warning(f"音乐目录不存在: {self.music_dir}")
self._music_list = []
return []
# 查找音频文件
audio_extensions = ['.mp3', '.wav', '.flac', '.m4a', '.ogg']
files = []
for file in self.music_dir.iterdir():
if file.is_file() and file.suffix.lower() in audio_extensions:
files.append(file.name)
self._music_list = files
logger.info(f"加载了 {len(files)} 个音乐文件")
return files
except Exception as e:
logger.error(f"加载音乐列表失败: {e}")
self._music_list = []
return []
def list_music(self) -> List[str]:
"""获取音乐列表"""
return self._music_list
def _monitor_playback(self):
"""监控播放状态"""
while not self._stop_event.wait(1.0):
try:
if not pygame.mixer.music.get_busy() and self.is_playing:
# 音乐自然播放结束
logger.info("音乐播放自然结束")
self.is_playing = False
self.current_music = None
break
except Exception as e:
logger.error(f"监控播放状态时出错: {e}")
break
logger.debug("音乐播放监控线程结束")
def play_music(self, music_name: str) -> str:
"""播放指定音乐"""
try:
# 查找音乐文件
music_file = self._find_music_file(music_name)
if not music_file:
return f"很抱歉,未找到歌曲《{music_name}》"
# 停止当前播放
if self.is_playing:
self.stop_music()
music_path = self.music_dir / music_file
# 实际播放音频
try:
pygame.mixer.music.load(str(music_path))
pygame.mixer.music.set_volume(self.volume)
pygame.mixer.music.play()
self.current_music = music_file
self.is_playing = True
# 启动监控线程
self._stop_event.clear()
self._playback_thread = threading.Thread(target=self._monitor_playback, daemon=True)
self._playback_thread.start()
music_title = Path(music_file).stem
logger.info(f"开始播放音乐: {music_path}")
return f"🎵 正在为您播放《{music_title}》,音量: {int(self.volume * 100)}%"
except Exception as e:
logger.error(f"播放失败: {e}")
return f"播放失败: {str(e)}"
except Exception as e:
logger.error(f"播放音乐失败: {e}")
return f"播放失败: {str(e)}"
def play_random_music(self) -> str:
"""随机播放音乐"""
if not self._music_list:
return "很抱歉,当前没有可播放的音乐"
random_music = random.choice(self._music_list)
return self.play_music(random_music)
def stop_music(self) -> str:
"""停止播放音乐"""
if not self.is_playing:
return "当前没有正在播放的音乐"
try:
pygame.mixer.music.stop()
logger.info("停止音频播放")
except Exception as e:
logger.error(f"停止播放失败: {e}")
# 停止监控线程
self._stop_event.set()
if self._playback_thread and self._playback_thread.is_alive():
self._playback_thread.join(timeout=2.0)
self.is_playing = False
music_title = Path(self.current_music).stem if self.current_music else "未知歌曲"
self.current_music = None
logger.info(f"停止播放音乐: {music_title}")
return f"🛑 已停止播放《{music_title}》"
def set_volume(self, volume: float) -> str:
"""设置音量"""
try:
# 验证音量范围
volume = max(0.0, min(1.0, volume))
self.volume = volume
# 实时调整音量
try:
pygame.mixer.music.set_volume(volume)
logger.info(f"调整音量到: {volume}")
except Exception as e:
logger.error(f"音量调整失败: {e}")
percentage = int(volume * 100)
logger.info(f"音量设置为: {percentage}%")
return f"🔊 音乐音量已设置为 {percentage}%"
except Exception as e:
logger.error(f"设置音量失败: {e}")
return f"设置音量失败: {str(e)}"
def increase_volume(self, step: float = 0.1) -> str:
"""增加音量"""
new_volume = min(1.0, self.volume + step)
return self.set_volume(new_volume)
def decrease_volume(self, step: float = 0.1) -> str:
"""减少音量"""
new_volume = max(0.0, self.volume - step)
return self.set_volume(new_volume)
def get_current_status(self) -> Dict[str, Any]:
"""获取当前播放状态"""
return {
"is_playing": self.is_playing,
"current_music": self.current_music,
"volume": self.volume,
"music_count": len(self._music_list)
}
def _find_music_file(self, music_name: str) -> Optional[str]:
"""查找音乐文件"""
# 精确匹配
if music_name in self._music_list:
return music_name
# 不带扩展名匹配
name_without_ext = Path(music_name).stem
for music_file_item in self._music_list:
if Path(music_file_item).stem == name_without_ext:
return music_file_item
# 模糊匹配
for music_file_item in self._music_list:
if name_without_ext.lower() in Path(music_file_item).stem.lower():
return music_file_item
return None
# 全局音乐播放器实例
_music_player_instance: Optional[SimpleMusicPlayer] = None
def get_music_player() -> SimpleMusicPlayer:
"""获取全局音乐播放器实例"""
global _music_player_instance
if _music_player_instance is None:
_music_player_instance = SimpleMusicPlayer()
return _music_player_instance
def initialize_music_player(music_dir: str = "music"):
"""初始化音乐播放器"""
global _music_player_instance
_music_player_instance = SimpleMusicPlayer(music_dir)
return _music_player_instance
操作手順:
操作動画
具体的な操作(テキスト版)
回路接続が完了したら:
- ターミナルからリモートアクセスします。ターミナルで次のコマンドを入力し、Enter キーを押します。
ssh sunrise@192.168.8.111
- サービスを起動します。llm フォルダに移動し、メインプログラム python main.py を起動します。

Enter キーを押して起動を待ちます。次の画面が表示されたら、http:// 0.0.0.0:8002 を開けることを示しています。

- ブラウザを開き、URL http:// 0.0.0.0:8002 を入力してアクセスします。起動すると LLM インテリジェント対話アシスタントのメイン画面が表示されます。

- 右側のメニューの「音楽コントロール」をクリックすると、音楽リストの表示、ランダム再生、音量調整などの操作ができます。


実験結果
音楽リストを表示し、音楽をランダム再生できます。また、音量の調整(上げる・下げる)や再生の停止もできます。
サポート窓口
資料のダウンロード・技術サポートについては、HumansX株式会社までお問い合わせください。
ドキュメント