//------------------------------------------------------------------- // persists.cpp // // This program causes the soundcard to play a continuous tone // till a signal is received indicating that was hit. // // We thank Thomas Cheong for diagnosing the 'early exit' bug. // // NOTE: We added variable-names that clarify the mathematical // basis for our computations of pulse-codes for a given tone. // // programmer: ALLAN CRUSE // written on: 22 SEP 2005 // bug repair: 23 SEP 2005 -- use 'atexit()' in case of exit() // revised on: 24 SEP 2005 -- supply clarifying variable-names //------------------------------------------------------------------- #include // for printf(), perror() #include // for open() #include // for exit() #include // for signal() #include // for sin() #include // for tcgetattr(), tcsetattr() #include // for read(), write(), close() #include // for ioctl() #include #define SAMPLE_RATE 8000 #define NUM_SAMPLES (SAMPLE_RATE / 20) #define TWO_PI (3.14159 * 2.0) char devname[] = "/dev/dsp"; unsigned char pulsecode[ NUM_SAMPLES ]; struct termios tty_orig; void my_atexit( void ) { // ignore any further keystrokes signal( SIGIO, SIG_IGN ); // restore canonical keyboard-input tcsetattr( 0, TCSAFLUSH, &tty_orig ); } int done = 0; void upon_input( int signum ) { // signal-handler for keyboard-input notifications int inch = 0; read( STDIN_FILENO, &inch, sizeof( inch ) ); if ( inch == '\e' ) done = 1; } int main( int argc, char **argv ) { // specify the parameters for our tone's pulse-code data double frequency = 220.0; // in cycles-per-second int amplitude = 64; // midrange volume-gain int silence = 128; // neutral volume-level // compute the sine-argument's rate-of-change in radians-per-sample double radians_per_circle = TWO_PI; double circles_per_second = frequency; double samples_per_second = SAMPLE_RATE; double samples_per_circle = samples_per_second / circles_per_second; double radians_per_sample = radians_per_circle / samples_per_circle; // now generate the pulse-code data for our fundamental tone double anglesize = radians_per_sample; for (int sample = 0; sample < NUM_SAMPLES; sample++) { double x = anglesize * sample; double y = amplitude * sin( x ); pulsecode[ sample ] = silence + (int)y; } // enable noncanonical keyboard input tcgetattr( STDIN_FILENO, &tty_orig ); struct termios tty_work = tty_orig; tty_work.c_lflag &= ~( ECHO | ICANON | ISIG ); tty_work.c_cc[ VTIME ] = 0; tty_work.c_cc[ VMIN ] = 1; tcsetattr( STDIN_FILENO, TCSAFLUSH, &tty_work ); // request asynchronous notification of keyboard input signal( SIGIO, upon_input ); fcntl( 0, F_SETOWN, getpid() ); fcntl( 0, F_SETFL, fcntl( 0, F_GETFL, NULL ) | O_ASYNC ); // insert our own function into this task's 'exit-list' atexit( my_atexit ); // open the playback device (and reset its default parameters) int dsp = open( devname, O_WRONLY ); if ( dsp < 0 ) { perror( devname ); exit(1); } if ( ioctl( dsp, SNDCTL_DSP_RESET, NULL ) < 0 ) { perror( "ioctl" ); exit(1); } // now playback the sound continuously (until 'done' is true) while ( !done ) { unsigned char *data = pulsecode; int morebytes = NUM_SAMPLES; while (( !done )&&( morebytes > 0 )) { int nbytes = write( dsp, data, morebytes ); if ( nbytes < 0 ) break; morebytes -= nbytes; data += nbytes; } } // reset the playback devive (to stop its sound immediately) if ( ioctl( dsp, SNDCTL_DSP_RESET, NULL ) < 0 ) perror( "soundcard reset failed" ); // close the audio playback device close( dsp ); }