たのしい人生

4x7 split keyboard Ergo42 をつくったときの失敗話

この記事は、自作キーボード Advent Calender の25日目クリスマスの記事です。

昨日の記事は、ないんさんの キー配列頂上決戦!さいつよなレイアウトはどれだ! でした。

はい

[PR] KbD C93

KbD C93 という自作キーボード同人誌が 12/29(金) 東3 カ54b で頒布されます。

昨年あたりの ErgoDox での話題から、今年は Let's Split の大ブレークなど、2017 は自作キーボード元年ともいえる盛り上がりだったのではないでしょうか?そういった状況で、自作キーボードを職場で見かけたり、ブログや podcast などで見聞きした人、そして興味を持たれた人もだいぶ多くなってきたのではないかと思います。

その一方で、キーボードを自作するというのはおそらくまだまだ "得体の知れない" 行為である部分も多々あると思います。半年前の私もそうでした。

そこでそういった、自作キーボードに興味はあるけど、どこから手を付けていいかわからない、という人たちにとってわかりやすい入門のきっかけとなるように本を作りました。

この記事で紹介する Ergo42 の制作過程についても載っているので、ぜひ。

なにがしかの方法で通販もしようかと思いますので、当日来れない方もぜひよろしくお願いします。ご一報いただけると販売数を調整できるかもしれないので気軽に Twitter 等でご連絡ください。

宣伝終わり。

What ― 4x7 split keyboard Ergo42

Ergo42 という split / ortholinear / 4 行 7 列 のキーボードを作りました。

42 の理由は The Answer to the Ultimate Question of Life, the Universe, and at least Keyboards. です(ググってみてください)。

github.com

https://raw.githubusercontent.com/Biacco42/Ergo42/readme/readme_image/ergo42_image.jpg

Why ― バランスのいい分割キーボードが欲しかった

もともと去年の夏頃から ErgoDox EZ / ErgoDox を使っていて概ね満足していたのですが、使っていく中でいくつか気になることが出てきました。

  1. レイアウト切り替え機能が標準的な QMK を利用できるキーボードでは、指を伸ばして 5 行を使うのはちょっと余りある
  2. 特に親指の扱えるキーは 6 キーも振られているが親指でそこまで扱えない
  3. にも関わらずキー数の多さゆえにサイズが大きく持ち運びが大変
  4. エルゴノミックデザインということで、列ごとにキーがずらされているがこれが有効か確信が持てなくなってきた

そんな折、今年になって大ブレークしたのが Let's Split です。Let's Split は

  1. 4 行
  2. 親指で扱えるのは 2 キー程度
  3. 4x6 の最小限な構成と小型設計
  4. 完全直行配置

ということで、完全に自分のニーズにマッチするようで飛びつこうかと思ったのですが、ErgoDox で一番内側のキー(7 列目)を多用していた私としては 4x6 配列だとどうしてもキーが入り切らず、煮え切らないまま購入に踏み切れずにいました。

そこで、自分の考えるようなことは誰でも思いつくだろうと思って 4x7 split ortholinear なキーボードがないかなと思って探してみたのですが、なんと驚いたことに存在していませんでした。

ないなら作るしかないかということで作りました。

How ― どこから手を付けていいかわからないので先人の知恵に頼る

上記で Why を突き詰めていった結果、やりたいことはこの時点でかなり明確になっていました。その一方で、何をやったらいいかは全く不明でした。

ErgoDox でキーボード自体の組み立ては経験済みだったため、キーボードの構成要素やおおよそどう作られているかはわかっていましたが、自作するとなるとどうしたものかわからなかったのでマネすることにしました。オープンソース万歳。

詳しいことは薄い本 KbD の方にまとめてあるのでぜひ手にとっていただくとして 、大抵こういう場合はなんかいい感じにうまくいってしまった話がまとめられてしまうものなので、ここでは本に載せられなかった失敗談をメインに書いていきます。失敗が怖くてなかなか踏み出せない私みたいな人が一歩を踏み出すきっかけになれば幸いです。

失敗 1 ― プロジェクトが開けない

とりあえず最初は慣れ親しんだ ErgoDox の KiCad プロジェクトを見てみようとしたのですが、なぜか KiCad の PCB デザインファイルが開けずいきなり詰まる。KiCad のファイルには編集した KiCad のバージョンが記録されておりかつ、基本的に下位互換性を保証しない = 編集された KiCad バージョンより古い KiCad ではファイルを開けないという仕組みになっているのですが、コミットされていたバージョンのファイルの KiCad のバージョンが通常のバージョン命名規則から逸脱しており(ベータ版?)、最新の安定版どころかリリースされているいずれのバージョンでも開けなくなっていました。しょーもなくて疲れた。

ある程度コミットを遡って PCB 設計ファイルを開けるようになったのですが、最新版との差分も気になるし精神衛生上よろしくなかったので、この時点で ErgoDox をベースに作成する案はなしになり、代わりに Let's Split をベースに方針転換しました。今思うと KiCad のファイルはテキストファイルなので、バージョン番号だけ適当に書き換えてチャレンジしてみても良かったかもしれない。

失敗 2 ― 回路の流用はかえって大変

というわけで Let's Split をベースにするようにしたのですが、プログラマーの方ならわかるでしょうが、他人のソースコードを読むのは、それがよほど配慮して書かれていない限り、それなりに苦労があります。それを拡張するとなればいわんやをや。

最初は Let's Split のプロジェクトを改造して適当に 1 列足せばいけるやろ~w とか思っていたのですが、回路の設計意図を読み解いて、それを壊さずに拡張するのがめっちゃしんどいことが発覚し、またライブラリ等についても KiCad が結構フリーダムな仕様になっていて、もとの設計を流用するほうが困難だとわかりライブラリも含めて全部自分でやることにしました。この決断をするまでにも結構うだうだして時間を食った。

失敗 3 ― 買い物は慌てずに

これは失敗というかは微妙なのですが、ErgoDox では赤軸(45 g)を使っており、新しいキーボードにはリニアでもっと軽いキーならもっと楽になるのではという単純な発想で、Gateron 白軸(35 g)を買ってみました。で、組み立て終わって使ってみたのですが、ハチャメチャに軽い。軽くて打鍵感が軽いのはもちろんでそれは良かったのですが、簡単に沈み込んでしまうため底打ちしてしまいやすく、指を跳ね返す力が弱いので自分で指を持ち上げなければいけないという不思議体験をすることになり、個人的にはいまいちあいませんでした。後輩は荷重 30 g のキーボードを気に入っているそうなので個人差が大きそうですが。

キースイッチはキーボードの打鍵感の大半を決める重要なファクターなので、時間とお金を惜しまずキースイッチサンプルセット等をまず買って好みのものをしっかり見極めるほうが結果的に節約になると学びました。急がば回れ

失敗 4 ― 世の中にはレーザー加工機を使う人間が思ったよりいるし、ファッションコワーキングスペースはそんなに怖くない

ケースを製作するにあたって、3D プリンタは経験がなくなんとなく難しそうだったため(アホ)、アクリルをレーザー加工機で加工するよくある方式でやることにしたのですが、レーザー加工してくれる業者も、レーザー加工機を貸してくれる場所も意外と少ない。はじめてのケース製作では東急ハンズでアクリル板を買って、レーザー加工機のレンタルだと有名所な FabCafe 渋谷に雨の中いったのですが、まさかの予約で完全に埋まっており利用できず。FabCafe は予約できるのですが、予約の最小枠が 40 分でキーボードを加工するにはちょっと過剰なのでドロップイン利用を狙っていたのですが、世の中にはレーザー加工機を使う人間が思ったよりいる。

その後、雨の中悲しい気持ちだったのですが、とっさに調べて原宿にある coromoza さんに電話して、幸い空いていることを確認できてアクリル板を加工することができました。coromoza さんは 表参道にあるファッションのコワーキングスペース ということでくっそビビっていたのですが、スタッフさんが丁寧に対応してくれてとてもよかったです。5 分単位で予約もできるので、足が伸ばせる人はおすすめ。

失敗 5 ― 心配するよりやってみたほうが吉

最大の失敗は、自作キーボードなんて自分に作れるのか?といってビビっていることでした。やってみた結果わかったのですが、正直キーボードの自作は電子工作としてはかなり簡単な方です。高周波回路とかでもないし、レイアウトには余裕があるので分布定数とか厳密なことは考えなくてもぶっちゃけ動きます。そしてなにより自分で手を動かしてみるとやはり理解が進みます。当方ボーカルプロ志向バンドメンバー求むに陥らず、ぜひ手を動かしていきましょう💪

そしてそんな人を応援する C93 東3 カ54b のサークルたのしい人生(Biacco42)の KbD と Discord サーバーをどうぞよろしく。お後がよろしいようで。

この記事は、Ergo42 rev.1 で書かれました。

Self-Made Keyboards in Japan Discord Server を開設した話

Self-Made Keyboards in Japan という Discord サーバーを立ち上げました。

Self-Made Keyboards とは?

キーボードは買うものだと思っていませんか?作れるんです。

解説本としては @pekaso 氏が有名?

でも、難しいんでしょう?

多分電子工作の中では かなり簡単な方 です。完全自作だと多少は知識が必要になりますが、ハンダ付けするだけで作れるキット的な製品も存在しますし、ファームウェアもオープンソースのものがあります。しかも実用品なので、作った後は実際に使えるので満足度が高いです。

そして、そんな自作キーボードについて会話して、作り方まで聞けちゃう Discord サーバーができました。

Self-Made Keyboards in Japan

最近だと、ErgoDox や Let's split がかなりメジャーになったおかげで、自作キーボードを見かけることも増えたんじゃないかと思います[要出典]

ちょっとでも興味が出たら、覗いてみてください。

おしまい

Scala で for 式の中括弧{} 内で <- expression が受け取れる型について

どういう経緯だったか忘れてしまったのだけれど

Haskell の do 記法は Monad 型クラスの >>= (bind?) 等に変換される糖衣構文で、do 記法で使えるのは Monad 型クラスのインスタンスを持つ型だけだけど、Scala の for 式はどういう形で受け取る型を決めているんだろう?

という話になり、「TraversableflatMap とか定義されているから Traversable が受け入れられるのでは?」と素朴に思ったのですが、Option とか Future とか明らかに違うやつがいくらでもいるので調べました。

※さらにいうと、Scala 仕様書の for のところ見ても糖衣構文だよーとかどう分解するかということまでは書いてあるものの受け入れられる型みたいな話はなかったり、"Scala for 受け入れられる型" みたいなググり方をしてもなかなかたどり着けなかったので、私みたいな検索弱者用に書く

公式ドキュメントにめっちゃ書いてあった

Scala’s “for comprehensions” are syntactic sugar for composition of multiple operations with foreach, map, flatMap, filter or withFilter. Scala actually translates a for-expression into calls to those methods, so any class providing them, or a subset of them, can be used with for comprehensions.

