Tag Archives: op-amp

The Great [Volume] Leveler (AVR Audio Compressor)

Like many people, I have fairly diverse taste in music.  My media library holds tracks from Bach, Beethoven, Billy Joel, Bonobo, Brent Lamb, Brahms, Brian Hughes, and the Bee Gees (to name just the “B” section).  I love variety.  The trouble is, all of these different genres tend to require slightly different volume settings.  Worse still, in the case of some classical music, you can get quite a wide range of volumes within a single piece (e.g. O Fortuna).  So if I hook up my BlackBerry and set it to shuffle, I find myself having to continually adjust the volume knob – either because I can hardly hear the current track, or because my neighbors are about to come banging on my door.

Well, I’m not the first one to have this problem.  Nor am I the only one to attempt to solve it.  In fact, it’s already been solved.  As you may know, there are plenty of software solutions out there for so-called volume leveling.  But before the advent of the BlackBerry, or the personal computer, there was the analog compressor.

The Buzzaudio SOC-1.1 Compressor

The sole purpose of such a device is to compress the dynamic range of an incoming audio signal: amplifying the soft parts and reducing the volume of the loud parts (thus decreasing, or compressing, the range of the track’s volume).  It’s quite a simple thing, really.  And you can buy one of these units for between $100 and $200.  But why buy something when you can build it from spare parts? 🙂

Now for all of you comp-sci majors out there who are planning on commenting on any one of a million different audio leveling programs out there, don’t worry, I know they exist.  The trouble is, even if I leveled every single MP3 I possess, what about internet radio?  Specifically, the Pandora app for my BlackBerry?  Alright, maybe there’s a software-only solution for that too (although I haven’t found it), but you know what?  Just for the fun of it, I’m going with hardware this time.  Well, mostly.  Actually, it’s going to be a mix of analog hardware and an 8-bit Atmel AVR running some embedded C code.  Sound good?  Alright, then let’s get started!

First Prototype: Powered by LabVIEW

As frequent readers of my blog will know, I’m a big fan of the LabVIEW programming language.  I can use it to whip up fairly complex data acquisition and processing applications in a matter of minutes (examples).  Much faster than writing code for an MCU.  So I decided to use it, along with my handy Mobile Studio IOBoard and Bus Pirate, for my first compressor prototype.  Before we get into the brains though, let’s talk about the guts (and by that I mean let’s talk about the analog circuitry which will be common between the LabVIEW and the MCU prototypes).

First off, we need some way of detecting the volume of our incoming audio signal.  To do this, you could simply connect your left and right audio channels directly to your ADC inputs.  Two problems with this though.  First, unless you’ve really cranked up your source’s volume, you may only see peak signals of around 100mV.  Such small voltages will mean poor resolution.  In other words, without some type of amplification, we won’t be able to detect fine changes in the input’s volume.

To counter this, I have added two operational amplifiers (op-amps, U1) which connect, via high-pass filters, to our incoming left and right audio signals.  Each op-amp increases the amplitude of its respective signal by about 50x (click to enlarge):

Audio Compressor: Analog Stage Schematic (click to enlarge)

The purpose of those two AC-coupling high-pass filters is to remove any DC offset from the input signals.  Without these filters, we would also be amplifying that offset, such that even a 0.1V bias would saturate this first set of op-amps (which are supplied by +5V and -5V).

Once amplified, there is one more thing we need to consider.  Since audio waveforms change rapidly, and swing between positive and negative voltages, our input signal may appear to change wildly between ADC samples.  What we need then is a circuit that can smoothly follow the peak amplitude of our input signal.  Kindof like an old VU meter.

Peak Hold Circuit Results (Blue = Input Audio, Green = Output)

The amplified input audio signal is shown above in blue, while the output from our “peak hold” circuit is shown in green (this plot displays 1V/div vertically, 50ms/div horizontally).  Just how does this work, you ask?  Well, scroll back up to my earlier schematic.  The key is the two sets of diodes (D1 and D2) and 10uF capacitors (C3 and C4) you see just past the first amplifier stage.  Diodes only allow current to flow in one direction (from left to right, in this case).  Thus, when the left channel amplifier’s output is greater than the voltage across C4 plus 0.35V (due to the voltage drop across the diode), C4 will be charged to a higher voltage.  Once the op-amp’s output drops, the diode D2 prevents C4 from discharging.  Thus, it “holds” the op-amp’s peak voltage.  However, because we don’t want C4 to hold this voltage indefinitely, I’ve added resistor R15 (R14 on the right channel), which gives C4 a path through which it may slowly (kindof) discharge.  The result is the scope plot above.

