蛇ノ目の記

技術のことも。そうでないことも。

midoを使ってPythonでMIDIファイルを作る - テキストからメロディを作ってみた

半年以上からMIDIコントローラーを買ったりしていたものの何もしないまま歳を越してしまったわけだが、とうとう軽く触ってみることを始めた。

とりあえず『ゼルダの伝説 時のオカリナ』の"時の歌"を打ち込んでlofiっぽい感じにして遊んでみた。

MIDIはファイルとして管理されているのだからPythonで記述できるのでは、と思い探してみるとmidoというライブラリが見つかった。

pypi.org

midoの基礎

midを使うと簡単にMIDIファイルを作成できる。以下のコード例は、ドの音の四分音符を一つ鳴らすだけのシンプルすぎる例。

import mido
from mido import Message, MidiFile, MidiTrack, MetaMessage

mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)
# BPM120のMIDIトラックを作成
track.append(MetaMessage('set_tempo', tempo=mido.bpm2tempo(120)))  #  BPMを120に設定

# C3(ド)の音の四分音符をMIDIトラックに追加 (C3はいわゆる真ん中の高さのド)
track.append(Message('note_on', note=60, velocity=64, time=0))
track.append(Message('note_off', note=60, time=480))

# MIDIファイルを保存
mid.save("new_song.mid")

C3が60で表されることがわかれば、他の音もわかる。

60: "C",
61: "C#",
62: "D",
63: "D#",
64: "E",
65: "F",
66: "F#",
67: "G",
68: "G#",
69: "A",
70: "A#",
71: "B",

オクターブを変えたいときはそれぞれの値に±12をすればよい。

自由研究

俺はメロディを思いつくことがほぼできないので、自動化しようと思い立った。

初めはなんらかのAPIで取れる連続的なレスポンスをどうにかしてメロディに変換しようと思ったが、天気系のAPIの使い方を調べているうちに面倒になり、まずはシンプルにテキストの長さを音の並びに変換しようという考えになった。

というわけで、手始めにWebエンジニアの多くが見たことがあるであろう"Lorum ipsum ..."を題材とした。

Lorum ipsum ... is 何

Webデザインなどにおいて、文章を入れる箇所に仮置きするダミーテキスト。

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

処理の流れ

  1. テキストをスペースで区切って、単語に分ける。カンマやピリオドは削除しておく
  2. 単語の長さの最小値、最大値を取得する
  3. 最小値<=n<=最大値の範囲の数列を、スケールの構成音の数で分割する
  4. それぞれの単語の長さをスケールの構成音に変換する
  5. MIDIトラックに音を追加していく
  6. MIDIファイルに保存する

Cメジャースケールでの例

Lorem ipsum dolor sit amet, . . .

["Lorem", "ipsum", "dolor", "sit", "amet"]

メジャースケールの構成音は「1, 3, 5, 6, 8, 10, 12, 13」なので、分割数は8となる。

ダミーテキストの単語の最小値は2, 最大値は17なので、この範囲を8分割する。

[array([2, 3]), array([4, 5]), array([6, 7]), array([8, 9]), array([10, 11]), array([12, 13]), array([14, 15]), array([16, 17])]

各単語の長さを、この配列に当てはめて音を決定する。

[5, 5, 5, 3, 4, . . .]

[1, 1, 1, 0, 1, ...]

となる。

キーがC(ド)の場合「レ, レ, レ, ド, レ」となる。

各音をMIDIトラックに追加、ファイルを保存して完了となる。

音の長さと強さ

音の長さは4分音符、2分音符、全音符をランダムに割り振っている。

音の強さ(ベロシティ)も64を基準に±10の範囲でランダムに設定している。

スケールの構成音の取得

スケール名と構成音の一覧を記載しているWebサイトをスクレイピングして一覧用のファイルを作成した。

スクリプト実行時にスケール名のIDを入力して、使用するスケールを決定する。

scale-player.vercel.app

琉球音階を使った楽曲の例

今後の展望

  • 青空文庫にある文学作品からメロディを生成する
  • スケールの構成音からコードを生成する

付録

scraping.py

import csv
from pathlib import Path
import re

