
音は数の列 ── サイン波を数式から鳴らす
デジタル画像の正体なら、多くの方が知っています。写真を拡大していくと四角いピクセルの並びが現れて、1枚の絵は結局「色を表す数の並び」だった、というあれです。では、音はどうでしょう。スマホから流れる音楽、ゲームの効果音、プログラムで鳴らす「ピー」という音。あれの正体を数で言い切れる人は、画像に比べるとぐっと少なくなります。 答えは画像と同じくらい単純です。スピーカーは、1秒に44100回、「いま膜をどれだけ押し出すか」という数を受け取って、そのとおりに膜を動かしているだけ。音の正体は、この数の列です。 スピーカーの中では、紙やフィルムでできた薄い膜が前後に動いて空気を押したり引いたりしています。この空気の振動が耳に届いたものが音です。そして膜の動きを指示しているのが、図の左上に並んだ数の列。+の数が来たら押し出し、−の数が来たら引っ込める。指示は1秒に44100回のペースで途切れなく届きます。録音した曲の再生も、プログラムがその場で作る合成音も、最後は必ずこの形になってスピーカーへ渡ります。裏を返せば、この数の列さえ自分で作れれば、どんな音でも鳴らせるということです。 この記事では、その一番小さな実例として、サイン波を数式から作って鳴らすところまでをやります。ここで連載の案内を少しだけ。当ブログでは、楽器の音をプログラムで作る仕組みを音の鳴るデモつきで紹介するシリーズを続けていて、エンベロープ やカープラス・ストロング の回では、今回の「音=数の列」を毎回さらっと前提にしてきました。今回はその土台を一本で正面から扱います。筆者は個人で音感トレーニングアプリ Harmonize を開発しており、アプリで鳴るピアノもオルガンも、録音ではなく、鳴らすたびにプログラムが計算したこの数の列です。 Harmonize 相対音感と純正律の感覚を鍛える音感トレーニングアプリ 今すぐ無料でダウンロード App StoreGoogle Play なめらかな波も、拡大すると点の列 音の教科書を開くと、まず出てくるのはなめらかな波の絵です。ところがコンピューターの中の音を拡大していくと、画像のピクセルとまったく同じことが起きます。 なめらかに見えていた波は、実は1/44100秒おきに置かれた点の列です。この一粒一粒をサンプルと呼びます。本物の空気の振動は切れ目なく連続していますが、コンピューターはそれを1秒あたり44100回のスナップショットで代表させている、というわけです。この「1秒を何個の数で表すか」をサンプリング周波数と呼び、44100Hz(44.1kHz)はCDが採用して以来の標準的な細かさです。人間に聞こえるいちばん高い音(およそ2万Hz)の揺れを描き取るのに足りるだけの細かさとして選ばれた数字です。なぜその2倍強で足りるのかは面白い話なので、この連載のエイリアシング(ナイキストの壁)の回であらためて取り上げます。さしあたっては「1秒=44100個の数」とだけ覚えれば十分です。 つまりプログラムで音を出す仕事とは、44100分の1秒ごとに数をひとつ決めて、スピーカーへ送り続けることです。では、その数をどんな式で決めれば「ラの音」が鳴るのでしょうか。 どう実現するか ── 位相をぐるぐる回す 鳴らしたいのは、たとえば440Hzのサイン波だとします。440Hzとは「1秒に440回、押し引きをくり返す」という意味です。ここで、1回のくり返しを円の1周にたとえるのが定石です。円の上を回る点を思い浮かべて、その角度を$\varphi$(ファイ)と書き、位相と呼びます。1周は$2\pi$。1秒に440周してほしくて、数を書き出すチャンスは1秒に44100回あるのですから、1サンプルあたりに進むべき角度は決まっています。 $$\Delta\varphi = \frac{2\pi f}{f_s} \qquad (f = 440,\ f_s = 44100)$$ あとは、1サンプルごとに$\varphi$をこの$\Delta\varphi$だけ進めて、その瞬間の高さ$\sin\varphi$を数として書き出すだけです。 書き出した数を時間順に並べると、それがそのままサイン波の点列になっています。この「回しては書き出す」の小さなループを、サウンドプログラミングではオシレータ(発振器、音の源になる部品)と呼びます。コードにするとこれだけです。 /* サイン波オシレータ(C風の擬似コード) */ double phase = 0.0; double dphi = 2.0 * M_PI * f0 / fs; /* 1サンプルぶんの角度 */ for (n = 0; n < length; n++) { output[n] = amp * sin(phase); /* いまの高さを数として書き出す */ phase += dphi; /* 位相を進める */ if (phase >= 2.0 * M_PI) phase -= 2.0 * M_PI; /* 1周したら戻す(値が育ちすぎないように) */ } ここで、$\sin(2\pi f t)$と時刻$t$から直接計算すればいいのに、なぜわざわざ位相を足し込んでいくのか、と思うかもしれません。両者は同じ波を作りますが、差が出るのは周波数を途中で変えた瞬間です。時刻から直接計算する式で$f$を差し替えると、波の高さがいきなり別の場所へジャンプして「プチッ」という雑音が鳴ります。位相を積み上げる方式なら、$\Delta\varphi$を差し替えても$\varphi$自体は連続なので、波はなめらかにつながったまま音程だけが変わる。実用のシンセサイザーがそろってこの方式なのは、それが理由です。 ...





