ジンジャー研究室

長めのつぶやき。難しいことは書きません。

綺麗な矩形波を作る

オーディオプログラミングをしていて、よくある矩形波の作り方は 1-1 を周期的に繰り返すというもの。 で、最近までなんの疑問も持たずにそれで実装していたんだけど、 FFT周波数スペクトルを描いてみて気づいた。

なんか汚い。

f:id:jinjor:20210225143730p:plain

いやいやまさかね、何かの間違いでしょと思ってよく耳を澄ますと、わずかに高音にブジジジィィィ...というノイズ混じりの音が聴こえる。気がする。耳より目を信じるミュージシャンなので、見た目がそうならきっとそう。 おかしいなと思って検索しまくったら、同じことを Stackoverflow で質問して自己解決している人がいて "Band-limited waveform" を使えば良さそうと書いてあった。

自分なりの解釈だと、真の矩形波フーリエ級数展開すると無限の周波数までの足しあわせになるんだけど、実際のデジタル波形はナイキスト周波数(サンプリング周波数の半分)までしか出ないので、それを超えた分は低い周波数に折り返されてノイズになるということだと思う。たぶん。

というわけで、ナイキスト周波数を超えない範囲で正弦波を足しあわせて矩形波を再現することにする。具体的な方法は以下が参考になる。リアルタイムに計算するのはコストが馬鹿にならないので Wavetable をあらかじめ作っておく。

https://www.musicdsp.org/en/latest/Synthesis/17-bandlimited-waveform-generation.html

面倒なのが、周波数の上限が決まっているので低い音は倍音が沢山出せるが高い音だと少ない倍音で打ち切るしかない。なので、ノート毎に 128 個の配列を用意する(glide する時は線形補完)。 という感じで Wavetable を用意したら、40MB にもなってしまった。うーん。

ともあれ、結果こうなった。

f:id:jinjor:20210225151548p:plain

正弦波を足しているのだからそれはそう、という感じ。でもちゃんと矩形波の音が聴こえるし、このスペクトルを見ながら聴くとよりクリーンに聴こえるぞ(いいのか)。ノコギリ波も同じ要領で出来た。

以下、課題。

  • ギブス効果を考慮していないのでそのうち対応したい(上の資料に方法が書いてある)
  • LFO とかでも同じ Wavetable を使うべきだろうか?

センスの良い仕事

ふわっとつぶやいたんだけど、これって要するに何が言いたいんだっけとずっと考えてた。

センスの良い仕事とは一体なんだろう。 仕事というのは会社の業務に限らず、同人活動とか OSS とか割と色々当てはまると思うが、「センスがいいな〜」という仕事を見ると気持ちがいい。 じゃあどういう時に気持ち良さを感じるかというと、

「何も言わずに最適な答えを一発で出す」

のを見た時だと思う。センスというと先天的な才能みたいなイメージがあるが、おそらくそれまでの努力なり経験の蓄積が積み重なった結果、暗黙のうちに間違った答えが削ぎ落とされるのだろう。なんならちょっと気の利いたプラスアルファまで付いてくる。 阿吽の呼吸というか、信頼関係のある人と仕事をすると「そうそうそれそれ」っていうレスポンスが返ってきてやりやすい。

ところが、組織がスケールするとそういう再現性のない暗黙知のようなものは嫌われる。 彼はちゃんと関係者と合意も取らずに勝手に進めてしまうし、長期的な観点も足りていない。もっと決められたやり方を守って進めましょう、ということになる。

センスの対極にあるのは「正しいやり方」だと思う。

正しいやり方は正しいので厄介だ。何も進んでいなくてもそれを守っていれば仕事した気になるし、必要なコストとして許容されてしまう。今週は課題を適切に発見する方法を決めるための会議のための資料作りをやっていました、みたいな話になってくる。そして出てきた答えは必ずしも良いものとは限らないが、正しいプロセスを経たのでショボくても OK するしかなくなってしまう。

あとは単純にクールかどうかという問題があって、絵とか音楽にしても、下手な人ほど「ああでもないこうでもない、ここをああしてどう...」と言っていて一向に完成しないが、上手い人は第一声が「できた」なのは有名すぎる話。

もちろん黙ってやれば良いというわけではない。合意をとるべきところで勝手に下手なことをやってしまうのはセンスがない。 最初から正しい答えを出したから議論の必要性が生じなかった、あるいは合意形成に必要でない情報を切り捨てることが出来たというのが正確かもしれない。そう考えるとセンスというのはテクニックではなく実力そのものであるというなかなか厳しい話だ。