Now at this point, I could’ve stopped and wired both peak voltage outputs directly to my ADCs.  However, I would then have needed to read and average two voltages within my leveling program.  Since I’m on a hardware kick, I decided to simply sum the two using an inverting summing amplifier configuration (R7, R8, R9, U2).  Because this circuit’s output is inverted (negated), I then passed the signal through a unity gain (1x), inverting amplifier (R10, R11, U2).  Now we’re ready for measurements!  By the way, in case you have as much trouble as I do remembering what all of these op-amp circuits look like, you should download or bookmark this nifty guide from TI.  I use it all the time.

Audio Compressor First Prototype (Click to enlarge)

Before I move onto the brains of the device, I should explain how I intend to digitally control volume.  Recently, I acquired a Bus Pirate and a few SPI digital potentiometers.  My plan here is to use these potentiometers (P/N MCP42010), as 256-position voltage dividers.  They will be wired in-line with each audio channel.  You can see one of them (one IC containing two pots) shown as a green box in my earlier schematic.  The output from each pot will be fed into a buffer op-amp in order to keep the outputs strong (capable of driving headphones or line inputs).  For my initial LabVIEW prototype, I will be communicating with the potentiometers via an RS232 connection to the bus pirate.

LabVIEW Audio Compressor VI

Alright, so onto the brains!  [Insert zombie joke here]  The image above is a screenshot of my VI (virtual instrument, also known as a LabVIEW program).  As you might expect, it is quite simple.  I have a control for the potentiometers’ wiper positions, as well as indicators for the instantaneous and filtered ADC inputs.  The analog input is being provided by my old friend, the RPI IOBoard.  Its interface consists of just a few blocks on the diagram (for those of you unfamiliar with LabVIEW, what you see here is equivalent to written code):

LabVIEW Audio Compressor Block Diagram (Click to enlarge)

I have divided the program into two while loops (the large grey-border rectangles), each running at a 50ms rate.  The top loop handles communication with the bus pirate (initial bus pirate configuration I perform once, by hand, through the terminal).  This communication is quite simple, and consists of just two bytes.  The first byte is a command which tells the potentiometers to write the second byte to their wiper position registers.

The bottom loop is responsible for determining the appropriate wiper position, based on the incoming voltage measurements.  My logic is quite simple.  Perhaps you can determine it from the diagram alone?  Basically, I compute a difference between the current (instantaneous) voltage measurement and our last filtered value.  Then, if the current measurement is greater than the filtered measurement, I divide the difference by 40 and add it to the filtered measurement.  If the current measurement is less than the filtered measurement, I divide the difference (which is negative) by 240 and add it to the filtered measurement.  The result of this is that for loud sounds, the filtered measurement increases fairly quickly (within a second or two).  However, if the input volume drops, the filter will take perhaps five times longer to respond.

Once the filtered measurement has been computed, the appropriate wiper position is determined via a look-up table.  That table, when plotted, looks something like this:

Look-Up Table Graph

These values were determined empirically.  I simply listened to the circuit’s output for various inputs and adjusted the wiper position manually to achieve the best sound.  However, as you can see, it implements a log(x) function, as you might expect with an audio signal.  But rather than attempting to determine a precise formula for this function, as I am designing this for use on an 8-bit MCU, I chose the look-up table approach.

With my VI wired and ready to go, I proceeded to tune my filtering algorithm to provide leveling over the whole musical spectrum.  The results sounded pretty good.  But before I demonstrate anything, let’s talk about the final implementation using the ATMega328.

Second Prototype: Powered by Atmel

For my embedded prototype, I again chose an AVR microcontroller.  I’ve long been a fan of these chips.  They’re just so easy to work with, and quite powerful.  Implementing the SPI interface required just a few simple commands, since the protocol is natively supported by the ATMega’s hardware.  This particular chip also has a built-in, six-channel ADC, so that was no big deal either.  And since I’d already developed the algorithm and settings in LabVIEW, the most time-consuming part of this whole process was wiring up this lovely 10-segment LED bar graph.  Now I need more resistors…

AVR-Powered Audio Compressor (Limiter)

Although I would have liked two bar graph displays, I had only enough IO to fully support one of them.  So rather than just hard-coding the chip to display one value, I added a small button (you’ll notice it just to the left of the MCU, the largest IC) which allows you to select between displaying the instantaneous voltage input (VU meter mode) and the current wiper position.  This proved very helpful in debugging.

