shingoushori's dialy

音信号処理を専ら研究していた元博士後期課程の学生によるメモ

やっつけ都市計画 〜 ちょっとした本体改造編

こちら、↓ こちらへの投稿を目論んでの記事になります。

adventar.org

=======================================================

[口上]

フリーの交通?シミュレーション
Simutrans(http://japanese.simutrans.com)が, 素晴らしい.

それでもって私は人工地盤っぽいことをやっています。
Simutransで人工地盤してみる - shingoushori's dialy
大規模な都市開発をさっさとやるべく、Pythonでセーブデータをいじっています

=======================================================

[要旨]

みなさん、乱開発、捗っていますか!
思い思いの高架、快適に作れていますか?

Pythonでセーブデータをいじっても、
時々刻々と発展していくマップにちょこちょこと手を加えたい衝動には、抗えませんよね。

そう、私もついに、本体を改造しないと、解決できない不満に行き着いてしまったのです。

本体改造のハードルが高かった私は、安直に Pythonによるセーブデータいじり に勤しんでいました。
(こちらにまとめました → やっつけ都市計画 〜 Pythonによるセーブデータいじり編 - shingoushori's dialy)

その成果によるセーブデータの特異性により、本体改造の必要性が高まってしまったのです。

本稿では、私が2017年にやってきた、
ちょっとした本体改造についてまとめます。

=======================================================

[GitHub]
↓ こちらが私のリポジトリです↓

github.com

各アイデアごとにコミットしています。
サンプルのシンプルな動画をYouTubeにあげてまして、
主のコミットには、そのアドレスを貼り付けてあります。

↓ それでは、個別に紹介していきます ↓
=======================================================

・高架にするとマーカーがガシガシ上にズレていくように見える件
 についての応急処置

セーブデータいじりで、
マーカーを追加したり、文字列を置き換えたり、オーナーを変更したりは、最早できるのです。
しかし、そのマーカーの表示位置は、GUIによるものです。
標高が高くなるにつれて差が激しくなります。
セーブデータいじりによって、天空にバス停を設置しまくったがためのイライラでした。

 
↓ 特定のプレイヤーだけにしたり、外したりする系 ↓
スクリプトで追加し放題だからといって、極端なプレイヤーを作ってしまいますと、邪魔です。
仕方がないので、そのプレイヤーだけ外したりでもするモードを追加しました。

・表示するマーカーを、アクティブなプレイヤーのものだけに絞ってみた

・アクティブなプレイヤー以外の 売上ポップアップ を消してみた

・路線図に、特定のPlayerだけを非表示にできるようにしてみた <お気に入り!>

・待機状態, 駅名/都市名含むコメントをアクティブプレイヤーのものだけに表示を絞れるようにしてみた <お気に入り!>

www.youtube.com

 

 

・道路/線路の削除を 接続解除と削除の二つに分けてみた <!おすすめ!>

正直、全然ウケなくてしょんぼりしています ...
Pythonでセーブデータいじるのが酷すぎたのかな ...
せっかく積み上げた高架を調整する時とか、
バス停をなんとか配置しないといけないとか、
削除だと行き過ぎで、もう一度作り直すのが苦しいことって、私には結構あります
だから、これが動いた時は本当に嬉しかったです

 

マーカーを使って 信号の接続方向を調整できるようにしてみた <!うけた!>https://www.

www.youtube.com

オフセットも併用すると、ちょっと複雑なものも作れます。

www.youtube.com

当初、マップ回転に非対応でした。遅れて対応した時のがこちら ↓

↓ ひめしさんの記事で取り上げていただけてます。恐縮です。↓
himeshi.hateblo.jp

 

・早送りを、とても早送りにする改造 <お気に入り!>

標準の早送りだけだと、不自由じゃないでしょうか?
早送りじゃない方については、実は調整できます。
しかし、試してみたら微調整程度の利き方でしょんぼりしました。
そこで、人間の感覚は対数的なので、x2, /2 で効かせるのがいいだろう!と思い立ったわけです。

・ハイトカット表示モードに新モード 'L' を追加してみた <お気に入り!>

天空にバス停を配置しまくった結果、地上がさっぱり見えづらくなってしまいました。
仕方がないので、ハイトカット表示モードにしっぱなしにするようになったのです。
しかし、レベルの設定が使いにくいと思いました。カーソル位置で設定されても ...
ということで、プリセットできるようにしてみました。 

・駅を作った時に、
 ちょっと離れたところにある駅にも接続して作れるようにしてみた <!おすすめ!>

ちょっと離れたところに作るために、わざわざ一時的に作り、壊す...
そのために、間にある建物や木を破壊せざるを得ない...
AI Playerが近いところに作りすぎてカオス...
これらを解決するには、やはり本体改造です。
苦戦したのが AI Player です。どうも、別プロセス?になるんでしょうか。
このため、本体で適当に追加した変数の変更を AI Player で読み取らせるために、
なにやら怪しい実装をやったような覚えがあります。
さらに、実際に AI Player に使わせたところ、間抜けなことが起きました。

近接した産業間の輸送用の路線が作られた時、同一バス停を結ぶ路線となり、
さっぱり運転されない!そして、そのまま破産してしまいました ...

・AI Player を眺めていたら、そのうちにセーブデータが読み込めなくなった件
 についての応急処置

skip "loadsave_t::rdwr_str() string too long" · shingoushori/simutrans@de85c83 · GitHub

AI Player だけじゃない、何か別のところの問題も直せるような気がしないでもないです。
そういうケースが他に見つかっていません。
他のセーブデータのロードでのエラーも拾っていきたいところです。 

 

=======================================================
[我流 バスの追い越し拡張 現況 ...]
本当は、ひめしさんのOTRPatchのマージ → 自リポジトリでの細々とした保持を狙っていました。
が、GitHubの機能がよくわからなかったので、挫折しています。
とりあえず、回り道として我流でちょっと追い越し条件の緩和をやってみています。

あー、これは難しい。実は、追い越しが必要になるプレイを全くしていませんでした。
だから、新しいプレイスタイルを模索し、新しいマップを用意するところからやっています。
新しいマップが形になってくると嬉しいですね!
しかも、新しいマップの方が一応たくましくなっている分、楽しいんだと思う、
うん、しょうがない。


=======================================================

[さて]
どうでしょう、本体改造。
どれもちょっとした改造ですが、結構便利そうじゃないでしょうか。
まだまだ改造のしどころがありそうじゃないでしょうか、Simutrans。

ちょっとした改造に抑えるためには、色々と諦める必要はあります。
ボタンで操作したい!
→ 処理本体で苦戦した後で、GUIでもう1ラウンド苦戦したくない ... であるとか。
そこは柔軟に、やりやすい方法をソースコードと相談してもいいと思います。
印象的だったのが、マーカーの便利さです。
不格好ですが、うまく表示を制御することも含めて考えるのも、面白いのではないでしょうか。

ドイツ語混じりで意味が取りづらく苦戦したり、
なにより私はテキストエディタmacでmiを用いています )で無理やりやっているので、
頭を抱えることも多いです、正直。GitHubもよくわかりません、正直!
とはいえ、まあ、なんとかできています。動くと楽しいですよ!
動かなくても、変な表示も楽しいですよ! 異常終了も、うん、まぁ、荒ぶれる!

皆さんも是非改造してください!
期待しています。

ありがとうございました。

 

やっつけ都市計画 〜 Pythonによるセーブデータいじり編

こちら、↓ こちらへの投稿を目論んでの記事になります。

adventar.org

=======================================================

[口上]

フリーの交通?シミュレーション
Simutrans(http://japanese.simutrans.com)が, 素晴らしい.

それでもって私は人工地盤っぽいことをやっています。
Simutransで人工地盤してみる - shingoushori's dialy
大規模な都市開発をさっさとやるべく、Pythonでセーブデータをいじっています

=======================================================

[要旨]

みなさん、Simutransは何がお好きですか?
あるいは、鉄道・交通をどうお楽しみでしょうか?
私が好きなのは、なんか、眺めること!
そして、その行き交う交通システムに改良を加えて、いいことした気分になること!

しかし、Simutransを普通にプレイしていると、とても眺めてばかりいられません。
改良する元となる在来の交通システム+都市を育てるだけで、何夜も過ぎます。
そうこうしているうちにSimutrans自体のバージョンは上がれば、
パソコンすらも代替わりします。
するとどうでしょう、手塩にかけて育てたセーブデータがロードすらできないことも。
そして気づくのです、
「あれ、Simutransを眺めながらやろうしていた作業が何もできていないぞ」と。
(私の場合、それは学位であり、音楽制作であり、業務改善のための勉強でした。)

このように、私が感じる3大悲劇
・やりたい規模の都市を育てるまでが大変
・セーブデータがロードすらできない!
・技術的な進歩の機会の浪費
を克服する1手法を実践しています。
すなわち、"Pythonによるセーブデータいじり"です。

本稿では、私が2017年にやってきた、
セーブデータの黎明期の都市計画をやっつけ仕事するための、
Pythonによるセーブデータいじりについてまとめます。

=======================================================

[やっつけ都市計画]
↓ こんな街路を都市の成長に合わせて張り巡らせ、多層化しています ↓

[格子状街路 〜 天空編]
格子を敷くにも、
庁舎や産業などの制限から、どういうパラメータの格子にするかで毎回悩みます。
いざ決めても、坂や川に泣きます。

そこで、もういっそのこと天空の街路を作ってしまおうと思い立ったのです

... というわけで、天空に格子状街路を浮かせてしまいます。
高架を重ねた後にその下の建造物を消すことで、GUIででもできますね。
これをセーブデータいじりにより、ダイレクトに機械的に浮かせたところに配置します。

・↓ こんな感じです

PythonでSimutransのマップに天空の市道の碁盤の目を追加してみた - shingoushori's dialy

・↓ バス停も追加できました しかも、交差点にです

PythonでSimutransのマップに天空の市道の格子点にバス停を追加してみた - shingoushori's dialy

・↓ そして、
 ↓ 専用のプレイヤーも追加できましたし、路線にも登録できました

www.youtube.com

ごめんなさい、サンプルコードが未公開でした。
さすがに長くなったこともあり、GitHubを活用しようと思ってあれこれしているうちに、
コードの整理と公開を忘れていました ... 来年の課題です ...


[格子状街路 〜 地上編]
天空に配置はできました、専用のプレイヤーまで建てられました。
とは言っても、配置しただけでは足りませんでした。
天空に道路があっても、地上に道路がないと市街は広がらないんですね ...
 

・空き地に道路を格子で敷いてみた
PythonでSimutransのマップに空き地に道路を格子で敷いてみた - shingoushori's dialy

乱開発のために、市街を広げさせて交通需要を増やすための布石です.

・特定の行と列を平坦化してみた
PythonでSimutransの特定の行と列を平坦化してみた - shingoushori's dialy

これにより、GUIでも高架が敷設しやすくなります.

 ・特定の行と列の海底のレベルを持ち上げてみた
PythonでSimutransの特定の行と列の海底のレベルを水面まで持ち上げてみた - shingoushori's dialy

これにより、海を横切る高架が敷設しやすくなります!
↓ こんなことをやるのも楽しいんですけれども ... 

 

=======================================================

[その他]

・悲惨な市道化防止策 : 地下掘って、高架にする

PythonでSimutransの道路の下を掘って、ペデストリアンデッキを設置してみた - shingoushori's dialy

主観的に、一番みなさんの反響が大きかったのが、これです。
こういった、地味に繊細な作業になる、下ごしらえのような開発は、
セーブデータいじりの出番だと思っています。

 

・線路と道路の持ち主の変更

PythonでSimutransの線路と道路の持ち主を変えてみた - shingoushori's dialy
複数のプレイヤーを行き来していると、
気がつかないうちに線路や道路の持ち主が混ざりませんか?
そして、それが気持ち悪くないですか?
直そうとしても、プレイ画面で探し出すのは困難です。
「セーブデータいじりで補正できる。」
この余裕は、乱開発を地味に後押ししてくれます。

 

・全乗り物のスケジュールの待機を一括変更

PythonでSimutransの全乗り物の待機を上書きしてみた - shingoushori's dialy

利益率を微調整して、資金のカンストや破産を防ぎたいと思いました。
そこで単純に思いついたのが、待機の待ちの調整です。
でも、1台ずつ、1路線ずつ設定するのが苦しく感じました。
そこで、セーブデータいじりです。
いじってみると、
GUIでは入れられなかった/入れられると気づかなかった値が大丈夫だったりします。

 

・元々の庁舎を残す方法 : 街の座標を庁舎からずらしておく

PythonでSimutransの街の座標をずらしてみた - shingoushori's dialy
庁舎の建て替え → 移築 → 元々の庁舎の場所、雑居ビルの密集地!
という流れに、どうにもしょんぼりしてしまうのです。
とりあえず、小さい頃こそ庁舎横付けで、街の代表駅を作りたくなりませんか?
初めはpakをいじっていたんです、ランドマーク扱いの。
しかし、セーブデータいじりによって、どうやら実現できてしまいそうです。

 副産物 〜 魔改造

 

 

 

=======================================================

[方法]
やっていることは、実に原始的です。
<用意するもの>
・比較できるテキストエディタ : macmiを用いています。
・スクロプト化したい差分以外をできるだけ排した、セーブデータ対
<やること>
ひたすら観察 ⇔ トライアンドエラー ⇔ 一般化できないか悩む

今後は、本体のソースコードを参照することでの効率化を検討していきます。

 

[今後]
GitHubでいい感じにサンプルコードを管理
・ロードできなくなったセーブデータのサルベージ
 そういえば本文中にありませんが、
 セーブデータがロードできなくなったことが、セーブデータいじりに手を染めた元々の動機でした。
Pythonスクリプトのタイマー実施
 オートセーブとAI Playerあたりで、擬似的にでもできないものかと妄想中です。
・路線図出力
・ちゃんと都市計画
・なんか賢い自動なんとやら

=======================================================

[用語]
都市計画は幼い頃からなんとなく好きだったものの、
そういえばちゃんとは勉強していないので、用語が覚束ないです。
以下に、本稿執筆に際して「そういえば」で調べて出てきた分を、貼り付けます。

・方格設計
方格設計 - Wikipedia
理解:碁盤の目、格子状に道路を配置する、都市計画の設計

・格子状街路
格子状街路 - Google 検索
専門用語っぽくするとしたらこんなのだろうなと、私が勝手に思っている言葉
採用している論文、実在の自治体のHPがあるあたり、悪くない言葉だとの認識

・碁盤病
 ↓ 参考文献 11:52 あたり

www.nicovideo.jp

 

=======================================================

[さて]
セーブデータいじりによって、本体改造、さらにはセーブデータのフォーマット拡張をせずとも、
ある程度は無茶苦茶なことができるんだということを、感じていただけましたでしょうか。
そして、GUIでのやり直しが発生すると苦しそうな作業を自動化することで、
もっと大規模で徹底的な乱開発に邁進できる希望が膨らみましたでしょうか。

 
... 私はこの一連のセーブデータいじりの成果によって、
本体改造したくなってしまいました。

ということで、やっつけ都市計画 〜 ちょっとした本体改造編 に続きます。
ありがとうございました。

 

PythonでSimutransの道路の下を掘って、ペデストリアンデッキを設置してみた

フリーの交通?シミュレーション
Simutrans(http://japanese.simutrans.com)が, 素晴らしい.

それでもって私は人工地盤っぽいことをやっています。
Simutransで人工地盤してみる - shingoushori's dialy
大規模な都市開発をさっさとやるべく、Pythonでセーブデータをいじっています

... 人工地盤プレイに限らない、Simutransの話題に
「市道化防止パッチ」というのがあります
twitter.com
市街地が拡がった時、せっかく設置した高規格道路が市道化されてバス大迷惑
それも歯抜けで拡がるから見た目にも苦しい、加減速を繰り返すバス可哀想
という苦しみです

色々議論がありますが ...
私なりの苦しい回避策が 「道路の下を掘って、高架化する」です

↓こんな感じです↓

できるだけ本体改造せず、セーブデータも独自拡張せず、
やりたい私だけがちょっとした苦労だけに抑えるべく、
セーブデータの改造でできるだけ済ませたいのです
喧嘩、怖い 門前払い、心と体が保たない いじめ、よくない

やってみると、色々ありそうなのですけども
とりあえず、2点気づきました
・掘ったところに設置するなら、箱積み駅舎のペデストリアンデッキがオススメ
 → 掘ったところに何かを設置しないと、そこに家がたってしまいます
  どうせなら、後々地下鉄...的な何か 開削工法だと言い張ってみる!を設置したいですよね
  そうすると、消しやすい何かがいいのです
  となると、箱積み駅舎のペデストリアンデッキならば、実態は運河なのでちょうどいい はずです
↓こんな感じです↓

・AIプレイヤーの道路は割り込んでくる
 →どういう処理なのかは未確認ですが、勝手に接続して来ちゃいます
  市道は接続しなかった気がするのですが ...
↓こんな感じです↓

なお、サンプルコードでは、とりあえず次のようにしています
・特定のプレイヤー target_player_num の道路についてのみ処理
・掘ったところに敷く道路/フェンス/ペデストリアンデッキの接続方向は 0

↓サンプルコードです。
↓※絶対に元のデータを上書きしないでください。
↓※データや設定が破壊されても、責任は一切負えません。
↓version=0.120.4, 自作改造パック pak.japan_custom 用になっていますが ... 適宜正しい値になっていないと、面白いくらい開けないデータになります、多分
↓私はそれで苦戦しました
↓さらに、文字コードとかあれこれ読み込みが繊細です。

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import xml.etree.ElementTree as ET

filename_in = 'testout_elv_road.sve'
filename_out = 'testresult.sve'


# 特定プレイヤーの道路の下を掘って、何やらを設置
def dig_gut_under_way(root, target_player_num, target_gut_way, target_gut_way_type):
  
  # 検索用
  l_planquadrat = list(root.findall('planquadrat_t'))
  
  count_p = 0
  count_x = 0
  count_y = 0
  for planquadrat in root.iter('planquadrat_t'):
      count_x = count_p % N_x
      count_y = count_p / N_x
      if len(planquadrat[1].getchildren()) < 6 :
        print str(count_x) + " " + str(count_y)
        count_p = count_p + 1
        continue
      if (planquadrat[1][5].text == '1'):
        # 道路あり
        player_num = int(planquadrat[1][6][0][0].text)
        if (player_num != target_player_num):
          count_p = count_p + 1
          continue
        
        #接続方向
        #print str(count_x) + " " + str(count_y) + " " + planquadrat[1][6][0][1].text
        connect_arg = int(planquadrat[1][6][0][1].text)
        # 1:toN, 2:toE, 4:toS, 8:toW
        
        # 掘らないタイプの道路
        target_way = planquadrat[1][6][1].text
        if (target_way == target_gut_way):
          count_p = count_p + 1
          continue
        
        # #### 処理
        
        # 高度 海面 計画
        target_h = int(planquadrat[1][0].text) # 高度
        target_hs = int(planquadrat[1][1].text) # 海面?
        target_hs2 = planquadrat[1][2].text # 
        
        target_hd = target_h - 1 # 高度 掘
        if (target_hs >= target_hd) :
          target_hs = target_hd - 1
        target_h = str(target_h)
        target_hs = str(target_hs)
        target_hd = str(target_hd)
        
        target_grad = planquadrat[1][4].text
        #  0 : 平地
        #  4 : 南北 ud b00000100
        #  8 : 南北 ud b00001000 h
        # 12 : 東西 ud b00000110
        # 28 : 東西 du b00010110
        # 36 : 南北 du b00100100
        
        # 高架追加
        count_ch = len(planquadrat.getchildren())
        
        elem_1 = ET.Element('id')
        elem_1.text = '6'
        elem_2 = ET.Element('grund_t')
        subelem_1 = ET.SubElement(elem_2, 'i8')
        subelem_1.text = str(target_h) # 高度
        subelem_1 = ET.SubElement(elem_2, 'i8')
        subelem_1.text = target_hs # 海面
        subelem_1 = ET.SubElement(elem_2, 'i8')
        subelem_1.text = target_hs2 
        subelem_1 = ET.SubElement(elem_2, 'CDATA')
        subelem_1.text = '' # 
        subelem_1 = ET.SubElement(elem_2, 'i8')
        subelem_1.text = target_grad # 勾配
        subelem_1 = ET.SubElement(elem_2, 'id')
        subelem_1.text = '1' # 
        subelem_1 = ET.SubElement(elem_2, 'strasse_t')
        subelem_2 = ET.SubElement(subelem_1, 'weg_t')
        subelem_3 = ET.SubElement(subelem_2, 'i8')
        subelem_3.text = str(target_player_num) # owner?
        subelem_3 = ET.SubElement(subelem_2, 'i8')
        subelem_3.text = str(connect_arg) # 接続方向
        subelem_3 = ET.SubElement(subelem_2, 'i16')
        subelem_3.text = '0' # 速度 (ロード時 pakの情報に上書き)
        subelem_3 = ET.SubElement(subelem_2, 'i8')
        subelem_3.text = '0' # 
        subelem_3 = ET.SubElement(subelem_2, 'i32')
        subelem_3.text = '0' # 
        subelem_3 = ET.SubElement(subelem_2, 'i32')
        subelem_3.text = '0' # 
        subelem_3 = ET.SubElement(subelem_2, 'i32')
        subelem_3.text = '0' # 
        subelem_3 = ET.SubElement(subelem_2, 'i32')
        subelem_3.text = '0' # 
        subelem_2 = ET.SubElement(subelem_1, 'CDATA')
        subelem_2.text = target_way #'city_road'
        subelem_1 = ET.SubElement(elem_2, 'id')
        subelem_1.text = '-1' # 
        subelem_1 = ET.SubElement(elem_2, 'i8')
        subelem_1.text = '0' # 
        planquadrat.insert(count_ch-1, elem_1)
        planquadrat.insert(count_ch, elem_2)
        
        # 沈め
        planquadrat[1][0].text = target_hd
        planquadrat[1][1].text = target_hs
        # 接続方向
        planquadrat[1][6][0][1].text = str(0)
        # 置き換え
        planquadrat[1][6][1].text = target_gut_way
        
        if (target_gut_way_type == 2) :
          planquadrat[1][5].text = '2'
          planquadrat[1][6].tag = 'schiene_t'
        
        if (target_gut_way_type == 3) :
          planquadrat[1][5].text = '3'
          planquadrat[1].insert(7, planquadrat[1][6][1])
          planquadrat[1].insert(7, planquadrat[1][6][0])
          planquadrat[1].remove(planquadrat[1][6])
        
      count_p = count_p + 1







f = open(filename_in)
data1 = f.read()  # ファイル終端まで全て読んだデータを返す
f.close()

data2 = data1.replace("<![CDATA[","<CDATA>")
data2 = data2.replace("]]>","</CDATA>")

root = ET.fromstring(unicode(data2.encode('utf-8')))


# マップのサイズを取得
N_x = int(root[0][0].text)
N_y = int(root[0][13].text)



target_player_num = 1
target_gut_way = 'city_road'
target_gut_way_type = 1 # 道路は、1 ※ただし、市道化される
target_gut_way = 'green_fence2'
target_gut_way_type = 2 # フェンスは、2 ※ただし、一括で消せない
target_gut_way = 'wa-cs-pedway-ground'
target_gut_way_type = 3 # ペデストリアンデッキは、3
dig_gut_under_way(root, target_player_num, target_gut_way, target_gut_way_type)

data3 = ET.tostring(root, encoding="utf-8")

data4 = "<?xml version=\"1.0\"?>\n" + data3
data4 = data4.replace("<CDATA>","<![CDATA[")
data4 = data4.replace("</CDATA>","]]>")
data4 = data4.replace("<CDATA />","<![CDATA[]]>")
data4 = data4.replace("<Simutrans pak=\"pak.japan_custom\" version=\"0.120.4\">","<Simutrans version=\"0.120.4\" pak=\"pak.japan_custom\">")


f = open(filename_out,'w') # 書き込みモードで開く
f.write(data4)  # 引数の文字列をファイルに書き込む
f.close()

Mac で フォルダ同士で差分比較する目的で TextWrangler を入れてみた

タイトル通りです.

Windowsでは、WinMargeをとりあえず使っています.すごい便利.

 

Macでも、フォルダ同士で差分比較したくなりました.

目下、Simutrans の他の方が作っていらっしゃるソースコードの、
どこが改造されているのかの確認です.
OTRPatch を覗いてみようとしているわけです.


調べて、色々入れてみたところ、TextWrangler に巡り合いました.

Macで文書ファイルやフォルダ構成などの差分比較(diff)にオススメなアプリ「TextWrangler」 - Blue Leaf

ようやく意図通り、サクッとフォルダ同士での差分比較ができました.

 

最近Webブラウザのタブに居続けたナイスURL

s0sem0y.hatenablog.com

s0sem0y.hatenablog.com

tech-blog.abeja.asia

blog.brainpad.co.jp

qiita.com

qiita.com

dsas.blog.klab.org

www.speechandhearing.net

www.slideshare.net

speakerdeck.com

ajaxsoundstudio.com

Mido - MIDI Objects for Python — Mido 1.2.8 documentation

aidiary.hatenablog.com

qiita.com

yukara-13.hatenablog.com

qiita.com