2008年8月14日木曜日

端末で「初音ミクに「Ievan Polkka」を歌わせてみた」を再現してみた のソースコード


先週くらいに作ったこの動画で、ソースが欲しいという米があったので、ここに私の酷いコードを晒します。ソース自体は無駄な長さなので、この記事の最後に置いときました。
あと、端末とは言ってますが実際はLinux上の端末エミュレータってやつですね。スミマセン。

実行した環境はUbuntu 8.10訂正:8.04のgnome-terminal上です。フォントはデフォのまま。

一応、動画作成に至った流れの解説というかなんというか。

コマンドプロンプトの人のを見る→せめてちゃんとしたAAで……俺が作るか!→よしゃ出来た。あれ?なんかもう他にも色々うpされてる→もっとなにか拈りを入れないと→他の顔のバリエーションも作ろう→AAを分解したらもっと短くなるけど、全体コピペして微調整したらAA分解するの面倒くさくなった。描画する関数書き変えるのも面倒→ソースの半分をAAが占めることに→どうせなら、背景のロゴも→どうせなら動画全体を再現……→プログラムの全体構造書き変えるの面倒くせえ。メインループそのままでぶん回しながら時間計ってifで→あ、歌詞入れるタイミングとネギ振りのタイミングが同期するわけじゃないから、ネギ振りに合わせてsleepすると……→まぁ、いいや。

他の方の環境では、フォントが違ったりしてAAがうまく表示され無いかもしれません。
ちなみに、私の環境で端末のウィンドウを横73文字x縦27行になるようにリサイズしたらちょうど512x384ピクセルくらいになりました。

ソースは途中で面倒くさくなって定数を#defineしなくなったりしてます。もうちょっと丁寧に作るべきだったか……。最後の1行分だけ歌詞抜けちゃってるし。
AAは等幅フォント用に作ってますが、私の環境で全角文字が半角幅で表示されるという現象が起こったので、半角スペースを入れて微調整してあります。なので、普通に表示できる環境ではAAの一部がズレてます。

Cursesは、2バイト文字対応のncurseswというライブラリを使いました。ライブラリを導入してから、-lncursesw とオプションを付けてコンパイルしてください。

以下、「動きゃいいんだよ動きゃあ」精神のコード。動画に使った物そのままです。
テキストエディタで特殊文字をエスケープ文字に置換してからコピペしましたが、マズってるかもしれないのでお気を付けて。
#include <stdio.h>
#include <ncurses.h>
#include <locale.h>
#include <time.h>
#include <sys/time.h>

//screen size(row x col)
// 27x73

#define true 1
#define false 0

//for sleep command
#define DELAY 1000

#define UP 0
#define DOWN 1

#define MIKU_SIZE_ROW 17
#define MIKU_SIZE_COL 37

#define REAL_MIKU_SIZE_ROW 26
#define REAL_MIKU_SIZE_COL 47

//Negi ASCII Arts
char *negiUp[] = {
"くヽ l^i ",
" \\l l ",
" \\l ",
" \\ ",
" \\",
" \"
};

char *negiDown[] = {
" <\ ",
" \\ ",
"┌ ーーーーーー",
"└ ーーーーーー"
};


//Miku ASCII Arts
char *normal[] = {
" rへ ___ __ ",
" /: : : : : Y: :ヽ ",
" /: ./l: lヽ::ヽ: :ヽ ",
" //: /ノ l:ト、ヽ: :':: .', ",
" / l: / ○ 丶l ○ l: :ハ : : ',",
" _/: l: l@┌ ー┐ @l:/:l: : .',",
"/:::\ヽl \ │ │ /://l : :.i",
"{:::::::`T7└-ー┴‐< l: : l",
" `rーー┼ーー/l/l//_ヽ l : : :l",
" /: : : | /:/:l: Y:::l l : : :l",
" ,': : :/ /:/: : l:::l. l : : :.l",
" i : : く/レへ : l::::', ! : : : l",
" l : : :\:::::ヽイ::::〉 l : : : l",
" l : : : :ト、:::::>tjtjノ |: : : : l",
" ',: : : :/├ ー┤ |─ '\"| l : : : :l",
" ヽ : :/ l:::|l::::l ',: : ::,'",
" \:/ l:::l|:::l ヽ: : /"
};


