Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第3章. 最初の完全なプログラム — 長音階の一音

この章の目的

この章を読み終える頃には、これまでの一行プログラムよりも大きな、完全なプログラムを一つ書いて実行した状態になります。プログラムは長音階のある一音の周波数を計算します。すべての細部を理解する必要はまだありません。ここで使うすべての考え方には、後の章で独立した一章が用意されています。この章の目的は、一つの完全なプログラムが端から端まで動く様子を見ることです。この章を読み終える頃には、これまでの一行プログラムよりも大きな、完全なプログラムを一つ書いて実行した状態になります。プログラムは長音階のある一音の周波数を計算します。すべての細部を理解する必要はまだありません。ここで使うすべての考え方には、後の章で独立した一章が用意されています。この章の目的は、一つの完全なプログラムが端から端まで動く様子を見ることです。

考え方: 一つの音は一つの周波数

音楽のすべての音は、1秒あたりの振動回数、ヘルツという単位で測られる周波数を持っています。ミドルCの上のAは440ヘルツで振動しています。この章ではこの音を基準にして、他のすべての音を測ります。音楽のすべての音は、1秒あたりの振動回数、ヘルツという単位で測られる周波数を持っています。ミドルCの上のAは440ヘルツで振動しています。この章ではこの音を基準にして、他のすべての音を測ります。

音を数字に結びつけるのは、二つの事実です。

  • 1オクターブ上がると、周波数は2倍になります。
  • 1オクターブは、半音と呼ばれる12個の等しい段に分かれます。

12個の等しい段で周波数を2倍にしなければならないので、1段ごとに周波数は同じ一定の倍率で大きくなります。その倍率は「2の12乗根」です。半音n個上昇すると、周波数は「2のn / 12乗」倍になります。12個の等しい段で周波数を2倍にしなければならないので、1段ごとに周波数は同じ一定の倍率で大きくなります。その倍率は「2の12乗根」です。半音n個上昇すると、周波数は「2のn / 12乗」倍になります。

音楽家や楽器は、よく音をMIDIナンバーと呼ばれる整数で表します。440ヘルツの基準音であるA4はMIDIナンバー69で、ミドルCはMIDIナンバー60です。MIDIナンバーmの周波数は次のようになります。音楽家や楽器は、よく音をMIDIナンバーと呼ばれる整数で表します。440ヘルツの基準音であるA4はMIDIナンバー69で、ミドルCはMIDIナンバー60です。MIDIナンバーmの周波数は次のようになります。

frequency = 440 * 2 raised to the power ((m - 69) / 12)

考え方: 長音階はステップのパターン

長音階は12個の半音すべてを使うわけではありません。ある根音(ルート)から始まり、半音ステップの一定のパターンで上昇します。長音階は12個の半音すべてを使うわけではありません。ある根音(ルート)から始まり、半音ステップの一定のパターンで上昇します。

0, 2, 4, 5, 7, 9, 11, 12

最初の音は根音そのもので、ゼロ半音上です。最後の音はオクターブで、12半音上です。間のパターンが、長音階特有の響きを生み出します。最初の音は根音そのもので、ゼロ半音上です。最後の音はオクターブで、12半音上です。間のパターンが、長音階特有の響きを生み出します。

プログラムを組み立てる

プログラムは3つの関数からできています。まずプログラム全体を読んでみてから、そのあとの解説を見てください。プログラムは3つの関数からできています。まずプログラム全体を読んでみてから、そのあとの解説を見てください。

use math::pow

fn midi_to_hz(m: Word) -> Float {
    440.0 * math::pow(2.0, ((m - 69) as Float) / 12.0)
}

fn scale_degree_hz(root: Word, degree: Word) -> Float {
    let steps = [0, 2, 4, 5, 7, 9, 11, 12];
    midi_to_hz(root + steps[degree])
}

fn main() -> Float {
    scale_degree_hz(60, 4)
}

