ある昼下がりの一室

色々と書いたり書かなかったりします

Kotlin 1.6.0で標準入力

この記事はNCCアドベントカレンダー1日目の記事です。

2021年11月16日にKotlin 1.6.0がリリースされました。そこで、標準入力に関する変更がありました。

New readline functions

Kotlin 1.6.0 offers new functions for handling standard input: readln() and readlnOrNull().

これまで、標準入力を行う際にはreadlne()を使っていました。しかし、返り値がString?のため、競プロで使う場合などは毎回!!を付けて非null許容型にしていました。それが、1.6からは返り値がStringreadln()が使えるようになり、わざわざ!!を付ける必要がなくなりました。また、これまでのreadline()readlnOrNull()になり、nullを許容するかしないか明確になりました。

実際の使用例はこのようになります。

1つの整数を読み込む場合

// val n = readline()!!.toInt()
val n = readln().toInt()

一行に複数の整数が空白区切りで渡される場合

// val list = readline()!!.split(" ").map{ it.toInt() }
val list = readln().split(" ").map{ it.toInt() }

素数nを入力し、続くn行に要素を入力していく場合

val n = readln().toInt()
val list = List(n) {
    readln().toInt()
}

当然ですが、標準入力の関数が変わった以外は変わってないです。競プロにおいて1.6.0のサポートは当然来ていないのでこれを競プロ使えるのがいつになるのか分かりませんが…。1.0.0のところはさっさとアップデートしてほしい

余談ですが、これらの関数は現状Kotlin/JSでは使えません。

Currently this function is not supported in Kotlin/JS and throws UnsupportedOperationException.

Discord.pyとGoogle Cloud Text-to-Speech APIで読み上げbotを作る

この記事はNCCアドベントカレンダー23日目の記事です。

サークルのハッカソンShovel喋太郎のようなDiscord読み上げBotを作ったのでそのメモを残しておきます。

環境

Windows 10 Home

Python 3.8.6

Discord.py 1.5.1

Google Cloud Text-to-Speech August 24, 2020

環境構築

Pythonの環境構築はそこら中に記事があるので省略します。

Discord.pyの導入

以下のコマンドを実行してインストールしてください。

py -3 -m pip install -U discord.py[voice]

Discord Botの作成

discord.pyのドキュメントにBotの作り方が載っています。 Bot Permissionsは"View Channels"、 "Send Messeages"、 "Read Message History"、"Connect"、"Speak"、"Use Voice Activity"にチェックを入れてください。 また、トークンは後で使うのでコピーしておきましょう。

Google Cloud Text-to-Speech APIの設定

この記事に詳しく書いてあります。(環境変数の設定までで大丈夫です)

外部の記事に任せすぎ

コードを書く

Botを起動させる

import discord

#ここに自分のBotのトークンを貼り付ける
TOKEN = "YOUR_TOKEN" 

#Botのオブジェクトを生成
client =discord.Client()

@client.event
async def on_ready():
    print('Login!!!')

client.run(TOKEN)

サーバーにBotが追加されていれば、たった数行のコードを実行するだけでBotがサーバーに接続されました。

ボイスチャンネルに参加させる

ここでは!connectとテキストを打つとその人が参加しているボイスチャンネルに接続するようにします。

import discord
from discord.channel import VoiceChannel

voiceChannel: VoiceChannel 

@client.event
async def on_message(message):
    global voiceChannel

    if message.author.bot:
        return
    if message.content == '!connect':
        voiceChannel = await VoiceChannel.connect(message.author.voice.channel)
        await message.channel.send('読み上げBotが参加しました')

ボイスチャンネルから切断させる

ここでは!disconnectとテキストを打つとその人が参加しているボイスチャンネルに接続するようにします。

import discord
from discord.channel import VoiceChannel

voiceChannel: VoiceChannel 

@client.event
async def on_message(message):
    global voiceChannel

    if message.author.bot:
        return
   if message.content == '!disconnect':
        voiceChannel.stop()
        await message.channel.send('読み上げBotが退出しました')
        await voiceChannel.disconnect()

チャットに入力された文章を再生する

まず、入力されたテキストをSSML(Speech Synthesis Markup Language)に変換します。

import html

def text_to_ssml(text):
    escaped_lines = html.escape(text)
    ssml = "{}".format(
        escaped_lines.replace("\n", '\n<break time="1s"/>')
    )
    return ssml