char *cool[] = {
" rへ ___ __",
" /: : : : : Y: :ヽ",
" /: ./l: lヽ::ヽ: :ヽ",
" //: /\ l:ト /: :':: .',",
" / l: / ○ 丶l ○ l: :ハ : : ',",
" _/: l: l@ @l:/:l: : .',",
"/:::\ヽl \ /\ /://l : :.i",
"{:::::::`T7ヽ ̄ー-< l: : l",
" `rーー┼ーー/l/l//_ヽ l : : :l",
" /: : : | /:/:l: Y:::l l : : :l",
" ,': : :/ /:/: : l:::l. l : : :.l",
" i : : く/レへ : l::::', ! : : : l",
" l : : :\:::::ヽイ::::〉 l : : : l",
" l : : : :ト、:::::>tjtjノ |: : : : l",
" ',: : : :/├ ー┤ |─ '\"| l : : : :l",
" ヽ : :/ l:::|l::::l ',: : ::,'",
" \:/ l:::l|:::l ヽ: : /"
};

char *tired[] = {
" rへ ___ __ ",
" /: : : : : Y: :ヽ ",
" /: ./l: lヽ::ヽ: :ヽ ",
" //: /ノ l:ト、ヽ: :':: .', ",
" / l: / ○ 丶l ○ l: :ハ : : ',",
" _/: l: l@ @l:/:l: : .',",
"/:::\ヽl \ /\ /://l : :.i",
"{:::::::`T7ヽ ̄ー-< l: : l",
" `rーー┼ーー/l/l//_ヽ l : : :l",
" /: : : | /:/:l: Y:::l l : : :l",
" ,': : :/ /:/: : l:::l. l : : :.l",
" i : : く/レへ : l::::', ! : : : l",
" l : : :\:::::ヽイ::::〉 l : : : l",
" l : : : :ト、:::::>tjtjノ |: : : : l",
" ',: : : :/├ ー┤ |─ '\"| l : : : :l",
" ヽ : :/ l:::|l::::l ',: : ::,'",
" \:/ l:::l|:::l ヽ: : /"
};

char *sleep1[] = {
" rへ ___ __",
" /: : : : : Y: :ヽ",
" /: ./l: lヽ::ヽ: :ヽ",
" //: /ノ l:ト、ヽ: :':: .',",
" / l: / =丶l =l: :ハ : : ',",
" _/: l: l@┌ ー┐ @l:/:l: : .',",
"/:::\ヽl \ │ │ /://l : :.i",
"{:::::::`T7└--u┴‐< l: : l",
" `rーー┼ーー/l/l//_ヽ l : : :l",
" /: : : | /:/:l: Y:::l l : : :l",
" ,': : :/ /:/: : l:::l. l : : :.l",
" i : : く/レへ : l::::', ! : : : l",
" l : : :\:::::ヽイ::::〉 l : : : l",
" l : : : :ト、:::::>tjtjノ |: : : : l",
" ',: : : :/├ ー┤ |─ '\"| l : : : :l",
" ヽ : :/ l:::|l::::l ',: : ::,'",
" \:/ l:::l|:::l ヽ: : /"
};

char *sleep2[] = {
" rへ ___ __",
" /: : : : : Y: :ヽ",
" /: ./l: lヽ::ヽ: :ヽ",
" //: /ノ l:ト、ヽ: :':: .',",
" / l: / =丶l =l: :ハ : : ',",
" _/: l: l@┌ ー┐ @l:/:l: : .',",
"/:::\ヽl\ │ │ /://l : :.i",
"{:::::::`T7└--u┴‐< l: : l",
" `rーー┼ーー/l/l//_ヽ l : : :l",
" /: : : | /:/:l: Y:::l l : : :l",
",': : : / /:/: : l:::l. l : : :.l",
"i : : く/レへ : l::::',! : : : l",
"l : : : \:::::ヽイ::::〉l : : : l",
"l : : : : ト、:::::>tjtjノ |: : : : l",
"',: : : :/ ├ ー┤ |─ '\"| l : : : :l",
" ヽ : :/ l:::|l::::l ',: : ::,'",
" \:/ l:::l|:::l ヽ: : /"
};

char *realMiku[] = {
" ,.-.‐ . へ",
" {少'\" : : : 幻、\",
" /:/:: : : Y::i : :ヽ.",
" /:/N: iヽ: l_:_: :l: ハ",
" ,{:/:Lヾ{ 'Tヽ|::} ハ:: :i",
" /:ヽ:|心 、 ゛ .}'-:'/l: :!",
" /: : :>:ヽ ー;,イiTく l: : l",
" /: : :/ `≧ 令ーツー.、 l: : :l",
" /: : :/ r‐ f^小:::./ }l: : :l",
" /: : :/ j//./::::Y i / l: : :l",
" /: : :/ /:}/./:::::ll. 01! l: : :l",
" /: : :/ /://./:::::::! ̄: | l: : :l",
" /: : :/ /://./:::::::/|;;;;| l: : :l",
" /: : :/ /// ':::::::/ |;;;;| l: : :l",
" /: : :/ /':/ i:::::{ |;;;;| l: : :l",
" /: : ://:::/ i::::::\|;;;;;| l: : :l",
" /: : :/::::〈 i:::::::`!;;:;;;| l: : :l",
" / : ;〈:::::/ヽ/::::::::: |;;;;;;l l: : :l",
" /: :∧ ヽ:::/;;;;\::::::/ |;;;;;;| l: : :l",
" /: :/;;Y ゝ;;;;;:::;\::/;.;ィ|;;;;;;|. l: : :l",
",: : /、;lヽ√`ート::::,;r-Y\"..\"ィ|;;;;;;| |: : l",
"!: :′l>、| `ー- L_ ̄ i 斗 ´ |;;;;;;l.l: : l",
"l: :i ^|` ー - | ̄|_,.- ‐ 1ヽ;;;;;ノ l: : l",
" ',:l |;;;;;;:! l;;;;;;| ヒ!ヘハ! l: ,'",
" ゛:, |;;;;;:i l;;;;;;| } :/",
" ヾ:. |;;;;;:! l;;;:;;| /:/",
" |;;;;;! l;;;;;;| /'"
};

char *mikuLogo[] = {
" _ _",
" ∪ ∪",
" ┌─ ─┐┌ ─ ┐┌ ─ ─┐ ∩ __",
" └─ ./└_┐ |└┐┌┐┌┘│ └ ─ ┐/ |",
" // _||||┌┘└┘└┐└ ─ ─ ┘\/~||",
"//||//||||└ ─ ─┘┌ ─ ─ ┐ /|",
"|/|トゝ|||| ┌ ─ ,└ ─ ─ ┘ //",
" || |||| │ =)┌ ─ ─ ┐//",
" └┘ └┘└┘ └ ─ '└ ─ ┐//",
" HACHUNE MIKU |/",
};

//Lyrics
char *lyric0 = "あらっつぁっつぁ〜やりびだびりんらばりったんりんだんれんだんどぅ";
char *lyric1 = "まばりぱっぱーっぱりぱり ぱりりりびりびりすてんれんだんどぅ〜";
char *lyric2 = "やばりんらんてんだんでんあろ〜 わらば るぶるぶるぶるぶれいえぶ〜";
char *lyric3 = "ばりったんりんだんでんらんろ〜 たた たたたたとぅとぅでんやぶ〜";
char *lyric4 = "りんらんてんだんりんらんてんだん りんらんりんらんりんりんりりり";