それぞれの部分について見ていきます。

  • use math::powは、ホストから関数を借りる宣言です。べき乗の計算はホストの数学ライブラリが提供してくれるので、useで名前を取り込んで使えるようにします。第6章で関数に戻り、第9部でホスト関数の出所を説明します。use math::powは、ホストから関数を借りる宣言です。べき乗の計算はホストの数学ライブラリが提供してくれるので、useで名前を取り込んで使えるようにします。第6章で関数に戻り、第9部でホスト関数の出所を説明します。
  • fn midi_to_hz(m: Word) -> Floatは、上で書いた周波数の式そのものです。MIDIナンバーmWordとして受け取り、Floatを返します。Floatは小数部分を持てる数値型で、周波数を表すのに必要です。(m - 69) as Floatという式は整数m - 69Floatに変換します。これは12.0で割れるようにするためです。この変換を「キャスト」と呼びます。WordFloat、キャストは第4章で扱います。fn midi_to_hz(m: Word) -> Floatは、上で書いた周波数の式そのものです。MIDIナンバーmWordとして受け取り、Floatを返します。Floatは小数部分を持てる数値型で、周波数を表すのに必要です。(m - 69) as Floatという式は整数m - 69Floatに変換します。これは12.0で割れるようにするためです。この変換を「キャスト」と呼びます。WordFloat、キャストは第4章で扱います。
  • fn scale_degree_hz(root: Word, degree: Word) -> Floatは、音階のパターンをstepsという「配列」に入れています。steps[degree]は、位置degreeにある要素を読みます。関数はその半音数を根音に加えて、midi_to_hzに周波数を尋ねます。配列は第12章で扱います。fn scale_degree_hz(root: Word, degree: Word) -> Floatは、音階のパターンをstepsという「配列」に入れています。steps[degree]は、位置degreeにある要素を読みます。関数はその半音数を根音に加えて、midi_to_hzに周波数を尋ねます。配列は第12章で扱います。
  • fn mainがプログラムを動かします。MIDIナンバー60(ミドルC)を根音とする長音階の、ディグリー4の音を尋ねています。fn mainがプログラムを動かします。MIDIナンバー60(ミドルC)を根音とする長音階の、ディグリー4の音を尋ねています。

実行する

プログラムをscale.kelという名前で保存して、実行します。

keleusma run scale.kel

出力は次のとおりです。

391.99543598174927

配列0, 2, 4, 5, 7, 9, 11, 12の位置4の値は7なので、この音はミドルCから半音7個分上の音です。この音はG4、Cメジャースケールの第5音(ドミナント)で、周波数は392ヘルツの少し手前です。プログラムはそれを最初から計算してくれました。配列0, 2, 4, 5, 7, 9, 11, 12の位置4の値は7なので、この音はミドルCから半音7個分上の音です。この音はG4、Cメジャースケールの第5音(ドミナント)で、周波数は392ヘルツの少し手前です。プログラムはそれを最初から計算してくれました。

書き換えて実行する

配列stepsには8つの要素があり、0から7まで番号が付いています。mainの中のscale_degree_hzの2番目の引数を変えて、もう一度実行してみてください。配列stepsには8つの要素があり、0から7まで番号が付いています。mainの中のscale_degree_hzの2番目の引数を変えて、もう一度実行してみてください。

  • ディグリー0は根音、つまりミドルCそのものになり、約261.63ヘルツです。
  • ディグリー7は根音の1オクターブ上で、約523.25ヘルツです。
  • 間のディグリーは音階の残りの音を返します。

1番目の引数を変えれば、音階全体を別の根音に移すことができます。MIDIナンバー69なら、音階はAの上に乗ります。1番目の引数を変えれば、音階全体を別の根音に移すことができます。MIDIナンバー69なら、音階はAの上に乗ります。

このプログラムは1回の実行で1つの周波数しか返しません。なぜなら、第2章で見たように、コマンドラインツールはmainが返した一つの値だけを表示するからです。音階全体を一度に計算してそれを耳で聴くというのは、第8部のピアノロールの仕事です。このプログラムは1回の実行で1つの周波数しか返しません。なぜなら、第2章で見たように、コマンドラインツールはmainが返した一つの値だけを表示するからです。音階全体を一度に計算してそれを耳で聴くというのは、第8部のピアノロールの仕事です。

この章のまとめ

このプログラム一つで、すでに言語の多くの部分を使いました。

  • パラメータと戻り型を持つ関数
  • Word型とFloat
  • ある型から別の型へのキャスト
  • 配列と、そこから要素を読み出すこと
  • useを使ってホストから借りる関数

これらすべてに、第4章の値と型から始まる独立した章が割り当てられています。これで一つのKeleusmaの完全なプログラムが動く様子を見たことになります。これが第1部のゴールです。これらすべてに、第4章の値と型から始まる独立した章が割り当てられています。これで一つのKeleusmaの完全なプログラムが動く様子を見たことになります。これが第1部のゴールです。