/*
 *   scope.c - A little audio/opengl demo 
 *   By John E. Stone
 *
 *   $Id: scope.c,v 1.11 1998/01/08 11:34:17 johns Exp $  
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <glut.h>

#include <unistd.h>
#include <sys/audioio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>

#include "fft.h"

#define CHANNELS 2
#define SAMPLES (CHANNELS * 2048)
#define EPSILON 0.0
#define SAMPLERATE 44100

int xsize = 512;
int ysize = 512;

int audiodev;      /* audio device descriptor */
audio_info_t ainf;

short buffer[SAMPLES];
struct cnum lspectrum[SAMPLES];
struct cnum rspectrum[SAMPLES];

void drawtext(GLfloat x, GLfloat y, GLfloat xs, GLfloat ys, char *text) {
  char *p;
  glPushMatrix();
  glTranslatef(x, y, 0);
  glScalef(xs, ys, 1.0);
  for (p=text; *p; p++) {
    glutStrokeCharacter(GLUT_STROKE_ROMAN, *p);
  }
  glPopMatrix();
}

void reshape(GLsizei w, GLsizei h) {
  xsize = w;
  ysize = h;
}

void keys(unsigned char c, int x, int y) {
  switch (c) {
    case 27:
    case 'q':
    case 'Q':
      exit(0);
  }
}

void getaudio() {
  int i;

  /* read audio samples into buffer */
  read(audiodev, buffer, SAMPLES*sizeof(short)); 

  /* do FFT's etc */
  for (i=0; i<SAMPLES; i+=2) {
    lspectrum[i / 2].real = buffer[i] / 32768.0; 
    lspectrum[i / 2].imag = 0.0;
  }
  for (i=1; i<SAMPLES; i+=2) {
    rspectrum[i / 2].real = buffer[i] / 32768.0;
    rspectrum[i / 2].imag = 0.0;
  }

  fft(lspectrum, (SAMPLES / CHANNELS), 1);
  fft(rspectrum, (SAMPLES / CHANNELS), 1);
}


void animate() {
  int i, imax;
  double rl, im, max, tmp, rcol;

  getaudio();
  
  glClear(GL_COLOR_BUFFER_BIT); 

  /* Raw wave, left channel */
  glViewport(0, 0, xsize / 2, ysize / 2);
  glLoadIdentity();
  gluOrtho2D(0.0, SAMPLES, -33000.0, 33000);
  glColor3f(1.0, 1.0, 1.0);
  drawtext(20.0, 29000.0, 1.5, 20.0, "Raw Waveform - Left Channel");
  glColor3f(0.0, 1.0, 0.0);

  glBegin(GL_LINE_STRIP);
  for (i=0; i<SAMPLES; i+=2) {
    glVertex2f(i, buffer[i]);
  }
  glEnd();


  /* Raw wave, right channel */
  glViewport(0, ysize / 2, xsize / 2, ysize - (ysize / 2));
  glLoadIdentity();
  gluOrtho2D(0.0, SAMPLES, -33000.0, 33000);
  glColor3f(1.0, 1.0, 1.0);
  drawtext(20.0, 29000.0, 1.5, 20.0, "Raw Waveform - Right Channel");
  glColor3f(0.0, 1.0, 0.0);

  glBegin(GL_LINE_STRIP);
  for (i=1; i<SAMPLES; i+=2) {
    glVertex2f(i, buffer[i]);
  }
  glEnd();

  /* FFT's */
  max = EPSILON;
  imax = 0;
  for (i=0; i<(SAMPLES/4); i++) {
    rl = lspectrum[i].real;
    im = lspectrum[i].imag;
    tmp = sqrt(rl*rl + im*im);
    if (tmp > max) {
      max = tmp;
      imax = i;
    }
    lspectrum[i].real = tmp; 
  }

  glViewport(xsize / 2, 0, xsize - (xsize / 2), ysize / 2);
  glLoadIdentity();
  gluOrtho2D(0.0, SAMPLES/4, -0.0, max);
  glColor3f(1.0, 1.0, 1.0);
  drawtext(2.0, max - max/16.0, 0.35, max/3000.0, "Frequency Spectrum - Left Channel");

  glColor3f(1.0, 1.0, 0.0);
  glBegin(GL_LINES);
  for (i=0; i<(SAMPLES / 4); i++) {
    rcol = (double) i / (SAMPLES / 4.0);
    glColor3f((1.0 - rcol), 0.0, rcol);
    glVertex2f(i, 0.0);
    glVertex2f(i, lspectrum[i].real);
  }
  glEnd();




  max = EPSILON;
  imax = 0;
  for (i=0; i<(SAMPLES/4); i++) {
    rl = rspectrum[i].real;
    im = rspectrum[i].imag;
    tmp = sqrt(rl*rl + im*im);
    if (tmp > max) {
      max = tmp;
      imax = i;
    }

    rspectrum[i].real = tmp; 
  }

  glViewport(xsize / 2, ysize / 2, xsize - (xsize / 2), ysize - (ysize / 2));
  glLoadIdentity();
  gluOrtho2D(0.0, SAMPLES/4, -0.0, max);
  glColor3f(1.0, 1.0, 1.0);
  drawtext(2.0, max - max/16.0, 0.35, max/3000.0, "Frequency Spectrum - Right Channel");

  glColor3f(1.0, 1.0, 0.0);
  glBegin(GL_LINES);
  for (i=0; i<(SAMPLES / 4); i++) {
    rcol = (double) i / (SAMPLES / 4.0);
    glColor3f((1.0 - rcol), 0.0, rcol);
    glVertex2f(i, 0.0);
    glVertex2f(i, rspectrum[i].real);
  }
  glEnd();



  glutSwapBuffers(); 
}

void oglsetup() {
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
  glutInitWindowPosition(0, 0);
  glutInitWindowSize(xsize, ysize);
  glutCreateWindow("John's OpenGL Oscilloscope");
  glutReshapeFunc((void (*)(int, int)) reshape);
  glutKeyboardFunc(keys); 

  glShadeModel(GL_FLAT);
  glutIdleFunc(animate);
  glutDisplayFunc(animate); 
  glMatrixMode(GL_PROJECTION);

#ifdef SMOOTH_LINES
  glEnable(GL_LINE_SMOOTH);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
  glLineWidth(1.5);
#endif

  glutMainLoop();
}



audiosetup() {
  if ((audiodev = open("/dev/audio",  O_RDWR)) == -1) {
    printf("couldn't open audio device!\n");
    exit(1);
  }

  ioctl(audiodev, AUDIO_GETINFO, &ainf);

  ainf.record.sample_rate = SAMPLERATE;
  ainf.record.channels = 2;
  ainf.record.precision = 16;
  ainf.record.encoding = AUDIO_ENCODING_LINEAR;
  ainf.play.sample_rate = SAMPLERATE;
  ainf.play.channels = 2;
  ainf.play.precision = 16;
  ainf.play.encoding = AUDIO_ENCODING_LINEAR;
  ioctl(audiodev,AUDIO_SETINFO,&ainf);
}


main(int argc, char **argv) {
  audiosetup();
  oglsetup();
}