//values
int nowNegiType = UP;
int sleepingType = UP;
int mikuRow;
int mikuCol;

void addLogo(int ha, int li, int vo){
int i;

if(ha){
for(i = 0; i < 10; i++){
mvaddstr(LINES - 14 + i, 0, mikuLogo[i]);
}
}

if(li){
for(i = 0; i < COLS; i++){
mvaddstr(LINES - 3, i, "-");
}
}

if(vo){
mvaddstr(LINES - 2, 2, "VOCALOID2");
}
}

void addAA(char *miku[]){
int i;
for(i = 0; i < MIKU_SIZE_ROW; i++){
mvaddstr(mikuRow + i,mikuCol,miku[i]);
}
}

void addLyric(char *lyric){
mvaddstr(LINES - 1, 5, lyric);
}

void addNegi(int type){
int i;
if(type == UP){
for(i = 0; i < 6; i++){
mvaddstr(mikuRow + i + 1, mikuCol - 11, negiUp[i]);
}
}else{ //DOWN
for(i = 0; i < 4; i++){
mvaddstr(mikuRow + i + 4, mikuCol - 14, negiDown[i]);
}
}
}

void mvAddNegi(int row, int col, int type){
int i;
if(type == UP){
for(i = 0; i < 6; i++){
mvaddstr(row + i, col, negiUp[i]);
}
}else{ //DOWN
for(i = 0; i < 4; i++){
mvaddstr(row + i, col, negiDown[i]);
}
}
}

void toggleSleepMiku(){
if(sleepingType == UP){
addAA(sleep1);
sleepingType = DOWN;
}else{
addAA(sleep2);
sleepingType = UP;
}
}

void toggleAddNegi(){
addNegi(nowNegiType);
if(nowNegiType == UP){
nowNegiType = DOWN;
}else{
nowNegiType = UP;
}
}

double getTimeOfDay_sec(){
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec + (double)tv.tv_usec*1e-6;
}

double getPassedTime(double startTime){
return (getTimeOfDay_sec() - startTime);
}