For simplicity, I’ve chosen to use the ATMega’s internally 8Mhz RC oscillator.  Timing isn’t very critical for this application, so I coded up a simple delay routine which works based on repeating an operation for a calibrated number of iterations.  I probably could’ve done a better (more generically useful) job on the look-up table too, but with only a few datapoints, I didn’t feel like spending much time on it.  Lazy me.  Anybody out there have some good code written for interpolating look-up tables? 🙂 Well, what I do have, you may download via the links at the bottom of this post.

Results

In order to demonstrate the performance of my device, I created an MP3 containing about 90 seconds of audio (music) at different volumes.  This file was played back on my laptop, whose volume setting was adjusted until the loudest sound produced just barely hit the maximum  input of the ADC.  The Audacity screens you see below show the original waveforms on the top and the “leveled” waveforms on the bottom (recorded via my laptop’s microphone input jack).  Enjoy:

As you can see and, hopefully, hear (sorry about my lousy sound card – the signal is much clearer than it sounds here) in the video above, the soft parts in the original track were amplified somewhat, while the loud parts were reduced in volume.  If not aurally obvious, I suspect the results are pretty easy to see in this screenshot from Audacity:

Audacity Demo Waveforms

Final Thoughts

Overall I think this project turned out alright.  I particularly liked the method of first prototyping with LabVIEW, the bus pirate, and my IOBoard.  This definitely sped up development of the final application and made debugging easier.  The only thing I wish is that I could use my VI to generate code for the AVR.  Such a thing is available, but only for 32-bit processors.  Oh well, maybe it’s time for me to upgrade by a few bits.

In the future, I may try amplifying the audio outputs.  Obviously, potentiometers by themselves can only reduce signals.  And that’s fine, as long as my input source is sufficiently loud on even the soft songs (because we can always cut the volume of the loud pieces).  But for even greater range, amplification would be nice.

You may wonder why I didn’t include any other adjustments, like an additional volume control, or knobs to adjust the filtering speed (these would be the attack and release knobs, which, by the way, are awesome names).  Well frankly, I wanted to keep things simple.  The point of this project was to reduce the amount of knob-turning I do.  If I left things adjustable, I’d probably be tweaking stuff all the time.  It’s just a compulsion.

Anyway, I hope you’ve enjoyed this project as much as I have.  As always, if you have questions, comments, or want help building something like this, please leave a comment!

List of Parts Used, with Prices  (Excluding Passives)

  • Bus Pirate ($29.95)
  • IOBoard (not readily for sale; you might be able to buy one off an RPI/Rose-Hulman student who doesn’t understand/care about the value of what the have)
  • MCP42010 Digital Potentiometer ($2.40)
  • TLC2272, 2.25Mhz, Rail-to-Rail, Dual Op-Amps ($1.83)
  • ATMega328 ($3.93)
  • LED Bar Graph, SSA-LXB10IW-GF/LP ($3.33)
  • LT1054, for generating the -5VDC rail ($1.62)

Downloads

Heatsink or Swim

Apparently I have a thing for testing.  I rather love to run experiments, even when there’s no immediate need for the results.  I guess I just enjoy trying stuff, and hey, maybe even learning a thing or two. 🙂

So last weekend I was doing a bit of tinkering and got to wondering about the performance of different heatsinks.  Now just intuitively, I know that larger heat sinks tend to dissipate more heat than smaller ones – particularly if they have larger surface areas.  But just how much better is a tall, finned heatsink than a small, clip-on device?  This is what I wanted to find out.  So I gathered up five sinks of varying sizes and started to design my test.

The Heatsink LineupPictured above, from left to right, we have the following parts:

  1. A small clip-on heatsink, 230-75AB, cost: $0.36
  2. A medium-sized, bolt-on heatsink, 7-340-2PP-BA, cost: $3.91
  3. A larger bolt-on heatsink, 531202B00000G, cost: $1.28
  4. A Pentium 4, Socket 478 CPU cooler, cost: ~$35.00
  5. A Crydom SSR heatsink, HE-54, cost: ~$25.00

I decided to test each of these sinks with a TO-220 package transistor (since, at least in the case of the first three devices, this is what they were designed to cool).  Specifically, I’ve chosen the IRL2703, an N-channel MOSFET rated for 30V and 24A.  Now, the TO-220 package is also commonly used with regulators, diodes, etc.  So these heatsinks might be found in a number of different applications.

In order to control the power dissipated in the transistor, I’ve built a very simple current-control circuit using an op-amp (LM741) and a 0.1Ω sense resistor (Rs):

MOSFET Current Control Schematic

I will be using the transistor as, essentially, a variable resistor connected to a 12VDC power supply.  The transistor’s drain-to-source resistance varies based on the voltage applied between its gate and source.  Control of this voltage is the responsibility of the op-amp shown in the schematic above.

