「ポチッと音を出す」で使った BeepAPI を用いて、こんなクラスを作ってみました。別にクラスにすることもないのですが、後で音階を作るのに便利なので。
クラスモジュール SoundClass
Public Freq As Long
'Freq は、オクターブが5の場合を基準にした周波数
'Ring で octave を4とすると、Freq の1オクターブ下の音が出る
' 6とすると、1オクターブ上
'--- オクターブ、継続時間、その後の休止時間を指定して音を出す
Public Sub Ring(octave, contTime, restTime)
BeepAPI Freq * 2 ^ (octave - 5), contTime
Application.Wait [now()] + restTime / 86400000
End Sub
このクラスを使って、こんな音を出してみました。
標準モジュール TimeSignal()
#If Win64 Then
Public Declare PtrSafe Function BeepAPI Lib "kernel32" Alias "Beep" _
(ByVal dwFreq As Long, ByVal dwDuration As Long) As Long
#Else
public Declare Function BeepAPI Lib "kernel32" Alias "Beep" _
(ByVal dwFreq As Long, ByVal dwDuration As Long) As Long
#End If
'----- 時報
Public Sub TimeSignal()
Dim n
With New SoundClass
.Freq = 440
For n = 1 To 3
.Ring 5,100, 900
Next
.Ring 6,1000, 0
End With
End Sub
TimeSignal を実行すると、440Hz 0.1sec が 0.9sec の無音を挟んで3回鳴った後、880Hz が 1sec 鳴ります。NHK の時報の真似。実際はこの後に 2sec のリリース(減衰)があるのですが、音量の調整のしかたがよくわからないので、これでよしとしました。どなたか音量減衰方法をわかる方がおられれば、ぜひご教示ください。
今度は、メロディーらしきものを発声できないかとやってみたのが、次の SingSong() です。上の標準モジュールの最初にあった API を呼ぶための宣言は、どこかに書いてあるものとして省略。
標準モジュール SingSong()
'----- 1オクターブ分の音名を列挙
Private Enum Note
c
cis
d
es
e
f
fis
g
gis
a
b
h
End Enum
Private notes(11) As SoundClass
'----- メトロノームのビート数
Private bpm As Long
'----- 各音名の周波数を平均律で設定
Public Sub CalcNote(pitch)
Dim basePitch, i
basePitch = pitch * 2 ^ (3 / 12) 'C5 の周波数に換算
For i = 0 To 11
Set notes(i) = New SoundClass
notes(i).Freq = basePitch * 2 ^ (i / 12)
Next
End Sub
'----- 演奏
Public Sub SingSong( _
Optional tuningPitch = 440, Optional tempo = 120 _
)
CalcNote tuningPitch
bpm = tempo
'--- 開始
xx c, 5, 2, 1 'C5 を 2拍分目一杯鳴らす
xx d, 5, 1, 1
xx e, 5, 1, 0.8 'E5 を 1拍分の80%鳴らしてちょっと休む
xx e, 5, 1, 1
xx d, 5, 1, 1
xx c, 5, 1, 1
xx g, 4, 1, 0.8
xx e, 4, 2, 1
xx g, 4, 1, 1
xx c, 5, 1, 0.6
xx c, 5, 1.5, 1
xx g, 4, 0.5, 0.5
xx g, 4, 1, 1
End Sub
'----- 1音分の発声
'音名、オクターブ、拍数、継続時間の割合
Private Sub xx(noteName, octave, beat, ratio)
Dim cTime, rTime
rTime = 60000 / bpm * beat
cTime = rTime * ratio
rTime = rTime - cTime
notes(noteName).Ring octave, cTime, rTime
End Sub
tempo はメトロノームのビート数で、1分当たりの四分音符の拍数です。楽譜の右上などに などと書かれているものです。
SingSong() は一応、第九の4楽章、歓喜の歌の第2声の4小節のつもり・・・・・
そもそも周波数の設定が整数なのと、サブルーチンの呼び出しのオーバーヘッドが思いのほかかかる(多少、改善の余地はあります)のとで、音程はおぼつかないし、拍数も不安定なので、とても気持ちが悪いとも言えるし、それはそれで味があるとも言えます。ロバに乗ってダービーに臨んだようなものですね。ロバにはロバのよさがありますが。
しかしまあ、さっきの時報の真似くらいが無難なところと言えるでしょう。
ARDUINO 用に C++ で書いてまずまず成功したのを援用したのですが、VBA では、クラスを使わないで書いたほうが、もう少しまともになると思います。
ただし、1オクターブ分の SoundClass のインスタンスを作る CalcNote() までは有用で、これを用意しておけば、さっきの時報はこういう風によりエレガントに記述できます。
時報の改良版 SoundDemo2()
'Enum と notes() の宣言、CalcNote() は省略
'事前にどこかで CalcNote() を実行しておく
'----- 時報2
Public Sub TimeSignal2()
Dim n
For n = 1 To 3
notes(a).Ring 4, 100, 900
Next
notes(a).Ring 5, 1000, 0
End Sub
えっ? それほどエレガントでない? では、音を A ではなく H でやってみようとか、最後の音の前に短い Gis で装飾音を入れてみようとかいった場合にどうなるか、試してみてください。