SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|

2024-05-11(Sat) 自製ゲームエンジンで在席管理

  前回のエントリからだいぶ間が開いてしまった。自分としてはそれなりに全開で作業をしているのだが、初見作業が多いせいか、コダわり過ぎか、単に手が遅いせいか、なかなか進捗しない。

  ここ1ヶ月そればっかりやっていたわけではないが、作ってみたかった機能が一段落したのでまとめておく。

  作ってみたかったのは、自製の在席管理アプリに追加できる「どこに座っているかを図示」する「名札(プレート)」プラグインだ。別に強く機能を要望されたわけじゃないんだが、それを通じていろいろと得るものがありそうなので取り組んでみた。

  まず、在席管理アプリにプラグインのクチを作った。様々に機能追加する都度、本体のコードに足していくとグチャグチャになるし、必要のない機能は無効にできるようにしたいからだ。割と複雑で大きな「名札」プラグインを実装することを念頭に置いてプラグインのクチを用意した。

  そして「名札」プラグイン。マウスのドラッグで「見たまんまのUI」を実現するために、だいぶ前に作ったゲームエンジンを流用する。

  さらに、現在は開発を中断しているが、レースゲームで対戦を可能にするためには通信が必要になるわけで、そこに流用することを念頭において通信スタックを実装する。今回はリアルタイムに名札位置を共有するために使用する。

  つうわけで、それなりに苦戦した部分もあったが、一応はできた。が、中断……続く。

  画像の説明


2024-05-27(Mon) CoffeeScriptでcallback

  前回の記事を「かきかけ」のまましばらく放置してしまっていたが、意図する機能の実装が「一応はできた」ものの、イマイチ美しくなく、記事に書くにはリファクタリングが必要な状況に陥ってしまったためであった。

  やってみたかったのは、名札インスタンスとして、ドラッグ操作で位置変更されたことを送信し、名札インスタンスとして、位置変更指示を受信するという実装。通信セッションは共用だが、名札インスタンスとしてはそれを意識せず、自由に送信し、自分宛てのものだけを受信する。要するに名札インスタンスに処理を閉じたい。各名札は他の名札のことを意識せず、通信を送受信したい。コールバックを受けたい、ということだ。

  既にRubyでは、Procやlambdaを理解できているつもりだが、CoffeeScript/JavaScriptではどう書くのだろう。結局、シンプルに以下で実現できた。

 class Mother
 
     constructor: ->
         @callbacks = []
 
     set_callback: (ins) ->
         @callbacks.push(ins)                    # 子インスタンスを配列に登録
 
     meal_ready: (menu) ->
         for ins in @callbacks
             ins.meal_call(menu)                 # 子インスタンスの関数を呼ぶ
 
 class Child
 
     constructor: (mother, name) ->
         @name = name
         console.log('I am [%s].', @name)
         mother.set_callback(@)                  # 親インスタンスに自分を登録
 
     meal_call: (menu) ->                        # 親インスタンスから呼ばれる関数
         console.log('I am [%s]. I like [%s]!', @name, menu)
 
 #-----------------------------------------------
 #
 #   Main
 #
 mom     = new Mother
 
 taro    = new Child(mom, 'Taro')
 jiro    = new Child(mom, 'Jiro')
 saburo  = new Child(mom, 'Saburo')
 
 mom.meal_ready('Pizza')                         # ゴハンだよー!!
結果は以下。
 
 I am [Taro].
 I am [Jiro].
 I am [Saburo].
 
 I am [Taro]. I like [Pizza]!
 I am [Jiro]. I like [Pizza]!
 I am [Saburo]. I like [Pizza]!

  今回は「WebSocketの@onmessage」の延長で子インスタンスに受信内容を渡したり、「Imageの@onload」の延長で子インスタンスにイメージの読み込み完了を伝えたりするのに使ってみたが、なかなかイイ感じに書けた。

  それとは別に「画面の書き換え処理により常時発生する負荷」の問題だ。ゲームならいいだろうが、起動しっぱなしの在席管理アプリではさすがに許容できない。

  試しに1/60秒の周期処理はそのままに、Screenクラスに@dirtyフラグを持たせ、画面の書き換えだけ抑制させたら、計測誤差レベルのCPU負荷に落ち着いたので、それでヨシとし、それでも1/60秒は過剰なので1/15秒に落とした。つうわけで、まだ多少のバグはあるかもしれないが、一応は開発完了。

  最近、それとは別に改めてレトロゲーに挑戦しているのだが、十分に楽しめるレベルに達しても、クリアまでには達しないという状況ばかりで歯がゆい。ドルアーガ(59)、スパルタンX(Mr.X)、アルゴスの戦士(13)、ブラックドラゴン(7)……どうにも頭打ちばかりでイヤになり、Factorioを再開してしまった。でも、それこそ容易には納得できないんだよなぁ……。


