サウンドの視覚化に必要な知識

サウンドを視覚化するとは, 音の実体である媒体の振動, つまり, 波形を描画することです. したがって, サウンドの視覚化を理解するためには, 音の特性に関する知識が必要です. とりあえず, サウンドの視覚化を実装するだけであれば必要ないかもしれませんが, 少しでも音の特性に対する知識があるほうが, 視覚化されたサウンドの意味を知ることができます. そんなに難しいことではないので, もしそういった知識に不安のある場合, まずは, 音の特性のページや他のWebサイト, 書籍などを参考に簡単でいいので理解をしてみてください.

そして, 音の特性に関する知識とともに, 音信号処理の知識も必要になります. この知識も, とりあえずサウンドの視覚化を実装するだけであれば必要ないかもしれません. しかし, 波形の描画を担うAnalyserNodeクラスで定義されているプロパティやメソッドの理解のためには少なからず必要になります.

また, 描画のためのAPIも必要です. サウンドの視覚化において, 音信号処理の知識は必須ではありませんが, このAPIの知識は必須です.

したがって, このページにおいては, サウンドの視覚化の準備となる音信号処理と描画のAPIを解説します. 具体的には, A - D変換とスペクトル, そして, HTML5 Canvasです. Web Audio APIと直接的には関係ないことなので, そんなの知ってるよという方はスルーしてください.

音信号処理

A - D変換

アナログ信号である音 (媒体の振動) をコンピューターで処理するためには, 0と1のみの情報, つまり, ディジタル信号に変換する必要があります. この変換処理のことを, A - D (Analog - to - Digital) 変換と呼びます.

A - D変換のアルゴリズムは, 大きく3つの処理があります.

  1. 1. 標本化 (サンプリング)
  2. 2. 量子化
  3. 3. 符号化

1. 標本化と2. 量子化の処理に共通することは, 連続した信号を離散した信号に変換することです. コンピューター (の内部) では連続した値や無限大となる値を扱うことが不可能だからです.

3. 符号化は, 2. 量子化の処理によって取得した数値データを2進数に変換するだけなので, 以下のセクションでは, 最初の2つの処理である標本化と量子化について解説します.

A - D変換のアルゴリズム

図3 - 1 - a. A - D変換のアルゴリズム

標本化 (サンプリング)

音の特性のページでは, いくつか音の波形のイラストを記載しましたが, それらは常に2つの連続した物理量 (次元) をもっていました. 時間振幅です. 標本化と量子化は, これら2つの連続した物理量を離散信号に変換する処理となります.

標本化は, 時間を離散した値に変換する処理です. 離散信号, すなわち, とびとびの値をとっていくためには, その間隔を決定するパラメータが必要になります. それが, サンプリング周期 (標本化周期) です.

サンプリング周期の逆数となるパラメータは, サンプリング周波数 (標本化周波数) です. 簡単に解説すれば, サンプリング周波数は, 1 secの間に, いくつのサンプル (離散点) をとるか?ということを意味しています. 例えば, サンプリング周波数が48000の場合, 1 secの間に48000サンプル (離散点) をとることになります.

サンプリング周波数

図3 - 1 - b. サンプリング周波数

周期と周波数は互いに逆数の関係であるので, サンプリング周期はサンプルの間隔を, サンプリング周波数は1 sec間におけるサンプル数を意味します.

標本化では重要な定理があります. それは, サンプリング周波数の1 / 2以上の周波数は元のアナログ信号に復元できないという定理です. この定理は, サンプリング定理 (標本化定理, シャノンの定理) と呼ばれます. (逆の視点で表現すれば, サンプリング周波数の1 / 2より低い周波数は元のアナログ信号に復元可能ということです.) また, 元のアナログ信号を復元できる上限の周波数はナイキスト周波数と呼ばれます. つまり, ナイキスト周波数の2 倍より高いサンプリング周波数に設定すれば, 元のアナログ信号に復元可能となります.

標本化の精度を高くするほど, すなわち, サンプリング周波数を高くするほど元のアナログ信号に対してより忠実度の高いディジタル信号に変換可能となります. 一方で, データサイズはサンプリング周波数に比例して大きくなってしまいます.

サンプリングの精度

図3 - 1 - c. サンプリングの精度