main(){

int i;
double passedTime;
double startTime;
int sleepTime;
int logoHa;
int logoLi;
int logoVo;

setlocale(LC_ALL,"");
initscr();
cbreak();
noecho();

mikuRow = LINES - MIKU_SIZE_ROW - 3;
mikuCol = COLS - MIKU_SIZE_COL - 1;

logoHa = false;
logoLi = false;
logoVo = true;

startTime = getTimeOfDay_sec();

sleepTime = 0;

//ここから動画の描画

//初期状態 VOCALOID2ロゴのみ
addLogo(false, false, true);
move(LINES - 1,COLS - 1);
refresh();
usleep(5500 * DELAY);

//ボーダーライン表示
for(i = 0; i < REAL_MIKU_SIZE_ROW; i++){
mvaddstr(LINES - REAL_MIKU_SIZE_ROW + i,
COLS - REAL_MIKU_SIZE_COL - 1,
realMiku[i]);
}
addLogo(true, false, true);
move(LINES - 1,COLS - 1);
refresh();
usleep(3500 * DELAY);

//初音ミク ロゴ登場
clear();
for(i = 0; i < REAL_MIKU_SIZE_ROW - 3; i++){
mvaddstr(LINES - REAL_MIKU_SIZE_ROW + i,
COLS - REAL_MIKU_SIZE_COL - 1,
realMiku[i]);
}
addLogo(true, true, true);
move(LINES - 1,COLS - 1);
refresh();
usleep(7000 * DELAY);

while(1){
clear();
passedTime = getPassedTime(startTime);
addLogo(true, true, true);

//ミク
if(passedTime <= 16.8){
//登場 休符
sleepTime = 100;
addAA(normal);
addNegi(UP);
}else if(passedTime <= 32.0){
//ネギ振り
sleepTime = 250;
addAA(normal);
toggleAddNegi();
}else if(passedTime <= 33.0){
//休符
sleepTime = 100;
addAA(normal);
addNegi(UP);
}else if(passedTime <= 48.5){
//ネギ振り
sleepTime = 250;
addAA(normal);
toggleAddNegi();
}else if(passedTime <= 49.0){
//休符
sleepTime = 100;
addAA(normal);
addNegi(UP);
}else if(passedTime <= 64.5){
//居眠り
sleepTime = 500;
toggleSleepMiku();
addNegi(UP);
}else if(passedTime <= 65.0){
//YO!!
sleepTime = 500;
mvaddstr(mikuRow - 1, mikuCol + 10, "!!!");
addAA(cool);
addNegi(UP);
addLyric("            YO!!!");
}else if(passedTime <= 80.0){
//激しい振り
sleepTime = 125;
addAA(cool);
toggleAddNegi();
}else if(passedTime <= 81.0){
//疲れ休符
sleepTime = 100;
addAA(tired);
addNegi(UP);
}else if(passedTime <= 96.0){
//疲れ振り
sleepTime = 250;
addAA(tired);
toggleAddNegi();
}else if(passedTime <= 97.0){
//りんらんりんらんりんらんりんらん

sleepTime = 125;
addAA(tired);
toggleAddNegi();

}else if(passedTime <= 113.0){
//ネギ振り
sleepTime = 250;
addAA(normal);
toggleAddNegi();
}else if(passedTime <= 114.0){
//休符
sleepTime = 100;
addAA(normal);
addNegi(UP);
}else if(passedTime <= 129.0){
//ネギ振り
sleepTime = 250;
addAA(normal);
toggleAddNegi();
}else if(passedTime <= 130.0){
//休符
sleepTime = 100;
addAA(normal);
addNegi(UP);
}else if(passedTime <= 145.0){
//居眠り
sleepTime = 500;
toggleSleepMiku();
addNegi(UP);
}else if(passedTime <= 145.1){
//ネギ落下 中段
sleepTime = 50;
addAA(sleep1);
mvAddNegi(mikuRow + 8, mikuCol - 13, DOWN);
}else if(passedTime <= 146.0){
//ネギ落下 中段
sleepTime = 50;
addAA(sleep1);
mvAddNegi(mikuRow + 13, mikuCol - 14, DOWN);
}else{
usleep(5000 * DELAY);
break;
}

//歌詞
if(passedTime <= 17.0){
//前奏
}else if(passedTime <= 20.0){
addLyric(lyric0);
}else if(passedTime <= 24.0){
addLyric(lyric1);
}else if(passedTime <= 29.0){
addLyric(lyric2);
}else if(passedTime <= 32.0){
addLyric(lyric3);
}else if(passedTime <= 37.0){
addLyric(lyric0);
}else if(passedTime <= 40.0){
addLyric(lyric1);
}else if(passedTime <= 45.0){
addLyric(lyric2);
}else if(passedTime <= 48.0){
addLyric(lyric3);
}else if(passedTime <= 81.0){
//間奏
}else if(passedTime <= 85.0){
addLyric(lyric0);
}else if(passedTime <= 89.0){
addLyric(lyric1);
}else if(passedTime <= 93.0){
addLyric(lyric2);
}else if(passedTime <= 97.0){
addLyric(lyric4);
}else if(passedTime <= 101.0){
addLyric(lyric0);
}else if(passedTime <= 105.0){
addLyric(lyric1);
}else if(passedTime <= 109.0){
addLyric(lyric2);
}else if(passedTime <= 113.0){
addLyric(lyric3);
}else if(passedTime <= 117.0){
addLyric(lyric0);
}else if(passedTime <= 121.0){
addLyric(lyric1);
}else if(passedTime <= 125.0){
addLyric(lyric2);
}else{
//do nothing
}

move(LINES - 1,COLS - 1);
refresh();
usleep(sleepTime * DELAY);
}

//終了
endwin();

}

0 件のコメント: