LoginSignup
3
5

More than 1 year has passed since last update.

pycordでボイスチャットの録音

Last updated at Posted at 2022-09-23

Qitta初投稿です。
とりあえず練習がてら、記事を書きます。

DiscordAPIには音声を録音する機能があります。
ですがDiscord.pyではプライバシーの面で実装されておらず、Discord.jsは頻繁に変更が施されるため、簡単に録音できる手段は確立されていませんでした。

そんな中で22年3月、Discord.pyの派生ライブラリ、pycordに録音機能が実装されました。
日本語での解説が見当たらなかったので、ここで説明します。

環境

python 3.8.6 64bit(ここに関しては3.10とかでも問題なし)

pipインストール
pip install discord
pip install git+https://github.com/Pycord-Development/pycord
pip install pydub

必ずDiscord.pyをインストールした後にpycordをインストールしましょう!!
また、音声を使用するのでffmpegをインストールすることを忘れずに。

サンプルコード

録音したファイルをDiscord上にアップロードする場合。

import discord

intents = discord.Intents().all()
bot = discord.Bot(intents=intents)
Token=""

@bot.command()
async def start_record(ctx:discord.ApplicationContext):
    # コマンドを使用したユーザーのボイスチャンネルに接続
    try:
       vc = await ctx.author.voice.channel.connect()
       await ctx.respond("録音開始...")
    except AttributeError:
       await ctx.respond("ボイスチャンネルに入ってください")
       return
        
    # 録音開始。mp3で帰ってくる。wavだとなぜか壊れる。
    ctx.voice_client.start_recording(discord.sinks.MP3Sink(), finished_callback, ctx)


async def finished_callback(sink:discord.sinks.MP3Sink, ctx:discord.ApplicationContext):
    # 録音したユーザーの音声を取り出す
    recorded_users = [
        f"<@{user_id}>"
        for user_id, audio in sink.audio_data.items()
    ]
    # discordにファイル形式で送信。拡張子はmp3。
    files = [discord.File(audio.file, f"{user_id}.{sink.encoding}") for user_id, audio in sink.audio_data.items()]
    await ctx.channel.send(f"録音終了! 音声ファイルはこちら! {', '.join(recorded_users)}.", files=files) 

@bot.command()
async def stop_recording(ctx:discord.ApplicationContext):
    # 録音停止
    ctx.voice_client.stop_recording()
    await ctx.respond("録音終了!")
    await ctx.voice_client.disconnect()


bot.run(Token)

ローカルに保存する場合。

import discord
from pydub import AudioSegment

intents = discord.Intents().all()
bot = discord.Bot(intents=intents)

Token=""

@bot.slash_command()
async def start_record(ctx:discord.ApplicationContext):

        # コマンドを使用したユーザーのボイスチャンネルに接続
        try:
            vc = await ctx.author.voice.channel.connect()
            await ctx.respond("録音開始...")
        except AttributeError:
            await ctx.respond("ボイスチャンネルに入ってください")
            return
        
        # 録音開始。mp3で帰ってくる。wavだとなぜか壊れる。
        ctx.voice_client.start_recording(discord.sinks.MP3Sink(), finished_callback, ctx)

@bot.slash_command()
async def stop_recording(ctx:discord.ApplicationContext):
        # 録音停止
        ctx.voice_client.stop_recording() 
        await ctx.respond("録音終了!")
        await ctx.voice_client.disconnect()

# 録音終了時に呼び出される関数
async def finished_callback(sink:discord.sinks.MP3Sink, ctx:discord.ApplicationContext):
    # 録音したユーザーの音声を取り出す
    for user_id, audio in sink.audio_data.items():
        # mp3ファイルとして書き込み。その後wavファイルに変換。
        song = AudioSegment.from_file(audio.file, format="mp3")
        song.export(f"./{user_id}.wav", format='wav')


bot.run(Token)

ポイントとなる点はこちら。

# 録音開始。mp3で帰ってくる。wavだとなぜか壊れる。
ctx.voice_client.start_recording(discord.sinks.MP3Sink(), finished_callback, ctx)

これで録音が開始されます。
discord.sinks.MP3Sinkでmp3形式で録音することを指定しています。
これをdiscord.sinks.WaveSinkにするとwavファイル、discord.sinks.M4ASinkにするとm4aファイルとして録音を開始します。

また、finished_callbackは録音終了時に実行される関数でsink(録音データ)とctx(スラッシュコマンドのデータ)を引数として持ってきます。

sinkの中にはユーザーid毎に録音内容のバイナリデータが入っていて、forで一つ一つファイルに変換しています。

その他

録音開始のコマンドを打っても、何か発言しなければ録音は開始されません。

例えばa,b,cの3人がボイスチャンネルにいたとします。
録音を開始し、aだけが発言した後終了するとaの録音データのみが返却されます。

ローカルに音声を保存する場合、mp3は問題ないのですが、なぜかwavで保存するとファイルが壊れます

原因についてよくわからなかったのでサンプルコードではmp3にした後wavに変換しています。

(2022/09/26 追記)
また、ステージチャンネルでは録音できないようです。

最後に

初投稿なので色々雑な部分が多いですが、お役に立ったら幸いです。

参考

stackoverflow-discord-receive-audio

3
5
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
3
5