ただし、How does yield work? という yield に特化したタイトルで、 Scala FAQs って謎階層にいたので、みつからないよ...って思った。(Scala の Gitter チャンネルで Tsuyoshi Yoshizawa さんに教えていただいた)

ためした

object Main extends App {
  val forPo = new Forable("Po")
  
  val po = for {
    u <- forPo
  } yield u
  
  println(po)
  
  // Kaboom! Cannot compile - lack of flatMap
  // val popo = for {
  //   u <- forPo
  //   v <- forPo
  // } yield u + v
  
  val multiForPo = new MultiForable("Po")
  val multiForPoPo = new MultiForable("PoPo")
  
  val popopo = for {
    po <- multiForPo
    popo <- multiForPoPo
  } yield po + popo
  
  println(popopo)
  
  // Kaboom! Cannot compile - lack of foreach
  // for {
  //   po <- multiForPo
  //   popo <- multiForPoPo
  // } println(po + popo)
  
  val sideEffectPo = new SideEffectForable("Po")
  val sideEffectPoPo = new SideEffectForable("PoPo")
  
  for {
    po <- sideEffectPo
    popo <- sideEffectPoPo
  } println(po + popo)
  
  // Kaboom! Cannot compile - lack of filter
  // val popo = for {
  //   popo <- multiForPoPo if multiForPoPo.x == "Po"
  // } yield popo
  
  val filterPoPo = new FilterForable("PoPo")
  
  val popo = for {
    popo <- filterPoPo if filterPoPo.x == "PoPo"
  } yield popo
  
  println(popo)
}

class Forable[A](val x: A) {
  def map[B](f: A => B): Forable[B] = new Forable(f(x))
  override def toString(): String = x.toString
}

class MultiForable[A](x: A) extends Forable(x) {
  def flatMap[B](f: A => Forable[B]): Forable[B] = f(x)
}

class SideEffectForable[A](val x: A) {
  def foreach(f: A => Unit): Unit = f(x)
}

class FilterForable[A](x: A) extends MultiForable(x) {
  def withFilter(expr: A => Boolean): FilterForable[A] = this // easy implementation
}

ダラダラと書いたけど、つまるところ map とかのシグネチャを満たしてれば普通に for comprehension<- の右側に使えることがわかった。

Scala の for 式は実は単に flatMap 等のへの糖衣構文だったり明らかに Haskelldo 記法 をパクった に近いにも関わらず、Haskelldo 記法が Monad 型クラスを要求するのとは結構毛色が違う感じがする。

受け入れられるシグネチャについても結構ゆるくて

class Forable[A](val x: A) {
  def map(f: A => A): MultiForable[A] = new MultiForable(f(x))
}

とか、かなり原型をとどめていなくても行けたので、シグネチャというより、本当にメソッド名さえあっていれば良くて、脱糖されてから型検査が通ればオッケー(実際脱糖は型検査以前に行われるらしい)、という感じのようだ。

結構ふわっとした仕様でちょっと意外だった。

おしまい。

エスペラント入門 #2 ~ または、ことのはアムリラート

本日の学習

ことのは 意味 備考
ボーナン マテーノン あいさつ? ボーネ や ボネーゲ がポジティブなようなので good morning 的な?
ファルタス キーエル ヴィ ファルタス? という形が何回か見かける
プレータ プレート = Dish? マテンマンヂョ エスタス プレータ = 朝ごはんです?
ヴィズィタント visitor
グーテン good? グーテン アペティート で食事の挨拶?おなかへった?
アペティート appetite? 同上
ヴェーレ チュ ヴェーレ = ホント?
ボーネ いいね? ボネーゲ が前回も褒められていたのでポジティブな単語らしい
チェース 待って? ヴィ ネ ベゾーナス スクリービ ミーアン ノーモン! と続いたが…
ゾル 悪い? ゾルグ!という形で「悪くないよ!」的文脈で
エラーラス 間違い? error だろうなぁ
カゥズィス Cause? オカーズィス(前回)同様 cause 系の文脈で出現してる気がする
コムプレーナス 前回のものと同じ? ミ コムプレーナス = 大丈夫?安心して?
ヴェンデーヨ
レゴーマ 野菜
フィシュ
ヴィアンド
パノ パン
クク ケーキ
リズブル おにぎり?
リブロ
the? 仏語?
カーフォ コーヒー
ペフェクテ perfect?
プルス plus
ミーアン my?
ノーモン 名前? name やろなぁ

単語レベルではわからんやつ

徐々に単語レベルではなくガンガン文章としてエスペラントが出てきたので、もはや単語での推測は困難になってきた。ので、文章単位で意味を予測していくことにした。

  • キーウン ヴィ プレフェーラス、チュ ポムスーコン アゥ オランデジュスーコン.....

    • キー* は疑問詞
    • プレフェーラス は謎だけど prefer か?
      • だとすると キーウン は Which かも。Which do you prefer?
    • ポムスーコン と オランデジュスーコン が語形から並置されているとすると アゥ が or か
    • 飲み物を運ぶシーンだけど、オレンジジュースってのは安直すぎるか…?
  • ニ マルシュ マーノン エン マーノ

    • 上のと同じで エン が and?
    • マルシュ が verb?
      • 近い発音だと仏語の marché = 市場だし後に商店街に行くので正しそうだが、品詞的には marche = 散歩 (march と同語源?)のほうが正しいかもしれない
  • レゴーモ、フィーショ、ヴィアンド

    • レゴム-ヴェンデーヨ、フィシュ-ヴェンデーヨ、ヴィアンド-ヴェンデーヨ
      • 舞台は商店街
      • ヴェンデーヨ = 店?
      • レゴーマ サンドヴィーチョ が野菜サンドだったっぽいので、八百屋、魚屋、肉屋、と来そう
    • どうも活用?して語尾が 〇〇eーyo みたいな感じになると〇〇屋になる?
  • チュ ヴィ ヴォーラス マンヂ パーノン?

    • Do you おなかへり? Want パン?
      • 形容詞に対しても チュ (Do っぽいと予測しているやつ) つかう?
  • ラ "ヴォルト-ラディーコ" エスタス "リブル"、ド "リブル プルス 『エーヨ』" ファリーヂャス "リブレーヨ"

    • ヴォルト-ラディーコ = 無活用形?
      • 無活用形がリブル、で、リブルとエーヨを足して finally リブレーヨ
  • ヴェーヌ アル ミ

    • 私についてきて?
  • ヴィ ネ ベゾーナス スクリービ ミーアン ノーモン!

    • ヴィーアン が あなたの と予測される文脈があったので、ミーアン は 私の かも
    • だとすると ノーモン は話の流れ的に 名前
    • ベゾーナス か スクリービ が書くという動詞のはずだけど不明

感想

f:id:biacco42:20171011011333p:plain

ルカちゃんが非常にかわいくなってきたけれど、彼女は日本語がほぼできないので、感情的な表現・流暢な表現となるとエスペラントなので、はやくルカちゃんと自由にコミュニケーション取れるようになってルカちゃんと仲良くなりたい...

だいぶ一筋縄には行かなくなってきた。単語や文章の意味を真面目に考えてメモしながらすすめるとなかなか進まない。けど、Ruka ちゃんがめっちゃ優しくがんばって教えてくれるのでがんばる。

エスペラント入門 #1 ~ または、ことのはアムリラート

ことのはアムリラートをはじめた

ことのはアムリラートという純百合 ADV を標榜するゲームがあります。当然『純百合』の時点で耳目を集めることは必定であるのですけれども、このゲーム、なんといっても異世界転生 ※ただし異世界の言葉は理解できない という、「なんで異世界転生したのにいきなり言葉が通じるんだよ」という消費者の声に真摯に応えた作品で、以下のような画面が展開される大変エモーショナルな作品として注目を浴びていました。

f:id:biacco42:20171007225329p:plain

典型的な (異世界での) 少女同士の出会いの一幕

この異世界言語は全くのオリジナルではなく、基本的にはエスペラントだそうです(厳密にはエスペラントと異なる部分もあるらしい)。

言語監修

藤巻謙一 『著書「はじめてのエスペラント」他』

協力(言語監修)

一般財団法人日本エスペラント協会

というわけで、ことのはアムリラートでエスペラント入門、です。

本日の学習

【注意】 ここから先はネタバレを含みます!

といってもエスペラントの話なのでネタバレもへったくれもないとは思いますが、ストーリーの進行と一致しているので念のため。

意味に?がついているものは完全な予測なので全く外れている可能性もあります。

ことのは 意味 備考
エス Yes
No
ヴィ あなた
私達
パルドーヌ ごめんなさい 英語の Pardon と同語源?
ダンコン ありがとう 独語の Danke と同語源?
キーオ 疑問詞? 文法的には文頭について疑問文をつくっている?
キーエ Where?
ヴァルマ あたたかい
ヴァルメーガ あつい
アクヴォ "ユ" => 湯 が ヴァルマ アクヴォ
オムレート オムレツ そのまま。朝ごはんだった
タグメーゾ 独語の Tag と同語源?
ィエ ラ ウヌーア 1 時
レゴーマ サンドヴィーチョ 野菜? サンドウィッチ
エクイーリ 出かける
アクツィデント 事故・Accident
ウーヌ 1
ドゥ 2
トリ 3
クヴァル 4
クヴィン 5
ヂス ラ じゃあね?
何らかの冠詞 or 前置詞?
レルネーヨ 学校?
ヴィーアン Your? You の所有格か?
セド But?
ホーロ When?時刻?
レヴェーニス 探す?
ボネーゲ すごい?
コンプレーナス 完璧? Complete?
ティーミス 心配する?
オカーズィス 起こる? Occur から根拠レス予測
イーア Some?
アル 前置詞?
エスタス やたら出て来るけれど不明。疑問文に出現しやすい気がする...
エスティス 活用形?ネ と同時に出現。Be っぽい運用な気がする...
チュ 単純疑問で使われている気がする。Do?

セリフはエスペラントではなくカタカナ表記なので、エスペラントについては完全な文盲になりそう。綴りがわからないの結構つらい....

f:id:biacco42:20171007232130p:plain

これは...わからないなぁ....

iOSDC 2017 でさらっと出てきた Phantom Type さらっとやった話

これはなに

先日開催された iOSDC 2017 に参加しました記事です。

Ray Fix 氏の 視覚化とSwiftのタイプについて という発表で

protocol CoordinateSpace {}

enum ModelSpace:   CoordinateSpace {}
enum UserSpace:    CoordinateSpace {}
enum DeviceSpace:  CoordinateSpace {}

struct CGPointT<Space: CoordinateSpace>: Point2DProtocol {
  var xy: CGPoint
  // 実装
}

struct CGAffineTransformT<From: CoordinateSpace, To: CoordinateSpace> {
  var matrix: CGAffineTransform
  public func inverted() -> CGAffineTransforT<To, From> {
    return CGAffineTransformT<To, From>(matrix.inverted())
  }
}

public func * <From, To, I>(from: CGAffineTransformT<From, I>,
                            to: CGAffineTransformT<I, To>) -> CGAffineTransformT<From, To> {
  return CGAffineTransformT<From, To>(from.matrix.concatenating(to.matrix))
}

みたいなサンプルコードが出てきて

let modelToUser: CGAffineTransformT<ModleSpace, UserSpace> = ...
let userToDevice: CGAffineTransformT<UserSpace, DeviceSpace> = ...
let modelToDevice = modelToUser * userToDevice // CGAffineTransformT<ModelSpace, DeviceSpace>

これ型パラメータ実装ないし、実質処理はなにもしてなくない?

というのが、幽霊型 (Phantom Type) です。

Phantom Type

上のコードがほぼ全てですが、型の状態 (モデル空間にある頂点なのかユーザー空間にある頂点なのか) を型パラメータとして表現することで、実行時ではなくコンパイル時に状態を検査することができます。が、この型パラメータは実装では利用されておらずコンパイルしたら消えてしまうので、幽霊型 (Phantom Type) というわけです。洒落てますね。

状態に依存したコード

struct StateDependent {
    var ready: Bool = false

    func doPo() {
        precondition(ready, "Not ready!")
        print("Po")
    }
}

let stateDependent = StateDependent()
hoge.doPo() // Boom! Exception in runtime!

Phantom Type で状態を型で表現したコード

protocol State {}
enum Ready: State {}
enum NotReady: State {}

struct PhantomTypeSafe<S: State> {
    static func create() -> PhantomTypeSafe<NotReady> {
        return PhantomTypeSafe<NotReady>()
    }

    func toReady() -> PhantomTypeSafe<Ready> {
        return PhantomTypeSafe<Ready>()
    }
}

extension PhantomTypeSafe where S == Ready {
    func doPo() {
        print("Po")
    }
}

let phantomTypeSafe = PhantomTypeSafe<NotReady>.create()
//phantomTypeSafe.doPo() // Cannot compile
phantomTypeSafe.toReady().doPo()

Phantom Type = コンパイル時に型検査するためのタグのようなもの

上記サンプルだと、状態を表す変数 var ready をフィールドに持った型では、実行時までエラーが検出できませんが、Phantom Type を用いた型では extensionwhere 句による制約を設けることで、ある 型の状態 でないと呼べない関数を表現することができており、コンパイル時に静的にエラーにできます。

Ray Fix 氏のサンプルだと、CGAffineTransformTModelSpace でも UserSpace でも型としての性質や実装は変わらないので、下手に型を増やさずに Phantom Type で状態を表現して、変換同士の接続に破綻がないかを型で検証できています。

繰り返しになりますが、型パラメータ自体は利用されておらず、あくまで状態を表現する変数のようなものとして扱われているところがおもしろいところです。

Phantom Type で型の条件付け

これだけだとアレなのでもうひとネタ。

ジェネリクスのある言語ならこの Phantom Type のテクニックは使えると思うのですが、関数の引数に受け取る型に制約をつけるといえば他にもあった気がしてきました。型クラスですね

というわけで Scala で Phantom Type と impicit を組み合わせて、受け入れる型に制約をつけてみます。

sealed abstract class =:=[From, To] extends (From => To) with Serializable
private[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x }

object =:= {
  implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A]
}

def doPoWhenEqualType[A, B](implicit et: A =:= B) = "Po"

val po = hoge[Int, Int] // Po
// val po = hoge[Int, String] // compile error

上記の =:= の定義は実際に Predef という、Scala が標準で読み込む定義に含まれています。

Swift でもやってみました。

class TypeTag<T, U> {}

func doPoWhenEqualType<T>(_ tag: TypeTag<T, T>) -> String {
    return "Po"
}

//let po = doPoWhenEqualType(tag: TypeTag<Int, String>()) // Cannot compile
let po = doPoWhenEqualType(TypeTag<Int, Int>())

なんかもっとうまく書けそうな気がします…

というわけで、Phantom Type をさらっとだけ見てみました。

おしまい。

HyperX cloud 2 を買ってはいけない

先日購入した HyperX cloud 2 という バーチャル7.1サラウンドサウンド を謳う 誇大広告商品 があまりにも悪質なので記事を書きます。

結論: 消費者を騙すつもりの製品を売る HyperX ブランド、特に HyperX cloud 2 については完全に誇大広告・事実誤認を意図した商品であるため絶対に購入するべきではない

ゲーミングサラウンドサウンドヘッドセット

この記事を読む時点でゲーミング、とくに 7.1 ch のバーチャルサラウンドサウンドヘッドセットを探している人だと思うので詳細は省きますが、5.1 ch や 7.1 ch 等のマルチチャンネルオーディオは FPS 等のゲームで音の到来方向がわかりやすくて有利なんて話もあり、昨今の PC ゲーミング市場の盛り上がりもあっていろいろと製品が投入されています。

マルチチャンネルオーディオとサラウンドサウンド

そもそも 7.1 ch のサラウンドサウンドオーディオは、7 個のスピーカーを物理的に利用者の周囲に配置して、ゲーム内で後ろから鳴る音はちゃんと後ろのスピーカーから鳴る (から敵の位置とかがわかりやすい) というやつです。もちろんこの 7 個のスピーカーはそれぞれの方向の音源を代表しており、ほぼそれっぽくなるようにそれぞれのチャンネル (7.1 "ch" のチャンネルはこれのこと。スピーカーの数と思ってもらってとりあえずは問題ないです) ごとに計算して鳴らしています。

もちろんこのそれぞれのチャンネルのオーディオを計算するには ゲーム内の情報が必要です。どこで音が鳴っているかを知っているのはゲームだけであり、そのゲーム内の幾何情報をもとにどのスピーカーでどんな音を流せばいいかを計算することで、はじめてサラウンドサウンドが実現できます。当然 2 ch ステレオで生成されたオーディオから 7.1 ch の情報を再現することはできません

ヴァーチャルサラウンドサウンド(本物)

まともなヴァーチャルサラウンドサウンドヘッドセットは、入力ソース自体が当然マルチチャンネルオーディオとなっており、事前にゲーム側でゲーム内情報をもとに計算された 7.1 ch オーディオを、ある空間で (例えば部屋) などで 7 個のスピーカーを置いた場合に耳 = 2 ch の入力で聞くとどのように聞こえるかを計算して、2 ch のヘッドホンで聞いても 7 個のスピーカーから音がなっているように感じることができる、という技術です。(詳しくは伝達関数・とくに頭部伝達関数等で調べてください)

7.1 ch の信号から、それをあたかも部屋で聞いたかのように 2 ch の信号に変換することから各社 バーチャル サラウンドサウンドと呼称しています。実際には 7 個のスピーカーはないのに 7 個のスピーカーから音がなっているように感じるからヴァーチャル(実質的) 7.1 ch なわけです。重要なのは 7.1 ch の情報からシミュレートして 2 ch の情報に情報量を落とすことはできるが逆はできない ということです。

HyperX cloud 2 の事実誤認誘導 "ヴァーチャルサラウンドサウンド"

HyperX cloud 2 の web ページ等での広報文言を引用します。

バーチャル7.1サラウンドサウンド

バーチャル7.1サラウンドサウンドによりオーディオの精度がさらに上がり、ゲーミングにおいて優位性を高めることができます

Cloudゲーミングヘッドセット - Cloud Core、Cloud、Cloud II、CloudX | HyperX - 主な特徴

この文言を見て、みなさん HyperX cloud 2 は 7.1 ch のサラウンドサウンドの 2 ch への変換に対応していると感じたことと思います。しかしこれは上述の 7.1 ch ヴァーチャルサラウンドサウンドとは 全く異なる技術 であり、ユーザーが期待するような空間情報を持ったサラウンドサウンド一切実現できません。HyperX cloud 2 はそもそも 2 ch オーディオしか受け付けることができません。そして一度 2 ch に生成された音声信号から 7.1 ch 信号を復元することはできません

前述の通り、まともな 7.1 ch ヴァーチャルサラウンドサウンドは、もともとゲーム内の幾何情報を活用して作成された 7.1 ch の信号をもとに 7 個のスピーカーが鳴っているように感じさせる 2 ch 音源を生成する技術でした。

しかし、HyperX cloud 2 はもともとデバイスの仕様上 2 ch のオーディオしか受け付けることができないため、そもそも正確な位置情報を持つサラウンドサウンドを再現することは不可能です。では、HyperX cloud 2 の言う "バーチャル7.1サラウンドサウンド" とは何なのでしょうか?これが大変お粗末で、単に 2 ch の信号にリバーブ等の適当にぼんやり 7.1 ch 風に聞こえるエフェクトを加える機能 です。はっきり言っておもちゃです。FPS 等のゲームをする際には むしろ音の到来方向がわからなくなる最悪な機能 です。これが ゲーミング ヘッドセットだというのだから笑わせてくれます。

HyperX cloud 2 の悪質な点

これだけだったら無知なメーカーの広報が何かを間違ってマーケティングをしてしまっただけかもしれませんが、残念ながら HyperX は 明らかに意図的にこの不誠実な表現を用いており非常に悪質です

以下、HyperX およびそのブランドを展開している Kingston が "バーチャル7.1サラウンドサウンド" がヴァーチャル 7.1 ch サラウンドサウンドではないということを自覚した上で意図的に消費者を騙そうとしている証拠を挙げます。

もう気づいた方もいるかもしれませんが、HyperX cloud 2 の問題の機能名は "バーチャル7.1サラウンドサウンド" です。これは私の誤植ではありません。"バーチャル7.1 ch サラウンドサウンド" ではないのです。

HyperX cloud の紹介サイトでは巧みにこの 7.17.1ch という表現が 使い分けられています。そう、この表現は脱字などではなく、明らかに意図的に ch という表記が落とされています。

以下、HyperX cloud の紹介ページの冒頭部分の一部を引用します。

多彩なCloudシリーズのヘッドセットファミリーは、あらゆるゲーマーのニーズ、プレー環境、プレースタイル、パーソナルスタイルに対応するように設計されています。PCゲームでも据え置きゲーム機でも、7.1chバーチャルサラウンド対応ヘッドセットをお探しならCloudシリーズからご検討ください。

ここで言う "7.1chバーチャルサラウンド" というのはユーザーが探しているものであり、HyperX cloud 2 の特徴ではないため嘘はありません。私だって 7.1 ch ヴァーチャルサラウンドサウンドヘッドセットを探していました。HyperX はここではわざわざ ch の表記を用いています。

続いて、HyperX cloud 2 の問題の機能について紹介した文言を改めて引用します。

バーチャル7.1サラウンドサウンド

バーチャル7.1サラウンドサウンドによりオーディオの精度がさらに上がり、ゲーミングにおいて優位性を高めることができます。

ここではわざわざ ch を外し、"バーチャル7.1サラウンドサウンド" と表記しています。前述の通り、HyperX cloud 2 は 2 ch オーディオしか受け付けないため、7.1 ch と書けば なにが 7.1 ch なのかすぐさま問い詰められてしまうでしょう。そこで HyperX はここの ch を削除することでなんの意味だかわからない 7.1 という数字だけを付け加えるという作戦に出ます。7.1 は 7.1 であって、別に 7.1 ch とは言っていない、ということでしょう。これ以降も、製品の仕様に関わる部分については "バーチャル7.1サラウンドサウンド" という文言で逃げ切っています。

さらに、HyperX cloud 2 のこの事実誤認狙いの 7.1 表現を知った上で、上記の紹介文言を読むとその狡猾さがさらに浮き彫りになります。

紹介文言には

バーチャル7.1サラウンドサウンドによりオーディオの精度がさらに上がり、ゲーミングにおいて優位性を高めることができます。

とあり、サラウンドサウンドでゲームに優位になる = 音の定位 (音の到来方向) がわかりやすく優位になるという誤認を誘起するような内容になっていますが、

  • 定位や音の到来方向といった具体的表現を避ける
  • オーディオの "精度" という何を言っているかわからない表現を使う

等、巧妙に誇大広告と取られかねない表現を回避しています。

これらを見るに、HyperX が HyperX cloud 2 について、誠実に製品広報を行う気がないことは言い逃れようのないものだと思われます。

オーディオは「わかりづらい」

私も上記の HyperX の狡猾な戦略にハマり HyperX cloud 2 を購入してしまったのですが、それをはじめて使ったときの違和感と不信感というのはなんとも表現しがたいものがあります。

オーディオ沼や、もう少し強い言い方をすればオーディオオカルトという言葉を聞いたことがあるかと思います。デジタル信号を伝送するケーブルを変えると音質が変わるとか、発電所によって音色が変わるとか、高音質の SD カードとかいうアレです。大概のリテラシのある人は、こういった商品について「何を馬鹿な」と一蹴できることと思いますが、微妙なのはオーディオの良し悪しというのは実際非常にわかりづらいのです。だからこそこんなインチキ商売がなりたっているのです。こんなに高価だし良いものらしいが、違いがわからないのは自分の耳が悪いだけかもしれない。実際に良くなっているに違いない。という消費者の不安等につけ込む悪質なビジネスを許してしまったオーディオ業界の怠慢です。

HyperX cloud 2 を買ってきて "バーチャル7.1サラウンドサウンド" を試したときの私はまさにこの状態でした。体感としては明らかに定位が悪くなっているのです。しかしまさか...という思いでしばらく試行錯誤をすすめるものの、結論は "バーチャル7.1サラウンドサウンド" を切ったほうが音の定位がはっきりするということでした。この結論、自分の耳の正しさを信じるまでには非常に不安でストレスがかかりました。

その後、7.1 ch 音源の再生を試してみるとやはりフロント LR チャネルしか鳴らないなど疑念は確信に変わり、Kingston に対する "バーチャル7.1サラウンドサウンド" は単なる WOW エフェクトではないか? という問い合わせをした記事も発見し、HyperX cloud 2 が事実誤認を誘導する悪質な商品であることがわかりました

HyperX cloud 2 は決して買ってはいけない

ここまで説明してきたとおり、HyperX cloud 2 は「こう言っとけば勝手に騙されるだろう」という消費者を馬鹿にした態度で作られた製品であり、決して買うべきではありません。特に安価な商品でもないため、他のまともなメーカーのサラウンドサウンドヘッドセットを購入することをおすすめします。

HyperX は Kingston が展開するゲーミングブランドとのことですが、このような詐欺まがいの商品展開をするメーカーの品など不安で絶対に買えません。HyperX シリーズはもちろん、Kingston 製品を買うことも二度とないでしょう。

こういった不誠実なメーカーが淘汰されることを願ってやみません。

おまけ HyperX のお粗末な(しかしやはり悪質な)マーケティング

HyperX cloud のページには 賞歴という項目があります。普通の感性を持っていたら、まぁそれなりに権威のある(もしくはせめて金で買い付けたものでも)賞が並んでいるのかと思ったらそうではありません。なんと 商品レビューブログ 記事へのリンクなんです。しかもサンプルを渡して書かせたようなやつで、内容は推して知るべしというか素人ユーチューバーのそれを思い浮かべてください... 賞歴とは...

どうせ賞歴とかこんなところまともに読むやついないやろw とりあえずたくさんあったらすごそうじゃんw みたいな感じだったんですかね。徹底的に消費者を馬鹿にするそのスタイルにもはや清々しさすら感じます。

しかし最悪なのが、レビューしているのが本当にこういったことに疎い素人なのか、HyperX の狡猾な戦略にまんまとハマって 7.1 ch ヴァーチャルサラウンド みたいにブログ記事で書いちゃってるんですよね。地獄かよ...

というわけで貴重な時間を無駄にしました。おしまい。

20 分で IntelliJ IDEA と Gradle で Kotlin 開発環境を作って fatJar ビルドしてコマンドラインツールしたり Kotlin Playground したりする話

Kotlin 入門の入門

Google IO で Android の開発言語として Kotlin が正式に採用されたりなど、最近またにわかに注目を浴びている Kotlin ですが

  • 別に Android 開発がしたいわけじゃない
  • Better Java として Kotlin は触っておきたいけど、Android Studio 入れたり実機で動作させるのは面倒
  • Kotlin 自体を学ぶのに Android の周辺知識が必要なのはちょっと…

