/* 

   nl.c - sound generator for the Richard Prince's ``The Aurora
   on All Three Channels''.

   Ed Casas, June-October, 1999

   The sound output is the sum of three frequency- and
   amplitude- modulated sinusoidal oscillators.  The third
   oscillator is also randomly frequency modulated to produce a
   noise-like sound.  In the hardware version the frequencies and
   amplitudes of the three oscillators are determined by three
   light sensors.  Random number generators are substituted in
   the software-only simulation.

   This code compiles for the Microchip PIC 16C73A
   microcontroller if the symbol _MPC_ is defined.  Otherwise it
   outputs audio samples to standard output.

   To generate bandpass noise we use the least significant 8 bits
   of a 15-bit maximal-length pseudo-random bit sequence
   generator to periodically change the frequency of the
   oscillator.
   
*/

#ifdef _MPC_

#include 
#define assert(x)

#else

#include 
#include 
#include 

#endif

/*  Microchip 16C73A pinout:
	Pin	Name	Function
	2	AN0	analog input 0
	3	AN1	analog input 1
	4	AN2	analog input 2
	5	RA3/VREF	A/D reference
       13	CCP1	PWM output
       28	RB7	goes low to turn on "over-range" LED
       17       RC6     (debug) high indicate "computing"
       16       RC5     (debug) high indicates ISR is running
       14       RC3     (debug) low indicates sampling over-run
	1	MCLR*	reset (tied to Vdd (+5, high)
	9	OSC1	oscillator (one side of crystal)
       10	OSC2	"
       20	Vdd	+5V power supply
       19	Vss	ground
	8	Vss	ground
	all others unconnected 

   Configuration bits (address 0x2007):
	7	bit 7: n/a (0)
	6	brown-out reset: on (0)
	5,4	code protection: all off (11)
	3	power-up timer*: on (0)
	2	watchdog timer: off (0)
	1,0	oscillator: HS (10)
	
	config = 0x32
*/

#ifdef _MPC_
__CONFIG(FOSC1|CP0|CP1) ;
#endif

/* Sine table (256 signed 8-bit values for sin(x) from 0 to
   2pi). */

const signed char sinetable [256] = {
   0,    3,    6,    9,   12,   15,   18,   21, 
  24,   27,   30,   33,   36,   39,   42,   45, 
  48,   51,   54,   57,   59,   62,   65,   67, 
  70,   73,   75,   78,   80,   82,   85,   87, 
  89,   91,   94,   96,   98,  100,  102,  103, 
 105,  107,  108,  110,  112,  113,  114,  116, 
 117,  118,  119,  120,  121,  122,  123,  123, 
 124,  125,  125,  126,  126,  126,  126,  126, 
 126,  126,  126,  126,  126,  126,  125,  125, 
 124,  123,  123,  122,  121,  120,  119,  118, 
 117,  116,  114,  113,  112,  110,  108,  107, 
 105,  103,  102,  100,   98,   96,   94,   91, 
  89,   87,   85,   82,   80,   78,   75,   73, 
  70,   67,   65,   62,   59,   57,   54,   51, 
  48,   45,   42,   39,   36,   33,   30,   27, 
  24,   21,   18,   15,   12,    9,    6,    3, 
   0,   -3,   -6,   -9,  -12,  -15,  -18,  -21, 
 -24,  -27,  -30,  -33,  -36,  -39,  -42,  -45, 
 -48,  -51,  -54,  -57,  -59,  -62,  -65,  -67, 
 -70,  -73,  -75,  -78,  -80,  -82,  -85,  -87, 
 -89,  -91,  -94,  -96,  -98, -100, -102, -103, 
-105, -107, -108, -110, -112, -113, -114, -116, 
-117, -118, -119, -120, -121, -122, -123, -123, 
-124, -125, -125, -126, -126, -126, -126, -126, 
-126, -126, -126, -126, -126, -126, -125, -125, 
-124, -123, -123, -122, -121, -120, -119, -118, 
-117, -116, -114, -113, -112, -110, -108, -107, 
-105, -103, -102, -100,  -98,  -96,  -94,  -91, 
 -89,  -87,  -85,  -82,  -80,  -78,  -75,  -73, 
 -70,  -67,  -65,  -62,  -59,  -57,  -54,  -51, 
 -48,  -45,  -42,  -39,  -36,  -33,  -30,  -27, 
 -24,  -21,  -18,  -15,  -12,   -9,   -6,   -3
 } ;

