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からは返り値がString
のreadln()
が使えるようになり、わざわざ!!
を付ける必要がなくなりました。また、これまでの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作成の方法も紹介しているサイトがあります。が、今回はそれを使いません。ライブラリは一切使用せず、自前で書き出しを行っていきます。