ふわっと始まったのでふわっと結論を言うと、クールでありたい。センスが欲しい。

Go 言語を始めた

1週間前くらいから Go 言語を触っている。

動機

オーディオ処理をやりたい。 今まで Web Audio API で色々やっていたが、細かいことをやろうとすると色々制約があってめんどくさい。

というわけで選択肢:

  • Node.js: パフォーマンスが不安
  • C++: 資産は多いけどそもそも C++ で安全に書くのが難しい
  • Rust: 安全で機能も充実してるけどコーディングで色々考えることが多くて面倒そう
  • Go: 並行処理が手軽に書けそうなので採用

(wasm ターゲットの場合 GC 不要な Rust が軽そうだけど)

最初の感想

1日目で本を読んで2日目に音が出た。簡単で最高。

現在の感想

ゴルーチンとチャンネルで手軽に色々できるんだけど、ちゃんと設計するのが難しい。

  • 関数内と呼び出し元のどちらに go を書くべきか
  • 関数内と呼び出し元のどちらでチャンネルを生成すべきか
  • WaitGroup で何をどこで待つべきか
  • どこで新しい Context を作るべきか
  • cancel()close(channel) のどちらでループを抜けるか
  • os.Signal をトラップしてループを止めるのが綺麗だけど失敗すると終了できない
  • ゴルーチンリークが不安で眠れない

可能性が無限大すぎる...色々試して確かめていくしかない。

読んだもの

TypeScript >= 4.1 の Template Literal Types を活用した引数パーサーを作ってみた

ヘルプっぽいものを書くと文字列をパースして型をつけてくれる。 デフォルト値を指定すると T | nullT になったりする。

f:id:jinjor:20201230222747p:plain

公開は今のところ GitHub Packages だけです(メンテしなくていい方法を考え中)

github.com

型レベルのパーサーはこちらの記事を大いに参考にしたというかパクりました。

(ネタ) TypeScript 型パズルで作るmini interpreter | by Yosuke Kurami | Medium

こんなに本格的じゃないけど。

難しいなと思ったのは、せっかく型レベルでパースしてもその結果を値レベルの実装に利用できない点。 なので、型は型、値は値でそれぞれパースして最終的に辻褄が合うように実装しました。

それでは皆さま良いお年を。

なるはや

なるはや(なるべく早く)」とはどういう意味なのだろう。

解釈1:優先度最高
なるべく早くなので、全リソースを注ぎ込んで考えうる最高の速度で終わらせるべき。

解釈2:優先度最低
なるべく早くなので、他の仕事の隙間時間に出来れば進める程度でいい。

分からないので、頼まれたら解釈2で進めることにしよう。

問題を解決したい話

なんとなく良い方法を提案されることがある。

「こっちの UI の方が良いと思う」
「新しいデザインにしましょう」
「あれもやった方がより良さそう」

感覚的になんとなくその方が良さそうというのはあるが、根拠が「そっちの方がいいから」であるうちは賛同できない。例えばデザインの話で言うと、良かれと思ってやった変更が不評なこともあるし、実は意図的にそうなっているのを知らずに壊してしまうこともある。あるいは、良くなるかもしれないが既に満足度が高くて必要のない変更かもしれない。

限りあるリソースを無駄にしないために「問題を解決する」ことにフォーカスして考えたい。 「そっちの方が良い」のであれば現状の問題になっている点は何か?ホームページに「明るい雰囲気の画像を置きたい」のは「暗い雰囲気を感じる」からであり、例えば採用に応募しにくいなどの実害が生じるのが問題だということになる。できれば本当にそうなのかを A/B テストなどで計測できれば良いが、難しい場合は仮説でも構わない。
問題が解決するならば、その分だけ確実に前に進むとわかる。かっこ悪い UI でも実際に問題になっていないのであれば放置でいい。逆に今は大した問題になっていないが問題が起きた時に甚大な被害を及ぼすなら優先度を上げて対応するといった判断もできる。フィーリングで「もっと良くしましょう」だと本当に意味のある仕事か分からないし、提案者の好みの問題で手を動かさないといけないのはモチベーションも下がってしまう。「良くしたい」と思うのは、提案者が無意識に問題だと感じていることがあるのだろう。だが、その問題は言語化してみると実は大した問題ではなかったり的外れであることも多い。