サンプリング周波数の具体例として, 音楽CDは44.1 kHzに設定されています. この理由は, 人間の聴覚が知覚可能な周波数はおよそ20 kHzであることを考慮して, そのうえでサンプリング定理を適用しているからです. さらに音質の高いものだと96 kHzに設定されている音楽CD (データ) もあります. しかし, 電話では, 8 kHzに設定されています. 音声の場合は, 多少音質が損なわれても相手の音声を聴きとることが可能なこと, 楽器音ほど高い周波数成分が含まれないこと, リアルタイムに通信するので可能な限りデータサイズを減らす必要があることなどが理由としてあげられます.

もっとも, Web Audio APIでサンプリング周波数を設定することはありません. AudioContextインスタンスのsampleRateプロパティでサンプリング周波数を参照することができますが, 読み取り専用でデバイスのサンプリング周波数 (44100, 48000…etc) に設定されています.

サウンドの視覚化の実装では, サンプリング周波数 (AudioContextインスタンスのsampleRateプロパティ) にアクセスすることはよくあります. したがって, サンプリング周波数が何を意味しているのか?ということ, および, サンプリング定理に関して理解しておくと役に立つでしょう.

量子化

量子化は, 振幅を離散した値に変換する処理です. 標本化と同じく, とびとびの値をとっていくためには, その間隔を決定づけるパラメータが必要になります. それが, 量子化ビット (量子化精度) です.

標本化されたアナログ信号は時間軸方向は, 離散化されていますが, 振幅軸の方向は, まだ連続したままです. 量子化では, 量子化ビットで指定された精度にしたがって, 振幅を整数値に丸める処理を実行します. 例えば, 量子化ビットが2 bitの場合, 4つのステップの値 (22 = 4) のいずれかに, 3 bitの場合, 8つのステップの値 (23 = 8) のいずれかに振幅が丸められます.

量子化ビット

図3 - 1 - d. 量子化ビット

このイラストの量子化ビットは3 bitです. マイナス方向に1つステップが多くなることに着目してください.

標本化の精度と同様に, 量子化の精度を高くするほど, すなわち, 量子化ビットを大きくするほど元のアナログ信号に対してより忠実度の高いディジタル信号に変換可能となりますが, データサイズは量子化ビットに比例して大きくなってしまいます.

量子化の精度

図3 - 1 - e. 量子化の精度

音楽CDでの量子化ビットは16 bitに設定されています. また, 大容量のデータが記録可能なDVD - Audioでは24 bitに設定されています.

量子化した (整数値に丸めた) 振幅を2進数に変換 (符号化) すると, コンピューターの内部で処理することが可能なディジタル信号となります. A - D変換によって取得したディジタル信号はPCM (Pulse Code Modulation) と呼ばれます.

スペクトル

もう1つ, サウンドの視覚化のために理解しておきたい音信号処理の知識があります.

音の特性のページでは, いくつか音の波形を記載しました. より詳細に説明すると, 時間に対する振幅という視点における波形でした. しかしながら, 音の特徴を分析するうえで有益なのは, 周波数に対する振幅という視点における波形です. なぜなら, 楽器音や音声は様々な周波数成分をもつ音によって構成されているからです.

周波数に対する何らかの物理量 (振幅・位相・パワー…etc) の関係はスペクトルと呼ばれます. つまり, 周波数に対する振幅であれば, 振幅スペクトルということです.

音の特徴は振幅スペクトル, すなわち, どの周波数成分がどのくらい振幅をもつのか?ということが大きく影響を与えているということです.

スペクトル

図3 - 1 - f. スペクトル

フーリエ変換

振幅スペクトルが音の特徴にとって重要な意味をもつことがわかりましたが, どうすればそれを取得することができるのでしょうか?

音楽CD (データ) で再生される音は, 時間に対する振幅データです. したがって, この振幅から振幅スペクトルを取得できれば合理的です. 幸運なことに, そのための数式が既に確立されており, それがフーリエ変換です.

X(f) = \int_{-\infty}^{\infty}x(t)\exp(-j2{\pi}ft)dt\ \ (-\infty \leqq f \leqq \infty)

x(t) = \int_{-\infty}^{\infty}X(f)\exp(j2{\pi}ft)df\ \ (-\infty \leqq t \leqq \infty)

図3 - 1 - g. フーリエ変換 (上) の逆フーリエ変換 (下) の公式

