License Free midi synthesizer in C

Go To Last Post
29 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is not directly GCC related is C related and it's a bit of a random shot but...

I've already written code to play basic beeps of varying pitch and volume, but they way I use timers for these beeps limits me to one beep at a time.
I'd like to expand this to something more complicated, i.e. mix sounds so I can (in effect have multiple sounds playing at once). I'm not after replicating actual instruments just basic beeps.

I'm after some license free, e.g. GNU/GPL or just general hobbyist code in C to achieve this.

Now unfortunately, music/audio is not exactly my area of expertise (that may be putting it politely :wink: ). So I'm not sure if this is just something too complex! (and also I'd struggle to get something of my own going).

Ben
-Using IAR (& ocasionally CodeVision)
0.7734
1101111011000000110111101101

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So how do you use timers then? What is the output, is it PWM?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jepael wrote:
So how do you use timers then? What is the output, is it PWM?

The existing method I use is a fairly basic PWM using a 16 bit timer.

I use a hardware counter. This counts between 0 and a TOP value (no down-count). (TOP is selected based upon the tone/frequency I want (e.g. G# / 3322.4 Hz). I then user a compare/match register which sets/clears a hardware pin connected to an external buzzer. The Value put in the compare/match register is selected based upon the volume I want to achieve.

This works well enough for basic single tone sounds - but I can't envisage how this could be easily expanded to multi-tone sounds (but I'm not exactly a sound/audio expert).

Ben
-Using IAR (& ocasionally CodeVision)
0.7734
1101111011000000110111101101

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So at present you are playing one stream of PCM. If you make a second PCM generator then to combine them you just sum the PCM values though this may overflow so you might find that you first need to scale the sample streams - possibly as simple as A>>1 + B>>1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Two PWM outputs can be combined in a resistive "mixer".

There are, however, LOTs of limitations using raw PWM as a tone generator (not talking about using high frequency PWM as a DAC to synthesize a complex waveform, yet). One limit is no amplitude control. Another is the somewhat jarring (to some ears) square-wave. Another is the limited resolution on pitch (frequency). If these are not an issue in your situation, then charge ahead!

Another option to create polyphonic tones (I think that is the correct term) is to create the waveform you want out of filtered PWM. You can create almost any waveform you want (within the limits of the PWM clock rate and count size and filter). This includes multiple tones AND varying amplitudes. Once you free yourself from the limits of raw PWM, you can even do things like "dithering" the the sample rate to get tones that are not integer divisors of the MCU clock frequency.

By the way, this current thread in the AVR list may apply:

https://www.avrfreaks.net/index.p...

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another good source of information is to google "Microsoft Wavefile Format", "Audio Wave" etc...

Basically you are running the data section of a RIFF through a PWM.

Look for c, c++, and .net code examples. C examples would probably be the easiest to conform, but other languages still will help you and be more plentiful .

After all your main concern would be mathematical formulas and structure.

Bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No, this is not PCM. The OP uses raw output of PWM in audible frequencies as square-wave tone.

As Jim said above, it is possible to mix in "analog" way the outputs of multiple timers, or multiple outputs of a single timer (there are two output compare units in one timer).

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

BenG wrote:
Jepael wrote:
So how do you use timers then? What is the output, is it PWM?

The existing method I use is a fairly basic PWM using a 16 bit timer.

I use a hardware counter. This counts between 0 and a TOP value (no down-count). (TOP is selected based upon the tone/frequency I want (e.g. G# / 3322.4 Hz). I then user a compare/match register which sets/clears a hardware pin connected to an external buzzer. The Value put in the compare/match register is selected based upon the volume I want to achieve.

This works well enough for basic single tone sounds - but I can't envisage how this could be easily expanded to multi-tone sounds (but I'm not exactly a sound/audio expert).

This is usually the first thing to generate tones, so good thing is you have now mastered it.

Now next step might be 8-bit PCM sound. So 8-bit bytes represent a range of values from -128 to +127 and sampling rate (update rate) might be for example 8kHz. This is almost telephone quality.

Next step is to play a simple tone like sine wave or 8-bit wav file. You can play it with 8-bit timer using PWM. As an example, if you use 8MHz AVR, you can get a sampling rate of 8MHz/256=31250Hz, which is high enough you can't hear it and can be easily filtered out. The point is to update PWM duty rate (compare register) when you want - for example the 31250Hz can be divided by 4 to get near 8kHz, or you can setup another timer at any rate you want to update the PWM duty.

Well, anyway, then you have to create some waves. Square is nice, but you can easily generate sine or more complex tones, even use FM synthesis. DDS is one method, increase a counter (phase accumulator) by some value (speed) for every sample, and use some counter bit as output, which might be +127 or -128 for square wave. Or use a waveform table like sine, and index it with the counter.

Do this for all square waves you want to generate, and sum them together. This is called mixing. Note that if you generate your square waves so that their combined amplitude never exceeds +127 or -128, for example four square waves at +/-31, their sum will be within clipping limits, and you don't have to divide the result to fit into 8-bit values.

Someday, when I have infinite time, I'll implement simple FM synthesis operator with AVR. Volume, ADSR envelope, feedback...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't quite understand this obsession with PWM instead of proper DAC, especially when it comes to audio. Is a dozen or two of resistors really that much of an investment, even for a hobbyists?

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just because PWM is easy.

I agree, R2R DAC is better, but I don't think either of them counts as a proper DAC :)

A proper DAC for audio would most likely be a chip, taking 8 parallel lines or fast SPI bus in and have buffered output.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, sure, a true DAC is the real thing; just not that often to be found in the hobbyists drawer... ;-)

Back to the sound itself, depending on the exact purpose and available resources (i.e. processor time to burn), there's also an intermediate way: simulating several (say, three) square-wave "generators" with a slow envelope, plus a noise generator. This is the basis of legendary AY-3-8910/12 and other similar chips of the era; the resulting sound is that arcade-style chirping, which might quite well be what is required. The software overhead is not that demanding as with a full FM synthesis or even wavetables.

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek: Wrote

Quote:
Well, sure, a true DAC is the real thing; just not that often to be found in the hobbyists drawer...

Here is a cheap enough SPI driven dac that fits just about any body's budget MCP4921. The MCP4921 is a 12 bit dac but they also have them in other bit configurations at a price that wont bust you out.

Bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
This is the basis of legendary AY-3-8910/12 and other similar chips of the era;

There is even one project that uses an AVR as a plug-in replacement for a Commodore 64 SID sound chip. Too bad there are a lot of undocumented stuff how it works on the digital side of the chip, and also there are analog parts (switching capacitor filter) that cannot be emulated well digitally. Also other analog aspects of the chip are hard to solve.

Speaking of SID, they only recently discovered the undocumented sound possibilities of an earlier home computer, Commodore VIC. The sound is produced by rotating and inverting bits around in a 8-bit shift register with some user specified clock divider, so it is able to produce square waves at different frequencies, but what was discovered that the user could use tricks to pre-set the contents of the shift register manually, so several different waveforms other than square wave is possible.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

No, this is not PCM. The OP uses raw output of PWM in audible frequencies as square-wave tone.

In which case the data samples being loaded into OCR are most definitely PCM data. I don't understand why you think this is not the case? PWM is being used to play PCM data.

Here's one I prepared earlier:

https://www.avrfreaks.net/index.p...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

In which case the data samples being loaded into OCR are most definitely PCM data. I don't understand why you think this is not the case? PWM is being used to play PCM data.

Even I did not think of it as PCM first, but it is just PWM tone generation with the period of the single tone, and duty defines the volume. So it is a special case of PCM if you twist your mind to it, and the sampling rate is always same as tone frequency. Largest volume achieved when duty=50%, minimum volume at 0% and 100%.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks guys for watching my back on this. I'm still to new here to flat out argue. Even when I know I'm right. :)

Bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Even I did not think of it as PCM

I'm just going simplistically by the very fact that you either use Sound Recorder to record direct to PCM in a Wav file or use an editor to convert a more advanced audio format to raw 8bit mono PCM. You then transfer the data to an AVR and stuff the samples into OCR on an overflow interrupt at the original sample rate and you've then got the same audio on the PWM output pin. Simple as that. So, yes, you are playing PCM.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Who is "you", Cliff?
The OP did not what your hypothetical "you" did or is supposed to do.
He simply plays a square wave. A beep, if you want. Using the PWM facility of timer at audio frequencies, rather than as a DAC.

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Anyways, I wanted the OP to do a little bit of the research himself to find that there is a lot of information on this subject and is kind of in line with what I've been working on for a few months. I think this will give you a good push in constructing your own Lib.

http://www.codeproject.com/KB/audio-video/CPIAudio.aspx

It is a C# application that creates wave files from nothing. I am not going to get into the exact direction that I am going with, but when examining the code notice how the author sets up his classes to handle note generation. Using overtones etc... To play songs.

Anyways JW, I think when the OP places a question as open as this and states they do not know very much about the subject that this leaves things wide open for hypothetical oppositions. After all the OP is seeking knowledge.

Cliff, I think you help a lot of people on the forum. I know you've helped me with a stupid question or two I've had in the past.

