エクセルでピアノの鍵盤を作る方法を解説します。直接的に何かの役に立つわけではなく、プログラミングの練習、もしくは単なる遊びです。しかし、勉強する事と遊びの境目がなくなれば 最強 です。楽しんでやれば挫折しないからです。それでは早速見ていきましょう。
目次
- エクセルでピアノを作る事の概要
- エクセルシートに鍵盤の絵を描く
- 標準モジュールにメインのプロシジャを挿入する
- 標準モジュールの冒頭にAPIの宣言を書く
- シートモジュールにメインのプロシジャを呼び出すプロシジャを記述する
- 音を出すようにする
- メインのプログラムを記述する
- VBA 上級技 エクセルでピアノを作る方法のまとめ
エクセルでピアノを作る事の概要
エクセルでピアノを作る方法についてその流れを押さえ、全体像を把握する事から始めます。その前に前提のお話を少しだけさせて下さい。
前提:APIは上級技だけど難しくない
題名に上級技とありますが、ピアノを作るエクセルのプログラミングが難しいと言うわけではありません。APIというWindowsの機能を借りてきて利用する事が一般にVBAでは「上級」と言われているためにその言い方に合わせました。他のシステムの機能を借りてきて使う事がエクセルの中だけで処理が済むプログラムより高度なので上級と呼ばれているのですが、上級=難しいと言うわけではありません。逆にIfやForなど基礎的な文法市価使っていなくても、複雑な分岐をする必要がある場合などはプログラミングをする事が難しい場合があります。このように、「上級」とされている技術を利用することが難しいとは限らないため、先入観で新しい技術を取り入れるのをためらわないようにしましょう。
エクセルでピアノを作る作業の流れ
エクセルでピアノを作る作業は下のような流れとなります。
- エクセルシートに鍵盤の絵を描く
- 標準モジュールにメインのプロシジャを挿入する
- 標準モジュールの冒頭にAPIの宣言を書く
- シートモジュールにプロシジャを挿入する
- 標準モジュールを仕上げるクリックした場所に応じた音を出すようにする
一見難しいようでもこのように作業を細分化し、ひとつひとつを着実にこなしていくことで目的を達成する事ができます。
エクセルシートに鍵盤の絵を描く
まず、ピアノの鍵盤となる部分を作成します。分かりやすい様に、シート名はSheet1からkey_boardに変更しました。
次に、行と列のスケールを調整します。私の場合は1行目の行の高さを150、2行目の行の高さを100にしました。そして、E列からAT列までの列の幅を1.5に変更します。これはE列からAT列の部分ピアノの鍵盤にするためです。これで行と列の幅を整える作業は完了です。
次に、黒鍵の部分を作ります。ピアノの鍵盤は白黒白黒白白黒白黒白黒白となっています。黒鍵は1行目に作ります。言葉で説明しても分かりづらいので、自作される方は下の図を真似して作成してみて下さい。
罫線を引けば鍵盤はほぼ完成です。見栄えを良くするためにセルの灰色枠線を非表示にしています。セルの枠線を非表示にする方法はリボンの上にあるタグから表示を選択して、目盛線のチェックボックスを外せばOKです。
標準モジュールにメインのプロシジャを挿入する
今回はイベントプロシジャを使いますが、押された場所に応じた音を鳴らす部分は標準モジュールに記述します。ピアノをもしたエクセルシートを作成するのが今回の目的のため、メインのプロシジャ名はPianoにします。
' これは標準モジュールに書きます Sub Piano() End Sub
そしてこのPianoプロシジャの中にAPIを呼び出して、クリックされた場所に応じた音を鳴らすプログラムを書いていきます。
標準モジュールの冒頭にAPIの宣言を書く
このプログラムの心臓部分となるPianoプロシジャの中身の作成に入る前に、APIを使う準備を行います。
APIとは、他のアプリケーションの機能を借りてくることです。ここではWindows API、つまりWindowsの音を鳴らす機能を借ります。エクセルには音を鳴らす機能が無いからです*1。そとから機能を借りてくるので、何の機能を借りてくるのかを予め宣言しておく必要があります。そこで、先ほどのイベントプロシジャの上にAPIの宣言を記述します。
'これは標準モジュールのPianoプロシジャの上に書きます。 Declare PtrSafe Function Beep Lib "kernel32" _ (ByVal dwFreq As Long, ByVal dwDuration As Long) As Long
APIの宣言は冒頭で行いますので、先ほど挿入したPianoプロシジャの上に記述して下さい*2。
なお、APIの使い方や必要なAPIの調べ方について詳しく知りたい方はVBA Windows APIを使う手順(調べる→宣言する→使う)で詳しく解説していますので参考にしてみて下さい。
シートモジュールにメインのプロシジャを呼び出すプロシジャを記述する
今回はイベントプロシジャを利用します。イベントとは、ユーザーの入力など外部からの影響をトリガーに実行されるプログラムのことです。今回はシートをクリックされたら、標準モジュールを呼び出すようにします。このようにする理由は、押されたら音を鳴らすというイベントプロシジャにする必要があることと、シートモジュールのようにオブジェクトにくっついているモジュールではAPIの宣言ができないためです。
このことからシートモジュールはメインのモジュールを呼び出すところだけを担当し、メインの押された場所に応じて音を鳴らす部分は標準モジュールに記述します。
シートモジュールはワークシートに付属したモジュールのため、既に存在します。ここに標準モジュールのPianoプロシジャを呼び出すVBAを記述します。イベントプロシジャを記述するときはモジュール名は自分で書くのではなく、トリガーとなる出来事に応じたプロシジャ名を選択します。
イベントプロシジャが始めてでよく分からない、と言う方はVBA 3分で分かる便利なイベントの使い方 ワークブックにチェック機能をつけるにイベントプロシジャの書き方を具体例を挙げながら解説しているので、参考にして下さい。
ユーザーがシート上でクリックをするとクリックをしたところのセルがActiveとなります。つまり選択した場所が変わります。このため、Worksheet_SelectionChangeを選択します。すると、下記の様になります。
'これはシートモジュールに書きます。 Private Sub Worksheet_SelectionChange(ByVal Target As Range) End Sub
このPrivate SubとEnd Subの間に標準モジュールに記述した音を流すプログラムを呼び出す記述をします。
'これはシートモジュールに書きます。 Private Sub Worksheet_SelectionChange(ByVal Target As Range) Call Piano(Target.Row, Target.Column) End Sub
押された場所が大事なので、呼び出すPianoプロシジャの所に引数にTarget.Row、Target.Columnを書く事で、クリックされた場所の行番号と列番号をPianoプロシジャに渡します。
音を出すようにする
準備が整いました。ここから実際にシート上のどこをクリックしたかで出す音が変わるプログラムを記述していきます。
音程とヘルツ数を調べる
ドレミのという音の高さはヘルツという数字で表されます。そこで、ドからシまでのそれぞれの音程を調べます。
261.626 :ド
277.183 黒鍵
293.665 :レ
311.127 黒鍵
329.628 :ミ
349.228 :ファ
369.994 黒鍵
391.995 :ソ
415.305 黒鍵
440.000 :ラ
466.164 黒鍵
493.883 :シ523.251 :ド
554.365 黒鍵
587.330 :レ
622.254 黒鍵
659.255 :ミ
698.456 :ファ
739.989 黒鍵
783.991 :ソ
830.609 黒鍵
880.000 :ラ
932.328 黒鍵
987.767 :シ
2オクターブ分の鍵盤があるので周波数も2オクターブ分調べました。
なお、この記事では音階を調べるために下のページを参考にさせて頂きました。
https://tomari.org/main/java/oto.html
今回使うAPIでは音の高さをヘルツを使って表現します。
とりあえず音が出るようにする
それではメインのPianoプロシジャを作成してみます。まずは音を単に音を鳴らしてみましょう。Beepという音を鳴らすAPIを使うという宣言を既に行っているため、たった一行で音を鳴らすことができます。まずはラの音を1秒流すだけのプログラムを書いてみましょう。
Sub Piano() Call Beep(440, 1000) End Sub
ラは440Hzなので、第1引数に440を渡しました。Beepは音を流す間の時間をミリ秒で指定します。1秒は1,000ミリ秒なので、1000を第2引数にしました。この状態でどこかをシート状のどこかのセルをクリックすると「ポー」という聴力検査のような音が出ます。APIの利用が完了です。たった一行でとても簡単ですね。冒頭で述べた上級技なのに簡単ということはこういうことです。
メインのプログラムを記述する
それでは次にメインのプログラムを作成していきます。
クリックされてアクティブになったセルの行と列の値を受け取れるようにする
Sub Piano(rw, clm) Call Beep(440, 1000) End Sub
まずは、クリックされた場所を知るためにイベントプロシジャから呼び出すときに渡された引数を受け取れるようにします。イベントプロシジャではPiano(Target.Row, Target.Column)というように2つの引数を渡しながらPianoプロシジャを呼び出しました。この値をPianoプロシジャ側ではrwとclmという変数で受け取ります。rwはRowで行の意味、clmはColumnで列の意味です。ここは自分がわかりやすい変数名にしておけばよいです。
条件分岐させてクリックされた場所に応じた音が出るようにする
どこを押されたかによって出す音が違うので条件分岐を行うことにします。そして、If文は最大で20個までしかElse Ifが使えません。そこで、IfとSelect Case分を組み合わせて使うことにします。
プログラムがしやすいようにセルの表示をR1C1形式にしておきます。リボンの上のタブのファイル⇒オプション⇒数式⇒R1C1参照形式を使用すると進みます。すると、列がABCではなく、1、2、3と言う表示になります。
ピアノの構成をよく見て条件分岐をどうするか考えます。まず、列は5列目から46列目の間以外は音が鳴らないことが確定です。5~46列目であれば、どこを選択したらド、などと一つ一つ条件分岐していきます。
例えば5列目、6列目ならド。7列目なら、1行目ならドの♯。2行目ならド、と言った具合です。これを地道にプログラミングしていくと下記の通りとなります。
Declare PtrSafe Function Beep Lib "kernel32" _ (ByVal dwFreq As Long, ByVal dwDuration As Long) As Long Sub Piano(rw, clm) If rw <= 2 Then Select Case clm Case 5, 6 Call Beep(262, 500) Case 7 If rw = 1 Then Call Beep(277, 500) Else Call Beep(262, 500) End If Case 8 If rw = 1 Then Call Beep(277, 500) Else Call Beep(294, 500) End If Case 9 Call Beep(294, 500) Case 10 If rw = 1 Then Call Beep(311, 500) Else Call Beep(294, 500) End If Case 11 If rw = 1 Then Call Beep(311, 500) Else Call Beep(330, 500) End If Case 12, 13 Call Beep(330, 500) Case 14, 15 Call Beep(349, 500) Case 16 If rw = 1 Then Call Beep(370, 500) Else Call Beep(349, 500) End If Case 17 If rw = 1 Then Call Beep(370, 500) Else Call Beep(392, 500) End If Case 18 Call Beep(392, 500) Case 19 If rw = 1 Then Call Beep(415, 500) Else Call Beep(392, 500) End If Case 20 If rw = 1 Then Call Beep(415, 500) Else Call Beep(440, 500) End If Case 21 Call Beep(440, 500) Case 22 If rw = 1 Then Call Beep(466, 500) Else Call Beep(440, 500) End If Case 23 If rw = 1 Then Call Beep(466, 500) Else Call Beep(494, 500) End If Case 24, 25 Call Beep(494, 500) 'オクターブ Case 26, 27 Call Beep(262 * 2, 500) Case 28 If rw = 1 Then Call Beep(277 * 2, 500) Else Call Beep(262 * 2, 500) End If Case 29 If rw = 1 Then Call Beep(277 * 2, 500) Else Call Beep(294 * 2, 500) End If Case 30 Call Beep(294 * 2, 500) Case 31 If rw = 1 Then Call Beep(311 * 2, 500) Else Call Beep(294 * 2, 500) End If Case 32 If rw = 1 Then Call Beep(311 * 2, 500) Else Call Beep(330 * 2, 500) End If Case 33, 34 Call Beep(330 * 2, 500) Case 35, 36 Call Beep(349 * 2, 500) Case 37 If rw = 1 Then Call Beep(370 * 2, 500) Else Call Beep(349 * 2, 500) End If Case 38 If rw = 1 Then Call Beep(370 * 2, 500) Else Call Beep(392 * 2, 500) End If Case 39 Call Beep(392 * 2, 500) Case 40 If rw = 1 Then Call Beep(415 * 2, 500) Else Call Beep(392 * 2, 500) End If Case 41 If rw = 1 Then Call Beep(415 * 2, 500) Else Call Beep(440 * 2, 500) End If Case 42 Call Beep(440 * 2, 500) Case 43 If rw = 1 Then Call Beep(466 * 2, 500) Else Call Beep(440 * 2, 500) End If Case 44 If rw = 1 Then Call Beep(466 * 2, 500) Else Call Beep(494 * 2, 500) End If Case 45, 46 Call Beep(494 * 2, 500) End Select End If End Sub
プログラムはかなり長くなってしまいましたが、どこを押されたかをひたすら分岐しているだけなので、命令としては1行しか実行されません。オクターブはヘルツ数を2倍しています。反省点としては音の長さが決め打ちな所です。クリックした瞬間から0.5秒を機械的にならしています。別のAPIを使えばマウスのクリックを離した瞬間*3に音が途切れるようにするなどが考えられます。また、複数の音を同時にならすこともできません。
APIに慣れ親しむ目的で紹介しましたので、このあたりでご容赦頂ければと思います。
VBA 上級技 エクセルでピアノを作る方法のまとめ
この記事の内容をまとめます。
- VBAによるプログラミングでAPIは上級向けとされているが、難しくはない
- APIは宣言さえ分かればあとは呼び出すだけで使える
- ピアノを作ろうとすると分岐が沢山必要である
- ピアノの鍵盤をセルで代用しようとすると、イベントプロシジャが必要となる
勉強とは思わずに、遊びでこのようなプログラムを作ってみるのも良いと思います。
<関連記事>
<お知らせ>
Excel VBAでクラスやオブジェクトの概念と使い方を丁寧に解説し、ワンランク上の実力を目指すガイドを書きました。この本で本物の実力を身に付けて一皮むけてみませんか?
books.rakuten.co.jp