from bs4 import BeautifulSoup as bs
import requests

URL = "https://scale-player.vercel.app/scale_type"
PAT = re.compile(r"([\d]){1,2}")

res = requests.get(URL)

soup = bs(res.content, "html.parser")

h3_tags = soup.find_all("h3")
scales = []
for h3 in h3_tags:
    try:
        scales.append(h3["id"])
    except KeyError:
        pass

with Path("scales.csv").open("w") as f:
    fieldnames = ("idx", "scaleName", "notes")
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    idx = 1
    for scale in scales:
        h3 = soup.find("h3", id=scale)
        parent = h3.parent
        dl = parent.css.select("dl > dd")
        for elem in dl:
            detail = elem.find("p").get_text("\n").split("\n")[0]
            notes = detail.replace("構成音:", "")
            scale_name = elem.find("img")["alt"]
            writer.writerow({"idx": f"{idx:02d}", "scaleName": scale_name, "notes": notes})
            idx += 1

main.py

from collections import defaultdict
import csv
import numpy as np
from pathlib import Path
import random

import mido
from mido import Message, MidiFile, MidiTrack, MetaMessage

PATH = Path("scales.csv")
KEY_MESSAGE = (
    "- C3 : 60\n"
    "- C#3: 61\n"
    "- D3 : 62\n"
    "- D#3: 63\n"
    "- E3 : 64\n"
    "- F3 : 65\n"
    "- F#3: 66\n"
    "- G3 : 67\n"
    "- G#3: 68\n"
    "- A3 : 69\n"
    "- A#3: 70\n"
    "- B3 : 71\n"
)
KEY_DICT = {
    60: "C",
    61: "C#",
    62: "D",
    63: "D#",
    64: "E",
    65: "F",
    66: "F#",
    67: "G",
    68: "G#",
    69: "A",
    70: "A#",
    71: "B",
}
PITCH_RANGE = 12
SAMPLE_SENTENCE = (
    "Lorem ipsum dolor sit amet, "
    "consectetur adipiscing elit, "
    "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
    "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
    "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur."
    "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
)


def load_scales():
    scales = defaultdict()
    with PATH.open("r") as f:
        fieldnames = ("idx", "scaleName", "notes")
        reader = csv.DictReader(f, fieldnames=fieldnames)
        next(reader)
        for row in reader:
            idx = row["idx"]
            scales[idx] = row["notes"].split(",")
    return scales

def fetch_sentence_lengths():
    sentences = SAMPLE_SENTENCE.split(" ")
    sentences = [sentence.replace(",", "").replace(".", "") for sentence in sentences]
    lengths = [len(sentence) for sentence in sentences]
    return lengths


def length_to_scale_notes(lengths, scales):
    print(lengths)
    scale_length = len(scales)
    split_base = np.array_split(
        list(range(min(lengths), max(lengths) + 1)),
        scale_length
    )
    print(split_base)
    scale_positions = []
    for length in lengths:
        for idx, base in enumerate(split_base):
            if base[0] <= length <= base[-1]:
                scale_positions.append(idx)
    scale_notes = [int(scales[idx])-1 for idx in scale_positions]
    return scale_notes


random.seed()

scales_map = load_scales()
bpm = int(input("BPM: "))
scale_id = input("Scale Id: ")
scales = scales_map[scale_id]
print(KEY_MESSAGE)
key = int(input("Key: "))
key_name = KEY_DICT[key]
pitch = input("Pitch(+/-): ")
if not pitch:
    pitch = 0
pitch_shift = int(pitch)
key += (pitch_shift * PITCH_RANGE)

lengths = fetch_sentence_lengths()
scale_notes = length_to_scale_notes(lengths, scales)
print(scale_notes)

mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)
track.append(MetaMessage('set_tempo', tempo=mido.bpm2tempo(bpm)))

for scale_note in scale_notes:
    rand_time = 480 * random.choice([1, 2, 4])
    rand_velocity = 64 + random.choice(list(range(-10, 11)))
    track.append(Message('note_on', note=key+scale_note, velocity=rand_velocity, time=0))
    track.append(Message('note_off', note=key+scale_note, time=rand_time))

