For our final project in ECE 5760, we built a sequencer/synthesizer using an Altera DE2 FPGA board for processing and MATLAB for user interface. The synthesizer uses a combination of sampled and additively synthesized sounds to produce various instruments. The FPGA based sequencer takes input from a MATLAB GUI and sequences each instrument separately, allowing the user to make compositions in real-time. Additionally, an automatic gain control algorithm was implemented to ensure that overflow would not result in distorted output.
Our main motivation for pursuing this project was our joint interest in music, and desire to pursue a synthesizer project in addition to our Lab 3, which was a drum head wave equation calculator. We came across some very interesting designs for synthesizers; some of our inspirations including the Monome and the web-based Sembeo synthesizers/sequencers.
We decided on a music based project because both of us are musically oriented, and we pursue music through many different avenues. One area we were jointly interested in was music computing, including digital synthesis and music composition. Lab 3 was an interesting venture into the world of physical instrument modeling, and we wanted to take this a step further and incorporate it into a synthesizer.
Some of our inspiration came from a device called the Monome, which is set up very similar to our GUI. Also, the Sembeo is set up just like our GUI. Our implementation builds on these designs by adding the ability to sample and store patterns.
This technique uses addition of a fundamental and a few harmonics along with amplitude envelope characteristics to synthesize a musical signal that can imitate any instrument. The main equation for synthesis is
Several harmonics can be chosen with parameters including amplitude, frequency, and phase . The choice of harmonics produce different instruments and timbres. For this project, we decided to use additive synthesis because it was a robust technique that allowed us to form many different types of sounds quickly and easily.
Our design starts with user input to a screen of buttons on a MATLAB GUI. The user input defines the sequences of different instruments. These sequences are periodically pushed out through a National Instruments Analog-Digital Converter into the GPIO port of the Altera DE2 board. Custom Verilog code reads in the sequence data and assigns sequences to 2 main types of instruments: sampled instruments stored in ROM and additively synthesized instruments. In total, 8 instruments are added and sent to a range compressor, which prevents clipping. The final output is sent to the audio codec for sound output.
The reason to implement this system in hardware is to be able to perform synthesis of many instruments in parallel. Polyphony is extremely hard for software; however, in hardware it is simply a matter of connecting many adders together and having a good compressor. We were forced to use MATLAB as a GUI because our original device, the Multi-touch LCD display by Terasic, did not work for our setup. We decided not to use a Nios II, because in our experience, it makes designs unnecessarily complicated.
To the best of our knowledge, there are no standards governing the creation of musical instruments and synthesizers. However, since our system does have sampling capability, we need to make sure all samples are used with knowledge of the distributor/owner. The samples used in this project were obtained on an open source sample website: www.freesound.com.
The additive synthesis technique for musical synthesis is a well known algorithm. The monome and other associated sound matrix synthesizers are more a form of creativity and expression rather than rigid industrial products such as Korg or Moog synthesizers. Our design does not infringe on any of these IPs, especially because we are not planning to make any of this commercial.
Everything except the user interface in MATLAB was implemented in hardware on the Altera DE2 board, which contains a Cyclone II FPGA. As shown above, the GPIO port is used to read in sequence data from the MATLAB GUI. This data is presented in 16 bit words, 1 for each instrument. A total of 8 instruments were used, so a state machine was built to read in 8 16 bit words. The state diagram for this machine is shown here:
The transitions from Idle 1 to Read upper word and from idle 2 to read lower word occur according to a read_in bit from MATLAB. This bit goes high when the upper 8 bits of a word are ready, and the subsequent negative edge indicates that the lower 8 bits of a word are ready. Along with sending this bit, MATLAB sends the 8 bits simultaneously that can be read in parallel using 8 GPIO ports. The state machine is looped until 8 16 bit words are read. Then the state machine returns to the Idle 1 state to wait for the next set of 8 words. The code for this read in setup is packaged in the data_in.v module.
A NIDAQ USB 6008 was used to transfer data from MATLAB to the GPIO pins. Since the GPIO pins commonly operate at 3.3V, a voltage divider had to be used to step down the voltage from 5V. However, the NIDAQ can only supply up to 8.5 mA, so the resistors had to be on the order of 30 k?. The resistors used for this setup were 30 k? and 18k?. They were placed in the following configuration:
In this configuration, the signal from the NIDAQ is digital and acts as a power source. If the bit is high (or 5V), the out to GPIO is 3 V. Otherwise, the output is close to 0V. The 3 and 0 at the output are sufficient to read in a digital 1 or 0 in the FPGA.
We wanted to include some fresh music samples in our synthesizer to actually have beats that sounded good. I hand picked these samples from the open source website freesound.com. I chose a bass drum, snare drum, and hi hat sound. These sounds were sampled at 44.1 kHz. I extracted the first half of a second of each and downsampled by 2 to reduce the size to 5000 18bit samples per sound. I converted these to fixed point using the MATLAB fixed point toolbox and wrote them into a .v file as ROM. The resulting ROM files are included in sounds.v.
The state machine to read the ROM files when given a 'hit' is shown below. The state machine starts at the Idle 1 state, and stays there until a hit signal is given by the sequencer. Then the state machine goes to the update address state, where it increments the address of the ROM on the positive edge of the Audio DAC clock. This clock is the 48 kHz sampling clock. The state machine stays at Idle 2 until the DAC clock goes low. At this time, the state machine assigns the word from ROM to the audio output. The state machine waits in Idle 3 until the DAC clock goes high and the cycle repeats until 1 of 2 conditions occur. Either the ROM is completely read, or the state machine is told to stop playing by the sequencer.
These instruments were additively synthesized with three harmonics at integer multiples of the fundamental. Direct digital synthesis was used to generate the harmonics from a sine table of 256 values. DDS works by using a look up table sin wave and sampling points on it at different intervals. The interval is referred to as the increment, and the higher the increment, the higher the pitch that results from sampling the sin wave. Our instruments use 3 harmonics: fundamental, 2*fundamental, and 3*fundamental. Our initial instruments had no amplitude envelope, but our final instruments included an ASR shaped envelope, which is shown below.
The beat to beat interval is calculated in a separate part of Verilog. This interval is calculated using a look up table for all of the possible tempos (50-240 BPM). The LUT consists of the intervals between 2 eighth notes in terms of clock cycles at 50 MHz. To switch to a 16th note, we simply shift the eighth interval right by 2. The end of the attack phase was simply the beat-beat interval divided by 4 or shifted right by 2. The beginning of the release was ¾ of the beat interval, which can also be easily calculated using only shifts. The peak of the waveform is at 1, which means the slope can be easily calculated by having a LUT of 1/interval for all possible intervals (same number as all possible tempos). Then we can shift the LUT by 2 to calculate the rising (and falling) slope of the waveform.
The state machine used for this instrument is trivial. It simply adds an increment to an accumulator at 50 MHz. The accumulator is used to index a sine table. All three harmonics are generated and added in this manner. Before the three harmonics are output to the compressor, they are fixed point multiplied with the envelope. Since the envelope is between 0 and 1, this does not amplify or attenuate the signal. The code for additive synthesis is packaged in add_synth.v.
The sequencer takes the data from the GPIO pins and assigns 'hits' to each instrument. It contains a state machine that only has 2 states: rest and play. It runs through the state machine at 50 MHz, but only changes to the play state once every beat. For example, if the beat was in eighth notes, the state machine would go to the play state on every eighth note and then immediately back to the rest state, where it would stay for a prescribed number of clock cycles. The number of clock cycles is determined by a LUT, which contains number of clock cycles between eighth notes for tempos between 50 and 240 BPM. In the play state, the sequencer can assign hits to any instruments that need to be hit.
An interesting problem we had to deal with was high tempo play. At higher tempos, some of the ROM instruments would not be done before they were hit again. To get around this issue, we simply issued a stop command to each instrument about 100-200 cycles before they would be hit again.
In order to output sound, all of the individual instruments need to be added together at each time step. However, we quickly run into the problem of overflow when adding the sounds, as the sound output of each instrument is encoded in 2.16 fixed-point, which is 18 bits long, and the final input into the audio codec needs to be 16 bits. For a single instrument, the solution is easy as the two least significant bits can be cutoff, giving the audio codec a 2.14 fixed-point input. However, when adding more instruments, the number of digits in the exponent, which are the most significant bits to the left of the decimal point, could increase, and in addition the sign bit also shifts over. Thus, a compressor is required to select which bits of the sum of all instruments to send to the audio codec.
The first major choice when designing the compressor was whether to make it static or dynamic. A static compressor applies a constant shift to the audio output. The advantages of this approach would be that it would be easy to implement and each instrument's amplitude would stay constant. However, it would also mean that the total audio amplitude would vary based on how many instruments were playing. The other option, a dynamic compressor, would scale the audio signal based on how many instruments are playing. While a dynamic compressor would keep steadier output amplitude, it is a bit more challenging to implement. Which type of compression is superior is also up for debate. Many audio professionals believe that dynamic compression distorts sound even though to the average listener dynamic compression sounds better as sounds have more 'kick'. We decided to go with dynamic compression as the amplitude difference between one instrument playing and 8 instruments playing was so large that if the volume on the speakers was turned up high enough to hear a single instrument then eight instruments would cause overflow on the analog end of the speaker system.
To properly scale the audio, we need to check at least a single period of each frequency to find the amplitude. We found the smallest possible frequency among our instruments to be the bass drum with a fundamental frequency of 50Hz. Thus, when running a 48kHz audio output, we need to check a history of 48000/50=960 samples to make sure the amplitude of all frequencies present is accounted for. We implemented this history by using a set of counters, one for each overflow bit. For example, if the sound overflows by one bit then the respective counter will reset to 960 and count own every 48kHz cycle. The audio output will then be compressed down by one bit for the next 960 cycles. If another 1 bit overflow is detected at the input during that time, then the counter is reset back to 960. The counter representing the highest overflow always takes precedence. So, for example, if both a 3 bit and 1 bit overflow are detected the output will be compressed by 3 bits until the 3 bit counter reaches zero.
The software GUI uses a matrix format to represent time and instruments. Time is on the horizontal axis. Depending on the division selected by the user, each tick can either be an eighth note or a sixteenth note. The vertical axis represents different instruments. Together, the matrix provides a way to play every possible combination of instruments and times. This format was inspired by a popular Sound Matrix synthesizer called Sembeo. An example of our GUI is shown here:
The user simply has to click on a button to activate an instrument at that tick. The buttons are togglebuttons in MATLAB, which means they change color and state upon activation and deactivation. In MATLAB, a callback can be generated each time a button is clicked, and the state of all the buttons in the system can be kept in a matrix. These states are reactive since they are updated only when a user clicks. However, the data transfer from MATLAB to the FPGA is periodic to ensure that rapid clicking from the user does not clog the data stream. Data is sent to the FPGA at 50Hz every 2 seconds. Due to overhead in MATLAB, sending data faster than 50 Hz is unreliable.
Some extra features were included to let the user save a drum pattern. This corresponds to all of the ROM sampled instruments. When the save button is clicked, the three rows corresponding to these instruments are saved in MATLAB. The user can then clear the instruments or change the pattern. The user can return to the saved pattern at any time by pressing the load pattern button. The load button essentially updates the state of the instruments by loading in the saved pattern. The same functions were implemented for the additively synthesized instruments.
The code for the sine table was taken from a previous ECE 5760 project, Additively Synthesized Ocarina by Joe Montanino. The rest of the additive synthesis code was written from scratch. The code for the ports in the top module were written by Bruce Land.
Possibly the most frustrating part of our project was spending a few weeks obtaining and working the Terasic Multi-touch LCD display. The demo code supplied with the display did not load and technical support, though active, was not very helpful. The display had come with a painter demo, which used a NIOS to control user input and graphical output. The Verilog compiled properly, but when we created a NIOS II IDE project and included all of the .h and .c files, an odd error occurred where the program would clear half of the screen and then get stuck in an infinite loop. The reason for the infinite loop was unclear and tech support determined that the demo could not be run on Quartus II 11.0 and Nios II 11.0 without modifications to some of the hardware infrastructure. We had to switch to a MATLAB display very late in the game. As a result of the lost time, we were not able to implement more complex synthesis algorithms as we had hoped.
Another element that we could not integrate into the final project was the PDE realtime drum machie which we had written for lab 3. We ran into several issues in compressing the audio output of the drums such that it would be compatible with the oter instruments. In addition, at one point we lost a working version of the drum machine and had to try and rebuild it from an older version. Because of these issues we eventaly decided to not pursue incorporating the drum machine into our final project.
The design met our expectations in that the music was synthesized on time and in the appropriate sequence. The user interface was friendly and quickly conveyed information from the user to the FPGA. The software functions to store and clear patterns were useful and worked as expected. The compression algorithm was able to prevent overflow and distorted sounds, while the envelope for the additively synthesized instruments was able to cut off instruments before the next beat.
Our design used all of the memory bits in the FPGA due to the ROM sampled instruments as well as LUT for tempo and sine waves. Our design did not make too much use of the logic elements because we could not experiment with more computationally heavy synthesis algorithms after we lost time with the Terasic display. Adding the envelope to more than 3 additively synthesized instruments increased the memory usage beyond the FPGA capacity. Interestingly, when we tried to reduce the entries in ROM for the sampled instruments in order to make more space, the compilation got stuck at 80% and we stopped the compilation after 30 minutes. So we created 2 separate implementations, one with ROM instruments and non-enveloped additive instruments and another with just enveloped additive instruments.
The additive instruments were played through speakers and held up to a guitar tuner. They registered within 5 cents of the intended note every time. Our design was not hazardous to human health. The only concern was having speakers too close to our ears when we were having overflow issues in the beginning. This implementation is very usable by other people because it just involves clicking buttons in MATLAB. Ideally, we would have liked the touch screen display because it would have provided even better usability.
Some compositions made by Akshay are included here. The first 2 compositions use a combination of ROM and additive instruments to create a full band experience. The scale used in the melodic instruments is taken from Two Pages by Philip Glass. The last composition uses only additive instruments and explores odd intervals, chord changes, and more.
As musicians, the implementation was extremely fun for us. After we got a first prototype, we spent many hours finding cool new beats and chord combinations. Additionally, the save and load pattern features allowed us to make compositions and real-time and be experimental/creative with our music. The design met our expectations of providing a user-friendly, real-time, synthesis/sequencer.
We have some thoughts for what we might do next time. We would make sure that there is sufficient documentation for a product before we ask for it. The MTL display was not sufficiently tested and very new. Thus, we encountered several insurmountable bugs that halted any progress. Next time, we would also plan our project so that we could do the music synthesis and touch screen interface in parallel as opposed to sequentially. This would ensure progress in some area.
If we had some more time we would definitely look into alternative synthesis methods and include a wider variety of instruments in our sequencer. We did a drum head synthesizer for Lab 3, but there are methods to calculate the wave equation on more complex surfaces. This would be an area that we would explore further.
The code was written in Verilog in Altera's Quartus II 11.1 64 bit. The design instantiated several of Alteras megafunctions including ROM, multiplication, and PLLs. Where necessary, Altera and Terasic were referenced at the top of .v files.
As mentioned before, we reused a sin table from Joe Montanino's ECE 5760 Project last year. The rest of the synthesizer code was written from scratch.
The samples stored in ROM were taken from the public domain on the website freesound.com, of which one of the teammates is a member.
There were no non-disclosure or intellectually sensitive materials involved in this project.
Patent opportunities are unlikely for this project since many synthesizers and topologies have been proposed in the past 30 years.
We are not infringing on any standards, restrictions, or codes of conduct with our project.
The top file, also contains code for sequencer, compressor, and sampled instruments
Exported from Notepad++
%pos = [left bottom width height]
%Clear workspace and open ports
clear all
newobjs=instrfind;
if ~(isempty(newobjs))
fclose(instrfind)
end
% Create object
param=daqhwinfo('nidaq');
dio=digitalio('nidaq', param.InstalledBoardIds{1}); % create digital IO object
addline(dio, 0:7, 0, 'out');
addline(dio, 0, 1, 'out');
%define figure and axis
figh = figure('Resize', 'off', 'Position', [10,10,1100,750], 'Color', [.3 .7 0]); clf;
imh = axes('Parent', figh, 'Position', [.05 .8 .2 .15]);
img=imread('Logo.png');
image(img, 'Parent', imh);
set(imh,'XTick',[]);
set(imh,'YTick',[]);
drawnow;
%define the quit button
quitbutton=uicontrol('style','pushbutton',...
'string','Quit', ...
'fontsize',12, ...
'position',[1000,50,75,50], ...
'callback','quitit=1;close;');
%define the reset button
resetbutton=uicontrol('style','pushbutton',...
'string','Reset', ...
'fontsize',12, ...
'position',[900,50,75,50], ...
'callback','reset=1;');
%define the save drum button
savedrumbutton=uicontrol('style','pushbutton',...
'string','Save Drum Pattern',...
'position',[800 650 125 50],...
'callback','savedrum=1;');
%define the load drum button
loaddrumbutton=uicontrol('style','pushbutton',...
'string','Load Drum Pattern',...
'position',[800 600 125 50],...
'callback','loaddrum=1;');
%define the save melody button
savemelodybutton=uicontrol('style','pushbutton',...
'string','Save Melody Pattern',...
'position',[600 650 125 50],...
'callback','savemelody=1;');
%define the load melody button
loadmelodybutton=uicontrol('style','pushbutton',...
'string','Load Melody Pattern',...
'position',[600 600 125 50],...
'callback','loadmelody=1;');
%define the save melody button
clearmelodybutton=uicontrol('style','pushbutton',...
'string','Clear Melody Pattern',...
'position',[400 650 125 50],...
'callback','clearmelody=1;');
%define the load melody button
cleardrumbutton=uicontrol('style','pushbutton',...
'string','Clear Drum Pattern',...
'position',[400 600 125 50],...
'callback','cleardrum=1;');
%-----------------------define instrument a buttons---------------------
%vector of presses
ab=zeros(1,16);
aout=false(1,16);
% Create the button group.
ha = uibuttongroup('visible','on','Units','pixels', 'Position',[20 525 1000 50]);
% Create three toggle buttons in the button group.
ab(1) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[50 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(2) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[110 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(3) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[170 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(4) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[230 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(5) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[290 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(6) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[350 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(7) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[410 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(8) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[470 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(9) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[530 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(10) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[590 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(11) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[650 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(12) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[710 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(13) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[770 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(14) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[830 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(15) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[890 530 40 40],'HandleVisibility','off','Callback','achange=1;');
ab(16) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[950 530 40 40],'HandleVisibility','off','Callback','achange=1;');
%-----------------------define instrument b buttons---------------------
%vector of presses
bb=zeros(1,16);
bout=false(1,16);
% Create the button group.
hb = uibuttongroup('visible','on','Units','pixels', 'Position',[20 465 1000 50]);
% Create three toggle buttons in the button group.
bb(1) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[50 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(2) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[110 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(3) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[170 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(4) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[230 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(5) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[290 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(6) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[350 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(7) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[410 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(8) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[470 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(9) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[530 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(10) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[590 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(11) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[650 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(12) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[710 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(13) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[770 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(14) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[830 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(15) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[890 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
bb(16) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[950 470 40 40],'HandleVisibility','off','Callback','bchange=1;');
%-----------------------define instrument c buttons---------------------
%vector of presses
cb=zeros(1,16);
cout=false(1,16);
% Create the button group.
hc = uibuttongroup('visible','on','Units','pixels', 'Position',[20 405 1000 50]);
% Create three toggle buttons in the button group.
cb(1) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[50 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(2) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[110 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(3) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[170 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(4) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[230 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(5) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[290 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(6) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[350 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(7) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[410 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(8) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[470 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(9) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[530 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(10) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[590 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(11) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[650 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(12) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[710 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(13) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[770 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(14) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[830 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(15) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[890 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
cb(16) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[950 410 40 40],'HandleVisibility','off','Callback','cchange=1;');
%-----------------------define instrument c buttons---------------------
%vector of presses
db=zeros(1,16);
dout=false(1,16);
% Create the button group.
hd = uibuttongroup('visible','on','Units','pixels', 'Position',[20 345 1000 50]);
% Create three toggle buttons in the button group.
db(1) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[50 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(2) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[110 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(3) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[170 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(4) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[230 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(5) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[290 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(6) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[350 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(7) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[410 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(8) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[470 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(9) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[530 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(10) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[590 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(11) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[650 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(12) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[710 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(13) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[770 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(14) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[830 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(15) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[890 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
db(16) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[950 350 40 40],'HandleVisibility','off','Callback','dchange=1;');
%-----------------------define instrument e buttons---------------------
%vector of presses
eb=zeros(1,16);
eout=false(1,16);
% Create the button group.
he = uibuttongroup('visible','on','Units','pixels', 'Position',[20 285 1000 50]);
% Create three toggle buttons in the button group.
eb(1) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[50 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(2) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[110 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(3) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[170 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(4) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[230 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(5) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[290 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(6) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[350 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(7) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[410 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(8) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[470 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(9) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[530 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(10) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[590 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(11) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[650 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(12) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[710 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(13) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[770 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(14) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[830 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(15) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[890 290 40 40],'HandleVisibility','off','Callback','echange=1;');
eb(16) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[950 290 40 40],'HandleVisibility','off','Callback','echange=1;');
%-----------------------define instrument f buttons---------------------
%vector of presses
fb=zeros(1,16);
fout=false(1,16);
% Create the button group.
hf = uibuttongroup('visible','on','Units','pixels', 'Position',[20 225 1000 50]);
% Create three toggle buttons in the button group.
fb(1) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[50 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(2) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[110 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(3) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[170 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(4) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[230 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(5) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[290 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(6) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[350 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(7) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[410 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(8) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[470 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(9) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[530 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(10) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[590 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(11) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[650 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(12) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[710 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(13) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[770 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(14) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[830 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(15) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[890 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
fb(16) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[950 230 40 40],'HandleVisibility','off','Callback','fchange=1;');
%-----------------------define instrument f buttons---------------------
%vector of presses
gb=zeros(1,16);
gout=false(1,16);
% Create the button group.
hg = uibuttongroup('visible','on','Units','pixels', 'Position',[20 165 1000 50]);
% Create three toggle buttons in the button group.
gb(1) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[50 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(2) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[110 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(3) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[170 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(4) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[230 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(5) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[290 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(6) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[350 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(7) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[410 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(8) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[470 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(9) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[530 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(10) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[590 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(11) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[650 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(12) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[710 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(13) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[770 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(14) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[830 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(15) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[890 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
gb(16) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[950 170 40 40],'HandleVisibility','off','Callback','gchange=1;');
%-----------------------define instrument h buttons---------------------
%vector of presses
hb=zeros(1,16);
hout=false(1,16);
% Create the button group.
hh = uibuttongroup('visible','on','Units','pixels', 'Position',[20 105 1000 50]);
% Create three toggle buttons in the button group.
hb(1) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[50 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(2) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[110 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(3) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[170 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(4) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[230 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(5) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[290 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(6) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[350 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(7) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[410 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(8) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[470 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(9) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[530 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(10) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[590 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(11) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[650 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(12) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[710 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(13) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[770 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(14) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[830 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(15) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[890 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
hb(16) = uicontrol('Style','togglebutton','Units','pixels',...
'pos',[950 110 40 40],'HandleVisibility','off','Callback','hchange=1;');
%flags
quitit=0;
achange=0;
bchange=0;
cchange=0;
dchange=0;
echange=0;
fchange=0;
gchange=0;
hchange=0;
send=0;
done_send=0;
savedrum=0;
loaddrum=0;
cleardrum=0;
savemelody=0;
loadmelody=0;
clearmelody=0;
reset=0;
%saved patterns
drumpattern = [aout bout cout];
melodypattern = [dout eout fout gout hout];
% Output data
Fs=.5; per=1/Fs;
% Configure the callbacks
dio.TimerFcn = @dooutput;
dio.TimerPeriod = 1/Fs; %256 Hz
% Setup the userdata
data=[aout bout cout dout eout fout gout hout];
dio.UserData = data;
%Start the object
start(dio);
%Infinite GUI loop
drawnow;
while (~quitit)
%If any user input changes occur, update the corresponding array
if (achange)
aout=logical(cell2mat((get(ab(:),'Value'))))';
achange=0;
send=1;
end
if (bchange)
bout=logical(cell2mat((get(bb(:),'Value'))))';
bchange=0;
send=1;
end
if (cchange)
cout=logical(cell2mat((get(cb(:),'Value'))))';
cchange=0;
send=1;
end
if (dchange)
dout=logical(cell2mat((get(db(:),'Value'))))';
dchange=0;
send=1;
end
if (echange)
eout=logical(cell2mat((get(eb(:),'Value'))))';
echange=0;
send=1;
end
if (fchange)
fout=logical(cell2mat((get(fb(:),'Value'))))';
fchange=0;
send=1;
end
if (gchange)
gout=logical(cell2mat((get(gb(:),'Value'))))';
gchange=0;
send=1;
end
if (hchange)
hout=logical(cell2mat((get(hb(:),'Value'))))';
hchange=0;
send=1;
end
%save instruments corresponding to drums
if (savedrum)
drumpattern = [aout bout cout];
savedrum = 0;
end
%load from memory the instruments corresponding to drum
if (loaddrum)
aout = drumpattern(1:16);
bout = drumpattern(17:32);
cout = drumpattern(33:48);
set(ab(aout),'Value',1);
set(ab(~aout),'Value',0);
set(bb(bout),'Value',1);
set(bb(~bout),'Value',0);
set(cb(cout),'Value',1);
set(cb(~cout),'Value',0);
loaddrum = 0;
send=1;
end
%save instruments corresponding to melody
if (savemelody)
melodypattern = [dout eout fout gout hout];
savemelody = 0;
end
%load from memory the instruments corresponding to melody
if (loadmelody)
dout = melodypattern(1:16);
eout = melodypattern(17:32);
fout = melodypattern(33:48);
gout = melodypattern(49:64);
hout = melodypattern(65:80);
set(db(dout),'Value',1);
set(db(~dout),'Value',0);
set(eb(eout),'Value',1);
set(eb(~eout),'Value',0);
set(fb(fout),'Value',1);
set(fb(~fout),'Value',0);
set(gb(gout),'Value',1);
set(gb(~gout),'Value',0);
set(hb(hout),'Value',1);
set(hb(~hout),'Value',0);
loadmelody = 0;
send=1;
end
if (cleardrum)
aout=false(1,16);
bout=false(1,16);
cout=false(1,16);
set(ab(:),'Value',0);
set(bb(:),'Value',0);
set(cb(:),'Value',0);
cleardrum=0;
end
if (clearmelody)
dout=false(1,16);
eout=false(1,16);
fout=false(1,16);
gout=false(1,16);
hout=false(1,16);
set(db(:),'Value',0);
set(eb(:),'Value',0);
set(fb(:),'Value',0);
set(gb(:),'Value',0);
set(hb(:),'Value',0);
clearmelody=0;
end
%reset all
if (reset)
aout=false(1,16);
bout=false(1,16);
cout=false(1,16);
set(ab(:),'Value',0);
set(bb(:),'Value',0);
set(cb(:),'Value',0);
dout=false(1,16);
eout=false(1,16);
fout=false(1,16);
gout=false(1,16);
hout=false(1,16);
set(db(:),'Value',0);
set(eb(:),'Value',0);
set(fb(:),'Value',0);
set(gb(:),'Value',0);
set(hb(:),'Value',0);
reset=0;
end
%reassign data for periodic output from dio object
data=[aout bout cout dout eout fout gout hout];
dio.UserData = data;
drawnow;
end