x(t) は時間領域での信号を表す関数, X(f) はスペクトルを表す関数です.

フーリエ変換の原理をきちんと理解するには, 高校~大学レベルの数学の基礎知識が必要になります. 以下に, そのキーワードを記載しておきます.

高校レベルの数学
三角関数 / 複素数 / 微分・積分
大学レベルの数学
テイラー展開 / フーリエ級数展開 / オイラーの公式 / デルタ関数
フーリエ変換と関連する数学
ラプラス変換 / z変換

しかしながら, Web Audio APIにおいて, サウンドの視覚化を実装するうえでその原理や公式を理解する必要はありません. なぜなら, そのためのメソッドが既に定義されているからです. 重要なことは, 振幅スペクトルが何を表すのか?そして, フーリエ変換によって (振幅) スペクトルが算出できるということです. もちろん, 数学が好きな方や, あるいは, ScriptProcessorNodeクラスを利用してスペクトル描画を実現したい場合はフーリエ変換を追求してみてください.

図3 - 1 - hは, フーリエ変換の原理を理解するためのイメージ図です.

フーリエ変換の原理

図3 - 1 - h. フーリエ変換の原理

周波数の異なる波の合成によって, 周期的な波が構成されている点が重要なポイントです.

高速フーリエ変換

フーリエ変換をそのままコンピューター上で実現することはできません. なぜなら, フーリエ変換は連続した信号に対して, -∞ から ∞ の区間にわたる積分によって定義されているからです. そのため, コンピューター上でフーリエ変換を実現可能なように定義されたのが, 離散フーリエ変換です.

X(k) = \sum_{n=0}^{N-1}x(n)\exp\left(\frac{-j2{\pi}kn}{N}\right)\ \ (0 \leqq k \leqq N-1)

x(n) = \frac{1}{N}\sum_{k=0}^{N-1}X(k)\exp\left(\frac{j2{\pi}kn}{N}\right)\ \ (0 \leqq n \leqq N-1)

図3 - 1 - i. 離散フーリエ変換 (上) と逆離散フーリエ変換 (下) の公式

x(n) は時間領域での信号を表す数列 (配列), X(k) はスペクトルを表す数列 (配列) です.

そして, 離散フーリエ変換において省ける計算を省いて処理を高速化したアルゴリズムが高速フーリエ変換 (FFT : Fast Fourier Transform) です.

計算量は, 離散フーリエ変換がO(n2), 高速フーリエ変換がO(nlog2n)となります. ソーティングアルゴリズムにおける, バブルソートとクイックソートのような関係です. しかしながら, 高速フーリエ変換がその高速性を発揮できるのは, データのサイズが2のべき乗の場合という条件があります.

この条件にしたがって, Web Audio APIで設定可能な高速フーリエ変換のデータサイズは, 32 ~ 2048までの2のべき乗の値のいずれかと定義されています.

表3 - 1 - a. スペクトル計算
AlgorithmDescription
フーリエ変換アナログ信号のスペクトルを計算
離散フーリエ変換ディジタル信号のスペクトルを計算
高速フーリエ変換 (FFT) 離散フーリエ変換を高速化

基本周波数と倍音

スペクトルが音の分析において重要な理由として, 楽器音や音声が様々な周波数成分をもつ音によって構成されているからと解説しました. では具体的に, その構成とはどのようになっているのでしょうか?

周波数成分は, 基本周波数と倍音に分類することができます. 最も低い周波数成分を基本周波数と呼び, 基本周波数の整数倍となる周波数成分を倍音と呼びます.

OscillatorNodeインスタンスのfrequency / detuneプロパティは周波数を設定するプロパティと解説しました. より正確には, 基本周波数を設定するプロパティです.

基本周波数と倍音

図3 - 1 - j. 基本周波数と倍音

基本波形を例にとって, 基本周波数と倍音をより具体的に解説します.

基本波形の最小単位は正弦波です. 正弦波は倍音をもちません. すなわち, 基本周波数の成分しかもたないので純音とも呼ばれます. そして, 基本周波数と倍音を合成した波形が, 矩形波やノコギリ波, 三角波です.

表3 - 1 - b. 基本波形の周波数成分
Wave TypeSpectrum
正弦波基本周波数成分のみをもつ
矩形波基本周波数と奇数次の倍音成分をもつ
ノコギリ波基本周波数と奇数次・偶数次の倍音成分をもつ
三角波基本周波数と奇数次の倍音成分をもつ (高音域の倍音成分が小さい)

基本波形と同じように, 楽器音や音声も基本周波数と倍音の周波数成分によって構成されています. 厳密には, このような自然の音は必ずしも整数倍になっていません. しかし, 実はこのことが人工的な音と感じさせない要因ともなっています.

ここまでのまとめとして, デモ 01を実行してみてください. 440 Hzを基本周波数として, 基本波形のスペクトルを表示します. スペクトルや倍音を感覚的に理解するのに役立ててみてください.

デモ 01

HTML5 Canvas

Canvas VS SVG

サウンドの視覚化のためには, 波形データをディスプレイに描画する必要があります. Web Audio APIでは描画のためのAPIは定義されていません. しかし, HTML5では, Canvas / SVG / WebGLといった描画のためのAPIが定義されているので, 波形の描画にはこれらのAPIを利用することになるでしょう.

CanvasとSVGは2D, WebGLは3DグラフィックスAPIです. 3Dグラフィックスは, 基本を習得するだけでも大変なので, 比較的容易に習得可能な2Dグラフィックスを対象に解説を進めることにします.

2DグラフィックスAPIのCanvasとSVGの違いは何でしょう?最大の違いは, ビットマップ形式かベクター形式かということでしょう. 簡単に表現すれば, Canvasは点の集まり, SVGは線の集まりということです. したがって, Canvasはピクセル単位の処理 (画像フィルターなど) には向きますが, 拡大縮小するとぼやけてしまいます. 一方, SVGは拡大縮小してもそのたびに数式から計算された線が再描画されるので鮮明なグラフィックスに向きますが, ピクセル単位の処理には不向きです.

それぞれの実体は, CanvasがUint8ClampedArray型の配列, SVGがXMLタグです.

表3 - 1 - c. HTML5 Canvas・SVGの違い
CanvasSVG
描画形式ビットマップ (点描画) ベクター (線描画)
実体Uint8ClampedArrayXMLタグ
メリットピクセル単位の処理
動的描画にも向く
拡大縮小
デメリット拡大縮小ピクセル単位の処理
動的描画に向かない
アプリケーションAdobe Photoshop
GIMP
Adobe Illustrator
Inkscape
その他Canvas自体は画像形式ではないSVG自体が画像形式
(image/svg+xml)

サウンドの視覚化において最も重要な違いは, 動的な描画に対する向き不向きです. なぜなら, リアルタイムに波形データを取得して, それを描画するということ, つまり, 動的な描画が要求されるからです. 表3 - 1 - cに記載しているように, Canvasは動的な描画に向いており, SVGはあまり向いていません.

以上のことから, 波形の描画においては, HTML5 Canvasを利用することにします. そして, このセクションでは波形の描画に必要なCanvasの処理である, パスの定義と描画について簡単に解説します. そんなの知ってるよ〜という方はスルーしてください.

HTMLCanvasElementとCanvasRenderingContext2D

まずは, HTMLCanvasElementの取得とCanvasRenderingContext2Dの生成を実行する必要があります.

サンプルコード 01



<!--HTML-->
<canvas width="300" height="150"></canvas>




// Get HTMLCanvasElement
var canvas = document.querySelector('canvas');

// Create CanvasRenderingContext2D
var context = canvas.getContext('2d');

console.log(canvas.width);   // -> 300
console.log(canvas.height);  // -> 150

HTMLCanvasElementのgetContextメソッド (引数に '2d' を指定) で, CanvasRenderingContext2Dを生成します. このコンテキスト (CanvasRenderingContext2D) が, Canvasで可能なほとんど処理の起点になります.

width属性とheight属性は, 省略するとwidthが300, heightが150に設定されます. CSSでwidthプロパティ・heightプロパティで値を設定すると, 表示されるサイズは変わりますが属性値は変わらないので描画が意図通りになりません. したがって, width属性・height属性は必ず指定するようしましょう. JavaScriptで動的に設定してもOKです.

Canvasの定義

図3 - 1 - k. Canvasの定義