filename = f"{key_name}_{bpm}_scaleId{scale_id}.mid"
mid.save(filename)

Lambda Labs 小ネタ - インスタンスの空き状況を通知するスクリプトを書いた

前回に引き続いてLambda Labsネタです。

nao-y.hatenablog.com

短所として

GPUインスタンスのキャバ不足が常態化している

と書いたわけですが、常にキャパ不足だからこそ空きができたときは秒でどこかの誰かに使われてしまうという面もあります。

そこで適当なインスタンス(ここでは社の常に稼働している環境)で動かしていて、Lambda Labs APIインスタンスの空き状況をチェック・WenhookでSlackに通知するスクリプトを書いてみた、というだけのネタです。定期実行はsystemd.timerです。

こんな感じで通知してくれます。

Capacity available instances:
1x H100 (80 GB PCIe), $2.49/hr
us-west-3(Utah, USA)
1x A10 (24 GB PCIe), $0.75/hr
us-east-1(Virginia, USA)
us-west-1(California, USA)
1x A100 (40 GB SXM4), $1.29/hr
us-west-2(Arizona, USA)

APIのドキュメントはこちら。

https://cloud.lambdalabs.com/api/v1/docs

Pythonまわり

main.py

import json

import requests

LAMBDA_LABS_API_BASE = "https://cloud.lambdalabs.com/api/v1"
LAMBDA_LABS_API_KEY = (
    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
)
WEBHOOK_URL = "https://hooks.slack.com/services/xxxxxxxx/yyyyyyyyyy/zzzzzzzzzz"


def notify_to_slack(message: str) -> None:
    message = f"Capacity available instances:\n{message}"
    payload = {
        "text": message,
        "username": "Instance Capacity Information",
        "icon_emoji": "information_source",
    }
    requests.post(
        WEBHOOK_URL,
        data=json.dumps(payload),
        headers={"Content-Type": "application/json"},
    )


def fetch_available_instances() -> str:
    message = ""
    endpoint = f"{LAMBDA_LABS_API_BASE}/instance-types"
    headers = {
        "Authorization": f"Bearer {LAMBDA_LABS_API_KEY}",
        "Content-Type": "application/json",
    }
    result = requests.get(endpoint, headers=headers).json()
    for instance_name, instance_type in result["data"].items():
        instance_description = instance_type["instance_type"]["description"]
        instance_price = instance_type["instance_type"]["price_cents_per_hour"] / 100
        available_regions = instance_type["regions_with_capacity_available"]
        if available_regions:
            message += f"{instance_description}, ${instance_price:.2f}/hr\n"
            for region in  available_regions:
                name = region["name"]
                description = region["description"]
                message += f"{name}({description})\n"
    return message


if __name__ == "__main__":
    message = fetch_available_instances()
    if message:
        notify_to_slack(message)

requirements.txt

requests==2.31.0

Dockerまわり

Dockerfile

FROM python:3.12.1-slim
COPY requirements.txt /workspace
COPY main.py /workspace

docker-compose.yml

version: "3"
services:
  lambdalabs-instance-watcher:
    build:
      dockerfile: Dockerfile
    container_name: lambdalabs-instance-watcher
    working_dir: /workspace
    command: >
        /bin/sh -c 'python main.py'

systemdまわり

home/ec2-user/Script/instance-watcher.sh

cd /home/ec2-user/Script/lambdalabs-instance-watcher-main
docker compose build
docker compose up -d

/etc/systemd/system/instance-watcher.service

[Unit]
Description=Notify available Lambda Labs instance.

[Service]
User=ec2-user
Type=oneshot
ExecStart=/bin/sh /home/ec2-user/Script/instance-watcher.sh

[Install]
WantedBy=multi-user.target

/etc/systemd/system/instance-watcher.timer

[Timer]
OnCalendar=*-*-* *:00/30:00
Unit=instance-watcher.service

30分に一度実行する設定。

感想

通知スクリプトを動かしてから実感したことですが、土日はVRAM多めのインスタンスが割と空きやすいですね。 逆に平日は1x A10 (24 GB PCIe)のみということがほとんどです。だからこそVRAM多めのやつが空いたときにすぐに気づけて便利。 みなさんも通知スクリプトでより快適なLambda Labs生活を。

