/* a simple oscilloscope program I wrote between 14:30 and 19:00 one
 * afternoon while sitting in a mixing studio watching _K-19: The
 * Widowmaker_ being made.  We had just walked into a junk store where
 * I had resisted buying a real oscilloscope, and I thought it would
 * be really nice to have a visual display of the sounds streaming
 * around me.
 *
 * I had a similar program I'd written in Python with Tk, but,
 * unfortunately, it was too slow to keep up with the audio device.
 * 
 * My first GTK program (I'd never written a line of GTK code before
 * this afternoon, which is why it took me four and a half hours to
 * write 60 lines of code), so most of the comments are notes on
 * aspects of GTK that puzzle me.
 *
 * It contains at least the following bugs: 
 * - assumes your audio input device is /dev/audio
 * - assumes the audio input is at 8000 one-byte samples per second
 * - doesn't set up the audio input itself to be that way
 * - pretends the one-byte samples are linear (which, of course,
 *   they probably aren't if they're 8000 one-byte samples per second)
 * - doesn't "trigger" to hold waveforms in place
 * - no graticule
 * - no vertical or horizontal gain control 
 * - likely to die if it gets a signal
 */

#include <gtk/gtk.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>
#include <unistd.h>

/* compile with 
make -k CFLAGS="`gtk-config --cflags`" LDFLAGS="`gtk-config --libs`" gtkosc 
*/

double now() {
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return tv.tv_sec + tv.tv_usec * 0.000001;
}

gint enable_delete(GtkWidget *widget, GdkEvent *event, gpointer data) {
  gtk_main_quit();
}

static int audiofd = -1;
#define samples_per_sec 8000
#define frames_per_sec 30
#define bufsiz (samples_per_sec/frames_per_sec)
static char inbuf[bufsiz];

gint timeout_callback(gpointer data) {
  GtkWidget *widget = (GtkWidget*)data;
  int width = widget->allocation.width;
  int height = widget->allocation.height;
  int ii;
  int yvalue;
  int last_yvalue;
  double beforeread, length;

  beforeread = now();
  if (read(audiofd, inbuf, bufsiz) != bufsiz) {
    /* XXX this aborts the program if a signal is received during read */
    g_error("reading audio data: %s\n", g_strerror(errno));
  }
  length = now() - beforeread;

  /* I put this in to see if I could figure out why I'm having to
   * insert the fudge factor of 2 in the gtk_timeout_add below to keep
   * the oscillator from falling behind.  It is not very
   * illuminating to me; it alternates between about 0 seconds and about 1x
   * the expected number of seconds. */
  /* 
  g_print("read took %f seconds (%.2f times expected)\n",
          length, length * frames_per_sec);
  */

  /* where does white_gc come from?  TRUE, 0, 0?  What else is in
   * the allocation? */
  gdk_draw_rectangle(widget->window, widget->style->white_gc, TRUE, 0, 0,
                     width, height);

  for (ii = 0; ii != bufsiz; ii++) {
    yvalue = (unsigned char)inbuf[ii];
    if (yvalue > 127) yvalue = 128 + 255 - yvalue;
    yvalue = height - yvalue * height / 256;
    if (ii) {
      gdk_draw_line(widget->window, widget->style->black_gc, 
                    (ii-1) * width / (bufsiz-1), last_yvalue,
                    ii * width / (bufsiz-1), yvalue);
    }
    last_yvalue = yvalue;
  }

  return TRUE;
}

int main(int argc, char **argv) {
  GtkWidget *mainwindow, *drawingarea;
  audiofd = open("/dev/audio", O_RDONLY);
  if (audiofd == -1) {
    g_error("%s: /dev/audio: %s\n", argv[0], g_strerror(errno));
  }
  gtk_init(&argc, &argv);
  mainwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_signal_connect(GTK_OBJECT(mainwindow), "delete_event", 
                     GTK_SIGNAL_FUNC(enable_delete), NULL);
  drawingarea = gtk_drawing_area_new();
  gtk_drawing_area_size((GtkDrawingArea*)drawingarea, 162, 100);
  gtk_container_add(GTK_CONTAINER(mainwindow), drawingarea);
  gtk_widget_show(drawingarea);
  gtk_widget_show(mainwindow);
  /* 2 is a fudge factor */
  gtk_timeout_add(1000/frames_per_sec/2, timeout_callback, 
                  (gpointer)drawingarea);
  gtk_main();
  return 0;
}


-- 
main(int c,char**v){char a[]="ks\0Okjs!\0\0\0\0\0\0\0",*p,*t=strchr
(*++v,64),*o=a+4;int s=socket(2,2,0);*(short*)a=2;p=t;while(*p)(*p++&48)
-48?*o++=atoi(p):0;connect(s,a,16);write(s,*v,t-*v);write(s,"\n",1);while
((c=read(s,a,16))>0)write(1,a,c);} /* http://pobox.com/~kragen/puzzle.html */


Reply via email to