cmdcatch

cmdcatchがおもしろい。すごくおもしろい。
おもしろいのだが、mplayerはないだろ、maplyerは。とか言いそうになった。

あと、なんかうちの環境だとコイーン、コイーン、コイーン...と連コインになってくれない。
個人的にはココココココココ...ココココイーンって感じでスクラッチしてほしい。


とか言うくらいなら自分で書けばいいのか。


なんとなく思いつきでファイル名はrencon.cc
どうでもいいがレンコンはうちの地元土浦の名産品だ。
久々のccで結構戸惑った。

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h>

#define BUFFER_SIZE (8000*10)
#define nCOINS_ONEUP 100
#define SLEEP_TIME (1000*100)

int g_coin_pos = BUFFER_SIZE;
int g_oneUp_pos = BUFFER_SIZE;
int g_dead_pos = BUFFER_SIZE;
int g_cnt = 0;

// シグナルハンドラの設定。定型文
void install_signal( int signum, int flags, void (*handler)(int))
{
	struct sigaction act;
	act.sa_handler = handler;
	sigemptyset( &act.sa_mask );
	act.sa_flags = flags;
	sigaction( signum, &act, NULL );
}

//音を頭から再生するようにrewindする(スクラッチをかけるための。
void rewinder( int signum )
{
	switch( signum ) {
		case SIGUSR1:
			// get coin♪
			g_cnt++;
			if( g_cnt >= nCOINS_ONEUP ) {
				g_cnt -= nCOINS_ONEUP;
				g_oneUp_pos = 0;
			}
			g_coin_pos = 0;
			break;
		case SIGUSR2:
			// you die
			g_dead_pos = 0;
			break;
	}
} 

int main( int argc, char** argv )
{
	// コインの音
	int fd_coin = open( "coin.raw", O_RDONLY );
	char* coin_buf = (char*)malloc( BUFFER_SIZE );
	ssize_t coin_size = read( fd_coin, coin_buf, BUFFER_SIZE );
	close( fd_coin );

	// 1アップの音
	int fd_oneUp = open( "oneUp.raw", O_RDONLY );
	char* oneUp_buf = (char*)malloc( BUFFER_SIZE );
	ssize_t oneUp_size = read( fd_oneUp, oneUp_buf, BUFFER_SIZE );
	close( fd_oneUp );

	// 死亡
	int fd_dead = open( "dead.raw", O_RDONLY );
	char* dead_buf = (char*)malloc( BUFFER_SIZE );
	ssize_t dead_size = read( fd_dead, dead_buf, BUFFER_SIZE );
	close( fd_dead );

	// /dev/dspを開く。
	int fd_dsp = open( "/dev/dsp", O_WRONLY );

	// シグナルハンドラをセット
	install_signal( SIGUSR1, 0, rewinder);
	install_signal( SIGUSR2, 0, rewinder);

	while( 1 ) {
		if( g_dead_pos < dead_size ) {
			// 死亡音
			write( fd_dsp, &dead_buf[g_dead_pos], 1 );
			g_dead_pos++;
			g_oneUp_pos++;
			g_coin_pos++;
		} else if( g_oneUp_pos < oneUp_size ) {
			// 1アップ
			write( fd_dsp, &oneUp_buf[g_oneUp_pos], 1 );
			g_oneUp_pos++;
			g_coin_pos++;
		} else if( g_coin_pos < coin_size ) {
			// コイン
			write( fd_dsp, &coin_buf[g_coin_pos], 1 );
			g_coin_pos++;
		}
		usleep( SLEEP_TIME );
	}
	close( fd_dsp );

	return 0;
}

"coin.raw", "ounUp.raw", "dead.raw"は適宜適当に用意してください。
形式は、"/dev/dsp"を直叩きしてるので、"/dev/dsp"に合わせてください。
うちの環境だとMono, 8000Hz, sample size 1byte, unsigned
ということで

sox -t wav -v 0.5 src.wav -t raw -c 1 -1 -u -r 8000 dest.raw

ってな感じで変換できます。
-tはファイル形式、-vはヴォリューム、-cはチャンネル数、-1はサンプルサイズ、-uはunsigned、-rはrateです。
あ、バッファのサイズ決め打ちなんで、大きいファイルを使いたいときは、BUFFER_SIZEを十分大きくしてくださいな。


使い方ですが、とりあえずどっかでrenconを起動しておく。
で、killallなんかを使ってrenconへユーザシグナルを送ります。
USR1で、コインゲット。100コインで1アップします。
USR2は死亡、問答無用で死亡音が流れます。

cmdcatchのmplayerの部分を"killall -USR2 rencon"(ng) "killall -USR1 rencon"(ok)にすればおっけー。


まぁ、大したプログラムでもないので説明はなしです。
死亡音が優先されるので死亡音がながれている間はコインも1アップも鳴らない、のは仕様です。



で、これ作ってたらカタワラにいたid:viver本人がRuby版作り始めちゃいました。
そのうち向こうにruby版がcoinsdとかいう名前で上がると思うのでそちらをお使いください。
たしか向こうはrawなんてひどいファイル使わなくて済むハズですので。