SCILABでDTM

SCILABで正弦波を作成し、soxで音声ファイルに変換するシェルスクリプトです。

6行目から20行目は音符ファイルです。音符ファイルは主として周波数と音量(振幅)で与えています。http://tomari.org/main/java/oto.html などを参考にさせて頂きました。音階と周波数を関連付けるユーザーインターフェースを作っておけば、さらに操作性は良くなると思います。

シンセサイザーはAttack、Decay、Sustain、Releaseといった変数で様々な音作りをしているそうです。今回は鍵盤を押しているあいだ音は鳴ったままという設定の効果としました。

test.txtの各行で指定された音階から65行目の正弦関数によって波形を計算し、順次waveというベクトル変数に加算してゆきます。そして最後にtest.rawというファイルにraw形式で書き出します。

raw形式というのは2バイト長整数の羅列からなるバイナリファイルでヘッダも何もありません。これをsoxコマンドでWAV形式の音声データに変換しています。

65行目の正弦関数を、例えば

wave=wave+amp(w)*(2*(fm(w)*t-floor(fm(w)*t))-1).*taper;

といった関数に変更すればsaw toothと呼ばれるノコギリ状の繰り返し波になります。

また

for i=1:N
if (fm(w)*t(i)-floor(fm(w)*t(i))) < 0.5 then
pulse(i)=1.0;
else
pulse(i)=-1.0;
end
end
wave=wave+amp(w)*pulse.*taper;

としてやると矩形波となります。

DTMの世界では正弦波よりノコギリ波や矩形波の方が高音がより多く含まれるため好まれているらしいです。

 


 

#!/bin/bash
fs=22050 #サンプリング周波数
leng=8 #全体は8秒間
count=8 #8拍

#音符ファイルの出力
#周波数(Hz),開始拍,終了拍,音量,Attack,Decay,Sustain,Release
cat << + > test.txt
523.251 0 0.25 1000 0 0 1 0
587.33 0.25 0.5 1000 0 0 1 0
659.255 0.5 2.5 1000 0 0 1 0
587.33 2.5 2.75 1000 0 0 1 0
523.251 2.75 3 1000 0 0 1 0
523.251 4 4.25 1000 0 0 1 0
587.33 4.25 4.5 1000 0 0 1 0
659.255 4.5 4.75 1000 0 0 1 0
587.33 4.75 5 1000 0 0 1 0
523.251 5 5.25 1000 0 0 1 0
587.33 5.25 7.25 1000 0 0 1 0
+

scilab -nw << _SCI_
fs=${fs}; // シェル変数をSCILABに取り込みます
leng=${leng};
rhythm=fs*${leng}/${count};

dt=1/fs;
t=[0:dt:leng]'; // 時刻ベクトルを作成します
N=size(t,'*');
wave=zeros(1:N)'; // 波形ベクトルを初期化します

// 音符ファイルをオープンして読み込みます
fd=mopen('test.txt','r');
[n,fm,st_beat,en_beat,amp,a_beat,d_beat,s_amp,r_beat]=\
mfscanf(-1,fd,'%f %f %f %f %f %f %f %f');
mclose(fd);

nwave=size(fm,'*');
for w=1:nwave
st=st_beat(w)*rhythm;
en=en_beat(w)*rhythm;
a=a_beat(w)*rhythm;
d=d_beat(w)*rhythm;
s=s_amp(w);
r=r_beat(w)*rhythm;

// ベクトルtaperは音色に関係します
// この例ではほとんど効果を発揮していません
taper=zeros(1:N)';
for i=1:N
if i<st then,
taper(i)=0.;
elseif i<st+a then,
taper(i)=(i-st)/a;
elseif i<st+a+d then,
taper(i)=(s-1)/d*(i-st)+(d-a*s+a)/d;
elseif i<en-r then,
taper(i)=s;
elseif i<en then,
taper(i)=s*(i-en)/r;
else
taper(i)=0;
end // if文の終わり
end // i-ループの終わり
wave=wave+amp(w)*sin(2*%pi*fm(w)*t).*taper;
end // w-ループの終わり

// バイナリファイルtest.rawを作成して
// 2バイト長整数を書き出します
fd=mopen('test.raw','wb');
mput(wave,'s',fd);
mclose(fd);
_SCI_

# test.rawをWAV形式音声ファイルに変換します
sox -r ${fs} -c 1 -b 16 -e signed-integer test.raw test.wav