音声

ボイスチャンネルに接続して音声を再生したり、録音したりする。 音楽 bot や読み上げ bot の核。@discordjs/voice パッケージを使う。

導入

npm install @discordjs/voice
npm install ffmpeg-static       # 音声変換
npm install libsodium-wrappers  # 暗号化(Discord 必須)

# 音楽再生に必要なライブラリ(どちらか)
npm install @discordjs/opus     # ネイティブ実装(速い、コンパイル必要)
# または
npm install opusscript           # ピュア JS(遅いがビルド不要)

暗号化ライブラリ

Discord の音声は暗号化必須。libsodium-wrappers または tweetnacl または sodium-native のどれかが必要。

VC に接続

import { joinVoiceChannel, VoiceConnectionStatus } from "@discordjs/voice"

if (interaction.isChatInputCommand() && interaction.commandName === "join") {
  const member = interaction.member as GuildMember
  const channel = member.voice.channel
  if (!channel) {
    return interaction.reply({ content: "VC に入ってからやって", ephemeral: true })
  }

  const connection = joinVoiceChannel({
    channelId: channel.id,
    guildId: channel.guild.id,
    adapterCreator: channel.guild.voiceAdapterCreator,
    selfDeaf: false,
    selfMute: false,
  })

  connection.on(VoiceConnectionStatus.Ready, () => {
    console.log("接続完了")
  })

  await interaction.reply("接続しました")
}

Intents

GatewayIntentBits.GuildVoiceStates が必須。

音声を再生する

import {
  createAudioPlayer, createAudioResource, AudioPlayerStatus,
} from "@discordjs/voice"

const player = createAudioPlayer()
connection.subscribe(player)

const resource = createAudioResource("./music.mp3")    // ファイルパス or URL or Stream
player.play(resource)

player.on(AudioPlayerStatus.Idle, () => {
  console.log("再生終了")
})
player.on("error", (err) => console.error(err))

AudioResource の入力

音量調整

const resource = createAudioResource("./music.mp3", {
  inlineVolume: true,         // 必須
})
resource.volume?.setVolume(0.5)   // 0.0 - 1.0+

YouTube から再生(音楽 bot)

play-dl または distube/ytdl-core を使う。Discord の規約と YouTube の規約両方に注意。

npm install play-dl
import play from "play-dl"

const stream = await play.stream("https://www.youtube.com/watch?v=...")
const resource = createAudioResource(stream.stream, { inputType: stream.type })
player.play(resource)
YouTube 再生の注意

- YouTube の規約上グレー。商用や大規模配布は避ける
- YouTube が API を頻繁に変えるので、ライブラリの更新が早い方を選ぶ
- 著作権を守る運用にする

キュー(音楽 bot の心臓部)

interface Track { title: string; url: string }
const queues = new Map<string, Track[]>()

function getQueue(guildId: string) {
  if (!queues.has(guildId)) queues.set(guildId, [])
  return queues.get(guildId)!
}

async function playNext(guildId: string, player: AudioPlayer) {
  const q = getQueue(guildId)
  const track = q.shift()
  if (!track) return
  const stream = await play.stream(track.url)
  player.play(createAudioResource(stream.stream, { inputType: stream.type }))
}

player.on(AudioPlayerStatus.Idle, () => {
  playNext(guildId, player)
})

切断

connection.destroy()

メンバーが全員 VC から抜けたら自動切断するパターン:

client.on(Events.VoiceStateUpdate, (oldState, newState) => {
  const channel = oldState.channel
  if (!channel) return
  // bot 以外がいなくなったら
  const human = channel.members.filter((m) => !m.user.bot)
  if (human.size === 0) {
    const conn = getVoiceConnection(channel.guild.id)
    conn?.destroy()
  }
})

録音(受信)

VC のユーザー音声を受信することもできる。文字起こし bot 等に。

import { EndBehaviorType } from "@discordjs/voice"
import * as fs from "node:fs"
import { pipeline } from "node:stream"

const receiver = connection.receiver

receiver.speaking.on("start", (userId) => {
  const audio = receiver.subscribe(userId, {
    end: { behavior: EndBehaviorType.AfterSilence, duration: 1000 },
  })

  // Opus → PCM → WAV など、外部ツールで変換
  const out = fs.createWriteStream(`./rec-${userId}-${Date.now()}.opus`)
  pipeline(audio, out, (err) => {
    if (err) console.error(err)
  })
})

Opus フォーマットで届くので、PCM や WAV にしたい場合は ffmpeg や prism-media で変換。

テキスト読み上げ(TTS)

google-tts-api や OpenAI TTS、say.js 等で生成した音声を createAudioResource で流す。

import googleTTS from "google-tts-api"

const url = googleTTS.getAudioUrl("こんにちは", { lang: "ja", slow: false })
const resource = createAudioResource(url)
player.play(resource)

レイテンシと品質

注意点

既製ライブラリ

音楽 bot を作るなら discord-playerdistube が高機能ラッパとして使える。 キュー管理・フィルタ・複数ソース対応が組み込み。