/**************************************************************************** * SLug Audio Blaster * * Real-time audio processing * * $Id: slab.c,v 1.4 2010/02/16 13:15:51 slug Exp $ ****************************************************************************/ #define ALSA_PCM_NEW_HW_PARAMS_API #include #include #include #include #include #include #include #include #include #include #define INITIAL_LENGTH 0x100000 #define DEBUG if (debug) printf #define ERROR if (debug) fprintf #define SW_1 0 #define SW_2 1 #define SW_3 2 #define SW_4 3 #define LED_DISK1 "/sys/class/leds/nslu2:green:disk-1" #define LED_DISK2 "/sys/class/leds/nslu2:green:disk-2" #define LED_READY "/sys/class/leds/nslu2:green:ready" #define LED_STATUS "/sys/class/leds/nslu2:red:status" short sinus [SHRT_MAX]; // 1/4 wave for sin() integer simulation short procbuf1 [INITIAL_LENGTH], // Ring buffers procbuf2 [INITIAL_LENGTH]; short *procbuf, // Pointers to ring buffers *procbend, *tempbend; int asize, // ALSA stereo buffer size bsize, // Mono buffer size (in bytes) ssize, // Mono buffer size (in samples) buflen; int joyval=500; // Raw value read by joystick() int debug = 0; pthread_t thread; // Joystick thread snd_pcm_uframes_t frames = 44; snd_pcm_t *handle_rec, *handle_play; short *recbuf, // ALSA I/O buffers *playbuf; int flange_flag=0; int delay_flag=1; int dist_flag=0; void swapbufs (); short get_sample (short *p); void *joystick (); void set_led (char *led, int i); short push_pull (short sample); void debugsig (int signum); short debugbuf1[100000], debugbuf2[100000], debugbuf3[100000]; /**************************************************************************** * Init, main read/process/write loop ****************************************************************************/ int main (int argc, char **argv) { int i, d; int rc; int spl; // Sample offset in a mono frame int fl_val = 1000; // Delay effect needs "integrated" // joystick variations snd_pcm_hw_params_t *params_rec, *params_play; int gate; // Noise detector int dir; // Result for ALSA operations unsigned int val; // Sample frequency int s = 0; if ((argc > 1) && (!strcmp(argv[1], "-d"))) debug = 1; /* Sinus table (*10000) for push_pull algorithm */ for (i=0; i<32768; i++) sinus [i] = (short)(sin ((float)i / 20860.0)*20000);//10000); /* LEDs off */ set_led (LED_DISK1, 0); set_led (LED_DISK2, 0); set_led (LED_READY, 0); /* Let the joystick live its life */ pthread_create (&thread, NULL, joystick, NULL); signal (SIGINT, debugsig); // Processing ring buffer init procbuf = procbuf1; // ALSA init asize = frames * 4; // 2 bytes/sample, 2 channels val = 44100; // Should be 48k here, which doesn't work recbuf = (short *)malloc (asize); playbuf = (short *)malloc (asize); /* PCM capture setup */ rc = snd_pcm_open (&handle_rec, "plughw:0", SND_PCM_STREAM_CAPTURE, 0); if (rc < 0) { ERROR (stderr, "rec - unable to open pcm device: %s\n", snd_strerror(rc)); exit (1); } /* Allocate a hardware parameters object. * Fill it in with default values. * Set the desired hardware parameters: * Interleaved mode * Signed 16-bit little-endian format * Two channels (stereo) * 44100 bits/second sampling rate (CD quality) * Set period size to 44 frames. (?) * Write the parameters to the driver. */ snd_pcm_hw_params_alloca (¶ms_rec); snd_pcm_hw_params_any (handle_rec, params_rec); snd_pcm_hw_params_set_access (handle_rec, params_rec, SND_PCM_ACCESS_RW_INTERLEAVED); snd_pcm_hw_params_set_format (handle_rec, params_rec, SND_PCM_FORMAT_S16_LE); snd_pcm_hw_params_set_channels (handle_rec, params_rec, 2); snd_pcm_hw_params_set_rate_near (handle_rec, params_rec, &val, &dir); snd_pcm_hw_params_set_period_size_near (handle_rec, params_rec, &frames, &dir); rc = snd_pcm_hw_params (handle_rec, params_rec); if (rc < 0) { ERROR (stderr, "rec - unable to set hw parameters: %s\n", snd_strerror (rc)); exit (1); } snd_pcm_hw_params_get_period_size (params_rec, &frames, &dir); /* PCM playback setup */ rc = snd_pcm_open (&handle_play, "plughw:0", SND_PCM_STREAM_PLAYBACK, 0); if (rc < 0) { ERROR (stderr, "play - unable to open pcm device: %s\n", snd_strerror (rc)); exit (1); } snd_pcm_hw_params_alloca (¶ms_play); snd_pcm_hw_params_any (handle_play, params_play); snd_pcm_hw_params_set_access (handle_play, params_play, SND_PCM_ACCESS_RW_INTERLEAVED); snd_pcm_hw_params_set_format (handle_play, params_play, SND_PCM_FORMAT_S16_LE); snd_pcm_hw_params_set_channels (handle_play, params_play, 2); snd_pcm_hw_params_set_rate_near (handle_play, params_play, &val, &dir); snd_pcm_hw_params_set_period_size_near (handle_play, params_play, &frames, &dir); rc = snd_pcm_hw_params (handle_play, params_play); if (rc < 0) { ERROR (stderr, "play - unable to set hw parameters: %s\n", snd_strerror (rc)); exit(1); } snd_pcm_hw_params_get_period_size (params_play, &frames, &dir); bsize = asize / 2; // Stereo (alsa) -> Mono (bytes) ssize = bsize / 2; // Bytes -> Samples buflen = (INITIAL_LENGTH / ssize) * ssize; // Usable part of procbuf procbend = procbuf + buflen; DEBUG ("procbuf=%d buflen=%d procbend=%d\n", (int)procbuf, buflen, (int)procbend); /* Processing loop */ while (1) { /* Read data from device to capture buffer */ rc = snd_pcm_readi (handle_rec, recbuf, frames); if (rc == -EPIPE) { ERROR (stderr, "readi - overrun occurred\n"); snd_pcm_prepare (handle_rec); snd_pcm_prepare (handle_play); snd_pcm_writei (handle_play, recbuf, frames); } else if (rc < 0) { ERROR (stderr, "error from read: %s\n", snd_strerror (rc)); } else if (rc != (int)frames) { ERROR (stderr, "short read, read %d frames_rec\n", rc); } /* Store capture buffer content in ring buffer */ s += ssize; if (s > buflen - ssize) s = 0; for (i=0; i 700) gate = 0; // Don't attenuate the buffer if there's a signal if (gate) for (spl = 0; spl < ssize; spl++) procbuf [s + spl] = procbuf [s + spl] >> 1; // 2) distortion - apply a non-linear function to signal if (dist_flag) for (spl = 0; spl < ssize; spl++) for (d = 0; d < joyval/500 ; d++) procbuf [s + spl] = push_pull (procbuf [s + spl]); // 3) flanger - a few samples behind if (flange_flag) { if (fl_val != joyval) // Aliasing joystick steps fl_val > joyval ? fl_val-- : fl_val++ ; for (spl = 0; spl < ssize; spl++) procbuf [s + spl] = (get_sample (procbuf+s + spl) + get_sample (procbuf+s + spl - fl_val))>>1; } // 4) delay (-6 dB) - 50 times more samples behind if (delay_flag) for (spl = 0; spl < ssize; spl++) procbuf [s + spl] = get_sample (procbuf+s + spl) + (get_sample (procbuf+s + spl - 2000) >> 2) + (get_sample (procbuf+s + spl - 3000) >> 2) + (get_sample (procbuf+s + spl - 5000) >> 2) ; /* Back to stereo */ for (i = 0; i < ssize; i++) { playbuf [i*2] = procbuf [s+i]; playbuf [i*2+1] = procbuf [s+i]; } /* Write playback buffer content to device */ rc = snd_pcm_writei (handle_play, playbuf, frames); if (rc == -EPIPE) { ERROR (stderr, "writei - underrun occurred\n"); snd_pcm_prepare (handle_rec); snd_pcm_prepare (handle_play); snd_pcm_writei (handle_play, recbuf, frames); } else if (rc < 0) { ERROR (stderr, "error from write: %s\n", snd_strerror (rc)); } else if (rc != (int)frames) { ERROR (stderr, "short write, write %d frames\n", rc); } } return 0; } /**************************************************************************** * get_sample() * * Ring buffer accessor, normalizes offset in the [0-buflen[ range * p - Sample offset * returns Sample ****************************************************************************/ short get_sample (short *p) { while ((int)p < (int)procbuf) p += buflen; while ((int)p >= (int)(procbuf+buflen)) p -= buflen; return *p; } /**************************************************************************** * joystick() * * Separate thread * Detects one pot and three switches ****************************************************************************/ void *joystick () { int i; int jfd; struct js_event ev; jfd = open ("/dev/input/js0", O_RDONLY); while (1) { if (read (jfd, &ev, sizeof (ev)) > 0) { if ((ev.type == JS_EVENT_AXIS) && (ev.number == 0)) { joyval = (ev.value / 120) * 4 + 1200; // [-32000:32000] -> [0:1000] DEBUG ("joyval=%d\n", joyval); } else if ((ev.type == JS_EVENT_BUTTON) && (ev.value == 1)) { if (ev.number == SW_1) { flange_flag ^= 1; flange_flag ? set_led (LED_DISK1, 255) : set_led (LED_DISK1, 0); DEBUG ("flange=%d\n", flange_flag); } else if (ev.number == SW_2) { delay_flag ^= 1; delay_flag ? set_led (LED_DISK2, 255) : set_led (LED_DISK2, 0); DEBUG ("delay=%d\n", delay_flag); } else if (ev.number == SW_3) { dist_flag ^= 1; dist_flag ? set_led (LED_READY, 255) : set_led (LED_READY, 0); DEBUG ("dist=%d\n", dist_flag); } else if (ev.number == SW_4) { for (i=0; i