#define COUNT_MAX 65536		/* number of counts per cycle */
#define FS 8000			/* sampling rate (8 kHz for audio files) */
#define K (COUNT_MAX/FS)	/* phase counter increment per Hz */

/* Symbols to define which oscillators are to be turned on and if
   the sample values are to be printed.  Mainly for debugging. */

#define USE1 1
#define USE2 1
#define USEAM 1
#define USE3 1
#define TRACE 0

#if 0				/* comment out frequency constants */
/* 

   Oscillator frequencies, bandwidths and functions to convert
   from the photosensor A/D converter input levels (unsigned
   chars) to frequency. The conversion must be tailored to the
   desired frequency range due to limited precision and dynamic
   range of 16-bit values.

   NOTE: The conversion factors and how this is done using shifts
   instead of multiplies were worked out and the optimized values
   shown below were used instead.
   
 */

/* First oscillator frequency range, minimum frequency and
   input-to-frequency-offset conversion function. */

#define DF1 200
#define F1 (520-DF1/2)
#define K1(x) ((x)*(DF1*K/256))

/* Second oscillator frequency range, minimum frequency and
   input-to-frequency-offset conversion function. */

#define DF2 20
#define F2 (0-DF2/2)
#define K2(x) (((x)*DF2)*K/256)

/* Third oscillator frequency range for signal and for random
   (noise) frequency offset, minimum frequency,
   input-to-frequency-offset conversion functions for A/D input
   and random inputs and duration (sample periods) of each random
   frequency hop. */

#define DF3 100
#define NF3 65
#define F3 (260-DF3/2-NF3/2)
#define K3(x) ((x)*(DF3*K/256))
#define KN3(x) ((x)*(NF3*K/256))

#endif /* end of frequency constants */

/* Values computed from the above and optimized for PIC: */

#define F1K 3441
#define K1(x) /* ((x)*6) */ (((x)<<2)+((x)<<1))
#define F2K -81
#define K2(x) /* ((x)*1) */ ((x))
#define F3K 1987
#define K3(x)  /* ((x)*3) */ (((x)<<1) + (x))
#define KN3(x) /* ((x)*2) */ ((x)<<1)

#define NCOUNT 8

/* Amplitude reduction of the noise relative to the sum of the
   first two oscillators.  In log base2 (e.g. a ratio of 1/2 is
   1, a ratio of 1/4 is 2, etc. */

#define LOG2A3 1

/* A 16-bit variable is used to model the shift register used to
   generate the PN sequence.  The variable is shifted right as
   each new bit is generated.  One feedback tap is at the LS bit
   position, the other at the 2nd LS bit. */

#define SRINIT 1		/* initial shift register value */

/* The number of samples to be generated (only used when writing
   sample values to a file for testing). */

#define NSAMPLE 80000

/* The frequencies and amplitudes of the sine waves and the
   center frequency of the noise are determined by three control
   signals read from an A/D converter connected to a photodiode.
   For simulation, these values are generated using NSOURCE
   random number generators. */

#define NSOURCE 3

/* Macros to extract the least significant and most significant
   bytes of a 16-bit integer in signed or unsigned format. NOTE:
   0/1 values depend on endian-ness. */

#define  LSB(x)  (*((unsigned char*)&x+0)) 
#define SLSB(x)  (*((  signed char*)&x+0)) 
#define  MSB(x)  (*((unsigned char*)&x+1)) 
#define SMSB(x)  (*((  signed char*)&x+1)) 

/* The output sample value. */

signed short x ;

/* Sample counter. */

#ifdef _MPC_
  unsigned char i ;
#else
  int i ;
#endif

#ifdef _MPC_

/* Analog I/O routines */

/* Configure A/D converter, select input channel 'ch' and
   return the 8-bit result of an A/D conversion.
	- set ADCON0 = 		
		10		clock = Tosc/32
		000 to 100	selects input channel 0-4
		0		to start a conversion
		0		n/a
		1 to enable A/D 
	- set GO bit (ADCON0 |= 0x4)
	- wait until DONE* goes low (1.6 us)
	- read result from ADRES
*/
   
#define ADSELECT(ch) (ADCON0 = 0x81 | ( (ch) << 3 ))
#define ADSTART() (ADGO=1)
#define ADWAIT { while ( ADGO ) ; }
#define ADVALUE() ADRES

/* Set PWM output to a duty cycle of x/256. */

void pwmout ( unsigned char x )
{
  CCP1X = ( x & 0x02 ) ? 1 : 0 ; /* set bits 5,4 to LS 2 bits of data */
  CCP1Y = ( x & 0x01 ) ? 1 : 0 ;
  CCPR1L = ( x >> 2 ) & 0x3f ;	/* set LS bits of period */
}	


/* Interrupt Service Routine.  Interrupts are generated by Timer
   0 at an 8 kHz rate.  The ISR outputs the sample value computed
   in main().  An ISR is used to avoid sampling time jitter. */

bit sent=0 ;			/* flag for isr/main synchronization */

void interrupt isr ( void )
{
  TMR0 = (256-156)+9 ;		/* reset count 5/(4*156) MHz = 8013 Hz */
  RC5 = 1 ;			/* turn on "ISR-on" indicator */
  T0IF = 0 ;			/* reset interrupt flag */
  pwmout ( x ) ;		/* output sample */
  sent = 1 ;			/* tell main() to proceed */
  RC5 = 0 ;			/* turn off "ISR-on" indicator */
}

#endif