また, 要素内の相対座標を取得する際に, イベントオブジェクトのドキュメント座標とHTMLCanvasElementのoffsetLeft / offsetTopプロパティもよく利用されるので, 少し記憶にとどめておくと役に立つかもしれません.

サンプルコード 02


/*
 * Add code to sample code 01
 */

// ....

console.log(canvas.width);
console.log(canvas.height);
console.log(canvas.offsetLeft);
console.log(canvas.offsetTop);

座標

パスを定義する前に, Canvasにおける座標について解説しておきます. Canvasでは, 左上を原点として, 右方向にx軸の値が増加, 下方向にy軸の値が増加します. 数学の (デカルト) 座標と異なり, y軸の増加は下方向なので注意してください. もっとも, この座標はCanvas固有のものではなく, コンピューターの世界ではこちらが一般的です (ブラウザの座標系, Flashのステージ, C言語のcursesライブラリ…etc)

Canvasの座標

図3 - 1 - l. Canvasの座標

パスの定義

パスの定義を開始するためには, beginPathメソッドを呼び出します. パスの開始宣言と考えればよいでしょう. また, それ以前に定義したパスをクリアするという重要な役割も果たします.

サンプルコード 03


/*
 * Add code to sample code 01
 */

// ....

context.beginPath();

次に, パスの開始座標を定義するために, moveToメソッドを呼び出します. 今回はとりあえず, 中央を開始座標としましょう. 中心を計算するために, Canvasのサイズを参照します. そして, x, yの順で引数を指定します.

パスの定義 1

図3 - 1 - m. パスの定義 1

サンプルコード 04


/*
 * Add code to sample code 03
 */

// ....

var startX = parseInt(canvas.width  / 2);
var startY = parseInt(canvas.height / 2);

context.moveTo(startX, startY);

そして, moveToメソッド以降のパスの定義には, lineToメソッドを呼び出します. 今回はとりあえず, 縦方向に50%の高さ, 横方向に100%の長さをもつ三角形を定義することにします. まず, 右側の辺から定義していきます.

パスの定義 2

図3 - 1 - n. パスの定義 2

サンプルコード 05


/*
 * Add code to sample code 04
 */

// ....

var rightX = canvas.width;
var rightY = canvas.height;

context.lineTo(rightX, rightY);

Canvasの右下の座標を指定することで, 三角形の右側の辺のパスを定義しています. この時点ではCanvasに何も描画されていません. あくまでも, パスを定義しているだけだからです. 描画のためには, 定義が完了したパスをCanvasに反映させるメソッドを呼ぶ必要があります.

同じように, 底辺, 左側の辺のパスを定義していきます.

パスの定義 3

図3 - 1 - o. パスの定義 3

パスの定義 4

図3 - 1 - p. パスの定義 4

サンプルコード 06


/*
 * Add code to sample code 05
 */

// ....

var leftX = 0;
var leftY = canvas.height;

context.lineTo(leftX, leftY);
context.lineTo(startX, startY);

今回のように, パスが開始点に戻る場合, すなわち, パスを閉じる場合には, 最後の定義をlineToメソッドではなく, closePathメソッドを呼び出してもいいでしょう.

サンプルコード 07


/*
 * Add code to sample code 06
 */

// ....

var leftX = 0;
var leftY = canvas.height;

context.lineTo(leftX, leftY);

context.closePath();  // = context.lineTo(startX, startY);

これで, 三角形のパスの定義が完了しました. あとは, 定義したパスをCanvasに描画する処理が必要です.

パスの定義 完成

図3 - 1 - q. パスの定義 完成

パスの描画

パスの輪郭を描画するには, strokeメソッドを呼び出します. デフォルトでは, 黒のラインですが, strokeStyleプロパティに文字列 (カラー表記, HEX, rgba, hsla) を設定することで輪郭 (ライン) の色を変更できます. また, lineWidthプロパティに数値を設定することで輪郭 (ライン) のサイズを変更できます (デフォルトの値は1).

パスの描画 輪郭

図3 - 1 - r. パスの描画 輪郭

サンプルコード 08


/*
 * Add code to sample code 07
 */

// ....

// Change style
context.strokeStyle = 'rgba(255, 0, 0, 1.0)';  // = 'red', '#ff0000', 'hsla(0, 100%, 50%, 1.0)'
context.lineWidth   = 3;

// Draw triangle on Canvas
context.stroke();

