いわゆる巨大ロボットもの。イェーガーという二人乗り巨大ロボットとカイジュウと呼ばれる存在との闘い、多くの犠牲を出しながらも勝利する。 初見時は、パイロットを載せたイェーガーの頭部がガコンとハマるシーンでアガり、主人公の再起、アジアンテイストのドロッとした違法な感じとか、カイジュウも別宇宙におけるイェーガーであったことが判明するシーンとか、総力戦・最終決戦の迫力、最後はサラッと済ませるニクさなど、全体的にバカっぽくてさわやかで面白い映画だったし、見た者は少年の心に還るのもうなづける出来だと感じた。ところが、最近見返してみると、カイジュウのほうがイェーガーよりも強く感じて、少し悲しくなってしまった。イェーガーの強さよりも弱さがむしろ強調されていることに気付いた。片方のパイロットが落ちるともう片方もアウトになる、イェーガーよりもカイジュウのほうが俊敏かつ強力な攻撃手段(EMP等)も持っている、イェーガーの脱出手段に問題あり、イェーガーは直立歩行で手足は細く、カイジュウは四足歩行で手足が太い。今でも楽しめる映画だと思うが、そういう部分が少し悲しくなる。
handの日記
プログラミング、フリーゲーム、読書の日記。
2023年2月3日
2021年3月25日
『シン・エヴァンゲリオン劇場版:||』感想
鑑賞時の感覚
『ヱヴァンゲリヲン新劇場版:Q』 直後の状況からスタート。ヴィレの一行はパリ市街で、冬月によって投入された44A(2体を連結した飛行タイプ)44Bと(2体を連結した電源供給用)4444C(4を連結したポジトロンライフル型のエヴァ)を突破し、アンチLシステムを起動、ユーロネルフを復元し、資源を回収する。
シンジ・アスカ・アヤナミレイ(仮称)の三人は徒歩でL結界密度の低いところへ移動し、ケンスケに回収されて「第3村」に移る。第3村でシンジは成長したトウジとケンスケ、洞木ヒカリに再会するが、ショックが大きく、まともに反応もできない。アスカはそんなシンジの態度にいら立ち、シンジにレーションを食わせながら感情を爆発させる。アヤナミレイ(仮称)は第3村の人に囲まれ、農業を通して(子供が本来学んでいるであろう)多くのことを学ぶ。第3村の中で徐々に立ち直るシンジ、その矢先にアヤナミレイ(仮称)が形象崩壊し、LCLとなってはじけ飛んだ。
同じ日にヴンダーが(アスカを回収し、決戦の準備をするために)第3村に到着し、シンジはそこでヴンダーへの乗船を志願する。
ネルフ本部への突撃準備を進めるヴィレの一行。シンジは厳重な部屋に隔離される。アスカとマリが出撃の直前にシンジの部屋を訪れ、マリは自己紹介し、シンジはアスカがなぜQで窓ガラスをぶん殴ったかについて、3号機が使徒に乗っ取られたときに自分で決断しなかったからだと答える。アスカはあまり納得していないが、シンジの答えを受け止める。マリはようやくシンジに自己紹介する。
ヴィレは「ヤマト作戦」を発動。セカンドインパクト爆心地へ移動するネルフ本部と「黒き月」を追撃する。ヴンダーに迫る(ものすごい数の)エヴァ軍団とヴンダーの同型機2機!エヴァ軍団をバッタバッタとなぎ倒す2号機と8号機!3機目のヴンダーの同型機がヴンダーを下から捉え、制御を乗っ取る。
(・・・このあたりで記憶が吹っ飛ぶ・・・)
ゲンドウがヴンダーの甲板に下り立ち、ミサト・リツコ・シンジと対峙する。頭を撃ち抜かれても死なないゲンドウ。「ネブカドネザルの鍵」で自身の情報を書き換えていたのだ。初号機を回収し、13号機とともにマイナス宇宙へ飛び立つゲンドウ、シンジがそれを追いかけようとエヴァへの搭乗を志願、ミサトはそれを認める。ヴィレのクルーがシンジを撃とうとするが、それはミサトが制する。シンジはマリとともに8号機にのって、マイナス宇宙へ向かう。
マイナス宇宙で13号機が量子テレポーテーションを繰り返す、シンジは(おそらく、それを見て)、初号機の中の綾波レイの元へテレポートし、初号機を覚醒させ、13号機とド突き合いながら「ゴルゴダオブジェクト」の元へ移動する。マイナス宇宙は認識の宇宙であり、戦いの舞台は記憶を反映したものになっている。13号機はロンギヌスの槍を、初号機はカシウスの槍をもって戦う。13号機が強く、初号機は勝てない。我々の決着の手段は暴力でないとし、ゲンドウは対話による決着を提案、シンジはそれを呑んだ。
電車の中での対話。ゲンドウの内心が明かされる。
(・・・このあたりで記憶が吹っ飛ぶ、寝てるわけではなく脳がパンクしているのだ・・・)
エヴァンゲリオン・イマジナリーに槍を突き刺し、アディショナルインパクトを起こす。リアルな巨大綾波が展開される。ヴィレは残った力でヴンダーの背骨から新たな槍を作り出し、それを届けるためにミサトが一人残り、ヴンダーで特攻を仕掛ける。ヴンダー上の8号機が槍を携えて巨大綾波の右目から侵入し、その槍「ガイウスの槍/ヴィレの槍」がシンジの元へ届けられる。
ゲンドウは自らの計画の頓挫を認め、ユイに会えなかったことを悔やむが、その矢先にシンジの向こうにユイ見出し、電車から降りる。
シンジが13号機に残留するアスカ・カヲル・綾波レイを救う。
シンジがアスカに呼びかけると、アスカの過去―彼女も綾波タイプと同じクローンだった―が明かされ、孤独なアスカに寄り添うぬいぐるみの中にケンスケがいた。赤い海の浜辺(EOEのアレ)でシンジは成長したアスカに別れを告げる。カヲルへの呼びかけると、カヲルはずっとシンジの救済を目的にしていたが、救われたかったのは自分だったことをシンジに教えられる。カヲルは加持リョウジと語らいながらシンジのもとを去る。最後にシンジは綾波に話しかけ、エヴァが必要ない世界に作り替えることを告げ、綾波を見送る。
シンジは初号機自身に槍を指そうとするが、シンジの中にいたユイがその役目を引き受け、シンジをもとの世界に送り返す。13号機と初号機、残りのエヴァ全部が槍に貫かれる。そして青い海の浜辺でシンジを迎えにきた8号機も最後のエヴァとなって消滅する。
(・・・このあたりから脳がパンクして夢見心地・・・「胸の大きいイイ女」しか覚えとらん)
感想
シンジ君の物語は完全に解決した。これは本当にすごいことである。TV版のトウジとケンスケ、それを取り扱うジュブナイル小説的な手つきが好きだったので、そこに戻ってきて個人的にはうれしい。シンジ君が14年置いてきぼりにされたのがやや不憫だけど、それはサードインパクトがシンジ君にとって14年間ほとぼりを覚まさないと受け止められなかったからだと考えると納得がいく。また、「エヴァンゲリオン」はどこまでもシンジ君の物語のための道具であり、極めて個人的な概念だったんだなと感じた。「超強い搭乗型ロボット」であればなんでもOKっぽいガンダムに対して、単なる「神の力を持つ肉の機械」とかではエヴァになってくれない。戦闘シーンが少し不満である。迫力満点だけど、とにかく何が起きているのか、どんな動きをしているのかが把握しづらい。CGだからなのか、敵のデザインが細かすぎるのか、なんかそんな感じがする。アスカとケンスケは寝ていたのだろうか?これはそうは思わない。ケンスケの気遣いや距離感を見るに、アスカ自身の問題が解決されるまでケンスケは手を出さなそう。なので、ヤマト作戦から帰還した後の(大人(ガイジン)になった)アスカがケンスケの家に降ってきたところから新しいドラマが始まる。ケンケン呼びは初めてそう呼んだ時に、ケンスケがちょっとうれしそうだったのかもしれない。宇部新川駅。あれ実写だったの?CGかジオラマ撮影を加工したものだと思ってた。
2014年8月3日
『GODZILLA(2014)』感想
ゴジラ(1954)を予習して今回のハリウッドゴジラ(字幕版)を見てきた。率直に言って不満が残る出来だったと思う。映像表現は素晴らしかったが、肝心のゴジラのキャラクターがダメ。とくに不満なのは「人間を無視してMUTOをひたすら狙う」という部分と「倒壊したビルやMUTOの攻撃でゴジラが痛がる」シーン。まずゴジラが目的に忠実なロボットみたいになってしまってて怖さがなく、不必要に暴れないので暴れたり火を吐くシーンがそもそも少ない!むしろミサイルを食らって所構わず当り散らすようなのを期待していただけに残念だった。あと、痛がるゴジラは劇作的には盛り上がるのかもしれないが、これもゴジラのキャラクターを損なっていると思う。今回のハリウッドゴジラはゴジラをある種の環境システムと位置づけている。調査可能で、行動が推測可能である。これはハリウッド映画の文法なのだろう。正体不明の存在は調査する。正体がわかれば対策をたてる。この点でゴジラは(神道における)神ではない。そこが結局不満ということだったのだろうか。うーむ。映像表現は素晴らしかった。とくに「汚し」が上手い。汚いところとか古びてぼろぼろになったモノの描写は、もしかするとアメリカでは日常的にそういうものがあるからかもしれない。
2014年8月1日
『思い出のマーニー』感想など
『思い出のマーニー』は米林監督の描く女の子に期待して観に行った。マーニーがとてもよかったので個人的には満足だ。あのナチュラルな上から目線に萌える男子は多いのではないだろうか。あとは杏奈が肌着をチラ見せしてくるのはなんなんだろうと気になった。本作はマーニーに萌えて、杏奈に共感していくという映画だったと思う。杏奈の心境、とりわけ孤独感には大いに共感させられた。物語前半の彼女の疎外感や、またパーティーの会場で花売りの格好でグダグダな感じになった辺りの逃げ出したい感じ、夏祭りで唐突にキレてしまうあたりのコミュ障具合が心に刺さった。杏奈のこういうところに共感できるかどうかが、本作を楽しめる条件なのではないだろうか?この映画は見た目に反して非常に狭く人を選ぶ映画だと思うし、スタジオジブリには珍しくはっきりと若者向けな作品だったのでは?杏奈が自分のバックグラウンドと居場所をはっきり掴んだことで映画としてはハッピーエンドを迎える。それで彼女が学校で快活になれるかというとそうではない。その居場所は学校ではないし、自分はクオーターだと知ってしまったことも微妙にマイナスに働くはずである。彼女の苦難はきっとまだまだ続くもので、それに打ち勝つことを切に願う次第である。この映画を見ていて、スタジオジブリは「上流階級の子女が不思議に出会う」というニッチな市場を持っていたんじゃないかと感じた。というかスタジオジブリは人間の「階級」について特筆すべき表現力を持っている。金持ちと設定されたキャラクターは説明なしでそれがわかるし、その家系の由緒、歴史とか、なにより思い出を描けるってのがめちゃくちゃすごい。その表現も多種多様で的確だ。それは単なる優劣にとどまらない。マーニーと杏奈の家庭環境もそうだし、都に出る前と後の竹取の翁、風たちぬの堀越二郎と街の子供たちの所得の差、ピッコロ社のジジイの札束単位での金勘定のシーン、サツキとカン太の家、シータとムスカの血統の差、王女としてのナウシカとクシャナの立ち振る舞いの違い……。とにかくあらゆる「違い」がありありとわかるなあなどと。
2012年8月10日
『おおかみこどもの雨と雪』感想
すごくクオリティが高い映画です。しかしながらリアル成分とファンタジー成分の配分がどうにもよくない、と感じました。全体的にはふわふわしており生活感があまりない構成です。背景をリアルに、人物の陰影を浅くして輪郭線をを茶色にしているので、これは意図的でしょう。その割にリアルにしようとしている描写が多いことに違和感があります。それはとくに前半に多く、例えば、狼男と子供を作るところはさらっと流しているのに、その子供が体の具合を悪くしたときに、母親が小児科か動物病院かで迷った末電話で相談して解決するところとかです。娘の雪が小学校の同級生の男子に「犬くさい」と指摘されて、自分が狼人間だということもあってすごく気にするのですが、それなら旦那の狼男も犬くさかったはずだと思いました。あと旦那の狼男は狼の姿で死ぬのですが、その死骸をごみ収集車に放り込んでいるシーンは保健所的にありなんだろうか?頭尾長1メートル越えのようだしそのあたりどうなんだろ? 子供たちは狼になったりヒトになったりするのです。遅かれ早かれアイデンティティの問題に直面することが明らかにわかります。子供たちはそれぞれに、姉の雪はヒトとして、弟の雨は狼として生きることに決めるのですが、その辺を旦那の狼男と相談したんだろうかということが、どうにも引っかかりました。また弟の雨は狼として生きるため、二度と「料理」を食べないわけですが、その辺名残惜しくはないのだろうか?例えば「平成狸合戦ぽんぽこ」では人間にまぎれるかどうかでという状況で「料理」に言及した狸がいました。まあ「おおかみこどもの雨と雪」と「平成狸合戦ぽんぽこ」の違いは同属ネットワークの有無なのかもしれません。 この映画では、本人たちはきちんと自分の生きるアイデンティティを見出したわけですが、親は選択肢を与えただけなので、それは運が良かったに過ぎません。ドラマ性を出すために、あるいは性善説にもとづいてか、こういったシチュエーションで親は選択肢だけ与え、ビジョンを示さないことが通例ですが、それって本当に正しい態度なのでしょうか?姉の雪はヒトとして生きますが、結婚して子供をつくった場合、似たような問題が発生しませんかね?
2012年5月1日
音声合成(3)
音声データ入力を自動化する処理を追加した。
1行目は左から、音量(Max 32767)、デフォルト周波数(Hz)、デフォルト長さ(ステップ)
2行目以降は 左から、1音分のローマ字表記(逆順)、周波数(Hz:省略可能)、長さ(ステップ:省略可能)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <math.h> #define PCM 1 #define BUFSIZE (1000) #ifndef UCHAR_MAX #define UCHAR_MAX (255) #endif #ifndef UCHAR_MIN #define UCHAR_MIN (0) #endif #ifndef SHRT_MAX #define SHRT_MAX (32767) #endif #ifndef SHRT_MIN #define SHRT_MIN (-32768) #endif #ifndef _MAX_PATH #define _MAX_PATH (255) #endif #define FORMANT_LENGTH (5) #define IIR_LENGTH (3) /*---------------------------*/ /* 発音ブロック データ構造体 */ /*---------------------------*/ typedef struct Voice_datum { double ffb[FORMANT_LENGTH];/*フォルマント(子音)*/ double volb;/*子音の音量*/ unsigned long lengthb[3];/*子音の入り方:フェードイン・長さ・フェードアウト*/ unsigned long lengthv[3];/*母音の入り方:空白・フェードイン・フォルマント移動帯*/ double ffv[FORMANT_LENGTH];/*フォルマント(母音)*/ double volv;/*母音の音量*/ double fc;/*基本周波数*/ unsigned long length;/*全声部の長さ*/ int conti;/*連結するかどうか。*/ }VoiceData; typedef struct Speak_tuple { double fc; double vol; unsigned long length; int vnum; int vnum2;/*2重母音用*/ int cnum; int conti; }SpeakData; /*-----------------------------*/ /* Waveファイル エラーチェック */ /*-----------------------------*/ void openerror(char *filename) { fprintf(stderr, "Can't open file: %s\n", filename); exit(1); } void formaterror(FILE *fp) { fprintf(stderr, "File format error : %ld\n", ftell(fp)); exit(1); } short datacheck(short data, short max, short min) { // オーバーフローする場合 + か - を表示 if (data > max) { fprintf(stderr, "+"); data = max; } if (data < min) { fprintf(stderr, "-"); data = min; } return data; } /*--------------------------------------*/ /* 16 bit データ -32768~32767 (無音 0) */ /* 8 bit データ 0~255 (無音 128) */ /*--------------------------------------*/ void _write_1ch1byte(short *data, short *dummy,FILE *wav, unsigned long count) { for(;count>0;(count--,data++)) { datacheck(*data, UCHAR_MAX, UCHAR_MIN); fputc(*data, wav); } } void _write_1ch2byte(short *data, short *dummy,FILE *wav, unsigned long count) { for(;count>0;(count--,data++)) { datacheck(*data, SHRT_MAX, SHRT_MIN); fwrite(data, sizeof(short), 1, wav); } } void _write_2ch1byte(short *left, short *right, FILE *wav, unsigned long count) { for(;count>0;(count--,left++,right++)) { datacheck(*left, UCHAR_MAX, UCHAR_MIN); datacheck(*right, UCHAR_MAX, UCHAR_MIN); fputc(*left, wav); fputc(*right, wav); } } void _write_2ch2byte(short *left, short *right, FILE *wav, unsigned long count) { for(;count>0;(count--,left++,right++)) { datacheck(*left, SHRT_MAX, SHRT_MIN); datacheck(*right, SHRT_MAX, SHRT_MIN); fwrite(left, sizeof(short), 1, wav); fwrite(right, sizeof(short), 1, wav); } } /*----------------------*/ /* wav ファイル書き出し */ /*----------------------*/ void wavwrite(unsigned long length, short *chL, short *chR, char *filename, unsigned short ch, unsigned short bytes, unsigned long sr) { unsigned long file_size, var_long; unsigned short var_short; FILE *file_wave; void (*datawrite[2][2])(short *left, short *right, FILE *wav, unsigned long count)= { {_write_1ch1byte,_write_1ch2byte}, {_write_2ch1byte,_write_2ch2byte} }; file_size = length * ch * bytes; if ((file_wave = fopen(filename, "wb")) == NULL) { openerror(filename); } fwrite("RIFF", 1, 4, file_wave); /* ファイルサイズ */ var_long = file_size + 36; fwrite(&var_long, sizeof(long), 1, file_wave); /* WAVEヘッダ */ fwrite("WAVE", 1, 4, file_wave); /* chunkID (fmt チャンク) */ fwrite("fmt ", 1, 4, file_wave); /* chunkSize (fmt チャンクのバイト数 無圧縮 wav は 16) */ var_long = 16; fwrite(&var_long, sizeof(long), 1, file_wave); /* wFromatTag (無圧縮 PCM = 1) */ var_short = PCM; fwrite(&var_short, sizeof(short), 1, file_wave); /* dwChannels (モノラル = 1, ステレオ = 2) */ fwrite(&ch, 2, 1, file_wave); /* dwSamplesPerSec (サンプリングレート(Hz)) */ fwrite(&sr, sizeof(long), 1, file_wave); /* wdAvgBytesPerSec (Byte/秒) */ var_long = bytes * ch * sr; fwrite(&var_long, sizeof(long), 1, file_wave); /* wBlockAlign (Byte/サンプル*チャンネル) */ var_short = bytes * ch; fwrite(&var_short, sizeof(short), 1, file_wave); /* wBitsPerSample (bit/サンプル) */ var_short = bytes * 8; fwrite(&var_short, sizeof(short), 1, file_wave); /* chunkID (data チャンク) */ fwrite("data", 1, 4, file_wave); /* chunkSize (データ長 Byte) */ fwrite(&file_size, 4, 1, file_wave); /* ヘッダ (44 Byte) 書き込みここまで */ if (ftell(file_wave) != 44) { fprintf(stderr, "%s : wav header write error\n", filename); exit(1); } /* waveformData (データ) 書き込み */ datawrite[ch-1][bytes-1](chL,chR,file_wave,length); fclose(file_wave); } /*----------------------*/ /* 各種波形生成エンジン */ /*----------------------*/ double SineWave(unsigned long T,unsigned long sr,double vol) { /*Sine curve*/ static double t; t += ((double)T / (double)sr); t = (t - floor(t)); return sin(2*M_PI*t) * vol; } double Wave(unsigned long T,unsigned long sr,double vol) { /*Rosenburg波を出力する関数*/ static double t; double t1 = 0.4; /*声門解放期における声門拡張期の割合*/ double t2= 0.4; /*一連の運動における声門解放期の割合*/ t += ((double)T / (double)sr); t = (t - floor(t)); if(t <= t1*t2){ return vol*( (3*t*t)/(t1*t1*t2*t2) - (2*t*t*t)/(t1*t1*t1*t2*t2*t2)); }else if(t <t2){ return vol*(1 - ((t/t2-t1)*(t/t2-t1))/((1-t1)*(1-t1))); }else{ return 0; } } double Wave2(unsigned long T,unsigned long sr,double vol) { static double t; double w; t += ((double)T / (double)sr); t = (t - floor(t)); w = sin(2*M_PI*t); w = (w<0)? - sqrt(-w) : sqrt(w); return w * vol; } double Wave3(unsigned long T,unsigned long sr,double vol) { /*のこぎり波を出力する関数*/ static double t; t += ((double)T / (double)sr); t = (t - floor(t)); return (t - 0.5)*2 * vol; } double GaussNoise(double vol) { /*擬似的なノイズ出力*/ return vol* ((double)rand() / (double)(RAND_MAX) - 0.5); } /*-------------------------*/ /* IIRフィルター作成・適用 */ /*-------------------------*/ void IIR_resonator(double fc, double Q, double *a, double *b) { fc = tan(M_PI * fc) / (2.0 * M_PI); a[0] = 1.0 + 2.0 * M_PI * fc / Q + 4.0 * M_PI * M_PI * fc * fc; a[1] = (8.0 * M_PI * M_PI * fc * fc - 2.0) / a[0]; a[2] = (1.0 - 2.0 * M_PI * fc / Q + 4.0 * M_PI * M_PI * fc * fc) / a[0]; b[0] = 2.0 * M_PI * fc / Q / a[0]; b[1] = 0.0; b[2] = -2.0 * M_PI * fc / Q / a[0]; a[0] = 0.0; } void IIR_LPF(double fc, double Q, double a[], double b[]) { fc = tan(M_PI * fc) / (2.0 * M_PI); a[0] = 1.0 + 2.0 * M_PI * fc / Q + 4.0 * M_PI * M_PI * fc * fc; a[1] = (8.0 * M_PI * M_PI * fc * fc - 2.0) / a[0]; a[2] = (1.0 - 2.0 * M_PI * fc / Q + 4.0 * M_PI * M_PI * fc * fc) / a[0]; b[0] = 4.0 * M_PI * M_PI * fc * fc / a[0]; b[1] = 8.0 * M_PI * M_PI * fc * fc / a[0]; b[2] = 4.0 * M_PI * M_PI * fc * fc / a[0]; a[0] = 0.0; } void IIR_apply(double *a,double *b,double *tap,double *out,double base) { int k; for(k=IIR_LENGTH-1;k>0;k--) { tap[k]= tap[k-1]; out[k]= out[k-1]; } tap[0]=base;out[0] = 0; for(k=0;k<IIR_LENGTH;k++) { out[0] += (b[k] * tap[k]); } for(k=0;k<IIR_LENGTH;k++) { out[0] -= a[k] * out[k]; } } /*--------------------*/ /* さまざまの補助関数 */ /*--------------------*/ double _slide(unsigned long a,unsigned long b,double x1,double x2) { double w; if((b==0) || (a > b)) { return x2; }else{ w = ((double)a)/((double)b); } //w = (w<0.5)? w*w*2 : -2 * w * (w - 2) -1; //w = sin(M_PI * (w -0.5)) * 0.5 + 0.5; return (1-w) * x1 + w * x2; } /*------------------*/ /* 音声生成エンジン */ /*------------------*/ double *Voice_production(unsigned long chunks,double *wave_output,unsigned long sr,VoiceData *Voice1) { unsigned long h,i,j; double av[IIR_LENGTH],bv[IIR_LENGTH],ab[IIR_LENGTH],bb[IIR_LENGTH],ac[IIR_LENGTH],bc[IIR_LENGTH]; double voicev,tapv[FORMANT_LENGTH][IIR_LENGTH]={0},outv[FORMANT_LENGTH][IIR_LENGTH]={0},basev; double voiceb,tapb[FORMANT_LENGTH][IIR_LENGTH]={0},outb[FORMANT_LENGTH][IIR_LENGTH]={0},baseb; double tapc[IIR_LENGTH]={0},outc[IIR_LENGTH]={0}; for(h=0;h<chunks;h++) { printf("開始(音声生成%d)\n",h); for(i=0;i<Voice1[h].length;i++){ wave_output[i] = 0; /*母音*/ if(i > Voice1[h].lengthv[0]){ if((i < (Voice1[h].lengthv[0] + Voice1[h].lengthv[1]))){ basev = Wave(Voice1[h].fc,sr,_slide(i-Voice1[h].lengthv[0],(Voice1[h].lengthv[0] + Voice1[h].lengthv[1]),0,Voice1[h].volv)); }else if(/*(Voice1[h].conti !=0 ) &&*/(h+1<chunks) && (i > (Voice1[h].lengthv[0] + Voice1[h].lengthv[1]+ Voice1[h].lengthv[2]))){ basev = Wave(_slide(i-(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].length -(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].fc,Voice1[h+1].fc),sr,_slide(i-(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].length -(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].volv,Voice1[h+1].volv)); }else{ basev = Wave(Voice1[h].fc,sr,Voice1[h].volv); } //printf("%f",basev); IIR_LPF(3000.0 / 44100,0.7,ac,bc); IIR_apply(ac,bc,&tapc[0],&outc[0],basev); basev = outc[0]; //printf("%f\n",basev); voicev = 0; for(j=0;j< FORMANT_LENGTH ;j++){ if((Voice1[h].conti != 0) && (h+1 < chunks) && (i> Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2])){ IIR_resonator(_slide(i-(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2] ),Voice1[h].length - (Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].ffv[j],Voice1[h+1].ffv[j])/44100,7,av,bv); }else{ IIR_resonator(Voice1[h].ffv[j]/44100,7,av,bv); } IIR_apply(av,bv,tapv[j],outv[j],basev); voicev += outv[j][0]; } if ((i > Voice1[h].lengthv[0])){ wave_output[i] += _slide(i - Voice1[h].lengthv[0],Voice1[h].lengthv[1],0,voicev)/ FORMANT_LENGTH ; }else{ wave_output[i] += voicev/ FORMANT_LENGTH ; } } /*子音*/ if(i < Voice1[h].lengthb[0] + Voice1[h].lengthb[1] + Voice1[h].lengthb[2]){ if(i< Voice1[h].lengthb[0]){ baseb = GaussNoise(_slide(i,Voice1[h].lengthb[0],0,Voice1[h].volb)); }else if(Voice1[h].lengthb[0] + Voice1[h].lengthb[1] < i){ baseb = GaussNoise(_slide(i - (Voice1[h].lengthb[0] + Voice1[h].lengthb[1]),Voice1[h].length - (Voice1[h].lengthb[0] + Voice1[h].lengthb[1]),Voice1[h].volb,0)); }else{ baseb = GaussNoise(Voice1[h].volb); } voiceb = 0; for(j=0;j< FORMANT_LENGTH ;j++){ IIR_resonator(Voice1[h].ffb[j]/44100,5,ab,bb); IIR_apply(ab,bb,tapb[j],outb[j],baseb); voiceb += outb[j][0]; } wave_output[i] +=voiceb/ FORMANT_LENGTH ; } } wave_output+=i; } printf("実行終了(音声生成)\n"); return wave_output; } /*----------------------*/ /* 母音と子音の列から、 */ /* 発音ブロック列を作る */ /*----------------------*/ unsigned long formants(unsigned long chunks,VoiceData *voices,unsigned long sr,SpeakData *Speak1){ unsigned int h,i; unsigned long total=0; double ffv[][FORMANT_LENGTH + 1]= { //a/0 {800,1300,4500,3500,4500,0.8}, //i/1 {250,1900,4500,3500,4500,0.9}, //u/2 {400,1400,4500,3500,4500,0.9}, //e/3 {450,1800,4500,3500,4500,0.7}, //o/4 {450,900,4500,3500,4500 ,0.8}, //j/5 {300,1900,4500,3500,4500,1.0}, //w/6 {400,800,4500,3500,4500 ,0.6} }; VoiceData consonant[]= { // /00 {{0},0.0,{0},{0,0,500},{0},1,1,0,1}, //k/01 {{1400,2600,3200,3500,4500},0.8,{200,0,1200},{1000,2000,0},{0},1,1,0,1}, //kh/02 {{200,1320,3372,3889,4500},1.0,{200,0,1200},{1000,600,0},{0},1,1,0,1}, //k,/03 {{200,2700,4200,3500,4500},0.9,{200,0,2000},{1800,2000,0},{0},1,1,0,1}, //s/04 {{276,3446,4018,3500,4500},1.0,{4000,0,2000},{3000,2000,0},{0},1,1,0,1}, //sh/05 {{95,3738,4937,3500,4500},1.2,{4000,0,2000},{3000,2000,0},{0},1,1,0,1}, //t/06 {{500,2100,4300,3500,4500},1.0,{0,200,400},{500,400,0},{0},1,1,0,1}, //ch/07 {{3900,4900,5400,3500,4500},0.6,{0,0,2000},{1500,500,0},{0},1,1,0,1}, //ts/08 {{1500,4000,4700,3500,4500},0.6,{0,100,1000},{900,500,0},{0},1,1,0,1}, //h/09 {{250,1000,1400,3500,4500},0.6,{500,1000,3000},{3500,500,0},{0},1,1,0,1}, //h,/10 {{250,400,4500,3500,4500},0.5,{1000,800,3000},{3500,500,0},{0},1,1,0,1}, //p/11 {{241,2369,3604,4556,5473},0.4,{300,0,1000},{1000,1000,0},{0},1,1,0,1}, //n/12 {{0},0,{0},{0,500,0},{500,1359,4151,4031,4825},0.25,1,3000,0}, //m/13 {{0},0,{0},{0,500,500},{250,1200,3294,4308,4500},0.4,1,2000,0}, //r/14 {{0},0,{0},{0,1500,0},{375,2069,3745,3500,4500},0.5,1,2400,1}, //rj/15 {{0},0,{0},{0,1500,0},{248,2358,3847,3500,4500},0.5,1,2400,1}, //g/16 {{0},0,{0},{0,1500,1500},{400,800,2200,3500,4500},1.0,1,3000,1}, //z/17 {{338,2518,4761,3500,4500},0.8,{500,0,2000},{1000,1000,0},{324,1396,2445,3997,4915},0.8,1,3000,1}, //dg/18 {{150,3600,4500,3500,4500},0.6,{500,0,2000},{1000,1000,0},{400,2000,2800,3500,4500},0.6,1,2500,1}, //d/19 {{304,1353,2080,2923,4654},0.6,{300,0,2000},{800,800,0},{480,2106,3361,3880,5443},1.0,1,2500,1}, //b/20 {{200,250,400,3500,4500},0.3,{500,0,1000},{1000,1000,0},{500,1300,2500,3500,4500},0.8,1,2000,1}, }; for(h=0;h<chunks;h++) { *voices = consonant[Speak1[h].cnum]; total += Speak1[h].length; if(Speak1[h].cnum > 11) { (*voices).volb *= (*voices).volv * Speak1[h].vol; (*voices).volv *= Speak1[h].vol; (*voices).fc *= Speak1[h].fc; (*voices).conti = 1; Speak1[h].length -= (*voices).length; voices++; *voices = consonant[0]; } if(Speak1[h].vnum2 >0) { for(i=0;i < FORMANT_LENGTH;i++) { (*voices).ffv[i] = ffv[Speak1[h].vnum2][i]; } (*voices).volv *= ffv[Speak1[h].vnum2][FORMANT_LENGTH] * Speak1[h].vol; (*voices).volb *= (*voices).volv; (*voices).conti = 1; (*voices).length = 6000; (*voices).fc *= Speak1[h].fc; (*voices).conti *= Speak1[h].conti; Speak1[h].length -= (*voices).length; voices++; *voices = consonant[0]; } for(i=0;i < FORMANT_LENGTH;i++) { (*voices).ffv[i] = ffv[Speak1[h].vnum][i]; } (*voices).volv *=ffv[Speak1[h].vnum][FORMANT_LENGTH] * Speak1[h].vol ; (*voices).volb *= (*voices).volv; (*voices).fc *= Speak1[h].fc; (*voices).conti *= Speak1[h].conti; (*voices).length = Speak1[h].length; (*voices).lengthv[2]=(Speak1[h].length-(1500+(*voices).lengthv[0] + (*voices).lengthv[1]))*0.95; voices++; } return total; } /*------------------------*/ /* 1音節分のローマ字から */ /* 母音と子音の組を返す */ /*------------------------*/ unsigned long roman(char *c,SpeakData *speech) { int vnum=0,vnum2=0,cnum,count=0,conti=0; /*配列cは{母音,子音1,子音2}のようにデータが逆順だとする*/ printf("%c %c %c\n",c[0],c[1],c[2]); switch(c[0]) { case 'a':vnum=0;break; case 'i':vnum=1;break; case 'u':vnum=2;break; case 'e':vnum=3;break; case 'o':vnum=4;break; }; switch(c[1]) { case 'k': if(vnum==1) { cnum=1; }else if(vnum == 3){ cnum=2; }else{ cnum=3; }break; case 's': switch(c[2]) { case 't':if(vnum==2){vnum2=0;}else{vnum2=6;}cnum=8;break;/*ツァ・ツィ・ツ・ツェ・ツォ*/ default :cnum=4;break; };break; case 't':cnum=6;break; case 'n':cnum=12;break; case 'h': switch(c[2]) { case 'c':if(vnum==1){vnum2=0;}else{vnum2=5;}cnum=7;break;/*チャ・チ・チュ・チェ・チョ*/ case 's':if(vnum==1){vnum2=0;}else{vnum2=5;}cnum=5;break;/*シャ・シ・シュ・シェ・ショ*/ default :if(vnum==1){cnum=10;}else{cnum=9;}break; };break; case 'm': cnum=13;break; case 'y': if(vnum==1){vnum2=0;conti=1;}else{vnum2=5;} switch(c[2]) { case 'k':cnum=2;break; case 's':cnum=5;break; case 't':cnum=7;break; case 'n':cnum=12;break; case 'h':cnum=10;break; case 'm':cnum=13;break; case 'r':cnum=15;break; case 'g':cnum=16;break; case 'z':cnum=17;break; case 'j':cnum=18;break; case 'd':cnum=18;break; case 'b':cnum=20;break; case 'p':cnum=11;break; default :conti=1;cnum=0;break;/*後で修正する*/ };break; case 'w': if(vnum==2){vnum2=0;conti=1;}else{vnum2=6;} switch(c[2]) { case 'k':cnum=2;break; case 's':cnum=5;break; case 't':cnum=7;break; case 'n':cnum=12;break; case 'h':cnum=10;break; case 'm':cnum=13;break; case 'r':cnum=15;break; case 'g':cnum=16;break; case 'z':cnum=17;break; case 'j':cnum=18;break; case 'd':cnum=18;break; case 'b':cnum=20;break; case 'p':cnum=11;break; default :cnum=0;break;/*後で修正する*/ };break; case 'r':if(vnum==1){cnum=15;}else{cnum=14;};break; case 'g':cnum=16;break; case 'z':cnum=17;break; case 'j':cnum=18;if(vnum!=1){vnum2=5;}break; case 'd':cnum=19;break; case 'b':cnum=20;break; case 'p':cnum=11;break; default :conti=1;cnum=0;break;/*後で修正する*/ }; if(cnum > 11){count += 2;}else{count += 1;} if(vnum2 > 0){count += 1;} (*speech).cnum=cnum; (*speech).vnum=vnum; (*speech).vnum2=vnum2; (*speech).conti=conti; return count; } /*----------------------*/ /* 入力ファイルを読み */ /* 母音と子音の列を作る */ /*----------------------*/ /*---------------------------------------------*/ /* 入力ファイルの形式: */ /* 1行目: */ /* ベース音量、音節長、音程(省略可能) */ /* 2行目以降: */ /* ローマ字+音程(省略可能)+長さ(省略可能) */ /*---------------------------------------------*/ unsigned long linecount(char *filename) { FILE *file_script; unsigned long count=0; int c; if ((file_script = fopen(filename, "rb")) == NULL) { openerror(filename); } while((c = fgetc(file_script)) != EOF){ if(c == '\n'){count++;} } fclose(file_script); return count; } unsigned long makespeech(char *filename,double sr,SpeakData *speech) { FILE *file_script; unsigned long basetic= floor(sr*0.2),tic; double vol=20000,basefc=110,fc; char str[128],c,d,e,word[4]; unsigned long count=1; if ((file_script = fopen(filename, "r")) == NULL) { openerror(filename); } if(fgets(str, 128, file_script)==NULL) { openerror(filename); } sscanf(str,"%lf %lf %d",&vol,&basefc,&basetic); printf("音声データ%lf %lf %d\n",vol,basefc,basetic); while(fgets(str, 128, file_script)!=NULL) { tic = basetic; fc = basefc; sscanf(str,"%s %lf %d",word, &fc, &tic); count+=roman(word,speech); (*speech).fc = fc; (*speech).length = tic; (*speech).vol=vol; printf("%d %d %d %lf %lf %d \n",(*speech).vnum,(*speech).vnum2,(*speech).cnum,(*speech).vol,(*speech).fc,(*speech).length); speech++; } fclose(file_script); printf("%d\n",count); return count; } /*----------*/ /* main関数 */ /*----------*/ int main(void) { unsigned long i,j,k; short *chL,*chR; double *chwaveL,*chwaveR; unsigned long sr = 44100; unsigned short bytes = 2; unsigned short ch = 1; unsigned long points,count; SpeakData *speech; VoiceData *voices; /*サウンド生成*/ speech = (SpeakData *)calloc(linecount("input.txt"),sizeof(SpeakData)); count = makespeech("input.txt",sr,speech); voices = (VoiceData *)calloc(count,sizeof(VoiceData)); printf("子音+母音数 %d\n",count); points = formants(linecount("input.txt"),voices,sr,speech); printf("データ長 %d\n",points); chwaveL = (double *)calloc(points,sizeof(double)); chL = (short *)calloc(points,sizeof(short)); Voice_production(count,chwaveL,sr,voices); for(i=0;i<points;i++){ chL[i] = (short)floor(chwaveL[i]); } wavwrite(points,chL,chR ,"output.wav",ch,bytes,sr); free(speech); free(chwaveL); free(chL); return 0; }"input.txt" の内容を以下のように書くと、チューリップの歌を入力することになる。
1行目は左から、音量(Max 32767)、デフォルト周波数(Hz)、デフォルト長さ(ステップ)
2行目以降は 左から、1音分のローマ字表記(逆順)、周波数(Hz:省略可能)、長さ(ステップ:省略可能)
30000 110 20000 as 146 i 164 at 184 40000 as 146 i 164 at 184 40000 uhc 220 ir 184 up 164 on 146 ah 164 an 184 ag 164 40000 ar 146 ar 164 ar 184 40000 ar 146 ar 164 ar 184 40000 a 220 ak 184 ihs 164 or 146 ik 164 i 184 or 146 40000 od 220 on 220 ah 184 an 220 im 246 et 246 om 220 40000 ik 184 er 184 i 164 ad 164 an 146 80000
2011年10月17日
音声合成(2)
前回のコードでは基本周波数を変動しつつしゃべらせると波形がおかしくなる。それを改善する。サインカーブを描くコードは以下のように書かれる。(math.hをincludeすることが前提)
さらに子音の発音処理も追加して、以下のコードをかいた。
このコードをコンパイルして実行すると、「あえいうえおあお」としゃべるWAVEファイルが出力される。変数speechを変更することでさまざまな値をしゃべらせることができる。詳しい解説は完成版ができたときにまとめて行う。
double SineWave(unsigned long i,unsigned long T,unsigned long sr,double vol) { /*Sine curve*/ return sin(2*M_PI*(double)(T * i)/ (double)sr) * vol; }それを次のように修正する。時刻 i を必要としなくなり、代わりに静的変数として energy を用いる。
double SineWave(unsigned long T,unsigned long sr,double vol) { /*Sine curve*/ static double energy; energy += ((double)T / (double)sr); energy = (energy - floor(energy)); return sin(2*M_PI*energy) * vol; }この2つのコードは、基本周波数が変動しないときには同じ値を返す。変更後の関数では基本周波数が変動しても波形が急激に変動することはない。よく考えれば当たり前の話であった。
さらに子音の発音処理も追加して、以下のコードをかいた。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <math.h> #define PCM 1 #define BUFSIZE (1000) #ifndef UCHAR_MAX #define UCHAR_MAX (255) #endif #ifndef UCHAR_MIN #define UCHAR_MIN (0) #endif #ifndef SHRT_MAX #define SHRT_MAX (32767) #endif #ifndef SHRT_MIN #define SHRT_MIN (-32768) #endif #ifndef _MAX_PATH #define _MAX_PATH (255) #endif #define FORMANT_LENGTH (5) #define IIR_LENGTH (3) /*---------------------------*/ /* 発音ブロック データ構造体 */ /*---------------------------*/ typedef struct Voice_datum { double ffb[FORMANT_LENGTH];/*フォルマント(子音)*/ double volb;/*子音の音量*/ unsigned long lengthb[3];/*子音の入り方:フェードイン・長さ・フェードアウト*/ unsigned long lengthv[3];/*母音の入り方:空白・フェードイン・フォルマント移動帯*/ double ffv[FORMANT_LENGTH];/*フォルマント(母音)*/ double volv;/*母音の音量*/ double fc;/*基本周波数*/ unsigned long length;/*全声部の長さ*/ int conti;/*連結するかどうか。*/ }VoiceData; typedef struct Speak_tuple { double fc; double vol; unsigned long length; int vnum; int cnum; int conti; }SpeakData; /*-----------------------------*/ /* Waveファイル エラーチェック */ /*-----------------------------*/ void openerror(char *filename) { fprintf(stderr, "Can't open file: %s\n", filename); exit(1); } void formaterror(FILE *fp) { fprintf(stderr, "File format error : %ld\n", ftell(fp)); exit(1); } short datacheck(short data, short max, short min) { // オーバーフローする場合 + か - を表示 if (data > max) { fprintf(stderr, "+"); data = max; } if (data < min) { fprintf(stderr, "-"); data = min; } return data; } /*--------------------------------------*/ /* 16 bit データ -32768~32767 (無音 0) */ /* 8 bit データ 0~255 (無音 128) */ /*--------------------------------------*/ void _write_1ch1byte(short *data, short *dummy,FILE *wav, unsigned long count) { for(;count>0;(count--,data++)) { datacheck(*data, UCHAR_MAX, UCHAR_MIN); fputc(*data, wav); } } void _write_1ch2byte(short *data, short *dummy,FILE *wav, unsigned long count) { for(;count>0;(count--,data++)) { datacheck(*data, SHRT_MAX, SHRT_MIN); fwrite(data, sizeof(short), 1, wav); } } void _write_2ch1byte(short *left, short *right, FILE *wav, unsigned long count) { for(;count>0;(count--,left++,right++)) { datacheck(*left, UCHAR_MAX, UCHAR_MIN); datacheck(*right, UCHAR_MAX, UCHAR_MIN); fputc(*left, wav); fputc(*right, wav); } } void _write_2ch2byte(short *left, short *right, FILE *wav, unsigned long count) { for(;count>0;(count--,left++,right++)) { datacheck(*left, SHRT_MAX, SHRT_MIN); datacheck(*right, SHRT_MAX, SHRT_MIN); fwrite(left, sizeof(short), 1, wav); fwrite(right, sizeof(short), 1, wav); } } /*----------------------*/ /* wav ファイル書き出し */ /*----------------------*/ void wavwrite(unsigned long length, short *chL, short *chR, char *filename, unsigned short ch, unsigned short bytes, unsigned long sr) { unsigned long file_size, var_long; unsigned short var_short; FILE *file_wave; void (*datawrite[2][2])(short *left, short *right, FILE *wav, unsigned long count)= { {_write_1ch1byte,_write_1ch2byte}, {_write_2ch1byte,_write_2ch2byte} }; file_size = length * ch * bytes; if ((file_wave = fopen(filename, "wb")) == NULL) { openerror(filename); } fwrite("RIFF", 1, 4, file_wave); /* ファイルサイズ */ var_long = file_size + 36; fwrite(&var_long, sizeof(long), 1, file_wave); /* WAVEヘッダ */ fwrite("WAVE", 1, 4, file_wave); /* chunkID (fmt チャンク) */ fwrite("fmt ", 1, 4, file_wave); /* chunkSize (fmt チャンクのバイト数 無圧縮 wav は 16) */ var_long = 16; fwrite(&var_long, sizeof(long), 1, file_wave); /* wFromatTag (無圧縮 PCM = 1) */ var_short = PCM; fwrite(&var_short, sizeof(short), 1, file_wave); /* dwChannels (モノラル = 1, ステレオ = 2) */ fwrite(&ch, 2, 1, file_wave); /* dwSamplesPerSec (サンプリングレート(Hz)) */ fwrite(&sr, sizeof(long), 1, file_wave); /* wdAvgBytesPerSec (Byte/秒) */ var_long = bytes * ch * sr; fwrite(&var_long, sizeof(long), 1, file_wave); /* wBlockAlign (Byte/サンプル*チャンネル) */ var_short = bytes * ch; fwrite(&var_short, sizeof(short), 1, file_wave); /* wBitsPerSample (bit/サンプル) */ var_short = bytes * 8; fwrite(&var_short, sizeof(short), 1, file_wave); /* chunkID (data チャンク) */ fwrite("data", 1, 4, file_wave); /* chunkSize (データ長 Byte) */ fwrite(&file_size, 4, 1, file_wave); /* ヘッダ (44 Byte) 書き込みここまで */ if (ftell(file_wave) != 44) { fprintf(stderr, "%s : wav header write error\n", filename); exit(1); } /* waveformData (データ) 書き込み */ datawrite[ch-1][bytes-1](chL,chR,file_wave,length); fclose(file_wave); } /*----------------------*/ /* 各種波形生成エンジン */ /*----------------------*/ double SineWave(unsigned long T,unsigned long sr,double vol) { /*Sine curve*/ static double energy; energy += ((double)T / (double)sr); energy = (energy - floor(energy)); return sin(2*M_PI*energy) * vol; } double Wave(unsigned long T,unsigned long sr,double vol) { /*Rosenburg波を出力する関数*/ static double energy; double tau = 0.75; /*声門解放期における声門拡張期の割合*/ double tau2= 0.8; /*一連の運動における声門解放期の割合*/ energy += ((double)T / (double)sr); energy = (energy - floor(energy)); if(energy <= tau*tau2){ return vol*( (3*energy*energy)/(tau*tau) - (2*energy*energy*energy)/(tau*tau*tau)-0.5); }else if(energy <tau2){ return vol*(1 - (energy-tau)/((1-tau)*(1-tau))-0.5); }else{ return vol*(-0.5); } } double GaussNoise(double vol) { /*擬似的なノイズ出力*/ return vol* (double)(rand() + rand()) / (double)(RAND_MAX); } /*-------------------------*/ /* IIRフィルター作成・適用 */ /*-------------------------*/ void IIR_resonator(double fc, double Q, double a[], double b[]) { fc = tan(M_PI * fc) / (2.0 * M_PI); a[0] = 1.0 + 2.0 * M_PI * fc / Q + 4.0 * M_PI * M_PI * fc * fc; a[1] = (8.0 * M_PI * M_PI * fc * fc - 2.0) / a[0]; a[2] = (1.0 - 2.0 * M_PI * fc / Q + 4.0 * M_PI * M_PI * fc * fc) / a[0]; b[0] = 2.0 * M_PI * fc / Q / a[0]; b[1] = 0.0; b[2] = -2.0 * M_PI * fc / Q / a[0]; a[0] = 1.0; } void IIR_apply(double a[],double b[],double tap[],double output[],double base) { int k; for(k=IIR_LENGTH-1;k>0;k--) { tap[k]= tap[k-1]; output[k]= output[k-1]; } tap[0]=base;output[0] = 0; for(k=0;k<IIR_LENGTH;k++) { output[0] += (b[k] * tap[k]); } for(k=1;k<IIR_LENGTH;k++) { output[0] -= a[k] * output[k]; } } /*--------------------*/ /* さまざまの補助関数 */ /*--------------------*/ double _slide(unsigned long a,unsigned long b,double x1,double x2) { double w; if((b==0) || (a > b)) { return x2; }else{ w = ((double)a)/((double)b); } w = (w<0.5)? w*w*2 : -2 * w * (w - 2) -1; //w = sin(M_PI * (w -0.5)) * 0.5 + 0.5; return (1-w) * x1 + w * x2; } /*------------------*/ /* 音声生成エンジン */ /*------------------*/ double *Voice_production(unsigned long chunks,double *wave_output,unsigned long sr,VoiceData *Voice1) { unsigned long h,i,j; double av[IIR_LENGTH],bv[IIR_LENGTH],ab[IIR_LENGTH],bb[IIR_LENGTH]; double voicev,tapv[FORMANT_LENGTH][IIR_LENGTH]={0},outputv[FORMANT_LENGTH][IIR_LENGTH]={0},basev; double voiceb,tapb[FORMANT_LENGTH][IIR_LENGTH]={0},outputb[FORMANT_LENGTH][IIR_LENGTH]={0},baseb; for(h=0;h<chunks;h++) { printf("開始(音声生成%d)\n",h); for(i=0;i<Voice1[h].length;i++){ wave_output[i] = 0; /*母音*/ if(i > Voice1[h].lengthv[0]){ if(i < (Voice1[h].lengthv[0] + Voice1[h].lengthv[1])){ basev = Wave(Voice1[h].fc,sr,_slide(i-Voice1[h].lengthv[0],(Voice1[h].lengthv[0] + Voice1[h].lengthv[1]),0,Voice1[h].volv)); }else if(/*(Voice1[h].conti !=0 ) &&*/(h+1<chunks) && (i > (Voice1[h].lengthv[0] + Voice1[h].lengthv[1]+ Voice1[h].lengthv[2]))){ basev = Wave(_slide(i-(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].length -(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].fc,Voice1[h+1].fc),sr,_slide(i-(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].length -(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].volv,Voice1[h+1].volv)); }else{ basev = Wave(Voice1[h].fc,sr,Voice1[h].volv); } voicev = 0; for(j=0;j<3;j++){ if((Voice1[h].conti != 0) && (h+1 < chunks) && (i> Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2])){ IIR_resonator(_slide(i-(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2] ),Voice1[h].length - (Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].ffv[j],Voice1[h+1].ffv[j])/44100,20,av,bv); }else{ IIR_resonator(Voice1[h].ffv[j]/44100,20,av,bv); } IIR_apply(av,bv,tapv[j],outputv[j],basev); voicev += outputv[j][0]; } if ((i > Voice1[h].lengthv[0])){ wave_output[i] += _slide(i,Voice1[h].lengthv[1],0,voicev)/3.0; }else{ wave_output[i] += voicev /3.0; } } /*子音*/ if(i < Voice1[h].lengthb[0] + Voice1[h].lengthb[1] + Voice1[h].lengthb[2]){ if(i< Voice1[h].lengthb[0]){ baseb = GaussNoise(_slide(i,Voice1[h].lengthb[0],0,Voice1[h].volb)); }else if(Voice1[h].lengthb[0] + Voice1[h].lengthb[1] < i){ baseb = GaussNoise(_slide(i - (Voice1[h].lengthb[0] + Voice1[h].lengthb[1]),Voice1[h].length - (Voice1[h].lengthb[0] + Voice1[h].lengthb[1]),Voice1[h].volb,0)); }else{ baseb = GaussNoise(Voice1[h].volb); } voiceb = 0; for(j=0;j<3;j++){ IIR_resonator(Voice1[h].ffb[j]/44100,7,ab,bb); IIR_apply(ab,bb,tapb[j],outputb[j],baseb); voiceb += outputb[j][0]; } wave_output[i] +=voiceb/3.0; } } wave_output+=i; } printf("実行終了(音声生成)\n"); return wave_output; } /*----------------------*/ /* 母音と子音の列から、 */ /* 発音ブロック列を作る */ /*----------------------*/ VoiceData *formants(unsigned long chunks,VoiceData *voices,unsigned long sr,SpeakData *Speak1){ unsigned long h,i; double ffv[][FORMANT_LENGTH]= { //a/0 {800,1300,2500,3500,4500}, //i/1 {250,1900,2600,3500,4500}, //u/2 {400,1400,2200,3500,4500}, //e/3 {450,1800,2400,3500,4500}, //o/4 {450,900,2600,3500,4500}, }; VoiceData consonant[]= { ///00 {{0},0.0,{0},{0,0,500},{0},1,1,0,1}, //k/01 {{800,1100,1400,3500,4500},0.8,{200,0,2200},{2200,200,0},{0},1,1,0,1}, //kh/02 {{800,3200,3700,3500,4500},0.8,{0,0,2200},{1600,800,0},{0},1,1,0,1}, //k,/03 {{400,2300,4600,3500,4500},0.8,{0,0,2400},{1800,600,0},{0},1,1,0,1}, //s/04 {{250,400,4500,3500,4500},0.5,{500,0,5500},{4000,2000,0},{0},1,1,0,1}, //sh/05 {{500,2100,4300,3500,4500},0.6,{500,0,5500},{4000,2000,0},{0},1,1,0,1}, //t/06 {{500,2100,4300,3500,4500},1.0,{0,200,400},{500,400,0},{0},1,1,0,1}, //ch/07 {{3900,4900,5400,3500,4500},0.6,{0,0,2000},{1500,500,0},{0},1,1,0,1}, //ts/08 {{1500,4000,4700,3500,4500},0.6,{0,100,1000},{900,500,0},{0},1,1,0,1}, //h/09 {{250,10,1400,3500,4500},0.6,{500,1000,3000},{3500,500,0},{0},1,1,0,1}, //h,/10 {{250,400,4500,3500,4500},0.5,{1000,800,3000},{3500,500,0},{0},1,1,0,1}, //y/11 {{0},0,{0},{0,300,0},{200,2100,3100,3500,4500},0.6,1,3000,1}, //m/12 {{0},0,{0},{300,900,300},{200,250,300, 3500, 4500},0.4,1,1500,1}, //w/13 {{0},0,{0},{0,1500,1500},{400,800,2200,3500,4500},1.0,1,3000,1}, //r/14 {{0},0,{0},{0,2000,0},{300,2600,4900,3500,4500},0.8,1,3000,1}, //rj/15 {{0},0,{0},{0,1200,1200},{300,2600,4900,3500,4500},0.8,1,2400,1}, //n/16 {{0},0,{0},{500,1000,0},{250,1400,2500,3500,4500},0.3,1,3000,1}, //b/17 {{200,250,400,3500,4500},0.3,{500,0,1000},{1000,1000,0},{500,1300,2500,3500,4500},0.8,1,2000,1}, //z/18 {{150,250,4900,3500,4500},0.6,{500,0,2000},{1000,1000,0},{500,1300,2500,3500,4500},0.8,1,3000,1}, //dg/19 {{150,3600,4500,3500,4500},0.6,{500,0,2000},{1000,1000,0},{400,2000,2800,3500,4500},0.6,1,2500,1}, }; for(h=0;h<chunks;h++) { *voices = consonant[Speak1[h].cnum]; if(Speak1[h].cnum > 10) { (*voices).volb *= Speak1[h].vol; (*voices).volv *= Speak1[h].vol; (*voices).fc *= Speak1[h].fc; (*voices).conti = 1; Speak1[h].length -= (*voices).length; voices++; *voices = consonant[0]; } for(i=0;i < FORMANT_LENGTH;i++) { (*voices).ffv[i] = ffv[Speak1[h].vnum][i]; } (*voices).volb *= Speak1[h].vol; (*voices).volv *= Speak1[h].vol; (*voices).fc *= Speak1[h].fc; (*voices).conti *= Speak1[h].conti; (*voices).length = Speak1[h].length; (*voices).lengthv[2]=(Speak1[h].length-(1500+(*voices).lengthv[0] + (*voices).lengthv[1]))*0.5; voices++; } return voices; } /*----------------------*/ /* アルファベット列から */ /* 母音と子音の列を作る */ /*----------------------*/ /* SpeakData *makespeech(SpeakData *speech,char str1[]) { } */ /*----------*/ /* main関数 */ /*----------*/ int main(void) { unsigned long i,j,k; short *chL,*chR; double *chwaveL,*chwaveR; unsigned long sr = 44100; unsigned short bytes = 2; unsigned short ch = 1; unsigned long points; VoiceData voices[10],*ptr; SpeakData speech[10]={ {65*2,18000,8000,0,0,1}, {75*2,26000,8000,3,0,1}, {80*2,22000,8000,1,0,1}, {50*2,20000,8000,2,0,1}, {65*2,18000,8000,3,0,1}, {75*2,26000,8000,4,0,1}, {80*2,22000,8000,0,0,1}, {50*2,20000,8000,4,0,1}, }; ptr = formants(8,voices,sr,speech); /*サウンド生成*/ points=64000; chwaveL = (double *)calloc(points,sizeof(double)); chL = (short *)calloc(points,sizeof(short)); printf("実行終了(準備)\n"); Voice_production(8,chwaveL,sr,voices); for(i=0;i<points;i++){ chL[i] = (short)floor(chwaveL[i]); } wavwrite(points,chL,chR ,"output.wav",ch,bytes,sr); free(chwaveL); free(chL); return 0; }
このコードをコンパイルして実行すると、「あえいうえおあお」としゃべるWAVEファイルが出力される。変数speechを変更することでさまざまな値をしゃべらせることができる。詳しい解説は完成版ができたときにまとめて行う。
登録:
投稿 (Atom)
-
音声データ入力を自動化する処理を追加した。 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <...
-
音声合成が結構簡単にできると聞いて、遊んでみることにした。 今回は母音のみ。基本原理はこんな感じ。フォルマント式音声合成と呼ばれる。 基本周波数を元に原音を作る。歌声でラ(220Hz)の音なら220Hzの周波数で鳴らす。 フォルマント(周波数のピーク)を元に原音を加工す...
-
前回のコードでは基本周波数を変動しつつしゃべらせると波形がおかしくなる。それを改善する。サインカーブを描くコードは以下のように書かれる。(math.hをincludeすることが前提) double SineWave(unsigned long i,unsigned long T...