Available Languages?:

OSA usage example: QUARTET


Program quartet synthesizes melody by given notes (it plays part of J.S.Bach N1067). Unpack file quartet4.rar into foulder "C:\TEST\QUARTET". This project is written for PIC16F628A.

This program generates 4-channel melody, using 8-bit PWM at 78 KHz. Music is played with four samples: bass, violin and two guitars (of course, my samples are very different from reality). Each channel can be switched ON or OFF by switches.

Here is MP3 (download), recorded from device's output.


The scheme of device is below. 8-bit digital signal from PWM through RC-filter R22-C3 and volume regulator R24 goes to headphones.


Switch SW1 switchs bass ON/OFF.
Switch SW2 switchs voilin ON/OFF.
Switch SW3 switchs first guitar ON/OFF.
Switch SW4 switchs second guitar ON/OFF.


Program consists of four files:

  • quartet_main.c - main program: tasks and notes working functions.
  • music.c - contains frequencies table and notelists for each channel.
  • sinus.c - contains three arrays with sinus data for each instrument (bass, violin, guitar).
  • interrupt.c - interrupt function. It contains code of synthesizer (called every 100 us).

quartet_main.c described below.

Program consists of 5 tasks. Four of them are "musicants", and fifth is "conductor" that sinchronize first four tasks.

Sound control

To control sound type TSound defined in quartet_main.c. Variables of this type contains all sound information for "musicant":

  • current note;
  • notelist;
  • is "musicant" in pause;
  • is "musicant" repeats fragment;

for "conductor":

  • when it must give next command to "musicant";
  • did "musicant" stop playing (reach end of nitelist)

for synthesiser:

  • current sound frequency;
  • time of playing current note (to form envelope for signal).


There are four "musicants" in program: Task_BASS, Task_VIOLIN, Task_GUITAR1, Task_GUITAR2. All time "musicants" wait for command from "conductor" (binary semaphore). When getting command, "musicant" reads next note from his notelist, plays it (task forms data for synthesizer).

Note produced in NoteWork() function, witch gets variable of sound control TSound in parameters. Using data of this variable function reads next note (or instruction: repeat, pause, ect.) and makes several operations to form parameters to synthesizer, in addition this function forms duration varianble. This variable decreases on every command from "conductor".

When "musicant" reaches the end of notelist, he informs "conductor" about it (task clears corresponding bit in flag variable flags_Playing). After that "musicant" begins to wait for "conductor's" command to restart music (binary semaphore BS_START). When "musicant" gets it, he goes to begin of notelist (sound control variable re-initializates) and sends "conductor's" command to next"musicant".


Taks_CONDUCTOR ("conductor") has priority lower then "musicants". "Conductor" counts ticks like metronome and looks to ensure that all "musicants" get commands at correct time. Task_CONDUCTOR gets control every 150 ms. On every execution it checks for music state. When "conductor" determines that all "musicants" stopped to play (all bits in flag variable flag_Playing are cleared), he sends command to "musicants" to restart musig from the begin.

Two words about synthesizer

Sinthesizer is placed into interrupt routine if file interrupt.c. Interrupt takes all sound control variables and forms digital signal. To make this work it has:

  • three tables of sound patterns for each sample: bass[], violin[], guitar[] (see sinus.c). This arrays contains one sinus period per each sample.
  • envelope formulas (see comments FORMING_SIGNAL_xxx): attack, release, ect.
  • macro for fast multiplication (char) = high( (signed char)*(unsigned char) ), to multiply sinus value by enveloping amplitude.

All four current values of sound are summed up, and then result is divided by four. After that result copies into m_cDAC variable. On next start of interrupt routine this variable will be copied into PWM buffer register CCPR1L (two least significant bits are copied into bits CCP1X and CCP1Y of CCP1CON register).

About notelists

Few information about notelist will help you to create your music.

File music.c contains definitions for notes: C0, C0_, D0, D0_, ect. There are definitons for 57 frequencies (four with half octaves). When we create note in notelist (arrays notelist_xxx) using macro play(), we can to set note only with 5 bits (32 notes). Therefore there is possibleness to set base octave (macro setbase()). Note duration is selected using only 2 bits, so only four durations can be set. If you need note with longest duration, you can use either macro pause() (pauses sound by several ticks) or macro playmore() (continue playing sound for several ticks).

Notelist must be ended with macro stop() to avoid reading unitializes memory cells.

en/osa/ref/appendix/quartet.txt · Last modified: 07.10.2010 13:58 (external edit)
Creative Commons License Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki