/* 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 */