Thus began work on the sequencer_1 code.
The first iteration of the sequencer software that I put together for my Arduino was pretty basic, re-using a lot of existing example code (such as the frequency-output function freqout). It was very simple, with all of the interesting stuff happening in the loop function:
- check for the "record" button
- check the status of the scale and note knobs
- put any recorded note/scale combinations into the sequence at the current index
- read the tempo knob
- play the current note/scale for however long the tempo requires it to be played
- set the speaker pin HIGH for a little while
- then set the speaker pin LOW for a little while
- advance the index of the sequence
The second iteration of the sequencer software started to make use of the internal interrupt handlers. It attached Timer1 at a very high frequency and used it to flip the speaker pin between HIGH and LOW based on a dividing counter. The Timer2 interrupt was used at a lower frequency to service the tempo. The loop method was still used, but now it only performed the recording of the sequence. The input and output were nearly identical to the sequencer_1, except now there was no gap between the steps of the sequence-- a note could be held and would sound like a single solid tone.
Both of the previous sequencers had used a minimal number of parts, but had only been capable of outputting a square wave. I wanted to get into some analog action, so it was time to try and output a wave.
First I needed to figure out how to programmatically determine what voltage I should be attempting to produce at any given moment. I would commit an interrupt to updating the output voltage at a sampling frequency of around 10kHz. A second interrupt would act as the tempo handler. Whenever the tempo would update, it would set a global multiplier and divider pair. The divider becomes a counter which decrements each time the sampling interrupt fires. When it reaches zero, an output level is selected from a pre-defined table representing the sine wave. The index into this table is incremented by the multiplier each time the divider counter reaches zero. This has the net result of simulating a sampled wave at the appropriate frequency.
To actually convert the given calculated value into a voltage, a serial-to-parallel chip is fed the byte. When it latches, the eight-bit output is fed into a digital-to-analog converter. In my initial build, a simple R/2R ladder DAC worked out fine, but required a voltage divider on the output (to drop the signal to a normal 1.5V line-level range) as well as a passive low-pass filter to quiet down some component noise.
The code so far is over at my Google Code page