今年の4年ゼミ生の一人は音声分析・合成をテーマにしている.
EXCEL上でちまちま作業してもらっているが,
線形予測によるフォルマント抽出がうまくいかないのか,
なかなか母音「あいうえお」すら 波から合成できないでいる.
手軽にBASICで母音の合成ぐらいできないものかと探したら,
高々6つ程度の倍音の重ね合わせで「あいうえお」らしい音を作っているページを見つけた.
qiita.com
本当に手軽にできそうだったので,10進BASICでサウンド生成する方法を探して作ってみた.
十進BASIC−外部プログラムの利用
でもって,音程も適当に定めて実行したのが↓
う~ん,そう思って聞けば聞こえなくもない程度.
AquesTalkつかえば,簡単なんだけどね↓
https://www.a-quest.com/index.html
以下,BASICソースと使い方.
実行すると,母音と音程の列を入力するよう聞いてくるので,
母音[aiueo]と音程[0~9]の組合せをinput.
上掲の音源では
a3i7u9e0o2
とinputしてある.
するとソースのある場所に test.wav が生成されダイレクトにそれが再生される.
いや,その予定だったのだけど,やっているうちにダイレクト再生できなくなった.あれれ.
まぁ,生成された test.wav をクリックすれば再生できるようなので.
REM [疑似母音フォルマント シミュレータ] REM Ver. 2017/12/23 REM 参考: REM BASIC 元ネタ:http://hp.vector.co.jp/authors/VA008683/ExtProg.htm REM testwave.bas 十進BASICによる音声ファイル生成プログラム REM chr$関数の引数として全ての1バイト整数が許される様に、オプションメニューの REM 「互換性」「動作」で「文字列処理の単位」を「バイト」に設定して実行すること REM フォルマント:https://qiita.com/rild/items/339c5c36f4c1ad8d4325 OPTION CHARACTER BYTE DECLARE FUNCTION formant INPUT PROMPT "母音とその高さの列をどうぞ.(「あい↑う↑↑え↓お」は a3i7u9e0o2 など)":vw$ LET vwlen=LEN(vw$) LET d=0.3 !継続時間(秒) LET f0=220 !正弦波周波数(Hz) LET db=-10 !生成波形の最大値(±32767を0dBとする) LET fs=44100 !標本化周波数(Hz) LET bps=fs*4 !1秒当りのデータ量(ステレオ16ビット量子化) LET dsize=d*vwlen*fs*4 !オーディオデータサイズ LET fsize=dsize+36 !ファイルサイズ(先頭8Bを除く) LET fmtsize=16 !フォーマットサイズ LET channel=2^17+1 !ステレオPCMデータの指定 LET reso=2^20+4 !16ビットの指定 LET a=10^(db/20) OPEN #6 : NAME "test.wav" !ファイルを開き、この名前のファイルが ERASE #6 !既に存在していた場合には上書きを指定 LET t0=TIME PRINT #6 : "RIFF"; !以下、wavファイルのヘッダーを作成 CALL out4(fsize) ! PRINT #6 : "WAVEfmt "; ! CALL out4(fmtsize) ! CALL out4(channel) ! CALL out4(fs) ! CALL out4(bps) ! CALL out4(reso) ! PRINT #6 : "data"; ! CALL out4(dsize) !ここまでがヘッダー用の出力 LET al=a*32767 !左チャネル係数 LET ar=a*65535 !右チャネル係数 LET k0=f0/fs*PI*2 !引数の刻み LET audio$="" !オーディオデータバッファを初期化 FOR vw=0 TO vwlen/2-1 LET count=0 !カウンタをリセット LET vow$=vw$(vw*2+1:vw*2+1) LET ht=VAL(vw$(vw*2+2:vw*2+2)) PRINT vow$,ht FOR i=1 TO d*fs LET vsin=formant(vow$,k0*(1+ht/20)) LET lch=INT(vsin*al+0.5) IF lch<0 THEN LET lch=lch+65536 !負の数は補数表現 LET rch=INT(vsin*ar+0.5) IF rch<0 THEN LET rch=rch+65536 !負の数は補数表現 LET audio$=audio$&CHR$(MOD(lch,256))&CHR$(INT(lch/256))&CHR$(MOD(rch,256))&CHR$(INT(rch/256)) LET count=count+1 IF count=64 THEN !バッファデータが所定の長さ(64サンプル256Bがほぼ最適)に達したら PRINT #6 : audio$; !データをファイルに出力して LET audio$="" !バッファを初期化し LET count=0 !カウンタをリセット END IF NEXT i PRINT #6 : audio$; !バッファに残ったデータを出力して NEXT vw CLOSE #6 !ファイルを閉じる PRINT "elapsed time = ";TIME-t0;"seconds" PLAYSOUND "test.wav" SUB out4(i4) !4バイト整数の出力(little endian) LET j4=i4 FOR m=0 TO 3 PRINT #6 : CHR$(MOD(j4,256)); LET j4=INT(j4/256) NEXT m END SUB FUNCTION formant(v$,k) SELECT CASE v$ CASE "a" LET formant=0.19*SIN(i*k*4)+0.09*SIN(i*k*2)+0.08*SIN(i*k*3)+0.08*SIN(i*k*5)+0.07*SIN(i*k*1)+0.07*SIN(i*k*6) CASE "i" LET formant=0.19*SIN(i*k)+0.09*SIN(i*k*2)+0.08*SIN(i*k*11)+0.08*SIN(i*k*13)+0.07*SIN(i*k*12) CASE "u" LET formant=0.19*SIN(i*k)+0.09*SIN(i*k*6)+0.08*SIN(i*k*2)+0.08*SIN(i*k*5)+0.08*SIN(i*k*4) CASE "e" LET formant=0.19*SIN(i*k)+0.09*SIN(i*k*2)+0.08*SIN(i*k*3)+0.08*SIN(i*k*11)+0.07*SIN(i*k*4) CASE "o" LET formant=0.19*SIN(i*k*4)+0.09*SIN(i*k*2)+0.08*SIN(i*k)+0.08*SIN(i*k*3) CASE ELSE LET formant=SIN(i*k) END SELECT END FUNCTION END