Music Synthesizer Senior Project - Individual Report
Music Synthesizer Senior Project - Individual Report
Individual Report
Bryan Bellin
Evan Lew
Vikrant Marathe
Jordan Wong
Abstract 2
I. Introduction 2
Output Buffer 4
FFT Implementation 4
Graphic Equalizer 13
VI. Summary 38
1
Abstract
The Danalog is a 25 key portable digital music synthesizer that uses multiple synthesis
methods and effects to generate sounds. Sound varieties included three synthesis methods
including FM, subtractive, and sample-based, with up to eight adjustable parameters, at least four
effects, including reverb, chorus, and flange, with five adjustable parameters, and at least two
note polyphony, and a five band equalizer. The user would be able to adjust these effects using
digital encoders and potentiometers and view the settings on two LCD screens.
The finals project was unable to meet the original design requirements. The FM synthesis
method was primarily working in the end product. The synthesizer was built to produce two note
polyphony. The LCD screens displayed the information about the synthesis method as the user
plays.
I. Introduction
The purpose of this project was to create a portable, inexpensive digital music synthesizer for
amateur musicians. The intended customer base consists of young, amateur musicians who don’t
have a big budget for a more expensive music synthesizer.
Several other companies have their own digital synthesizers equipped with numerous features.
The Danalog’s main competitors would be the Yamaha Reface, Korg Minilogue, Roland
Boutique, and Arturia MicroBrute. The lowest price of these is the Arturia MicrBrute at $299 -
which the Danalog has beat by $100. The Danalog digital synthesizer is also smaller than the
other competitor’s options.
- The Danalog Synthesizer will produce notes over a 2 octave range via Frequency
Modulation Synthesis, with two note polyphony.
2
- The chassis will be made from lightweight plastic that is easy to carry and hold.
- All components, peripherals, and circuit boards are industry standard and well supported.
- The encoders, potentiometers, and switches will be strategically placed in a manner that
follows the logical path of the signal from generation, to equalizing, to modulating.
- The processing will be split among two IC’s: The ATmega2560 for peripheral
information, and the TMS320C5535 for Digital Signal Processing.
Performance Specifications
User Input: All of the user inputs are translated into 8-bit signals, handled by the ATMega2560
microcontroller. This includes MIDI protocol, potentiometer positions, encoder rotation
direction, switch and button positions (via mux), for a total of 30 bytes. All of them are traced to
an Arduino Mega development board on a PCB where the ATMega chip resides.
5V Power Supply: The synthesizer is powered via 5V 500mA USB power or a 5 volt battery.
There is a level shifter as well, because the Arduino Mega board runs on 5V while the
TMS320C5535 runs on 3.3V.
3
III. Independent Work
Output Buffer
To meet the specification of having both headphone outputs and balanced outputs the DAC
output would have to be buffered to allow the headphone output to not interfere with the
balanced outputs. To create a buffer a voltage follower is made. In order to determine
specifications the audio demo that came preinstalled with the DSP board was utilized to max out
the audio codec. This demo created a playback device on the computer so the DSP board
could act as the DAC for all sound being made from the computer. By playing youtube at max
volume and turning on persistence on an oscilloscope the open circuit voltage is determined to
never surpass ±1.5 Volts. So to cover all possible signal range the FSR will be considered to be
plus or minus 1.5 volts.
Since the DSP board and arduino deal with 5 Volt supplies I had the rails to my buffer go from
0-5 Volts. This is admittedly not enough to satisfy Professor Prodanov’s general rule of having
the rails of an amplifier at least 2 volts beyond the maximum signal voltage but testing later
proved the buffer was good enough.
FFT Implementation
4
In order to have an LED display of the audio being outputted and easily equalize any input from
audio generation it would be necessary to have a Fourier transform function that performed in
real time. The first attempt to perform an FFT within the DSP board was done by reading the
manual called spruh87h-trm.pdf. The manual provided example codes using the hwafft_Npts()
and hwafft_br() functions. With the exception of different input arrays the code worked as
shown below.
======================Hwafft Code======================================
#define N ( 16 ) //must change funct name and DATA_LEN_X if this is changed
#define ALIGNMENT 2*N //ALIGNS data_br_buf to an address with log2(4*N) zeros in the
least significant bits of the byte address
#include "stdio.h"
#include "hwafft.h"
#include "ezdsp5535.h"
/*
* main.c
*/
int main(void) {
//#include <std.h>
//#include <log.h>
//#include "hellocfg.h"
Int32 *result;
5
//Int32 *data_br:
Uint16 fft_flag;
Uint16 scale_flag;
Uint16 out_sel;
fft_flag = FFT_FLAG;
scale_flag = SCALE_FLAG;
data = data_br;
if (out_sel == OUT_SEL_DATA) {
result = data;
}else {
result = scratch;
}
return 0;
}
======================== End Hwafft Code ================================
This did not work. The project would compile but output would always be zero or unchanged
from the initial values.
Going back to square one. A DSP library for the C5535 DSP chip was found online and
installed into a project. An example for this FFT was already provided and worked perfectly. for
its test data.
6
Photo of test data input time domain
#include <stdlib.h>
#include <math.h>
#include <tms320.h>
#include <stdio.h>
#include <dsplib.h>
7
//#include "t2_NOSCALE.h"
//#include "t3_NOSCALE.h"
//#include "t4_NOSCALE.h"
//#include "t5_NOSCALE.h"
//#include "t6_NOSCALE.h"
//#include "t7_NOSCALE.h"
#include "t8_NOSCALE.h"
#include "Dsplib.h"
#include "Dsplib_c.h"
#ifndef SCALING
#define SCALING 0
#endif
void main()
{
// compute
#if SCALING
cfft(x, NX, SCALE);
#else
cfft(x, NX, NOSCALE);
#endif
cbrev(x,x,NX);
// test
eflag = test(x, rtest, NX, MAXERROR);
if(eflag != PASS)
{
exit(-1);
}
return;
}
8
Photo of test sine wave
A different approach was found in an example project posted on the Texas Instruments Forums.
The fft function used there was called CFFT_SCALE.
=====================CFFT_SCALE CODE================================
#include <stdio.h> //keeping for printf()
#include <math.h> //not touching till sin() is gone
//#include "TMS320.H"
#include "Dsplib.h" //critical for data declaration
#include "usbstk5505.h" // Do not touch
#include "Application_1_Modified_Registers.h" //no touch
#include "Audio_To_MIDI_Using_DMA_and_CFFT.h" //no touch
//#include "hwafft.h"
//#include "Output_MIDI.h"
Int16 OverlapInL[WND_LEN];
Int16 OverlapInR[WND_LEN];
Int16 OverlapOutL[OVERLAP_LENGTH];
Int16 OverlapOutR[OVERLAP_LENGTH];
9
Int8 BBITOverlapInL[WND_LEN];
Int8 BBITOverlapInR[WND_LEN];
Int8 BBITOverlapOutL[OVERLAP_LENGTH];
Int8 BBITOverlapOutR[OVERLAP_LENGTH];
Int8 BBITOutput_to_LEDS[8];
int Audio_To_MIDI_Using_DMA_and_CFFT(void) {
int i = 0;
int j = 0;
//int f = 0;
//DATA Peak_Magnitude_Value = 0;
//DATA Peak_Magnitude_Index = 0;
//int MIDI[256] = {0};
printf("Initializing Buffers\n");
/* Initialize buffers */
for (i = 0; i < WND_LEN; i++) {
OverlapInL[i] = 0;
OverlapInR[i] = 0;
}
10
//printf("Entering infinite loop\n");
/* Begin infinite loop */
//while (1)
//{
/* Get new input audio block */
printf("Overriding Mic input with own sine wave\n");
11
BufferL[i] = OverlapInL[i];
BufferR[i] = OverlapInR[i];
}
/* Perform FFT */
//cfft32(complex_data, HOP_SIZE, SCALE);
cfft_SCALE(complex_data, FFT_LENGTH); //11841 cycles later...
/* Perform bit-reversing */
cbrev(complex_data, complex_data, FFT_LENGTH);
// Find the Power of the audio signal using the cfft results and scale by
1/2
for(i = 0; i < FFT_LENGTH; i++) { // square the real vector and the
imaginary vector
realR[i] = realR[i] * realR[i];
imagR[i] = imagR[i] * imagR[i];
}
// ADD
for(i = 0; i < FFT_LENGTH; i++) {
PSD_Result[i] = realR[i] + imagR[i];
}
// Scale result by dividing again, because im not sure if I have the sqrt
C library runtime fuction
printf("FFT Result Points = ");
for(i = 0; i < FFT_LENGTH; i++) {
PSD_Result_sqrt[i] = sqrt(PSD_Result[i]); // WARNING DOUBLE USED
IN SQRT
printf("%i, ", PSD_Result_sqrt[i]);
}
12
======================END CFFT_SCALE CODE===========================
This function worked. It was able to produce an frequency spectrum with two distinct peaks
representing a tone reflected.
Photo of FFT produced. The noise here is a bit too much but since the values were being bit
shifted to avoid perform faster I can minimize the presence of noise by letting back on bit
shifting until the tone is at its maximum representation.
Photo of bit adjusted FFT spectrum. The drawback of letting back on bit shifting back 5 instead
of 8 can be seen as higher magnitudes at the tone frequency. This is fine since the LED display
will not need much accuracy.
CFFT_SCALE was tracked to an older version of the same DSP library used previously so it
was reasoned that installing the older library and using those functions would be successful.
However installing the DSP library into the master project was unsuccessful.
Graphic Equalizer
13
A requirement for the synthesizer was having a graphic equalizer. That is a set of knobs that
filtered the frequency bands of the audio to the levels defined by the user.
The first method of approach was creating a frequency sampling filter. It would have
logarithmically spaced bands that would appear to have linear interpolation between each set
knob frequency on a log scale. These bands had to be logarithmically spaced at intervals that
would line up exactly on the frequency points being specified by the knobs. This was already
achieved in matlab thanks to a previous class. It had to be converted however for two reasons.
Matlab code had to become C++ code and the values of everything had to be readjusted for
only five values which made five bands between values. This was achieved and can be seen
as code below.
14
realR[i] = realR[i]*EqualizerFD[i];
}
============= End Graphic Equalizer Code via Frequency Sampling==================
Using printf statements we were able to port multiple scenarios of inputs into excel and observe
the linear interpolation on the log scale. Unfortunately this had to be dropped out as FFT along
with IFFT would not run without the DSP library in the master project.
The second approach to making an equalizer was with the constraints of not having a DSP
library taken into account. Instead of having a frequency spectrum of points as a filter the
equalizer would have to be in the form of difference equations with predetermined coefficients.
These difference equations could account as five bandpasses with knob values determining
magnitude of effect. The bandpass results could then be averaged together essentially creating
a variable comb filter. To make sure any difference equation worked a simple lowpass
difference equation was created and tested.
#include <stdio.h>
#include <math.h>
#include <tsk.h>
#include "Dsplib.h"
#include "Dsplib_c.h"
#include "MISC.H"
#include "audio/singen.h"
#include "global_vars.h"
#include "TMS320.H"
Int16 Buffer[256];
Int32 x[128];
15
Int32 Bk[3] = {10, 14, 10};
Int32 Ak = 34;
Int32 y[128];
//Int16 nx = 128;
//Int16 nh = 3;
//DATA x[128];
//DATA r[128];
//Int16 oflag;
//Int16 nx = 128;
//Int16 nh = 501;
//DATA x[128];
//DATA h[501];
//DATA r[128];
//DATA dbuffer[501];
//Int16 oflag;
//Int16 var = 0;
SinState EQSine;
sin_compute_params(&EQSine, 6000);
16
Int16 i;
while(1) {
// eq code
x[i] = x[i]/2048;
// printf("%d,", x[i]);
printf("\nFilter ");
First_Term = Bk[0]*x[0];
Second_Term = 0;
Third_Term = 0;
y[0] = First_Term/Ak;
First_Term = Bk[0]*x[1];
Second_Term = Bk[1]*x[0];
Third_Term = 0;
y[1] = y[1]/Ak;
First_Term = Bk[0]*x[2];
Second_Term = Bk[1]*x[1];
Third_Term = Bk[2]*x[0];
y[2] = y[2]/Ak;
First_Term = Bk[0]*x[var];
Second_Term = Bk[1]*x[var-1];
17
Third_Term = Bk[2]*x[var-2];
y[var] = y[var]/Ak;
// Int16 EqualizerFD[256];
// EqualizerFD[0] = Knob_Values[0];
// //printf("\n%i,",EqualizerFD[0]);
// EqualizerFD[1] = Knob_Values[0];
// //printf("%i,",EqualizerFD[1]);
// Int16 l;
// Int16 m;
// Int16 n;
// Int16 Interp_M;
// //make array
// n = 1;
18
// n++;
// //printf("%i,",EqualizerFD[m]);
// }
// EqualizerFD[m] = Knob_Values[l+1];
// //printf("%i,",EqualizerFD[m]);
// }
// EqualizerFD[l] = Knob_Values[4];
// //printf("%i,",EqualizerFD[l]);
// }
// EqualizerFD[255-i] = EqualizerFD[i];
// //printf("%i,",EqualizerFD[128+i]);
// }
// printf("\n");
// for(i=0;i<256;i++) {
// complex_data[2*i] = EqualizerFD[i];
// complex_data[2*i+1] = 0;
// }
// for(i=0;i<512;i++) {
// }
// cifft_SCALE(complex_data, 256);
//cfft_SCALE(complex_data, 256);
TSK_sleep(100);
19
Using matlab the coefficients for the difference equation were determined so using this code
and a sine wave input and matlab the code was verified to be a working FIR filter. It was then
made into an IIR filter with three Aks and three Bks and verified again. Again matlab was used
to figure out how many Aks and Bks were expected to be handled. Then the code was
expanded to work as five bandpasses. These bandpasses were made based off the knob
frequencies in the frequency sampling filter before and had to be IIR to have a faster transition
or rather steeper peak in the frequency spectrum.
===============Difference Equation Graphic Equalizer Code======================
/*
Stand
ard C
inclu
des
*/
#include <std.h>
#include <stdio.h>
#include <math.h>
#include <tsk.h>
#include "Dsplib.h"
#include "Dsplib_c.h"
#include "MISC.H"
#include "audio/singen.h"
#include "global_vars.h"
#include "TMS320.H"
Int16 Buffer[256];
Int16 left_ping_eq[I2S_DMA_BUFFER_SIZE];
20
Int16 left_pong_eq[I2S_DMA_BUFFER_SIZE];
Int16 right_ping_eq[I2S_DMA_BUFFER_SIZE];
Int16 right_pong_eq[I2S_DMA_BUFFER_SIZE];
Int32 x[128];
Int32 y1[128] =
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0};
Int32 Next_y1[128];
Int32 y2[128];
Int32 Next_y2[128];
Int32 y3[128];
Int32 Next_y3[128];
Int32 y4[128];
21
Int32 Next_y4[128];
Int32 y5[128];
Int32 Next_y5[128];
Int32 y[128];
//Int16 nx = 128;
//Int16 nh = 3;
//DATA x[128];
//DATA r[128];
//Int16 oflag;
//Int16 nx = 128;
//Int16 nh = 501;
//DATA x[128];
//DATA h[501];
//DATA r[128];
//DATA dbuffer[501];
//Int16 oflag;
//Int16 var = 0;
SinState EQSine;
22
sin_compute_params(&EQSine, 6000);
Int16 i;
Next_y1[0] = Bk1[0]*x[0]/Ak1[0];
Next_y1[1] = (Bk1[0]*x[1]+Bk1[1]*x[0]-Ak1[1]*y1[0])/Ak1[0];
Next_y1[2] =
(Bk1[0]*x[2]+Bk1[1]*x[1]+Bk1[2]*x[0]-Ak1[1]*y1[1]-Ak1[2]*y1[0])/Ak1[0];
Next_y2[0] = Bk2[0]*x[0]/Ak2[0];
Next_y2[1] = (Bk2[0]*x[1]+Bk2[1]*x[0]-Ak2[1]*y2[0])/Ak2[0];
Next_y2[2] =
(Bk2[0]*x[2]+Bk2[1]*x[1]+Bk2[2]*x[0]-Ak2[1]*y2[1]-Ak2[2]*y2[0])/Ak2[0];
Next_y3[0] = Bk3[0]*x[0]/Ak3[0];
Next_y3[1] = (Bk3[0]*x[1]+Bk3[1]*x[0]-Ak3[1]*y3[0])/Ak3[0];
Next_y3[2] =
(Bk3[0]*x[2]+Bk3[1]*x[1]+Bk3[2]*x[0]-Ak3[1]*y3[1]-Ak3[2]*y3[0])/Ak3[0];
Next_y4[0] = Bk4[0]*x[0]/Ak4[0];
Next_y4[1] = (Bk4[0]*x[1]+Bk4[1]*x[0]-Ak4[1]*y4[0])/Ak4[0];
Next_y4[2] =
(Bk4[0]*x[2]+Bk4[1]*x[1]+Bk4[2]*x[0]-Ak4[1]*y4[1]-Ak4[2]*y4[0])/Ak4[0];
Next_y5[0] = Bk5[0]*x[0]/Ak5[0];
Next_y5[1] = (Bk5[0]*x[1]+Bk5[1]*x[0]-Ak5[1]*y5[0])/Ak5[0];
Next_y5[2] =
(Bk5[0]*x[2]+Bk5[1]*x[1]+Bk5[2]*x[0]-Ak5[1]*y5[1]-Ak5[2]*y5[0])/Ak5[0];
while(1) {
// Ak1[i]*y1[i]+Ak1[i-1]*y1[i-1]+Ak1[i-2]*y1[i-2] =
Bk1[i]*x[i]+Bk1[i-1]*x[i-1]+Bk1[i-2]*x[i-2]
// y1[i] =
(Bk1[0]*x[i]+Bk1[1]*x[i-1]+Bk1[2]*x[i-2]-Ak1[1]*y1[i-1]-Ak1[2]*y1[i-2])/Ak1[0];
y1[0] = Next_y1[0];
y1[1] = Next_y1[1];
y1[2] = Next_y1[2];
23
y1[i] =
(Bk1[0]*x[i]+Bk1[1]*x[i-1]+Bk1[2]*x[i-2]-Ak1[1]*y1[i-1]-Ak1[2]*y1[i-2])/Ak1[0];
}
Next_y1[0] = y1[125];
Next_y1[1] = y1[126];
Next_y1[2] = y1[127];
y2[0] = Next_y2[0];
y2[1] = Next_y2[1];
y2[2] = Next_y2[2];
y2[i] =
(Bk2[0]*x[i]+Bk2[1]*x[i-1]+Bk2[2]*x[i-2]-Ak2[1]*y2[i-1]-Ak2[2]*y2[i-2])/Ak2[0];
}
Next_y2[0] = y2[125];
Next_y2[1] = y2[126];
Next_y2[2] = y2[127];
y3[0] = Next_y3[0];
y3[1] = Next_y3[1];
y3[2] = Next_y3[2];
y3[i] =
(Bk3[0]*x[i]+Bk3[1]*x[i-1]+Bk3[2]*x[i-2]-Ak3[1]*y3[i-1]-Ak3[2]*y3[i-2])/Ak3[0];
}
Next_y3[0] = y3[125];
Next_y3[1] = y3[126];
Next_y3[2] = y3[127];
y4[0] = Next_y4[0];
y4[1] = Next_y4[1];
y4[2] = Next_y4[2];
24
for (i = 3; i < 128; i++) {
y4[i] =
(Bk4[0]*x[i]+Bk4[1]*x[i-1]+Bk4[2]*x[i-2]-Ak4[1]*y4[i-1]-Ak4[2]*y4[i-2])/Ak4[0];
}
Next_y4[0] = y4[125];
Next_y4[1] = y4[126];
Next_y4[2] = y4[127];
y5[0] = Next_y5[0];
y5[1] = Next_y5[1];
y5[2] = Next_y5[2];
y5[i] =
(Bk5[0]*x[i]+Bk5[1]*x[i-1]+Bk5[2]*x[i-2]-Ak5[1]*y5[i-1]-Ak5[2]*y5[i-2])/Ak5[0];
}
Next_y5[0] = y5[125];
Next_y5[1] = y5[126];
Next_y5[2] = y5[127];
left_output = left_pong_eq;
right_output = right_pong_eq;
} else {
left_output = left_ping_eq;
right_output = right_ping_eq;
25
}
left_output[i] = y[i];
right_output[i] = y[i];
// First_Term = Bk[0]*x[0];
// y[0] = First_Term/Ak;
// First_Term = Bk[0]*x[1];
// Second_Term = Bk[1]*x[0];
// Third_Term = 0;
// y[1] = y[1]/Ak;
// First_Term = Bk[0]*x[2];
// Second_Term = Bk[1]*x[1];
// Third_Term = Bk[2]*x[0];
// y[2] = y[2]/Ak;
//
// First_Term = Bk[0]*x[var];
// Second_Term = Bk[1]*x[var-1];
// Third_Term = Bk[2]*x[var-2];
// y[var] = y[var]/Ak;
// //printf("%d,", y[i]);
// }
26
// Int16 Knob_Values[5] = {1, 10, 20, 40, 30};
// Int16 EqualizerFD[256];
// EqualizerFD[0] = Knob_Values[0];
// //printf("\n%i,",EqualizerFD[0]);
// EqualizerFD[1] = Knob_Values[0];
// //printf("%i,",EqualizerFD[1]);
// Int16 l;
// Int16 m;
// Int16 n;
// Int16 Interp_M;
// //make array
// n = 1;
// n++;
// //printf("%i,",EqualizerFD[m]);
// }
// EqualizerFD[m] = Knob_Values[l+1];
// //printf("%i,",EqualizerFD[m]);
// }
// EqualizerFD[l] = Knob_Values[4];
// //printf("%i,",EqualizerFD[l]);
// }
27
// for(i = 0;i < 128; i++) {
// EqualizerFD[255-i] = EqualizerFD[i];
// //printf("%i,",EqualizerFD[128+i]);
// }
// printf("\n");
// for(i=0;i<256;i++) {
// complex_data[2*i] = EqualizerFD[i];
// complex_data[2*i+1] = 0;
// }
// for(i=0;i<512;i++) {
// }
// cifft_SCALE(complex_data, 256);
//cfft_SCALE(complex_data, 256);
TSK_sleep(100);
Knob Freq Digital Freq Radians Intensity Pole Real Pole Imaginary
28
9 5
This table was used to calculate the location of poles and zeros for each bandpass
29
With the poles and zeros above these
bandpasses are created. They are
represented in the project’s code as Aks and
Bks below.
Ak Bk Scaled Ak Scaled Bk
30
System Design - Functional Decomposition (Level 1)
The system can be broken down as shown in figure 4.1. The operation of the system can be
generalized as:
1. The user interacts with the device
a. Presses a key on keyboard
b. Sends a MIDI event
c. Changes a parameter on the front panel
2. The ATmega reads in information from the user
3. The C5535 requests an update on the status of the system
4. The ATmega responds with the latest information on key presses, parameter changes and
MIDI information
5. The C5535 generates a waveform based on the state of the system
6. The sound is enjoyed by the listener
31
VII. Physical Construction and Integration
1. Main PCB: The PCB connects all the devices together and functions mechanically to
hold all the components neatly in place inside the enclosure.
2. Arduino Mega: The Arudino Mega functions to interface with all the user input controls.
It communicates all the fundamental information to the C5535 via a SPI communication
bus
3. TI ezDSP C5535: This device is responsible for interpreting the information sent by the
Arduino and generating sound.
The PCB functions as the harness for all the front panel interface controls, which consist of
rotary quadrature encoders, rotary potentiometers, linear potentiometers, and rotary switches.
The organization of the user interface was decided by the team during the initial planning phase.
All interconnections on the PCB were made to accommodate the initial user interface design.
Each device is routed to pins on the Arduino Mega board. Since the amount of IO needed was
slightly more than the Arduino Mega provided we used multiplexers between the diode
connected matrix keyboard (figure 7.2a) and between the rotary switches and front panel buttons
(figure 7.2b)
32
Figure 7.2a: Diode connected keyboard multiplexer layout
The device also has two displays for outputting information about the state of the synthesis
engine and the state of the effects processor. The displays were purchased from sparkfun as
separate units not soldered to the the main circuit board. These displays were used because of
their simple serial interface which allowed us to use a hardware UART to communicate with the
display
33
Figure 7.3: Both LCD displays connected to the main PCB via wires
Since the both the Arduino Mega and TI ezDSP both can be driven by 5 volts USB power there
was no need to design any sort of power system. Additionally since the devices are low power,
as USB devices usually are, there is no need for any form of heat sinking inside of the enclosure.
The chassis was 3D printed on Evan Lew’s home 3D printer. Due to sizing constraints the
chassis was printed in two halves and then glued together to form the final chassis. Figure 7.4
shows the 3D model of the chassis and figure 7.5 shows the real life chassis supported by the
keyboard.
34
Figure 7.4: 3D model of the chassis
35
Figure 7.5: Chassis with internal hardware
Due to the ambitious and complex nature of the project we were not able to achieve all of our
goals. However, we were able to have a product at the end that was on the path to achievement.
At the end of our spring quarter, we had a functioning piece of hardware and functional synthesis
engine.
The FM synthesis works. The latency is tested by having the arduino send a pin high when a key
is pressed and measuring the delay between that transition and the start of the note being played.
Minimum Maximum
37
VI. Summary: While not all the specifications were met the FM synthesis has been
successfully created and works well. The output is a bit noisier when probing but sounds fine
without. Latency is low enough for the synthesis to be considered in real time. Shortcomings
were included to but not limited to failure to have working DSP library functions, losing progress
with corrupted projects, and failure to keep audio data not corrupted upon transfer to a new bank.
Having the DSP library work could be done by consulting Texas Instruments or posting on their
forum about how to not just install the DSP library onto a computer but also on what is necessary
to include the library in a project in code composer studio. To avoid having projects become
beyond repair github would be used from the beginning so as to revert back to stable checkpoints
easily. Avoiding audio corruption upon transfer could be potentially solved with data alignment
or just having a simple project be used to debug the problem.
38