そして、SSMLをGoogle Cloud Text-to-Speech APIに渡して音声ファイルに変換し、保存します。

from google.cloud import texttospeech

def ssml_to_speech(ssml, file, language_code, gender):
    ttsClient = texttospeech.TextToSpeechClient()
    synthesis_input = texttospeech.SynthesisInput(text=ssml)
    voice = texttospeech.VoiceSelectionParams(
        language_code=language_code, ssml_gender=gender
    )
    audio_config = texttospeech.AudioConfig(
        audio_encoding=texttospeech.AudioEncoding.MP3
    )
    response = ttsClient.synthesize_speech(
        input=synthesis_input, voice=voice, audio_config=audio_config
    )
    with open(file, "wb") as out:
        out.write(response.audio_content)
    return file

最後に、保存した音声ファイルを再生することでチャットに入力された文章をボイスチャンネルで読み上げることが出来ます。

import discord
from discord.channel import VoiceChannel
from discord.player import FFmpegPCMAudio

def play_voice(text):
    ssml = text_to_ssml(text)
    file = ssml_to_speech(ssml, "voice.mp3", "ja-JP", texttospeech.SsmlVoiceGender.MALE)
    voiceChannel.play(FFmpegPCMAudio(file))

一連のコードは以下のようになってます。(これまでに紹介したコードを少し改変しています)

import discord
import html
from discord.channel import VoiceChannel
from discord.player import FFmpegPCMAudio
from google.cloud import texttospeech

TOKEN = 'YOUR_TOKEN'
client = discord.Client()

voiceChannel: VoiceChannel 

@client.event
async def on_ready():
    print('Login!!!')

@client.event
@client.event
async def on_message(message):
    global voiceChannel

    if message.author.bot:
        return
    if message.content == '!connect':
        voiceChannel = await VoiceChannel.connect(message.author.voice.channel)
        await message.channel.send('読み上げBotが参加しました')
        return
    elif message.content == '!disconnect':
        voiceChannel.stop()
        await message.channel.send('読み上げBotが退出しました')
        await voiceChannel.disconnect()
        return

    play_voice(message.content)

def text_to_ssml(text):
    escaped_lines = html.escape(text)
    ssml = "{}".format(
        escaped_lines.replace("\n", '\n<break time="1s"/>')
    )
    return ssml

def ssml_to_speech(ssml, file, language_code, gender):
    ttsClient = texttospeech.TextToSpeechClient()
    synthesis_input = texttospeech.SynthesisInput(text=ssml)
    voice = texttospeech.VoiceSelectionParams(
        language_code=language_code, ssml_gender=gender
    )
    audio_config = texttospeech.AudioConfig(
        audio_encoding=texttospeech.AudioEncoding.MP3
    )
    response = ttsClient.synthesize_speech(
        input=synthesis_input, voice=voice, audio_config=audio_config
    )
    with open(file, "wb") as out:
        out.write(response.audio_content)
        print("Audio content written to file " + file)
    return file

def play_voice(text):
    ssml = text_to_ssml(text)
    file = ssml_to_speech(ssml, "voice.mp3", "ja-JP", texttospeech.SsmlVoiceGender.MALE)
    voiceChannel.play(FFmpegPCMAudio(file))

client.run(TOKEN)

これで読み上げbotに関する最低限の機能は実装できました。あとはお好みで色々な機能をつけてみると良いと思います。

【Processing】SMFを出力する

ProcessingでSMFを出力する方法を紹介します。


環境

Processing 3.5.3


SMFとは

SMFは正式名称Standard MIDI Fileといい、MIDIの演奏データを保存する基本ファイルフォーマットです。拡張子は.midです。SMFはファイルサイズが小さく、データ構造がシンプルであることが特徴です。よく分からんって人はデジタルの楽譜のようなものだと思ってもらえれば良いです。この記事ではファイル構造について適宜説明していきますが、詳しく知りたい方は下記のサイトを参照して下さい。 sites.google.com


Javaのパッケージについて

Javaのパッケージにjavax.sound.midiがあります。これを用いてSMF作成の方法も紹介しているサイトがあります。が、今回はそれを使いません。ライブラリは一切使用せず、自前で書き出しを行っていきます。


続きを読む

このブログについて

気ままに色々と書いていくブログです。

更新は不定期です。書くことがなければ何も更新されません。1ヶ月に1回更新出来ればいいほうだと思います。

技術系の話題がメインですがたまに全く関係ないことも書く予定です。