Rで音を生成するには audio
パッケージを使用する.
このパッケージの関数 play
では入力したベクトルデータを音に変換して再生する。
一般的にC4のドの音は261.6256Hzである.それぞれの鍵盤には番号が振られており(ノートナンバー),式\eqref{note2hz} の換算式が利用できる
\begin{equation} f = 440\times2^{\frac{n - 69}{12}} \label{note2hz} \end{equation} ここで $n$ はノートナンバー,$f$は 周波数である.これをまとめたものが表1である.オクターブ | 音名 | 音名(ドレミ表記) | ノートナンバー | 周波数 |
---|---|---|---|---|
4 | C | ド | 60 | 261.63 |
C# | ド♯ | 61 | 277.18 | |
D | レ | 62 | 293.66 | |
D# | レ♯ | 63 | 311.13 | |
E | ミ | 64 | 329.63 | |
F | ファ | 65 | 349.23 | |
F# | ファ♯ | 66 | 369.99 | |
G | ソ | 67 | 392.00 | |
G# | ソ♯ | 68 | 415.30 | |
A | ラ | 69 | 440.00 | |
A# | ラ♯ | 70 | 466.16 | |
B | シ | 71 | 493.88 |
この換算式とパッケージの関数を組み合わせて打ち込んだ音階を再生する.まず,\fn{note2hz}関数の作成を行う.
note2hz <- function(d){
freq <- 2^((d - 69)/12) * 440
return(freq)
}
一般に使用するサンプリング周波数である44.1kHzでは$p$秒のデータは式\eqref{seqmks}で作成可能である.
\begin{equation} \underbrace{\sin\left( 0\right), \cdots, \sin\left(2\pi f p\right)}_{44100\times p\mbox{個}}\label{seqmks} \end{equation}# 指定した音階をp秒ならす
mks <- function(num, p = 1){
freq <- note2hz(num)
playdata <- sin(seq(0, 2*pi*freq*p,
length = round(44100*p)))
play(playdata, rate = 44100) # データの再生
}
この mks
関数順に呼び出せば順番に音を鳴らすことができる.
play
関数は音を非同期で再生するので Sys.sleep
を使用している.
for(i in c(60, 62, 64, 65, 67, 69, 71, 72)){
mks(i, 1)
Sys.sleep(1)
}
和音の作成
和音を作成するには,とりあえず和を取ればいいはずである.音量が大きくなりすぎることに注意する.
for
文を使用してデータの和を取り,重ねた回数 $k$ で割れば良いので
mkw <- function(num, p = 1, output = F, sound = T){
freq <- note2hz(num)
data = rep(0, round(44100*p))
for(i in 1:length(num)){
data = data +
sin(seq(0, 2*pi*freq[i]*p,
length = round(44100*p))) / length(num)
}
if(sound)play(data, rate=44100)
if(output)return(data)
}
ドミソの和音は以下で再生できる.
mkw(c(60, 64, 67))
打ち込んだ音楽の再生
音階と長さを list
形式で保存し,再生を行う.オブジェクト noritz
というデータを用意する.これはある有名な音楽の一節である.(正確には4小節)
67番の音を0.5秒,次に65番の音を0.5ならす$\cdots$指示を記述したオブジェクトである.
noritz <- list(
c(67,0.5), c(65, 0.5),
c(list(c(64,48)),.5),c(list(c(64,48,55,52)),.5),
c(list(c(67,48,55,52)),.5),c(list(c(72,48,55,52)),.5),
c(list(c(71,50)),.5),c(list(c(71,50,55,53)),.5),
c(list(c(67,50,55,53)),.5),c(list(c(74,50,55,53)),.5),
c(list(c(72,52)),.5),c(list(c(72,52,60,55)),.5),
c(list(c(76,52,60,55)),.5),c(list(c(76,52,60,55)),.5),
c(list(c(52)),.5),c(list(c(52,60,55)),.5),
c(list(c(72,52,60,55)),.5),c(list(c(71,52,60,55)),0.5),
c(list(c(69,53)),.5),c(list(c(69,53,62,57)),.5),
c(list(c(77,53,62,57)),.5),c(list(c(74,53,62,57)),.5),
c(list(c(72,55)),.5),c(list(c(72,55,64,60)),.5),
c(list(c(71,55)),.5),c(list(c(71,55,65,62)),.5),
c(list(c(72,60)),.5),c(list(c(72,60,67,64)),.5),
c(list(c(72,67,64)),.5),c(list(c(72,65,62)),.5),
c(list(c(64,60)),1)
なお,用いる playnote
関数にはピッチ調整とテンポ調整を実装してある.関数内で先程の mkw
関数を呼び出している.
playnote <- function(data, tempo = 120, key = 0,
output=F, sound = T, ...){
da = numeric()
for(i in 1:length(data)){
tmp = mkw(unlist(data[[i]][1])+key,
unlist(data[[i]][2])/2*(120/tempo),
output = T, sound = sound, ...)
da = c(da, tmp)
if(sound){
Sys.sleep(unlist(data[[i]][2])/2*(120/tempo))
}
}
if(output)return(da)
}
パートごとに打ち込んで再生
上記の方法だと各パートごとに音符の長さが異なるときは不便である.したがって,各パートをlist
で入れ子にしたものを準備する.各パートだけで聞きたい方は playnote(kk01[[1]], tempo=120, key=12)
などでどうぞ.
# Keikyu Shinagawa Sta.
kk01 = list(
list(
c(67,.5),c(74,.5),c(76,.5),
c(74,.5),c(71,.5),c(69,.5),c(67,1),c(67,1),c(66,1),
c(66,1),c(67,1),c(67,.5),c(74,.5),c(76,.5),
c(74,.5),c(71,.5),c(69,.5),c(67,1),c(67,1),c(67,.5),
c(66,.5),c(67,.5),c(69,.5),c(67,1),c(67,.5),c(74,.5),c(76,.5),
c(74,.5),c(71,.5),c(69,.5),c(67,1),c(67,1),c(66,1),
c(66,1),c(67,1),c(67,.5),c(74,.5),c(76,.5),
c(74,.5),c(71,.5),c(69,.5),c(67,1),c(67,1),c(66,.5),
c(66,.5),c(67,.5),c(69,.5),c(67,2.5),
c(55,1/8),c(list(c(55,59)),1/8),c(list(c(55,59,62)),1/8),c(list(c(55,59,62,67)),1/8),c(list(c(55,59,62,67,69)),1/8),c(list(c(55,59,62,67,69,73)),1/8),c(list(c(55,59,62,67,69,73,76)),3+2/8),
c(55,1/8),c(list(c(55,59)),1/8),c(list(c(55,59,62)),1/8),c(list(c(55,59,62,67)),1/8),c(list(c(55,59,62,67,69)),1/8),c(list(c(55,59,62,67,69,73)),1/8),c(list(c(55,59,62,67,69,73,76)),3+2/8)
),
list(
c(0,1.5),
c(62,.5),c(55,.5),c(62,.5),c(64,.5),c(60,.5),c(64,.5),c(64,.5),c(62,1),
c(62,.5),c(60,.5),c(59,.5),c(59,.5),c(0,1),c(67,.5),
c(66,.5),c(62,.5),c(54,.5),c(60,1),c(60,1.5),
c(60,.5),c(57,.5),c(60,.5),c(59,1),c(0,1.5),
c(67,1),c(64,.5),c(64,.5),c(59,.5),c(64,1),c(62,1),
c(0,3),c(67,.5),
c(67,1),c(62,.5),c(60,.5),c(62,1),c(60,.5),c(60,.5),c(60,1.5)
),
list(
c(0,1.5),
c(47,1.5),c(48,2.5),
c(50,1.5),c(52,2.5),
c(47,1.5),c(45,.5),c(52,1),c(49,2),
c(50,.5),c(43,1),c(0,1.5),
c(52,1.5),c(49,1.5),c(49,.5),c(48,.5),
c(57,.5),c(62,.5),c(60,.5),c(59,.5),c(55,.5),c(47,1.5),
c(53,1.5),c(52,1),c(48,1),c(50,.5),
c(38,.5),c(40,.5),c(42,.5),c(43,2.5)
)
)
先程の playnote
関数をパートごとにデータを出力し,和を取っている.和音と同様に足した回数で割っている.
playnoteList = function(data, key = 0, output = F,
sound = T, tempo = 120, ...){
tmpData = list(); maxlen = 0
if(length(key) != length(data) && length(key) != 1){
stop("The length of key is wrong.")
} else if(length(key) == 1){
key = rep(key, length(data))
}
for(i in 1:length(data)){
tmpData[[i]] = playnote(data[[i]], tempo = tempo,
key = key[i], output=T, sound = F, ...)
maxlen = max(maxlen, length(tmpData[[i]]))
}
for(i in 1:length(data)){
tmpData[[i]] =
c(tmpData[[i]], rep(0, maxlen - length(tmpData[[i]])))
}
result = rep(0, maxlen)
for(i in 1:length(data)){
result = result + tmpData[[i]]
}
result = result/length(data)
if(sound)play(result, rate=44100)
if(output)return(result)
}
波形の変更
一般に生成される音の波形は正弦波(サインカーブ)が多い.(電気店で聞く電話コーナーのような音)今回も例に漏れず使用しているが,このカーブを他の関数に変更する.
これらの関数をRで実装するには,下記の関数を定義したうえで,mkw
関数内の sin
を置き換えれば良い.また,合成波の関数を式\eqref{composit}にしているが,任意の周期的な関数で作成すれば,まるで R がシンセサイザーかのように音を作ることができる,
\begin{equation}
f(x) = \sin(-x) - \frac{1}{2}\sin\left(-\frac{x}{3}+\frac{\pi}{2}\right)+0.1\cos(4x) \label{composit}
\end{equation}
# ノコギリ波
saw <- function(x){
return(2 * (x/pi/2 - floor(x/pi/2)) - 1)
}
# 矩形波
rectwave <- function(x){
return(floor(sin(x) + 1))
}
# 合成波
composite <- function(x){
return(sin(-x)-0.5*sin(-1/3*x+0.5*pi)+0.1*cos(4*x))
}