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