ことをややこしくするのが、「漠然とした問題」に対する「複数の問題を同時に解決するツール(方法論)」の存在だ。漠然とした問題は A, B, C に分解され、ツールが解決する問題は B, C, D, E, F だ。大抵は分解するところまでいかず「なんとなく不満を抱えている現状よりは良くなるんじゃないか」ということで導入を検討するが、意外と大変で頓挫するか、導入されたがどうもしっくりこないとモヤモヤを抱えるかになる。問題が分解できていれば、 A, B, C それぞれを解決するミニマムなツールを導入することもできるし、ツールの機能を部分的に導入した上で A だけは運用でカバーということもできる。

と、ここまでは問題を分析してピンポイントで解決することでコスパを良くしたい話。一方で、問題と一対一対応しないがとにかく手段を使ってみた方がいい場合がある。見えていない問題が発掘される可能性が高い場合や、手段が提供するメリットが見えていない場合だ。それらが世の中に広く浸透しているノウハウであれば、現在地が「守破離」の「守」である場合、と言い換えることもできる。
例えば、 SVN を使っている現場に Git を導入するとする。Git を使いこなす人々は「ブランチが手軽に切れるのがメリットだ」と言うが、 SVN しか使ったことのない人には何のことか良く分からない。ここで現場の人は「今の SVN の運用で何も問題は起きていない」と提案をつっぱねることもできるし、「問題を丁寧に分析した結果、3つの問題があり、 Git はその1つしか解決してくれない」と言うこともできる。が、同時に今見えていない問題を認識していないこともあるし、提案者の語らない Git のメリットもあるかもしれない。つまり、体験や学びが得られる期待値が高い場合は、試してみた方が長い目で見て得になることがある。

前半で「問題解決」にフォーカスすること、後半でそれに対しての反例を挙げたが、両立する話ではあると思う。リソースの少ない中で行き当たりばったりな方法を試す余裕はないが、一方で学びを目的として中長期的な戦略を取るのはアリ。「学びが得られず組織が硬直する」というのも問題の一つなので、それを解決することに対して意識的であれば、行き当たりばったりな解決とは違うし、仮説・検証もできる。

話が大きくなってしまったが、まず問題を定義してから解決したいし、言語化されていない問題を「なんとなく良くなるであろう方法」で解決するのを避けたい、という話でした。

紙にタスクを書くのが便利

抱えているタスクが増えてパンク寸前だったので、紙に書き出して PC の横に置いておいたら神ツールだった。紙だけに。

タスク管理に便利なツールは色々存在していて、専用のツールもあるし、開発する機能やバグであれば GitHub の Issue にすればいいし、複数のリポジトリを跨がる場合はプロジェクト機能もある。Slack でプライベートなチャンネルを作っておいてそこに書き留めておくこともできる。あとは Spread Sheet でもいいし、他にもタスクを管理できるツールは山ほどあるのだが、このようなデジタルなソリューションには共通して「アプリを開いて見に行かないといけない」という弱点があり、これが地味にめんどくさい。 何か作業をして、確認して、また作業に戻り、また確認してを繰り返すと、ウインドウを行ったり来たりするだけで消耗してしまう。ディスプレイを一台増やしてタスク管理専用画面にするのでもまあ良いとは思うが、常に電源が入っていて場所も移動しないという縛りがある。色々試して最終的にもっともアナログな紙に行き着いた。紙に書いて隣に置いておけば、画面に何が表示されていようとすぐに確認できる。

用意するのは A4 用紙1枚。1つのタスクは平均 10 文字くらい。詳細は書かない。詳細はオンラインで他人とも共有した方がいいし、手で文字を書くのは時間がかかる。 書くのはあくまで「直近で取り掛かっているタスク」のみ。1年後に直すかもしれないバグ修正とかを書き始めると数百単位になってしまうので、そういうのは GitHub の Issue にでも書いておけばいい。あくまで解決したいのは「今やっていることを記憶したい」という問題なので、まあ 20 個くらい。1日のうちに並行して取り掛かれるタスクはせいぜい2、3個なのだが、他の人に投げていたりブロックされているタスクを含めると、そこそこの数になる。仕様策定中の機能が5つ、次に実装する機能が2つ、すぐに修正すべきバグが2つ、WIP な PR が3つ、依頼されたレビューが2つ、他の人に振っているタスクが3つ、キャッチアップすべき課題が1つ、回答待ちの質問が1つ、 Qiita への返信が1つ、次のミーティングで共有すべき情報が1つ、事務作業が1つ...。紙に書いておくだけで、今何をしていたか思い出せる。

という、今更ながらの気付き。無理してデジタルで完結する必要はなかった。 まあ多忙な社会人はやってるだろうと思って調べたけど、意外と紙1枚でやっている人は少なそう。あとカレンダーに1日のタスクを書くというのも出てきたけど全然ピンと来ないので職種が違うのかもしれない。