GPUクラウド Lambda Labsを少しだけ使ってみた(解説・感想)

本年もよろしくお願いします。

2024年最初の更新で真面目記事体力を使い切りそうです。

LLMを使っていろいろやる仕事に就いたことで強いGPUインスタンスを使いたいモチベが湧いたものの、EC2はTesla T4x1の構成の上はT4x4という「その間が欲しいんじゃ」という状態に。

そこで見つけたのがLambda Labs。

lambdalabs.com

まだ日本語情報が多いとは言えないので使ってみた所感などをまとめてみる。

日本語情報の先達たち

zenn.dev

blog.hapins.net

Pros

  • EC2などと比較して時間あたりの価格が安い
  • GPU1枚、2枚、4枚、8枚の構成が選べる

Cons

  • インスタンスの一時停止が不可。起動or削除のみ
  • GPUインスタンスのキャバ不足が常態化している
  • データの永続化には別途ストレージの割り当てが必要

Lambda Labsの概要

GPUの種類と価格

選べるGPUは以下の4種。

  • RTX6000
  • Tesla V100
  • A100
  • A6000
  • H100

枚数によるバリエーション及び価格は以下の表の通り。

GPU VRAM per GPU vCPUs RAM SSD Price
1x RTX6000 24GB 14 46GiB 512GiB $0.50/hr
8x Tesla V100 16GB 92 448GiB 5.9TiB $4.40/hr
1x A6000 48GB 14 100GiB 200GiB $0.80/hr
2x A6000 48GB 28 200GiB 1TiB $1.60/hr
4x A6000 48GB 56 400GiB 1TiB $3.20/hr
1x A10 24GB 30 200GiB 1.4Tib $0.75/hr
1x A10 PCIe 40GB 30 200GiB 512GiB $1.29/hr
2x A10 PCIe 40GB 60 400GiB 1TiB $2.58/hr
4x A10 PCIe 40GB 120 800GiB 1TiB $5.16/hr
1x A100 SXM 40GB 30 200GiB 512GiB $1.29/hr
8x A100 SXM 40GB 124 1800GiB 6TiB $10.32/hr
8x A100 SXM4 80GB 240 1800GiB 20TiB $14.32/hr
1x H100 PCIe 80GB 26 200GiB 1TiB $2.49/hr
8x H100 SXM 80GB 208 1800GiB 26TiB $27.92/hr

参考. GPU Cloud - VMs for Deep Learning | Lambda (2024/01/15現在)

※ SXM: 高帯域のソケット

参考までに、Tesla T4を搭載したEC2 g4インスタンスでは、g4dn.xlarge(1x T4)が$0.71/hr, g4dn.12xlarge(4x T4)が$5.281/hrとなっている(いずれもap-northeast-1・オンデマンドの場合)。

単純比較するとLambda Labsの方が安くて大正義という感じもするが、前述の通り常にキャパ不足なので可用性においてはEC2が圧倒的に安定している。

リージョン

リージョンは北米、南米、アジア、欧州と広く用意されているがだいたい北米リージョンしか空いていない印象がある。とはいえ学習を回すだけの用途であればレイテンシは気にならないので空いているところを使えばいいと思う。

docs.lambdalabs.com

データの永続化

前述の通り、別途ストレージをインスタンスに割り当てる必要がある。これに関しては利用していないのでなんともいえない。ストレージの利用に関しては冒頭に挙げたZennの記事で解説されているのでそちらを参照ください。

学習を回してチェックポイントを保存するだけであれば、S3に入れるといった手段も取れるので工夫次第といったところか。仕事で現在動かそうとしているコードではS3に入れる方式を採っている。尚、絶賛バグ取り中なのでまだ動作確認ができていない(とあるモデルの学習の既存コードを写経・日本語向けに書き直したりしている)。

Lambda Labsを使う

ダッシュボードはこんな感じ。

最初に Settingsからカード情報を登録しておこう。

インスタンスを立ち上げる前にSSH鍵の作成or登録を忘れずに。

