1/(1+e^(-ax))

ポケモンとかPCとか

Discord Botで予め準備しておいた音楽を連続再生する

Top→Discord Botの作り方 - 1/(1+e^(-ax))
prev→Discord Botで予め準備しておいた音楽を再生する - 1/(1+e^(-ax))
next→Discord BotでCogを使ってみる - 1/(1+e^(-ax))

前回作成したbgmコマンドを、!bgmと入力した回数だけ再生されるようにしてみます。

作成にあたりこちらの記事を参考にしました。→Pythonで始める録音機能付きDiscord Bot: (4) 音楽ファイルを再生する - Qiita


前回vc.play(...)で音楽ファイルを再生しましたが、これをvc.play(... , after= function)とすることで再生終了時に関数functionを呼び出すことができるのでこれを利用します。
また、今回は特定の1曲だけを再生しますが、後々のことを考えて再生にはキューを使用することにします。
キューという概念の説明については割愛します。そこまで難しいものではないので軽く調べてもらえば理解できると思います。


pythonのasyncioというライブラリにキューが実装されてるので、これを都合よく改造します。

再生キューであるAudioQueueクラス

class AudioQueue(asyncio.Queue):
    def __init__(self):
        super().__init__(0)         #再生キューの上限を設定しない

    def __getitem__(self, idx):
        return self._queue[idx]     #idx番目を取り出し

    def to_list(self):
        return list(self._queue)    #キューをリスト化

    def reset(self):
        self._queue.clear()         #キューのリセット

asyncio.Queueクラスを継承しています。
クラスがよく分からない方は、とりあえずこれで再生キューが作成できたと思ってもらって結構です。


次に、現在の再生状況を管理するAudioStatusクラスを作成します。
これには、自分が今どのボイスチャンネルにいて、再生キューがどうなっているのかといった情報を保持してもらいます。

class AudioStatus:
    def __init__(self, vc):
        self.vc = vc                                #自分が今入っているvc
        self.queue = AudioQueue()                   #再生キュー
        self.playing = asyncio.Event()
        asyncio.create_task(self.playing_task())

    #曲の追加
    async def add_audio(self, path):
        await self.queue.put(path)

    #曲の再生(再生にはffmpegが必要)    
    async def playing_task(self):
        while True:
            self.playing.clear()
            try:
                path = await asyncio.wait_for(self.queue.get(), timeout = 100)
            except asyncio.TimeoutError:
                asyncio.create_task(self.leave())
            selfpath = os.path.dirname(__file__)
            self.vc.play(discord.FFmpegPCMAudio(executable=selfpath+"/bin/ffmpeg.exe", source=path), after = self.play_next)
            await self.playing.wait()

    
    #playing_taskの中で呼び出される
    #再生が終わると次の曲を再生する
    def play_next(self, err=None):
        self.bgminfo = None
        self.playing.set()
        return
            
    #vcから切断
    async def leave(self):
        self.queue.reset()  #キューのリセット
        if self.vc:
            await self.vc.disconnect()
            self.vc = None
        return


前回グローバル変数VCで行っていたボイスチャンネルの管理もこのクラスで行うので、切断用の関数をこちらに実装します。


AudioStatusはadd_audioでキューに曲を追加することができるので、プログラムの完成版は次のようになります。
import asyncioやawaitの抜けに注意しましょう。

import discord
from discord.ext import commands
import os
import asyncio

TOKEN  = "token" #トークン
PREFIX = '!'       #prefix=接頭辞

#bgmコマンドで使う再生キュー
class AudioQueue(asyncio.Queue):
    def __init__(self):
        super().__init__(0)         #再生キューの上限を設定しない

    def __getitem__(self, idx):
        return self._queue[idx]     #idx番目を取り出し

    def to_list(self):
        return list(self._queue)    #キューをリスト化

    def reset(self):
        self._queue.clear()         #キューのリセット

#bgmコマンドで使う,現在の再生状況を管理するクラス
class AudioStatus:
    def __init__(self, vc):
        self.vc = vc                                #自分が今入っているvc
        self.queue = AudioQueue()                   #再生キュー
        self.playing = asyncio.Event()
        asyncio.create_task(self.playing_task())

    #曲の追加
    async def add_audio(self, path):
        await self.queue.put(path)

    #曲の再生(再生にはffmpegが必要)    
    async def playing_task(self):
        while True:
            self.playing.clear()
            try:
                path = await asyncio.wait_for(self.queue.get(), timeout = 100)
            except asyncio.TimeoutError:
                asyncio.create_task(self.leave())
            selfpath = os.path.dirname(__file__)
            self.vc.play(discord.FFmpegPCMAudio(executable=selfpath+"/bin/ffmpeg.exe", source=path), after = self.play_next)
            await self.playing.wait()

    
    #playing_taskの中で呼び出される
    #再生が終わると次の曲を再生する
    def play_next(self, err=None):
        self.bgminfo = None
        self.playing.set()
        return
            
    #vcから切断
    async def leave(self):
        self.queue.reset()  #キューのリセット
        if self.vc:
            await self.vc.disconnect()
            self.vc = None
        return
    
#botの作成
bot = commands.Bot(command_prefix=PREFIX)

Audio_queue = None #AudioStatusの宣言

#予め決めておいた音楽ファイルを再生する
@bot.command()
async def bgm(ctx):
    """play music"""
    global Audio_queue   #この関数内ではVCはグローバル変数のVCを指す

    if (ctx.author.voice is None):  #送信者がボイスチャンネルにいなければエラーを返す
        await send_message(ctx.send, ctx.author.mention, 'ボイスチャンネルが見つかりません')
        return

    if ((Audio_queue is None) or (Audio_queue.vc is None)):      #botがボイスチャンネルに入っていなければ
        voice_channel = ctx.author.voice.channel.id                     #送信者の入っているボイスチャンネルのID
        vc = await bot.get_channel(voice_channel).connect()             #ボイスチャンネルに入る
        Audio_queue = AudioStatus(vc)

    path = os.path.dirname(__file__)    #このファイルが置いてあるディレクトリまでのファイルパス

    #music.mp3をキューに追加
    await Audio_queue.add_audio(path+'/bgm/music.mp3')

    return

#botをボイスチャンネルから切断する
@bot.command()
async def remove(ctx):
    await Audio_queue.leave()
    return

bot.run(TOKEN)


!bgmと2回打つとmusic.mp3が2回再生されるようになれば成功です。
お疲れ様でした。


next→Discord BotでCogを使ってみる - 1/(1+e^(-ax))