So keep right on typing!!!

Bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:

He simply plays a square wave. A beep, if you want. Using the PWM facility of timer at audio frequencies, rather than as a DAC.
JW

It depends on the viewpoint. Square wave yes, beep yes, timer at tone frequency yes, not as a DAC.

But also the pulse width is modulated to create volume.

So if definition for PCM is to vary pulse width to modify speaker output based on PCM data at some sampling rate, this is PCM.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also, keep in mind that the OP stated

Quote:

I'd like to expand this to something more complicated, i.e. mix sounds so I can (in effect have multiple sounds playing at once)...

My guess would be that the OP is looking for more than to...

wek wrote:

Quote:

He simply plays a square wave. A beep,...

After all he does say

Quote:

... I'm not after replicating actual instruments just basic beeps.

But sustained beeps are notes, but notes do not have to sound like instruments. The note and how it sounds all depends on how the wave is constructed.

The OP goes on to say

Quote:

I'm after some license free, e.g. GNU/GPL or just general hobbyist code in C to achieve this.

Be it from PWM or DAC to convert the signal back to analog is a matter of preference. Mine would be DAC.

The real matter is the mathematical formula that will be used in a function to generate a PCM sample that is to be used for the so called beep. Once this is developed the OP can then create the Library he is seeking to embed into his AVR.

But as far as I've seen their is no "C" Library in existence for this. This is something the OP would have to create.

Bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know if anyone has written a library for generating PCM samples for square wave tones.

And why would anyone, since all it takes is to toggle a value after every X samples when sampling rate is Y, for starters.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I too don't know why the OP wants a library for this. But he does ask for it.

The OP does state that he would like to generate more complex tones.

Maybe this is why he wants a library???

Bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wek wrote:
Who is "you", Cliff?
The OP did not what your hypothetical "you" did or is supposed to do.
He simply plays a square wave. A beep, if you want. Using the PWM facility of timer at audio frequencies, rather than as a DAC.

JW


Jan,

I know that but I was justifying my assertion that even if it's a simple beep with PWM or a complex waveform that adjusts the PWM on every 1/nth of a second at the sample rate it can still be considered PCM and if the OP now wants to move from monophonic to polyphonic then to combine two sound channels you just add the PCM samples (with a possible attenuation filter first to avoid clipping). That is all.