のように感じている人もわりといるのではないかと思います。

そういう人Scala をやるといいですよのための、Kotlin 入門のための入門記事です。

この記事では

  • Android に依存せず Kotlin が気軽に書ける IDE とビルドシステムの導入
  • Kotlin で非 Android アプリケーション開発・ビルド・fatJar パッキング
  • Swift PlayGround 的な Kotlin PlayGround 環境の構築

20 分でできる ことを目的とします。

扱わないこと

  • Kotlin 自体の言語仕様
  • Gradle ビルドシステムの詳細

IntelliJ IDEA のインストー

www.jetbrains.com

Kotlin は IntelliJ IDEA を開発している Jetbrains 社が開発している言語なので、IntelliJ でのサポートはばっちりというか、概ね IntelliJ をインストールしたら Kotlin の開発環境は整います。コンパイラ・Gradle こみなので他のダウンロードは不要です。

起動したら初期設定を適当に済ませてください。初期設定時におすすめプラグインとして Scala が出てくるので、インストールしておくと良いでしょう。

ここまで 5 分ぐらい

Gradle プロジェクトの作成と Gradle ビルド設定

プロジェクトの生成

IntelliJ を起動・初期設定したらさっそく Kotlin のプロジェクトを作ります。新しいプロジェクトの作成を選択して、プロジェクトのタイプで Gradle を選択します。扱うフレームワークとして Kotlin にチェックをいれる。

f:id:biacco42:20170524120920p:plain

Gradle 用に GroupId と ArtifactId を設定します。 GroupId は Java の package と同様にトップレベルドメインから。ArtifactId はアプリ名。

f:id:biacco42:20170524121526p:plain

プロジェクトの生成オプション。個人的には全部チェックつけるのがおすすめ。特に Create directries for empty content roots automatically は Gradle のビルド規約に則ったディレクトリを自動生成してくれるので、Gradle に慣れてない人はとりあえずチェックをつけておいたほうがよいです。

f:id:biacco42:20170524122911p:plain

最後に保存ディレクトリを決めて Finish。

エントリポイントの作成

Gradle の規約でソースコードsrc/main/言語/package階層 に配置します。まずは src/main/kotlin/ に適当なパッケージ(jp.co.biacco42.app 等)を追加し、そこに Kotlin ファイル Main.kt を追加します。

Main.kt に以下の通り記述します。

fun main(args: Array<String>) {
    println("Hello Kotlin")
}

Kotlin ではトップレベル関数が定義でき main(args: Array<String>) がいわゆる Java の Main クラスの public static void main(String[] args) エントリポイントに相当します。

Gradle のビルドスクリプト設定

上記プログラムの fatJar が吐けるように build.gradle の最後に以下を追記します。

task fatJar(type: Jar) {
    manifest {
        attributes 'Main-Class': 'jp.co.biacco42.app.tanoshiilife.MainKt'
    }

    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}

manifest の attributes では、jar をそのまま起動できるように、エントリポイントを持つクラスを Main-Class として指定しています。

先程、Kotlin はトップレベル関数が定義できると書きましたが、Kotlin はあくまで JVM 向けのバイナリを生成するので、これらのトップレベル関数もコンパイルした時点でなんがしらのクラスに所属することになります。それが manifest に記載した MainKt です。Kotlin でトップレベル関数を定義すると ファイル名Kt というクラスが定義されて、そのクラスのスタティックメソッドとしてコンパイルされます。

ここまで 10 分ぐらい

ビルドとテスト、Kotlin Playground

早速上記のプロジェクトをビルド・実行してみます。

ビルド

IntelliJ の右上にある Gradle タブをクリックして開きます。

f:id:biacco42:20170524142816p:plain

先程追加した Gradle task fatJar は、Tasks -> other にあるので、これをダブルクリックすると Gradel task を実行できます。

f:id:biacco42:20170524162520p:plain

ビルド完了したら、パッキング済みの fatJar が <ProjectDirectry>/build/libs にできているはずです。実行してみましょう。

$ java -jar <ProjectDirectly>/build/libs/tanoshii-life-1.0.0-SNAPSHOT.jar

Hello Kotlin と表示されれば OK です。これで実行可能なバイナリを Kotlin でつくることができました。

ただ、毎回ビルドして実行はめんどくさいので、Gradle の test task を使って PlayGround 的なものを作ります。

ここまで 15 分ぐらい

Kotlin PlayGround をつくる

src/test/kotlin/PlayGround.kt というファイルを追加して、以下をコピペします。

import org.junit.Assert.assertEquals
import org.junit.*

class PlayGround {
    @Test
    fun playGround() {
        println("Hello Kotlin PlayGround")

        assertEquals(true, true)
    }
}

IntelliJ 右側の Gradle パネルから verification -> test をダブルクリックすると、テストが走って画面下部のテストの出力に Hello Kotlin PlayGround と表示されるはずです。

あとは、このファイルに好き勝手にクラスを足すなりなんなりして、playGround メソッドでいろいろつっつきまわせます。これで Kotlin の言語機能を手軽にお試しできると思います。

ここまで 20 分ぐらい

おわり

というわけで、Kotlin で簡単にコードを書く環境ができました。導入は基本 IDE をインストールするだけというお手軽さです。

Kotlin そのものについてはすでにおもしろい記事があるとおもうので、構築した環境でそれらの記事を読みながら手を動かすと理解が深まるかと思います。

qiita.com

qiita.com

おしまい

Amazon.co.jpアソシエイト