パスを塗りつぶすには, fillメソッドを呼び出します. パスが閉じられていない場合には, 自動でパスを閉じて塗りつぶします. デフォルトでは, 黒で塗りつぶしですが, fillStyleプロパティに文字列 (カラー表記, HEX, rgba, hsla) を設定することで塗りの色を変更できます.

パスの描画 塗りつぶし

図3 - 1 - s. パスの描画 塗りつぶし

サンプルコード 09


/*
 * Add code to sample code 07
 */

// ....

// Change style
context.fillStyle = 'rgba(0, 0, 255, 1.0)';  // = 'blue', '#0000ff', 'hsla(240, 100%, 50%, 1.0)'

// Draw triangle on Canvas
context.fill();

これで, パスの描画ができました. しかし, 波形の描画ではもう1つ必要になる処理があります. それは, Canvasのクリア, つまり, 描画データの消去です. といっても難しいことではなく, clearRectメソッドを呼び出すだけです. このメソッドの引数で指定された矩形領域のデータをクリアします. 矩形領域は, 左上のx座標, y座標と幅, 高さで定義します.

サンプルコード 10


/*
 * Add code to sample code 08 - 09
 */

// ....

context.clearRect(0, 0, canvas.width, canvas.height);

以上で, 波形の描画において最低限必要なCanvasの処理を解説しました. 実際には, テキストの描画なども可能になると, よりよいサウンドの視覚化が実現できますが, それはこのあとのセクションで解説します.

それでは, Canvasでのパスの描画のまとめとしてデモ 02を試してみてください.

デモ 02

矩形とテキストの描画

波形を描画するだけであれば, パスの描画ができれば問題ありませんが, アナライザーらしくテキストやグリッドも表示したい場合は不十分です.

そこで, このセクションでは矩形とテキストの描画について解説します. といっても, 難しいことはなく描画のためのメソッドをいくつか習得するだけです.

矩形の描画

矩形の描画はパスの描画ができれば必ずしも必要というわけではありませんが, 矩形が必要となるたびにパスを定義するのはちょっとめんどいです.

rectメソッドを利用することによって矩形のパスを簡単に定義できます. このメソッドの引数は, 第1, 2引数に左上のx座標, y座標, 第3, 4引数に矩形の幅, 高さを指定します (要は, 矩形の領域情報を指定するだけです).

サンプルコード 14


/*
 * Add code to sample code 01
 */

// ....

var left = (canvas.width  / 2) - 50;
var top  = (canvas.height / 2) - 50;
var w    = 100;
var h    = 100;

// Define rectangle path
context.beginPath();
context.rect(left, top, w, h);

// Change style
context.strokeStyle = 'rgba(255, 0, 0, 1.0)';  // = 'red', '#ff0000', 'hsla(0, 100%, 50%, 1.0)'
context.lineWidth   = 3;

context.stroke();

// Change style
context.fillStyle = 'rgba(0, 0, 255, 1.0)';  // = 'blue', '#0000ff', 'hsla(240, 100%, 50%, 1.0)'

context.fill();

塗りつぶしのみならfillRectメソッド, 輪郭描画のみならstrokeRectメソッドを利用すれば, 矩形の描画を簡単に実装できます. 引数はrectメソッドと同様に矩形の領域情報を指定するだけです.

サンプルコード 15


/*
 * Add code to sample code 01
 */

// ....

var left = (canvas.width  / 2) - 50;
var top  = (canvas.height / 2) - 50;
var w    = 100;
var h    = 100;

// Change style
context.strokeStyle = 'rgba(255, 0, 0, 1.0)';  // = 'red', '#ff0000', 'hsla(0, 100%, 50%, 1.0)'
context.lineWidth   = 3;

context.strokeRect(left, top, w, h);

// Change style
context.fillStyle = 'rgba(0, 0, 255, 1.0)';  // = 'blue', '#0000ff', 'hsla(240, 100%, 50%, 1.0)'

context.fillRect(left, top, w, h);

テキストの描画

テキストを描画するには, fillTextメソッド, または, strokeTextメソッドを利用します. それぞれ, テキストの塗りつぶし, 輪郭描画を実行します. 引数の設定はどちらのメソッドも同じで, 第1引数に描画するテキスト, 第2, 3引数にx座標, y座標を指定します.

