/* how to build : gcc -lm r-sound1.c -o r-sound1 */

/* generate sound of the diffrential equation while solving the equation */
/* to modify the parameter value is possible by keyboard operation */

/* 微分方程式の解x,yを求めながら、
そのデータをステレオで音にして流すプログラム */
/* パラメータ値の変化がキーボード操作で可能 */

/* KITA Toshihiro  http://t-kita.net */
/* license : public domain */

#include <fcntl.h>
#include <limits.h>
#include <linux/soundcard.h>
#include <math.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>

#define BUFSIZE 35280

double Time_step= 0.1;
double a, b, c; /* PARAMETERS */
int ch;

void RKutta();
void DefDxdt();

static int setup_dsp( int fd );

////                                                      
#include <termios.h>                                  
//#include <term.h>                                     
// #include <curses.h>                                   
static struct termios initial_settings, new_settings; 
static int peek_character = -1;                       
void init_keyboard();                                 
void close_keyboard();                                
int kbhit();                                          
int readch();                                         
                                                      

int main( int argc, char *argv[] )

{
  double total = 0.2;

  short  BUF[ BUFSIZE ];
  int    fd, i, j, ii;

     int ct;
     double t,x,y,z,xx,yy,xxx;
     double m,n;
     double last_x=0.0, last_y=0.0, sect_x=0.0;
     char buf[80];
     x= 1.495; y= 3.4824; z=5.1731; t= 0.0;/* 初期値を与える */

     /* パラメータ設定 */
#if 0
     a=0.14;
     b=2.0;
     c=4.0;
#endif
#if 0
     a=0.173; /* 0.15: 4-period, 0.16: chaos, 0.168: full-band chaos */
     b=0.2;
     c=5.7;
#endif
#if 1
     a=0.3;
     b=2.0;
     c=4.0;
#endif

    if ( ( fd = open( "/dev/dsp", O_WRONLY ) ) == -1 )
    {
	perror( "open()" );
	exit( 1 );
    }

    /* /dev/dsp の設定*/
    if ( setup_dsp( fd ) != 0 )
    {
	fprintf( stderr, "Setup /dev/dsp failed.\n" );
	close( fd );
	exit( 1 );
    }

    printf("Use key p or n to change parameter value. Key q to quit.\n");
    init_keyboard();                                  
    for(j=0; j<=3000; j++){  // ------------------------------------

    if (kbhit()){
      ch = readch();
      /* printf("%d ",ch); fflush(stdout); */
      if (ch==113){ close_keyboard(); exit(1); } // q
      if (ch==112){ a+= 0.01; printf("a = %g\n",a); } // p 
      if (ch==110){ a-= 0.01; printf("a = %g\n",a); } // n
      if (ch==111){ close_keyboard(); printf("a = "); scanf("%lf",&a); init_keyboard(); } // o
    }

    for ( i = 0; i < BUFSIZE; i=i+2 ){
      t = ( total / BUFSIZE ) * i;
	
      /* yy = y;  xx = x; */
      RKutta(&t,&x,&y,&z);
      /*
      if( yy<=0 && y>=0 ){
	ii++;
	m = (y-yy)/(x-xx);
	n = m * (-xx) + yy;
	xxx = -(n/m);
	printf("%g %g %d\n",xxx,a,ii);
      }
      */

      BUF[i] =   SHRT_MAX * x * 0.1;
      BUF[i+1] = SHRT_MAX * y * 0.1;
      if (BUF[i]>=(SHRT_MAX-1) ||
	  BUF[i]<=-(SHRT_MAX-1)){ fprintf(stderr,"distortion!!\n"); }
    }
    
    if ( write( fd, BUF, sizeof(short)*BUFSIZE ) == -1 )
    {
	perror( "write()" );
	close( fd );
	exit( 1 );
    }

    } // --------------- for (j= ..) ----------------------
    close_keyboard();                                 

    close( fd );
    return 0;
}

void RKutta(double *t, double *x, double *y, double *z)
{
     double  k11,k12,k13,k21,k22,k23,k31,k32,k33,k41,k42,k43;
     double  xt,yt,zt;

     DefDxdt(*t,*x,*y,*z,&k11,&k12,&k13);
     *t=*t+Time_step/2.0;
      xt=*x+(Time_step/2.0)*k11;  yt=*y+(Time_step/2.0)*k12;
      zt=*z+(Time_step/2.0)*k13;
     DefDxdt(*t,xt,yt,zt,&k21,&k22,&k23);
      xt=*x+(Time_step/2.0)*k21;  yt=*y+(Time_step/2.0)*k22;
      zt=*z+(Time_step/2.0)*k23;
     DefDxdt(*t,xt,yt,zt,&k31,&k32,&k33);
      xt=*x+Time_step*k31;  yt=*y+Time_step*k32;
      zt=*z+Time_step*k33;
      *t=*t+Time_step/2.0;
     DefDxdt(*t,xt,yt,zt,&k41,&k42,&k43);
      *x+=Time_step*((k11+k41)/6.0+(k21+k31)/3.0);
      *y+=Time_step*((k12+k42)/6.0+(k22+k32)/3.0);
      *z+=Time_step*((k13+k43)/6.0+(k23+k33)/3.0);
}

void DefDxdt(time, x, y, z, dxdt, dydt, dzdt)
double time, x, y, z, *dxdt, *dydt, *dzdt;
{
     *dxdt= -y -z;
     *dydt= x + a*y;
     *dzdt= b + z*(x - c);
}



/* From http://homepage3.nifty.com/rio_i/lab/oss/index.htm */
/*
 * /dev/dsp を以下の様に設定する。
 *
 * 量子化ビット数     : 16   bits
 * サンプリング周波数 : 44.1 KHz
 * チャンネル数       : 2
 * PCM データは符号付き、リトルエンディアン
 *
 */
static int setup_dsp( int fd )
{
    int fmt     = AFMT_S16_LE;
    int freq    = 44100;
    int channel = 2;

    /* サウンドフォーマットの設定 */
    if ( ioctl( fd, SOUND_PCM_SETFMT, &fmt ) == -1 )
    {
  	perror( "ioctl( SOUND_PCM_SETFMT )" );
	return -1;
    }

    /* チャンネル数の設定 */
    if ( ioctl( fd, SOUND_PCM_WRITE_CHANNELS, &channel ) == -1 )
    {
	perror( "iotcl( SOUND_PCM_WRITE_CHANNELS )" );
	return -1;
    }

    /* サンプリング周波数の設定 */
    if ( ioctl( fd, SOUND_PCM_WRITE_RATE, &freq ) == -1 )
    {
	perror( "iotcl( SOUND_PCM_WRITE_RATE )" );
	return -1;
    }

    return 0;
}



/* From http://www.h3.dion.ne.jp/~unisoft/progbase.html */
                                                      
void init_keyboard()                                  
{                                                     
    tcgetattr(0, &initial_settings);                  
    new_settings = initial_settings;                  
    new_settings.c_lflag &= ~ICANON;                  
    new_settings.c_lflag &= ~ECHO;                    
    new_settings.c_lflag &= ~ISIG;                    
    new_settings.c_cc[VMIN] = 0;                      
    new_settings.c_cc[VTIME] = 0;                     
    tcsetattr(0, TCSANOW, &initial_settings);         
}                                                     
                                                      
void close_keyboard()                                 
{                                                     
    tcsetattr(0, TCSANOW, &initial_settings);         
}                                                     
                                                      
int kbhit()                                           
{                                                     
    char ch;                                          
    int nread;                                        
                                                      
    if(peek_character != -1)                          
        return 1;                                     
    new_settings.c_cc[VMIN]=0;                        
    tcsetattr(0, TCSANOW, &new_settings);             
    nread = read(0, &ch, 1);                          
    new_settings.c_cc[VMIN]=1;                        
    tcsetattr(0, TCSANOW, &new_settings);             
                                                      
    if(nread == 1) {                                  
        peek_character = ch;                          
        return 1;                                     
    }                                                 
    return 0;                                         
}                                                     
                                                      
int readch()                                          
{                                                     
    char ch;                                          
                                                      
    if(peek_character != -1) {                        
        ch = peek_character;                          
        peek_character = -1;                          
        return ch;                                    
    }                                                 
    read(0, &ch, 1);                                  
    return ch;                                        
}