2024-05-28(Tue) 「ナイショの話」の冒頭のカウントを歌うための話

  ヴォーカル修行を続けている。2018年の秋からなので、もうすぐ6年だ。我ながら長く続けられたものだ。主なメニューは、隔週でのレッスン、毎週3時間のカラオケでの自主練、ほぼ毎日のジョギング中などでの聴き返しだ。誰に聴かせるワケでもないのだが、時間を費やしている程度には上達している気がする。ほんのちょっとずつだが常に延びを感じられるので、これがなかなか飽きないのだ。

  何度も書いているが、歌は喉の筋肉だ。常に高音を出す鍛錬を絶やさず、上に延ばすことで、全音域での表現力が向上する。そこにコツはない。ヴォイストレーニングというだけあって、歌唱とは筋力トレーニングなのだ。そしてボチボチと女性キーが実用域に差し掛かりつつある。「♪夢の中で焦がれたあの高さは、遠くはない今でも近づいてる♪」のである。

  で、大概の曲をそれらしく歌えるようになってくると、レッスンの課題曲の選定が難しくなってくる。割と歌えるものを選んでもアレだし、とても歌いこなせないものを選んでもアレだし、かといってあまりにマイナーな曲を選んでもアレだし、という感じ。

  そういえばYOASOBIが化物語シリーズの主題歌を歌うらしいな、と思って聴いてみたがピンと来ず。そういえばClariSが化物語シリーズの主題歌を歌ってたな……以前に試してみた時は無理っぽかったが、今ならイケるかも。そうだ「ナイショの話」だっけ。さすがに原キーだと音が出ているだけになってしまうだろうと、マイナス2にしたところ……いい感じに挑戦的なレベルになった。結局、レッスンの時に候補曲として「僕たちの行方」「ナイショの話」「I believe what you said」「射手座☆午後九時 Don't be late」を歌ってみて、課題曲は「ナイショの話」に決めた。

  しかし、この曲は「妹」の曲であって「オッサン」の曲ではない。とはいえ、そこに「妹」らしい可愛げを含ませられないのであれば歌う意味がない。というようなことを、先生にムチャ振りしてみたところ、声帯模写とは言わないがやりようはある、とのこと。じゃ、新境地を目指しましょう、ということになった。

  ほんじゃ改めて頭から……と歌うことになったのだが、イキナリ「頭の『カウント』はどうします?」って……えっ!? いや、考えてなかったけど、頭の「ワン、ツー、ワンツースリーフォー」ってヤツか……って、先生、半ばネタっぽい課題曲に対して、どこまでマジに取り組んでくれるんですか(笑)。

  とはいえ、さっきカラオケで空気録りした音源では、カウントは既に女子の声で入っていてコーラス扱いになっている。カウントへのカウントはなくイキナリ入るので、そこを歌うことは想定されていないのだ。つうことで、その場は「無理」ということになったのだが……。

  後日、自主練のカラオケの時に気にしてみると、採点バーのカーソルの進みでカウントの入りのタイミングが取れることに気づいた。そんなら、そこも歌ってみるか。でも、レッスンの時には採点バーはない。じゃ、自分で音源に「カウントへのカウント」を足してしまうか。

  ……と、だいぶ長い前置きになったが、今回のネタは、既存のカラオケ音源の歌い出しに「カウントを足す」という話なのであった。既存のカラオケ音源はwav形式で手元にある。そんな時に使うのが自製のCUIのWAV編集ツール「cccdct」である。ところが問題発生。カウントのタイミングキッカリにチャプタを入れて切り貼りしたいのだが、無音レベルのノイズが大きく頭のカウントと大差ないために視覚的に識別できない。そりゃ、CDやライン経由の録音ならともかく、空気録りじゃそうなるわな。

  考えてみれば、ゲインはリニアな軸に表示するんじゃなく、対数軸に表示するのが正しいんじゃないの? と、いまさら思いつき、実装してみた……が、今度はノイズもデカくなってしまい、やはり視覚的に識別できない。苦し紛れに平方根軸に表示してみたが似たようなものだった。

  画像の説明 画像の説明

  そこで、カウントのタイミングを視覚的ではなく、聴覚的に取ればいいことに気づいた。微妙に再生開始位置を動かしつつインスタントリプレイ機能を使い、場所を探す……が、さらによりよい方法を思いついた。再生開始位置でカウントの発声位置を取るのではなく、再生終了位置でカウントの発声直前位置を探すと精度が上がるということだ。目的の位置の1秒前から1秒間再生し、再生終了時にギリギリ発声が確認できない位置を探せば、その1秒後が再生開始位置というわけである。過去の経験だが、点灯状態のLEDが一瞬消灯するタイミングを見切るのは難しいが、消灯状態のLEDが一瞬点灯するタイミングを見切るのは容易なのと似ているかもしれない。要するに人間は、音や光が「ない」ことよりも「ある」ことの方が知覚しやすいのだ。

  では、改めて編集作業を行う。まずは、空気録りした音源のゲインをノーマライズする。

$ sox naisyo.wav naisyo.norm.wav norm

  次に、先程の要領でcccdctにより[無音1秒][ワン][ツー][ワンツースリーフォー][タッタッタ……以下、曲の本体]の5つの部分に分割する。

  画像の説明

  「ワン、ツー、ワンツースリーフォー」は自分が歌うので、ボリュームを抑えた音声を生成する。

$ sox track02.cccd.wav track02.20.cccd.wav vol 0.2
$ sox track03.cccd.wav track03.20.cccd.wav vol 0.2
$ sox track04.cccd.wav track04.20.cccd.wav vol 0.2

  先頭部分に元の「ワン」「ツー」を挿入し、つなぐ。

$ sox track01.cccd.wav track02.cccd.wav track03.cccd.wav track02.20.cccd.wav track03.20.cccd.wav track04.20.cccd.wav track05.cccd.wav c_naisyo.wav

  結果[無音1秒][ワン][ツー][ワン][ツー][ワンツースリーフォー][タッタッタ……以下、曲の本体]となる。要するに[ワン][ツー]を重複させることで、歌い出しのタイミングが取れるようにしたわけだ。上記で生成した冒頭部分がコレだ。ついでにcccdctの最新バージョンも置いておく。

  そんなことをヤリつつ思い出したのだが、やっぱり「射手座☆午後九時 Don't be late」の冒頭では「アタシの歌を聴けェ!」ってヤリたいんですが、その入りはどうにかなりませんかねぇ……。