CS 353 Jan 2025     Syllabus       Labs/Projects

Project 03 Setup Timers Parallel Piano Extensions Submission

Project 03

Goals

  • Use registers to setup pin outs and a timer.
  • Use a timer to create an oscillator.
  • Implement code that runs quickly using bitwise shifting instead of division.
  • Choose variable types based on memory efficiency.
  • Implement Additive Synethesis signal generation techniques.

Setup

Setup

Start with your code from Lab 3 that determines which button is pressed using an analog input and plays a note using the tone library.

Save it as a new Arduino project titled colbyUsername_P03_Part1 (with your colby username, mine would be hewolfe_P03_Part1).

Notes on Variable Types

  • Chars are 1 byte, so we can use them to represent values while taking up less space than an int.
  • Unsigned ints are 2 bytes, representing values from 0 to 216.

Waveforms.h

  • Create a file Waveforms.h and put it in the same folder as your arduino project.
  • Add a header guard to the top of Waveforms.h
  • Hardcode a constant sine wave table with 256 chars, ranging from 127 to -127. Use __attribute__ to allocate the array on a 256-byte boundary.
  • Create a struct OscillatorState
    • It should have a field volume, which is the volume multiplied for the oscillator. The max value for the volume variable should be 127, so we can use a char to store this value.
    • With a field, keep track of the phase step, which is the frequency of the oscillator. This value will be between 0-216.
    • With a field, keep track of the accumulated phase, which steps through this oscillator. This value will be an unsigned int (2 bytes).
    • Add a OscillatorState constructor header, with a parameter float frequency.
    • Add a function set_phaseStep header, with a parameter float frequency. This method coverts from frequency to phase step.
    • Add a function update header which returns an int. This gets the current value of the oscillator multiplied by the oscillator’s volume.

Timers

Timers

You will be creating a phase correct PWM at the nominal frequency of 31372.549.
Updating the compare match at each interrupt will be used to create a sinusodial signal.

Videos and Datasheet Reading

Introduction to Registers
Introduction to External Interrupts
Introduction to Timer Interrupts
Review 16.9-16.11.8 of the ATmega328P Datasheet

Create your C++ file Waveforms.cpp

  • Create a file Waveforms.cpp and put it in the same folder as your arduino project.
  • Include Waveforms.h
  • Create a global volatile variable curWave which is a pointer to sine256.
  • Implement OscillatorState, set_phaseStep and update
    • OscillatorState initializes the the phase step using set_phaseStep, volume and phase accumulation.
    • set_phaseStep sets the phase step to be the frequency multiplied by 216 (as an unsigned int), divided by the timer’s frequency. (Reference: Arduino Datasheet 16.9.4)
    • update adds the phase step to the phase accumulator and returns the current value of the oscillator multiplied by the oscillator’s volume. To find the current value of the oscillator, we can use the “high byte” of the phase accumulator as the current sample index, which is the equivalent of dividing the value by 256. This can be done using a bitwise shift.

Notes on Bitwise Shifting

Set Registers in Setup()

  • Begin a Serial connection at 9600 bps.
  • SWITCH SPEAKER TO DIGITAL PIN 9.
  • Using a bitwise shift to set pin 9 to be an output. Port Registers & Pin Mapping
  • Set Timer1 to a phase correct PWM at the nominal frequency of 31372.549 Arduino Reference
  • Set the Timer/Counter1 Control Register A to Clear OC1A on Compare Match when upcounting and set OC1A on Compare Match when downcounting. (See Arduino datasheet, 16.11.1, Table 16-3)
  • Set the Timer/Counter1 Control Register A to PWM, Phase Correct, 8-bit (See Arduino datasheet, 16.11.1, Table 16-4)
  • Set the Timer/Counter1 Interrupt Mask Register to have Overflow Interrupt Enabled (See Arduino datasheet, 16.11.8)

Timer Overflow Interrupt

  • In your .ino file, create a function called getByteLevel with an int as a parameter which returns an inline byte.
    • This is expected to receive a value from your wavetable (-127 to 127) multiplied by volume (max 128)
    • Use a bitwise shift to divide the input value by 128
    • Add 128 so the value is between 0 and 255
    • Return the value as an inline byte
  • Create a global variable in your .ino file which is of type OscillatorState.
  • In your .ino file create an interrupt that will be called when timer1 overflows ISR(TIMER1_OVF_vect){ … }
    • Get current value of the oscillator multiplied by its volume.
    • Set OCR1A to the byte level of the value.

Replace Tone() with an Oscillator

  • In your .h file, create a frequency array with the 7 note frequencies (1 per button A-G) using frequencies from the arduino tone tutorial. Higher notes (greater than octave 4) will sound better.
  • In your .h file, create a enum with the note for each frequency.
  • Inside your loop, update your oscillator's volume and phase step dependent on the button pressed instead of using tone().

Parallel Piano

Additive Synthesis

You will use additive synthesis to play two notes at once.

Save part 1 as a new Arduino project titled colbyUsername_P03_Part2 (with your colby username, mine would be hewolfe_P03_Part2).

Suggested Readings from Physical Audio Signal Processing For Virtual Musical Instruments and Audio Effects by Julius O. Smith III

Detect 2 Keys Pressed

Modify your code so if two buttons are pressed at the same time, it prints the name of the note associated with both buttons. Use the equation you derived for Vout when two buttons are pressed at the same time to calculate the expected A0 readings.

Hard coding a series of if statements fullfills the requirement. A well organized, designed, and documented implementation of a solution like the one described below can be worth up to 2 extension points.

Suggestions:

  • Write your code and test it (unit testing) on your laptop before incorporating it into the micro-controller code.
  • Create a KeyValuePair struct with fields for Vout, note 1 and note 2.
  • Create an array of KeyValuePairs with the Vout for single and double button presses, which is sorted by Vout. This can be done programmatically.
    • Write a function to calculate the Vout for a given set of resistors.
    • Create an array of the expected length of voltages.
    • For each single resistor and pair of resistors, calculate the Vout, create a KeyValuePair, and insert it into the array.
    • Using a sorting algorithm to order the elements of the array. For example, binary insertion sort could be used when inserting the items into the array.
  • Use a search algorithm (eg. binary search) to search the array to find the Vout which is closest to a given value.

Parallel Piano

Modify your code to be able to play two notes at once.

  • Create a second oscillator
  • Inside ISR(TIMER1_OVF_vect) update both oscillators. Calculate the byte level of the two values added together. (The volume of the two oscillators combined should never be greater that 128).

Inside your loop, update your oscillator's volume and phase step dependent on the button or buttons pressed.

You can use your rolling average struct for smoothing the signal with a short window if needed, though if it is too long it may create a glissando between notes.

Extension

Extensions

Optionally you can complete 1-2 extensions. Extensions can build on the pre-existing circuit for this project or can be a new circuit. The complexity of the chosen extension, the implementation and the quality of the documentation effects the final grade. I encourage ideas not on the list. I reccommend discussing your extension idea with me (in person or over email) if you are unsure if it reasonable.

Extension code must be separate from the project.

Software Extension

Software extensions build on the pre-existing circuit for this project.

Here are some ideas:
  • Use a different audio synthesis technique.
  • Detecting 2 button presses using a sort and search algorithm.
  • Use the keys to control different parameters of the audio synthesis.
  • Modify the code to use timer2 to control the oscillator instead.
  • Detect other combinations of key presses.

Hardware Extension

Design and implement an idea for a new project which involves adding new components and modifying the circuitry.

  • It must incorporate the materials from this project; the piezo element (as an input or output device) and buttons.
  • It must include atleast one of the following components: a second piezo element (your partners), the IR emitters, IR receivers, and/or the vibrating motors.
  • It can include a potentiometer, extra buttons, and/or red LED.

Include in the documentation:
  • A circuit diagram.
  • A photograph of the circuit board.

Document your Extensions

  • Submit your code on filer in a folder inside your project folder.
  • A 1-3 minute video documenting your extension working.
  • A description of the extension.
  • A section on implementation.
  • A section on results & reflection.
  • Include any relevant photographs, drawings, charts or graphs.

Submission

Submit your code on Filer

Submit your arduino code in a folder titled colbyUsername_P03 (with your colby username, mine would be hewolfe_P03).

Submit your report and video on Google Classroom

Upload a 1-3 min Documentation Video of your keyboard, showing that each note works and each pair of notes work. Describe what is happening.

Attach a Google Doc Report which includes:
  • Photo of circuit. Clear photo of the circuit in which the wiring is clearly visible.
  • Implementation Section
    • Explain how you implemented the project.
    • Explain what algorithms or techniques you used.
    • Compare your implementation to the Tone Library. Explain the differences in implementation.
  • Results & Reflection Section
    • How well does your implementation work?
    • How does the output of your implementation compare to the tone() library?
  • Acknowledgements Section
    • What resources did you use? Include websites, books and anyone you received help from or discussed the project with.
-->