If you can recall the “golden rules” of ideal op-amps, you’ll remember that, with negative feedback (that is, a path between the op-amp’s output and its inverting (-) terminal), the voltage at the op-amp’s inverting (-) terminal will be equal to the voltage at its non-inverting (+) terminal.  In the circuit above then, if Vin = 1V, after being passed through the potentiometer’s 10:1 voltage division, the voltage at the op-amp’s non-inverting (+) terminal, and consequently its inverting terminal, will be 0.1V (remember also that no current flows into either of these “ideal” terminals).

So notice that the op-amp’s inverting terminal is connected to one leg of our sense resistor, Rs.  So when our input voltage is 1V, the voltage across Rs will be 0.1V (remember the voltage divider).  By Ohm’s law then, the current through the sense resistor will be I = V/R = 0.1/0.1 = 1Amp.  So an input of 1V will yield 1A through the transistor.  With an input voltage of 12V then, we’ll be dissipating approximately 12W in the transistor (actually 11.9W, to be precise; we’ll lose 0.1W in the sense resistor).

For my experiments, I’ll be using a constant current of 0.5A, which will yield a power of 6W.  This seems to be a reasonable value to use with all of my heat sinks.  And it’s still not quite enough to completely toast a naked (no heatsink) transistor.

In order to measure the temperature of the transistor as the test proceeds, I’ll be using a Kintrex infrared thermometer, model IRT0421, which I’ve had for years.  My plan is to simply move the thermometer around the transistor, at close range (1-2″), and record its maximum reading.  Admittedly, this isn’t the most precise solution.  A better idea would probably be to attach a small thermocouple between the transistor and the sink.  But that’s something I don’t have.  My method should at least provide a good indicator of relative, if not absolute, performance.

My procedure here is quite simple.  First, I will attach a heatsink to the transistor using a small thermal pad.  I will then apply a current of 0.5A at 12VDC, and allow the transistor to reach a stead-state temperature.  I will then allow everything to cool, remove the thermal pad, and try the same test again.  Perhaps my video-self can explain this better:

Before diving into the results, here are a few more pictures of the circuit and setup.  By the way, in case you’re wondering what that silver pen-like device is that’s jammed into my protoboard next to the opamp, it’s my home-made scope probe.  I took an old BIC pen, removed the guts, and then epoxied in a dulled sewing needle and wire.  It’s actually a great little probe; I leave it connected to my IOBoard most of the time.

Current Control Circuit

This is how the transistor was attached to the Crydom heatsink:
Crydom SSR HeatsinkAnd for the Pentium 4 cooling package, a single 4-40 hole was tapped (note that this image has been flipped horizontally, for aesthetic reasons, so ignore the wire colors):

Pentium 4 CPU Cooler

Results

Alright, so I’m sure you can’t wait to learn how things turned out, right?  Well, without further ado, I present to you my grand table of results:

Heatsink Under Test
Temp @ 10 Mins.
Temp @ 40 Mins.
Rating
(Expected Temp)
NONE
152°C N/A 58.7°C/W
(373°C)
Clip-On Heatsink 136°C N/A 28.5°C/W
(192°C)
Bolt-On Medium Heatsink 93°C N/A 3.1°C/W
(40°C)
Bolt-On Large Heatsink 78°C N/A 7.5°C/W
(66°C)
Pentium 4 Heatsink 32°C
(no fan)25°C
(fan on)
38°C
(no fan)25°C
(fan on)
N/A
Crydom SSR Heatsink 28°C 31°C 0.9°C/W
(26°C)

(Note that these test were performed at an ambient temperature of 21°C.  The “Expected Temp” numbers are calculated by multiplying the manufacturer’s ratings by 6W, and then adding the ambient temperature.  Also, the smaller heatsinks were only tested for 10 minutes, as it only took this long for them to reach steady state.  The more massive heatsinks were given 40 minutes.)

So now you may be wondering, “Wait, what happened to the tests with and without the thermal pads in place?”  Well I’ll tell you: the difference in final temperature with and without the pads was insignificant (perhaps one or two degrees at most).  This was a little surprising to me, as I’ve always been told to be liberal with the thermal grease/paste (and these thermal pads serve the same purpose, they’re just cleaner).  So I figured the exposed side of the transistor would be somewhat warmer without the pads in place.  And yet, that does not appear to be the case.  But am I confident enough in this result to stop using thermal paste/pads?  Eh, not really.  I’d probably still use the pads, and I’d make sure the heatsink was firmly attached (since that makes for better conduction).

Another interesting point to note: the snap-on heatsink I tested here performed little better than operation without a sink (it yielded just a 13% lower temperature rise).