As others have now noted this would mean that the simple case would need to be expanded out to time divide even a square wave into 1/nth second samples (though it's just going to be alternating on/off samples). These could then be mixed with a more complex waverform such a sine or triangle or something even more complex.

Cliff

PS Completely off topic but if anyone has an iPhone/ITouch I HIGHLY recommend you download the two free apps "mobilesynth" and "NlogFree". Two analog synths in the palm of your hand!

http://appshopper.com/music/nlog...
http://appshopper.com/music/mobi...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

WLS wrote:
Anyways JW, I think when the OP places a question as open as this and states they do not know very much about the subject that this leaves things wide open for hypothetical oppositions. After all the OP is seeking knowledge.

Basically yes, I'm after understanding. Writing code to (using a technical term) wiggle output lines, isn't a issue. It's knowing when to wiggle the line and at what frequency. It's partially a question of constructing complex audio sounds and partially a question of implementing that.

WLS wrote:
I too don't know why the OP wants a library for this. But he does ask for it.

The reason I asked for that is I hoped to be able to see how it is done. Perhaps my question would have been better asked if I'd asked "How Do You Do It?".

I am enjoying reading the responses so far, (and hopefully learning something as well).

Ben
-Using IAR (& ocasionally CodeVision)
0.7734
1101111011000000110111101101

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hmm, for starters, I'd change to 8-bit timer. When AVR speed is 8MHz, and timer prescaler is 1, the 8-bit timer will overflow every 256 counts so sampling rate Fs=8e6/256=31250 Hz.

In overflow interrupt, reload the timer compare register from a volatile uint8_t variable for example MixedOutput. So this happens at 31250 Hz. Set MixedOutput initially to 0x80, halfway, to generate silence (or actually you generate 31250Hz square wave, but you are supposed to have an RC filter in the output, so the filtered output is VCC/2).

Then like before, you toggle the output after suitable amount of time, here to get about 440Hz tone, toggle it every 35 samples.

The point is, now you can have for example four separate "software" timers or counters, that toggle their value after some period of time. With four tones, a single tone can have a volume of 31, toggling between values +31 for positive and -31 for negative. Sum all four together to get MixedOutput value.

volatile uint8_t mixout;
volatile int8_t tonevol1;
volatile int8_t tonevol2;
volatile int8_t tonevol3;
volatile uint8_t toneperiod1;
volatile uint8_t toneperiod2;
volatile uint8_t toneperiod3;
volatile uint8_t tonecounter1;
volatile uint8_t tonecounter2;
volatile uint8_t tonecounter3;

void timer_overflow_interrupt(void)
{
    // update timer hardware immediately
    OC1=mixout; 
   
    tonecounter1++;
    if (tonecounter1>toneperiod1) // period sets freq
    {
        tonecounter1=0;
        tonevol1=-tonevol1; // wiggle between +vol and -vol
    }

    mixout=0x80+tonevol1+tonevol2+tonevol3;
}

int main(void)
{

    tonevol1=31; // max vol
    tonevol2=0;  // silence, means tone stopped
    tonevol3=0;  // silence, means tone stopped

    toneperiod1=35; // 440Hz tone : 31250/440/2=35 

     // start timer

    while(1)
    {
    }

}

This generic idea works for generating few square waves with limited precision. When you have it working, come back and then let us change the counters to 16-bit DDS, where counter has a fixed increment and perhaps the MSB is used for square wave.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I agree, 8-bit counters will give you the minimum amount of clock cycles used allowing for maximum frequency response and are the simplest to start with.

Here's a link I think you'll find to be of intrest it incorporates the bases of all sine waves by using the Math.Sin() function.

http://blogs.microsoft.co.il/blogs/tamir/archive/2008/02/17/sound-tone-and-dtmf-generation-by-using-managed-directsound-and-c-and-sine-tone-detection-with-pure-managed-goertzel-algorithm-implementation.aspx

It speaks of construction from a simple monophonic sine wave with some math and then gets into DTMF which is a prime example of a polyphonic tones.

Bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ben,

Just one question about your hardware.

What AVR are you running?

If you're running a ATmega168 you can crank it up to 20 mhz. This would allow for a faster sample rate.

Bill

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm new to AVRs (and programming in general,) but this sounds exactly like what I need for a synth project. I need to parse the MIDI input into an amplitude value (which is fed out as a constant DC level to a big pile of analog stufF), a frequency value (which is also fed out at a constant DC level to a big pile of analog stuff), and a nice, neat square wave - ideally, six times over on the same AVR, though I'd be open to using two of them.

Can anyone make any comments on my project idea? Square waves sound pretty awful, but with the right sort of analog circuitry, you can make them into almost anything.

Incidentally, if anyone wants a sine wave with minimal muss or fuss, look at the XR2206 function generator IC. By switching a few components in or out of the circuit, it generates a square, sawtooth, or sine wave with a frequency proportional to an input voltage, easily generated through PWM. They're a bit pricey at $6/each, though.