Launch Instance を押すとGPU選択画面が出る。GPUを選択したらリージョンとFileSystems選択をしてしばらく待てばインスタンスが立ち上がる。

SSH接続のほか、JupyterLabを使うこともできる。

ssh ubuntu@xxx.xxx.xxx -i ~/.ssh/id_rsaとか打つのが面倒なのでシェルスクリプトを書いて、ssh-lambdalabs xxx.xxx.xxxエイリアスsshできるようにした。

ssh ubuntu@$1 -i ~/.ssh/id_rsa

Lambda LabsでDockerを使う

dockerはインストール済みなので別途インストールの必要はなく使うことができる。そのまま使うとPermission Deniedになるのでsudoをつけているのだけども、「こうするのがベター」というのがあれば教えてください。

Dockerコンテナの中でCUDAを使う

PyTorchのイメージを使うなどいろいろあるとは思うけども、Lambda Labsが提供しているLambda StackのDockerfileがあるのでそれを使っている。

Dockerfiles with rolling-release Lambda Stack, designed for use with nvidia-container-toolkit

とあるのでこれを使っておけば良いのかなと思っている。

github.com

Dockerfile.xxxだけでなく、controllambda.gpgを置くのを忘れずに。

GitHubからリポジトリをcloneする

プライベートリポジトリをcloneする場合、デプロイキーの登録が必要だがインスタンスを立てるたびに色々やるのが面倒なのでローカル環境で鍵を作って、公開鍵を登録。秘密鍵はscpで送ることで解決している。これもset-lambdalabsというエイリアスにしている。setって主語がデカすぎるやろ。「こうするともっといいぞ」というのがあったら教えてください。

scp -i ~/.ssh/id_rsa ~/.ssh/lambda_labs_deploy_key ubuntu@$1:/home/ubuntu/.ssh/id_rsa

学習を実行する

ここはケースバイケースなので省略。Dockerコンテナの中で学習を回すようにしているけど、インスタンスにデフォルトでPythonだって入っているので別にコンテナでやることない気もするけど。

Lambda Labs APIを使う

Lambda Labsは起動しているインスタンスの一覧、インスタンスの起動・停止ができるAPIを提供している。

学習が終わってからもインスタンスが起動しっぱなしになってインスタンス代が無駄に嵩むのを防ぐためにも学習終了と同時に停止させられるようにしておくと良い。

起動中インスタンスのID取得とインスタンス停止のサンプルを置いておきます。

import json

import requests

LAMBDA_LABS_API_BASE = "https://cloud.lambdalabs.com/api/v1"
LAMBDA_LABS_API_KEY = "YOUR_API_KEY"

def fetch_instance_id() -> str:
    endpoint = f"{LAMBDA_LABS_API_BASE}/instances"
    headers = {
        "Authorization": f"Bearer {LAMBDA_LABS_API_KEY}",
        "Content-Type": "application/json",
    }
    running_instances = requests.get(endpoint, headers=headers).json()
    running_instance_id = running_instances["data"][0]["id"]  # インスタンスが一台だけ起動している前提
    return running_instance_id

def terminate() -> None:
    endpoint_terminate = f"{LAMBDA_LABS_API_BASE}/instance-operations/terminate"
    headers = {
        "Authorization": f"Bearer {LAMBDA_LABS_API_KEY}",
        "Content-Type": "application/json",
    }
    running_instance_id = fetch_instance_id()
    terminate_instance = json.dumps(
        {
            "instance_ids": [
                running_instance_id,
            ]
        }
    )
    requests.post(endpoint_terminate, headers=headers, data=terminate_instance)

感想

つよつよGPUを比較的安価に使えるという観点ではかなりいい選択肢だと思う。キャパ不足が目立つのがネック。

現状では学習を回す環境としての使い方がメインという印象。任意のGPUインスタンスが安定して供給されるようになればデプロイ先としても使いたい。

2023年まとめ - 執筆とか転職とか

前回の更新から4ヶ月以上経ってしまい、もう年の瀬に。

TL;DR

  • 本が出た(共著・プライベート)
  • 本が出た(共著・仕事)
  • 転職した
  • 写真アカウントを始めた
  • たくさん遠征した

本が出た その1

2019年頃からじわじわと書いていた本がようやく陽の目を見た。

共著者のsusumuis、レビュアーのみなさん、編集の方、出版社の方、TLで拡散してくれたすべての人にありがとう。

Pythonを題材にしていますが、内容的にはプログラミングそのものへの入門を目指したものになっているので「プログラミングやってみたい」って方はぜひお手に取ってみてください。文法の解説やスクリプトの書き方だけでなく、プログラマーコミュニティに足を運んでみるところまで説明しているところが特色です。

ありがたいことにAmazonにて9件のレビューをいただいている。

https://www.amazon.co.jp/dp/B0BRPT6D45/

そして推しに拙著を献本するというレア実績も解除することができた。

告知ツイートにMoon In Juneの方からメンションが来て驚愕するなどした。マジでありがたいし、Moon In Juneはマジでいいシューゲイズ。

open.spotify.com

本が出た その2

一般社団法人Pythonエンジニア育成推進協会が実施しているPython 3 エンジニア認定基礎試験の対策問題集を仕事で書かせていただきました。

問題構成は監修で入ったメンバーが、執筆陣は問題構成に沿って問題と解説を作成しました。

前述の拙著では資格取得の話もしているので、併せてお手に取っていただければ……!

インタビューを受ける実績も解除。

("電気羊"Tシャツに気づいてくれたのはhirokikyだけでした)(インタビューにネタを仕込むな)

オタクはチェキツイで顔を隠しがちだし本名も不明なことが多いけど、執筆&インタビューでそのどちらもインターネットの海に公開する形となった。

転職した

転職しました。実際には休職を挟んでからの転職。

よくわからない理由でメンタルをやり4月から9月末まで社会人を活動休止していたのだけども、その間に「Webを続けていきたいのか」という気持ちが湧いてビープラウドを去る決断をしたという流れ。現在はPython界隈の知り合いの紹介でAIを使ったチャットbotのアプリを開発するスタートアップに入り、LLMについて調べたりプロダクトで使えるようにすることをやっています。

がっつりAIというよりも「AIをWebで使う」的な観点の仕事なのだけども、こういうところでビープラウドで過ごしたおよそ6年で得た経験が活かせてとても良い。

写真アカウントを始めた

たまに更新しているのでフォローよろしくお願いします。

https://twitter.com/N4o0515

最近の写真だったり昔の写真を気紛れに載せてます。

たくさん遠征した

今年も。

  • 札幌(1月)
  • 沖縄(3月)
  • 京都・大阪(3月)
  • 京都・金沢・大阪(10月) ※ 京都・金沢はシーシャ遠征
  • 沖縄(11月)

オタク遠征はいつものように現場で湧き、チェキを撮り、オタクと飲むといういつものやつ。ついでにご当地シーシャ屋巡り。札幌では3軒を新規開拓。沖縄では2軒新規開拓。10月の大阪では3軒新規開拓。

京都・金沢シーシャ遠征では京都3軒、金沢2軒を新規開拓。飯も食わずにシーシャ屋3軒ハシゴすると普通に喰らうので、飯は食べたほうがいい。喰らうとね、冷汗が止まらなくなるんですよ。たぶん血中の酸素濃度がなんかアレしているのだと思う。


以上2023年まとめでした。

来年はDTMを始めたり、Spotify APIを使って地下アイドル楽曲を分析してグループごとの特徴を可視化するやつをやったり、画像生成系モデルで遊んだり、家シーシャを始めたりしたい。

夏だし怪談を聞こうぜという話

前回の更新からだいぶ時間が空いてしまいました。その間に、弊推しがyumegiwa last girlを脱退したり、ソロでの音楽活動を始めたりしていました。そういうわけなのでなんとか生きていくことができています。

ここ2, 3年くらい暑くなってくるとなぜか怪談を聞き出すという習慣が生まれ、YouTubeでいろいろと聞いているのだけども、今回はおすすめ怪談をいくつか紹介するというやつをやります。

夏といえば山と海とリゾート地。この3つのテーマでいくつか載せていきます。涼しくなろうな。