Strangely though, the snap-on heatsink was the only one that seemed to measure up to its manufacturer’s °C/W rating.  I calculate a value of (136-21)/6 = 19.2°C/W, which is less than the advertised 28.5°C/W (a good thing).  But neither of the bolt-on heat sinks met their advertised ratings.  The medium-sized sink reached just 12°C/W while the larger one hit 9.5°C/W.  I’m not sure what to make of this.  Is there something I’m missing here?  Perhaps an error in my calculation?  It sure seems straight-forward…

Well finally, I was rather pleased with the performance of the Pentium 4 cooler as well as with the Crydom heatsink.  I did expect both to do well though.  The Socket-478 Pentiums could produce about 60W, so you’d expect even a stock heat sink to be able to handle one-tenth of that power with little problem.  I was amazed at just how quickly turning on the fan brought down the temperature though.  Within just a few minutes the heatsink felt cool to the touch (having just been at about 38°C/100°F).

Conclusion

In light of this data, which of these heatsinks would I choose?  Well, for this transistor, the datasheet lists a maximum operating junction temperature of 175°C.  In the above tests, I’ve measured case temperatures, so we’ll need to factor that in.  Again, the datasheet lists a thermal resistance from junction to case of 3.3°C/W.  So when dissipating 6W, as in the tests above, the junction temperature will be about 20°C warmer than the temperature of the case.  So our maximum allowed case temperature will be 155°C.  You’ll notice that with no heat sink, the transistor reached a temperature of 152°C, so in theory, you could safely operate at 6W with no heatsink at all.  But should you?  No.  For one thing, stuff starts to smell nasty at that temperature.  Plus, continuously running hot will almost certainly reduce the operating life of your transistor.

In general, I’d suggest keeping transistors well below 100°C.  So in this case, I’d be comfortable with either of the two bolt-on heatsinks.  Anything more is overkill.

By the way, here’s a neat article on defacing currency some home-made heat sinks.

So, comments, questions, suggestions?  Feel free to leave them below.  Thanks!

Update (11/24/2011):

Christoph kindly pointed out in the comments section (below) that I’d forgotten to incorporate the dissipation of the TO-220 package itself when predicting case temperature values based on the heatsink specs.  Although not quite half of the transistor’s surface area is attached to the heatsink, the front is still sitting in open air.  Now, the datasheet specifies a thermal resistance of 62°C/W from junction to ambient.  I’m measuring case temperatures, so we need the thermal resistance between case and ambient.  To get this, I need to subtract out the junction to case resistance of 3.3°C/W, for a case to ambient package resistance of 58.7°C/W.

Now, instead of trying to figure out the thermal resistance for just the front of the heatsink (which would just be a guess, really), let’s see what the effect of adding in the transistor datasheet’s full thermal resistance has on our predicted temperatures.

As with electrical resistances, we can determine the combined thermal resistance of the heatsink and transistor by adding them in parallel, as follows:

R_tot = 1/(1/R_transistor + 1/R_heatsink)

Let’s apply this for the clip-on heatsink:

R_tot = 1/(1/58.7 + 1/28.5) = 19.2°C/W.

That’s a pretty big difference!  For 6W, this predicts a temperature of 136.2°C – much less than the 192°C I calculated above, and much closer to my measured value (136°C).  Here is a summary of the new predicted resistances and temperatures (I have NOT yet updated the table above with these new values):

Clip-on Sink: 19.2°C/W, 136°C
Medium Bolt-on Sink: 2.9°C/W, 38°C
Large Bolt-on Sink: 6.7°C/W, 61°C
P4 CPU Cooler: N/A
Crydom Sink: No Effective Difference (the supposed resistance of this heatsink is already so low, adding in the dissipation of the transistor itself makes no noticeable difference)

So making this correction helps with the clip-on sink, but it’s made things worse (at least by comparison with the tested value) for the two bolt-on sinks…

The other adjustment we could make would be to add the case-to-sink resistance of the transistor.  The datasheet lists this value as 0.5°C/W for a flat, greased surface.  Which in this case adds 3°C for 6W.  This could account quite well for the difference in measured values for the Crydom heatsink, but it doesn’t make a huge difference for the others.

Again, these are all pretty much approximations, and as I’ve already admitted, my testing procedure is not terribly accurate.  However, I do believe that it’s still a fairly good relative comparison of these sinks.  If I were measuring the temperatures of the sinks directly, yes, there would be differences in their emissivity, which would affect my IR thermometer.  However, I held the thermometer very close to the transistor and measured the temperature of the same point on the case of the same transistor in all tests.  So I’m not too worried about this.  Again, thanks to all for the comments!