そして, fontプロパティに文字列を設定することで, フォントやフォントサイズを変更可能です. また, パスの描画と同様にfillStyle / strokeStyleプロパティで色を変更できます. さらに, strokeTextメソッドであれば, lineWidthプロパティの設定も有効です.

サンプルコード 11


/*
 * Add code to sample code 01
 */

// ....

// Change style
context.strokeStyle = 'rgba(255, 0, 0, 1.0)';  // = 'red', '#ff0000', 'hsla(0, 100%, 50%, 1.0)'
context.lineWidth   = 3;

context.fillStyle = 'rgba(0, 0, 255, 1.0)';  // = 'blue', '#0000ff', 'hsla(240, 100%, 50%, 1.0)'

context.font = '36px "Times New Roman"';

var x = canvas.width  / 2;
var y = canvas.height / 2;

context.strokeText('Canvas', x, y);
context.fillText('Canvas', x, y);

ところで, サンプルコード 11は中心にテキストを描画したいのですが, 実際にはそのようになりません. その理由は, 中心からテキストが描画されるからです. そこで, テキストのサイズを測定して, その値を基に座標をずらして描画すれば, センタリングできそうです.

measureTextメソッドを利用することによって, それを実現できます.. このメソッドの引数にサイズを測定したいテキストを指定します. メソッドの戻り値として, TextMetricsオブジェクトを取得できます. そして, TextMetricsオブジェクトのwidthプロパティにアクセスすることで, テキストの幅を取得可能です.

measureTextメソッドでテキストの高さは取得できません. そこで, テキストの高さはfontプロパティに設定しているフォントサイズから取得します (ただし, 正確な高さではありません).

サンプルコード 12


/*
 * Add code to sample code 01
 */

// ....

var text = 'Canvas';

// Change style
context.strokeStyle = 'rgba(255, 0, 0, 1.0)';  // = 'red', '#ff0000', 'hsla(0, 100%, 50%, 1.0)'
context.lineWidth   = 3;

context.fillStyle = 'rgba(0, 0, 255, 1.0)';  // = 'blue', '#0000ff', 'hsla(240, 100%, 50%, 1.0)'

context.font = '36px "Times New Roman"';

var x = canvas.width  / 2;
var y = canvas.height / 2;

// for centering text
var textMetrics = context.measureText(text);

var offsetX = textMetrics.width / 2;
var offsetY = Math.floor(parseInt(context.font.match(/\s*(\d+)px.*/)[1]) / 4);

context.strokeText(text, (x - offsetX), (y + offsetY));
context.fillText(text, (x - offsetX), (y + offsetY));

実は, 横方向のセンタリングはより簡単な方法があり, textAlignプロパティに 'center' を指定することです. 状況によってはテキストの幅を取得したい場合もあると思います. そんなに難しいことではないので, どちらの方法も習得しておくと役に立つはずです.

サンプルコード 13


/*
 * Add code to sample code 01
 */

// ....

var text = 'Canvas';

// Change style
context.strokeStyle = 'rgba(255, 0, 0, 1.0)';  // = 'red', '#ff0000', 'hsla(0, 100%, 50%, 1.0)'
context.lineWidth   = 3;

context.fillStyle = 'rgba(0, 0, 255, 1.0)';  // = 'blue', '#0000ff', 'hsla(240, 100%, 50%, 1.0)'

context.font = '36px "Times New Roman"';

var x = canvas.width  / 2;
var y = canvas.height / 2;

// for centering text
context.textAlign = 'center';
var offsetY = Math.floor(parseInt(context.font.match(/\s*(\d+)px.*/)[1]) / 4);

context.strokeText(text, x, (y + offsetY));
context.fillText(text, x, (y + offsetY));

イントロダクション まとめ

このページでは, サウンドの視覚化の準備となる知識を解説してきました. 準備とはいえ, コンテンツ量も多かったので, 重要なエッセンスをまとめておきます.

A - D変換
  1. 1. 標本化, サンプリング定理
  2. 2. 量子化
スペクトル
  • フーリエ変換, 高速フーリエ変換 (FFT)
  • 基本周波数, 倍音
HTML5 Canvas
  • HTMLCanvasElementの取得
  • CanvasRenderingContext2Dの生成
  • パスの描画
  • Canvasのクリア