短い話なのであらすじは書かずに適当なコメントだけ書きます。

www.youtube.com

夜の海って怖い、わかる。関東某所にある海岸の話らしい。

www.youtube.com

夜の海って(ry

ところでサムネの表情やばすぎんか。裏拍手してますね、そういうことです。怖い。

この動画の語り手の田中さんは1つ目の動画のチャンネルをやっていて、そっちでは怪談や不思議な話をしつつ要所要所で関西ノリの茶々を入れるのでそこまで怖くなく気楽に観れるのでいろいろ観てみるのもいいかも。

www.youtube.com

いわゆる山怪という部類。日本なんてのはほとんどが山なわけで、山にまつわる不思議な話は枚挙に暇がないですね。昨今の有名どころだと「くねくね」もその一種ですかね?

www.youtube.com

偶然にも弊推しが最近、屋久島に旅行に行ったらしく自分も行ってみたいなと思ってはいるんですが、これを聞いてから屋久島怖いなという気持ちも生まれつつあります。ルールは守りましょう。死にますよ。

kakuyomu.jp

これはフィクションですが、掲示板の書き込みや雑誌記事の体裁をとっていてリアル感がとても高い。書籍化もされるそうな。

リゾート地

リゾートと言いつつ沖縄だけ。

沖縄ネタはどちらもユタの祖母を持つミュージシャンの人による怪談。

www.youtube.com

www.youtube.com

偶然にも何年も前、この人がやっているバンドのMVを観たことがあった。

www.youtube.com

それにしてもユタって不思議な存在ですね。死者の言葉を代弁するイタコとは異なり、一人一人に神やら仏がついていてその言葉を代弁するのだとか。イタコが口寄せするときは任意の死者の名前やら生前の住所を伝えるそうなのでDNSっぽいし、ユタはDBっぽい。それぞれどういう脳の活動をしているか観測してみたらどんな結果が出るのか気になって仕方がない。

www.youtube.com

事故物件

夏関係ないけど面白いので紹介。松原タニシは事故物件書籍を出しており、何年か前に公開された事故物件映画はそれが原作。そちらもちらっと観てみたけれども、話で聞いた方が想像の余地があるしなによりガチ感が違うなと感じた。

www.youtube.com

www.youtube.com

それにしてもこれだけのやばい事故物件に住み続けていてよく無事でいられるな……。普通、自分も含めた周囲の人の留守電に"何か"入っているなんてことが起きたら心折れる。

全世界のドルオタに送る生誕委員Discordのススメ

おことわり

「俺が生誕委員やったぜ」アピールをする記事ではありません。ご理解ください。

生誕委員ってなによ

(ブログ更新が社Slackに流れしまうので簡単な説明)

(社の人が読むかはわかりませんが)

多くのアイドルは誕生日に合わせて誕生祭イベントを開催します。

イベント本編は運営やアイドル本人が企画しますが、オタクたちは自主的に有志を募って花束やフラワースタンド、ケーキを用意したり、会場を装飾したりします。その有志の集まりを生誕委員と呼びます。

また、過度な「俺が生誕委員やったぜ」アピールは粋ではないとされており、角が立ちやすいです(なので冒頭に「おことわり」を書いています)。

※ 弊推しである夢際りんの誕生日は3月9日ですが、今回は1ヶ月遅れでの誕生祭となっています

本編

ドルオタの皆さんは生誕委員の活動においてどのようなコミュケーションツールを用いていますか?

TwitterのグループDM、グループLINEが主流ではないかと思います。

Twitterは全ドルオタが使っていますし、LINEも限界オタク会話といった日常的なオタク間コミュニケーションで利用していることでしょう。

オタク飲み会や遠征といったシングルイシューなコミュニケーションではTwitterのグループDM、グループLINEで必要十分です。

ですが、生誕委員となると花束やフラワースタンド、ケーキ、プレゼントなど管理すべきタスクは複数あります。それらのやり取りを前述のツールで行うと情報の把握は難しくなります。そこでチャンネルを複数作ることのできるDiscordの出番となります。

夢際りん誕生祭2023生誕委員は、以下のようなチャンネル構成でタスクを管理しました。

このように課題ごとにチャンネルを分けることで、それぞれの準備の進捗状況の報告や共有の効率は格段に向上します。

チャンネル名の先頭に絵文字をつけておくと見た目がポップになって良いです。

会議用に音声通話のチャンネルも用意しましたが、生誕委員をやるようなオタクはだいたいの現場におり、同期的なコミュニケーションが必要なときはそこで用が済むので今回は利用しませんでした。

また、生誕委員Twitterアカウントのツイートを通知するチャンネルの作成も考えたのですが、Twitter APIの仕様変更によりIFTTT経由での通知ができなくなったので諦めました。そういうとこやぞイーロン・マスク

DiscordはTwitter、LINEと比べると、(ドルオタ内での)普及の度合いがやや落ちるため、日頃からDiscordを利用しているオタクがサーバー作成・チャンネル作成、ロール管理、使い方の簡単な説明の役を担うことになります。するとプロジェクトマネージャー的な立ち位置になるでしょうが、だからといって偉いとかそんなことはないです。推しを推すという行為は生誕委員であろうがなかろうが、みんな違ってみんな偉い。

Discordではチャンネルをカテゴリに分けることができます。そのため、グループ全体の生誕委員の包括的なコミュケーションの場としても活用できます。フラワースタンドやケーキを注文するお店の情報や価格などの情報は属人性が高いので、これらをメンバー生誕委員の間で共有できるのはとても便利だと思います。

グループ全体の生誕委員でDiscordを使う場合は、カテゴリとロール管理が重要です。以下のようなカテゴリ、ロールを用意すると良いでしょう。

  • カテゴリ
    • 全体的な連絡・相談
    • ○○生誕
  • ロール
    • 管理者
    • ○○生誕委員

カテゴリは横断的な連絡・相談ができるテキストチャンネルが1つと、メンバー生誕ごとのものがあれば十分でしょう。

管理者は読んで字の如く、新規カテゴリ、チャンネルの作成、オタクへのロール付与などDiscordサーバー全体の面倒を見る役目です。

○○生誕委員も読んで字の如く、メンバーごとの生誕委員のロールです。担当の生誕委員以外へのネタバレ防止として、カテゴリ(○○生誕委員)の閲覧・投稿を制限する目的です。

と、書いてきましたがyumegiwa last girlでは現在のところ夢際りん生誕2023でDiscordを使っているのみです。他メンバーのオタクの皆さんからのお声掛けお待ちしています。Discord管理くらいの役割であれば毎回お手伝いできます、よろしくお願いします。

結び

管理すべき課題の多い生誕委員にとってDiscordは理想的なコミュケーションツールといえます。

Discordで快適で円滑な生誕委員活動を!!

大切な告知

『yumegiwa last girl "universe" tour FINAL』は絶賛チケット販売中です。

4/24(月)新宿BLAZEで、夢際りんのアイドルとして最後の大舞台を見届けてください!

tiget.net

www.youtube.com

The Snutsはいいぞ

2023年初の記事が本を出した話でもアイドル現場まとめでもなく、なぜUKのバンドの話なのか。

それはサマソニでThe Snutsが国内で跳ねたときに古参面したいからに他ならないわけで。 絶対に自分より前から注目している人間は大勢いるけども。

音的には『Whatever People Say I Am, That's What I'm Not』から『Suck It And See』の頃のArctic Monkeysを彷彿とさせるポスト・ブリットポップのそれ。

『Burn The Empire』、『Zunkerpunch』、『Hallelujah Moment』が特にいい。

open.spotify.com

思っていたより早く来日が決まってシンプルに驚いている。いいぞサマソニ

Arctic Monkeysは来日が決定したけど、チケットはほぼ即完してしまったようで……。こうなったら絶対サマソニでThe Snutsを観るしかない。 High Flying Birdsも観たいけど、Oasis再結成の噂が出た今は普通にOasisが観たい。リアムはサマソニに出るけど、ノエルはフジロックに出たりするんだろうか。仮にほぼ同タイミングで来日するならいっそ一緒にやれよ。やってください。