void main(void)
{
  /* Oscillator phase accumulators and increments.  0 to 65535
     represents a phase from 0 to 2pi and overflow keeps the
     phase modulo 2pi.  */

  unsigned short p1, p2, p3 ;
  signed short dp1, dp2, dp3 ;

  /* Counter to "stretch" duration of over-range LED output */

  unsigned short ledcount=0 ;
  
  /* The 8-bit analog input values. */

  unsigned char a, a1, a2, a3 ;

  /* The 16-bit shift register used to generate the PN sequence
     that provides random phase modulation of the third
     oscillator.  For interpolation of the frequency between hops
     we also store the previous value, the increment per sample
     and the number of samples remaining in this hop. */

  unsigned short sr = SRINIT, lastsr ;
  signed char dsr ;
  unsigned char ncount=0 ;

  /* Due to the time required for A/D acquisition and conversion
     it is not possible to read all 3 analog inputs in each cycle.
     Instead, we cycle through the channels, reading one 
     each cycle. */

  unsigned char ch=0 ;

  assert( sizeof(short) == 2 ) ; /* For compatibility with embedded system.  */

#ifdef _MPC_
  /* Peripheral Initialization */

  TRISA = 0xff ;		/* all port A bits are (analog) inputs */
  TRISB = 0x00 ;		/* all port B bits are (digital) outputs */
  TRISC = 0x00 ;		/* all port C bits are (digital) outputs */

  PR2 = 0x3f ;			/* timer 2 period register to 6 bits (0x3f) */
  T2CON = 0x04 ;		/* timer 2 prescale 1:1 and on (T2CON<2>=1) */
  CCP1CON = 0x0c ;		/* set bits 3,2,1,0 to 1100 (PWM mode) */
  ADCON1 = 0x03 ;		/* RA0,1,2,5 are analog in, rest are digital */

  T0CS = 0 ;			/* set Timer 0 to timer mode */
  PSA = 0 ;			/* allocate prescaler to Timer 0 */
  PS2=0 ; PS1=0 ; PS0=1 ;	/* set Timer 0 prescaler to 1:4 */
  TMR0 = 0 ;			/* set Timer 0 for a long initial count */
  T0IF = 0 ;			/* reset interrupt flag */
  T0IE = 1 ;			/* enable Timer 0 interrupts */
  GIE = 1 ;			/* enable interrupts */

  RC3 = 1 ;			/* turn error LED off */

#endif

  /* Main loop */

#ifdef _MPC_
   for ( i=0 ; 1 ; i++ ) {
#else
   for ( i=0 ; i>= 1 ;
      if ( ( LSB(lastsr) ^ LSB(sr) ) & 1 ) {
	sr |= 0x4000 ;
      }
      #ifndef _MPC_
      if ( sr == SRINIT ) {
	fprintf ( stderr, "PN pattern repeats at %10d\n", i ) ;
      }
      #endif
      dsr = ( sr - lastsr ) / NCOUNT ;
      ncount=NCOUNT ;
    } else {
      lastsr += dsr ;
      ncount-- ;
    }

    /* The first oscillator's frequency varies linearly with
       input 2 from F1 up to F1+DF1 Hz. */

    dp1 = F1K + K1(a2) ;
    p1 += dp1 ;

    /* The second oscillator's frequency is equal to the first
       oscillator's plus a frequency offset from F2 to F2+DF2 Hz */

    dp2 = dp1 + F2K + K2(a1) ;
    p2 += dp2 ;

    /* The third oscillator's frequency varies linearly with
       input 3 from F3 upto F3+DF3 Hz and has a pseudo-random
       frequency between 0 and NF3 added to it. */

    dp3 = F3K + K3(a3) + KN3(LSB(lastsr)) ;
    p3 += dp3 ;
    #if TRACE
       fprintf ( stderr, "f1: %4d  f2: %4d  f3: %4d - ", 
		 dp1/K, dp2/K, dp3/K ) ;
    #endif

#ifdef _MPC_
    ADSTART() ;			/* Start A/D conversion >12 us from select. */
#endif

    /* Compute next sample value. */

    x = 0 ;

    /* First oscillator output */

#if USE1
  x += sinetable[ MSB(p1) ] ;
  #if TRACE
     fprintf ( stderr, "x1: %4d", x ) ;
  #endif
  assert ( x >= -128 && x <= 127 ) ;
#endif

  /* Second oscillator output */

#if USE2
  x = ( x + sinetable[ MSB(p2) ] ) >> 1 ;
  #if TRACE
     fprintf ( stderr, "  x2: %4d", x ) ;
  #endif
  assert ( x >= -128 && x <= 127 ) ;
#endif

  /* Amplitude modulation using analog input 3.  The (relative)
       level of the modulation varies between 0.5 and 1.5. */

#if USEAM
  x = ( x * ( ( a3 + 127 ) >> 1 ) ) ;
  x = SMSB(x) ;
    
  #if TRACE
     fprintf ( stderr, "  a: %4d  xa: %4d", a3, x ) ;
  #endif
  assert ( x >= -128 && x <= 127 ) ;
#endif

  /* Add the third oscilator output after scaling down. */

#if USE3
  { /* work-around for char sign-propagation bug in Hi-Tech compiler */
  signed short y ;  
  y = sinetable[ MSB(p3) ] ;
  y >>= 1 ;
  x = ( x + y ) >> 1 ;
  }
  
  #if TRACE
     fprintf ( stderr, "  x3: %4d", x ) ;
  #endif
  assert ( x >= -128 && x <= 127 ) ;
#endif

  #if TRACE
  fprintf ( stderr, "  x: %4d\n", x ) ;
  #endif

/* make x unsigned */

  x += 128 ;
  assert ( x >= 0 && x <= 255 ) ;
  
#ifdef _MPC_

  /* Make sure conversion done (it should have completed by now) */

  ADWAIT ;

  /* Get A/D value */

  a = ADVALUE() ;

  /* Turn RB7 low (turns on a warning LED) for 200ms if
     any input is saturating.  This is used to adjust the
     gain controls. */

  if ( a == 255 ) {		
    ledcount = 2*(FS/10) ;
  } else {
    if ( ledcount ) {
      ledcount-- ;
    }
  }

  RB7 = ledcount ? 0 : 1 ;
  
  switch (ch) {
  case 0: a1 = a ; ch=1 ; break ;
  case 1: a2 = a ; ch=2 ; break ;
  case 2: a3 = a ; ch=0 ; break ;
  }

  RC6 = 0 ;			/* turn off the "computing" bit */
  if ( sent && i != 0 ) {	/* indicate under-run except first time */
    RC3 = 0 ;
  }
  while ( ! sent ) ;		/* wait for ISR to run */
  sent = 0 ;
#else
  fputc ( SLSB(x), stdout ) ;
#endif

  }

}