Measuring amplifier output power (watts)

I have an audio amplifier (TPA3122, Class D amplifier: http://www.mouser.com/ds/2/405/slos527a-88277.pdf) and I wish to use an Arduino to measure its output power to make sure I'm not feeding too much into my speaker. It doesnt have to be exact, a ballpark estimate will suffice (10-20% error may be acceptable). I'm new to signal processing and I don't know where to start. This value is intended to be put on a display, so it doesn't need to update very fast. Once or twice per second is good.

I've hooked up a circuit according to the Simplified Application Circuit in the datasheet using only the left output for sigle-ended mono and had it succefully play music from my phone.

I don't know quite where to start with this. If I just hook a low-pass filter to the output of a large time constant, it'll just smooth it out to the DC level. Make it less, and I might be able to follow the audiop waveform, but I don't know how fast the ADC can work, if it'll catch stuff without aliasing problems.

I have plenty of op-amps, resistors, caps, and diodes I can use. I just need some guidance.

My end game for this project is to create a digitally controlled amplifier+speaker in a box that I can use to play music from my phone's 3.5mm jack, with possibly some equalizer filters like bass/treble boost and such. For that, I need to know what size speaker I need for a given loudness, as well as possibly have software control to turn down the volume if the power is too high. It's still in the planning stages right now.

Measuring "power" output will not help you get to your goal.

You can have a 1000 Watt amp and 100 Watts speakers and never blow anything. Having a 100 Watt amp, and 1000 watts speakers usually ends in destroyed speakers.

When you overdrive your system, you will get high frequency oscillation which destroys the speakers. You could do some FFT analysis to see, if something is being overdriven.

// Per.

For that, I need to know what size speaker I need for a given loudness,

Size does not (directly) determine what the 'loudness' of a specific speaker is when driven with a specific reference power value. Even a speakers 'maximum power rating' does not tell you how 'loud' that speaker will be at it's maximum power output. Better speaker manufactures will publish a specification called SPL (specific power Vs loudness) and it's a direct indication of the efficiency of that specific speaker, that is how much signal voltage will generate how much absolute sound level.

Speakers are not always or even usually selected by either their maximum power rating or their SPL rating alone, as speaker frequency response 'flatness' is an even more important specification as it's often more important that a speaker be able to respond accurately for best sound reproduction.

Full range 20-20,000 Hz 'hi-fi' single speakers are rather hard to obtain and why most quality hi-fi speakers have multiple speakers inside their box often called woofer, tweeter, mid-range etc. This then requires a passive cross-over filter so the audio power will be automatically directed preferentially to each driver so that the over all audio output sound is 'flat' across the audio frequency range desired. More expensive/quality hi-fi speakers often use fuses to protect the driver voice coils from over-driving them.

And of course the box one mounts the speaker(s) has as much to do with the accurate frequency response and efficiency as well as it's size and constructed (ported Vs sealed) must be 'tuned' or 'matched' to the speaker specifications, esp the woofer.

Audio reproduction is a mature technology with tons of information available on the web. While the state of the art in audio electronics has continued to progress (class A, AB, D, etc) speaker tech is pretty mature and known. As in most things quality results are often directly related to costs.

Lefty

Zapro:
You can have a 1000 Watt amp and 100 Watts speakers and never blow anything. Having a 100 Watt amp, and 1000 watts speakers usually ends in destroyed speakers.

You'll have to explain that some more, since it doesn't make any sense at all to me.

@ retrolefty:
By "size" I was not referring to physical size, but rather the power rating. Right now the only speaker I have is a generic 0.5W 8 ohm speaker like this one from Sparkfun: Speaker - 0.5W (8 Ohm) - COM-09151 - SparkFun Electronics. I'd like something a bit louder, maybe in the order of a few watts or so.

I'm not really looking for a hi-fi, just something that's an improvement (in loudness and possibly quality) over my phone's speaker. I'm not an audiophile, so little imperfections are fine.

The primary reason I'm doing this though is because it seems interesting and I want to learn things.

The fact that there's tons of information can be a negative as well as a positive, since a lot of information online is garbage. Do you know some good sites you'd recommend?

In terms of the actual measurement, I've been doing some research of my own. I've put the Arduino's ADC is free running mode with a prescaler of /64. With that setup and my ISR, I'm getting about 18,500 conversions per second. I've hooked a low pass filter up the the output according to the attached schematic that also attenuates the signal by 11. This lets me use the internal reference instead of AVcc, since I'm powering the TPA amplifier with 12 volts for now.

My ISR fills a large ring buffer with the ADC reading.When it wraps around back to the beginning, it calculates the RMS voltage of the values in the ring buffer (referenced to the DC center) and stores that in another ring buffer that is used to average out short scale bursts of power.

Sketch:

unsigned long timer = 0;
unsigned long ADC_count=0;

#define RING_BUFFER_SIZE 500
volatile int ring_buffer[RING_BUFFER_SIZE];
volatile unsigned int ring_buffer_index = 0;

#define RMS_BUFFER_SIZE 50
volatile int rms_ring_buffer[RMS_BUFFER_SIZE];
volatile unsigned int rms_buffer_index = 0;

#define ADC_REF_MVOLTS 1100UL


void setup()
{
  Serial.begin( 19200 );
  
  ADCSRA = _BV(ADEN) + // ADC Enable
            _BV(ADATE) + // ADC Auto Trigger Enable
            _BV(ADIE) + // ADC Interrupt Enable
            0x06; // /64 prescaler, ADC does approximately 18,500 conversions per second.
  ADCSRB = 0; // Set ADC to freerunning mode
  ADMUX =   (0x3<<6) +  // 1.1 V Reference
            0;          // Channel 0
  
  ADCSRA |= _BV(ADSC); // Start first conversion.
  delay( 50 ); // Delay a little bit to fill the buffer.
}

void loop()
{
  unsigned long t_now_ms = millis();
  
  if( t_now_ms - timer > 1000 )
  {
    timer = t_now_ms;
    noInterrupts();
    int rms_average = average_array( rms_ring_buffer, RMS_BUFFER_SIZE );
    unsigned long temp = ADC_count;
    ADC_count = 0;
    interrupts();
    
    Serial.print( "RMS: " );
    Serial.println( rms_average );
    Serial.print( "RMS (mV): " );
    int speaker_rms = unscale_voltage_divider(adc_raw_to_mv(rms_average));
    Serial.println( speaker_rms );
    Serial.print( "Output (mW): " );
    Serial.println( (unsigned long)speaker_rms * speaker_rms / 8000 );
    Serial.print( "ADC passes: " );
    Serial.println( temp );
    Serial.println();
  }
}

ISR(ADC_vect)
{
  ADC_count++;
  ring_buffer[ring_buffer_index] = ADC;
  ring_buffer_index++;
  if( ring_buffer_index==RING_BUFFER_SIZE )
  {
    ring_buffer_index = 0;
    int average = average_array( ring_buffer, RING_BUFFER_SIZE );
    int rms = rms_array( ring_buffer, RING_BUFFER_SIZE, average );
    rms_ring_buffer[rms_buffer_index] = rms;
    rms_buffer_index++;
    if( rms_buffer_index == RMS_BUFFER_SIZE )
      rms_buffer_index = 0;
  }
}

int average_array(volatile int array[], int size)
{
  long sum = 0;
  for( int i=0; i<size; i++ )
    sum += array[i];
    
  return sum/size;
}

int rms_array(volatile int array[], int size, int zero_level)
{
  long sum = 0;
  int val;
  for( int i=0; i<size; i++ )
  {
    val = abs( array[i] - zero_level );
    sum += (unsigned long)val * val;
  }
  
  long square_mean = sum / size;
  
  return sqrt( square_mean );
}

int adc_raw_to_mv(int raw)
{
  int result = raw * ADC_REF_MVOLTS / 1024;
  return result;
}

int unscale_voltage_divider( int output )
{
  return output*11;
}

Serial output:

RMS: 131
RMS (mV): 1540
Output (mW): 296
ADC passes: 18544

RMS: 137
RMS (mV): 1617
Output (mW): 326
ADC passes: 18539

RMS: 148
RMS (mV): 1738
Output (mW): 377
ADC passes: 18564

RMS: 133
RMS (mV): 1562
Output (mW): 304
ADC passes: 18584

Is this algorithm the right approach? I know I could probably make this more efficient, but it's a start.

it calculates the RMS voltage of the values in the ring buffer

The RMS power is not a measure of loudness.
I did a consultancy years ago for some one who owned a night club. He had bought a sound system of a certain wattage and he complained it did not sound loud enough. He got me and another guy in to measure what his system could produce.
We had to explain the various ways of measuring power and how none of them were directly related to perceived loudness. But perhaps peak music power is the closest but note it is not the same as RMS. Normally it produces a higher number and it has been miss-used in advertising.

I'm not trying to electronically measure "loudness", I'm trying to measure the power going into a speaker to ensure it stays within the speaker's limit. I'll be judging the approximate loudness I want with my ear.

And you mentioned peak power as being a better measure of loudness than RMS power, but this post (Auphonic Blog: Audio loudness measurement and normalization with EBU R128 (Calm Act, ATSC A/85)) says:

Instead, audio productions were (and still are) normalized to peak levels, which do in no way determine how loud a signal is.

(Emphasis mine)

I spotted at least 2 flows in design. First one in hardware, what makes you think 10k resistor and 22nF capacitor has a cut-off 8 kHz?
Second one in software, your math is absolutely wrong, calculation of averages before you get RMS doesn't make any sense.
DC offset needs to be subtracted at first stage, than you do RMS , as RMS = sqrt( V1^2 + V2^2 + ....+ Vn^2), and you may average after that

Magician:
I spotted at least 2 flows in design. First one in hardware, what makes you think 10k resistor and 22nF capacitor has a cut-off 8 kHz?
Second one in software, your math is absolutely wrong, calculation of averages before you get RMS doesn't make any sense.
DC offset needs to be subtracted at first stage, than you do RMS , as RMS = sqrt( V1^2 + V2^2 + ....+ Vn^2), and you may average after that

There's a voltage divider formed by the 10k and 1k resistor. Rearrange that into the Thevanin equivalent and it's a 900 ohm resistor in series with a Vin/11 voltage source. 900 ohms and 22nF has a cutoff of around 8kHz by my math.

Software wise, the calculation of the average of the ring buffer is the DC offset. I use that value in the RMS function to subtract from the measured value.

One of the speed improvements I could have is to calculate the DC offset at the beginning with the amplifier muted (50% duty cycle). This was just a proof-of-concept sketch to make sure I got the algorithm right.

Right, this small resistors always bug me.. 8)
Sampling 18k, you have about 50 usec between samples, it's mean doing average 500 would take ~ 1 millisec or so, AFAIK summ long about 2 usec each. Than you taking RMS, where at least 20 samples skipped. More during other math, so it's not going to be accurate, there is no real-time?.
"abs" is not necessary.
What is the board?

The RMS calculation happens inside the ISR. Even if the ADC is still free running, it won't update the array until everything is done.

Just tested the RMS calculation time with a for loop on another sketch, with a buffer that size it takes 2.3 ms to do the calculation. So I'd miss out on 2-3 ticks of millis() during that time. Is that what you're concerned about?

This is just on my breadboard right now. I don't have this built up on a PCB or perfboard yet.

Jiggy-Ninja:

Zapro:
You can have a 1000 Watt amp and 100 Watts speakers and never blow anything. Having a 100 Watt amp, and 1000 watts speakers usually ends in destroyed speakers.

You'll have to explain that some more, since it doesn't make any sense at all to me.

If you have a heavy load on a wimpy amplifier, you will overlad the amplifier at some point when someone cranks the volume too high. Then the power supply in the amplifier will not be up to the job, and you will get distortion - which destroys your speaker and your ears.

// Per.

This is just on my breadboard right now. I don't have this built up on a PCB or perfboard yet.

Than what is the chip?
Regarding timing, my concern is the accuracy, you can get any precision skipping samples, 2-3 msec out of ? 500/18000 = 27.77 msec. There is no logic to calculate DC offset in each frame, if results of by 10%. Better approach is HPF inside ISR to have deal with varying DC.

@Zapro: Wouldn't the load the speaker resents depend on it's impedance, not wattage?

@Magician: Chip is TPA3122, which is linked in my first post.

The DC offset doesn't vary that much. I could calculate it once with the speaker on mute and be done with it.

What is HPF? High Pass Filter? I assume a software implementation. Wouldn't that take even more time?

@Magician: Chip is TPA3122, which is linked in my first post.

I mean arduino a-like chip, what you have to run a code? AtMega328?

What is HPF? High Pass Filter? I assume a software implementation. Wouldn't that take even more time?

Could be, the question is what factors you estimate/ considered more important for accuracy, and what less.
Obviously, arduino (not DUE) is not capable to run 20-20000 power measurements precise, so you 'd need to sacrifice something but keep accuracy 1-3% , which is as good as most multimeters have.
Factors are: sampling rate; tracking DC offset; rounding errors - do math in 16 or 32 or even 64 bits, flatness over full audio band .
From mine point of view, leaving a 10 % gaps in sampling is a big error, especially accounting that samples goes squared (^2) and error goes up to 30%. So I would run code real-time, but sacrificing DC tracking, for example doing DC calibration ones in while (month/year or by request, keeping data in EEPROM).

I took a quick look at some speaker protection info here. Since you only need a ballpark estimate of power and the thermal time constant of a voice coil is relatively slow, I think power estimation, thermal protection and AGC could be done with a sample rate in the range of 10-20 sps and using an RC filter of around 50-100ms. This could simplify your code and processing demand in your project.

@ Magician: I'm running an Uno, so ATmega328p. I also have a Leonardo, but I doubt I'll be able to push that any faster since it's clocked the same.

I'm aware I won't be able to get full 20 - 20,000 readings. That's why my compromise is at 18,000 - 19,000 readings per second, which gives just a slight oversampling of the Nyquist rate for my 8,000 Hz low pass filter. 8,000 Hz is an ear-piercing shriek, and I don't think most music goes that high anyway. The sacrifice I've chosen for this algorithm is to drop some samples while calculating the RMS voltage of the buffer, as well as some of the higher frequencies which might not even be significant to begin with.

This speaker won't be running for months or years at a time, so I think finding some way to calculate the DC offset at startup is probably best.

@dlloyd: If I make the RC filter that low, won't it just average everything out to the DC offset? That would be useless to measure.

Last year I did a project, audio VU meter. Same arduino UNO, sampling rate 40 000 , calculates RMS and dB.

#include <glcd.h>                     // http://playground.arduino.cc/Code/GLCDks0108
#include <avr/pgmspace.h>

#include "fonts/allFonts.h"           // system and arial14 fonts are used
#include "bitmaps/allBitmaps.h"       // all images in the bitmap dir 

#define  SMP_RATE                   40       // Sampling Rate, in kHz
#define  SMP_TMR1 ((16000/SMP_RATE) -1)      // Sampling Period of Timer1

/* VU Meter / The audio level meter most frequently encountered is the VU meter. Its characteristics are 
defined as the ANSI specification C165. Some of the most important specifications for an AC meter
are its dynamic characteristics. These define how the meter responds to transients and how fast the reading 
decays. The VU meter is a relatively slow full-wave averaging type, specified to reach 99% deflection in
300 ms and overshoot by 1 to 1.5%. In engineering terms this means a slightly underdamped second order 
response with a resonant frequency of 2.1 Hz and a Q of 0.62. 
While several European organizations have specifications for peak program meters, the German DIN specification
45406 is becoming a de facto standard. Rather than respond instantaneously to peak, however, PPM specifications re-
quire a finite “integration time” so that only peaks wide enough to be audible are displayed. DIN 45406 
calls for a response of 1 dB down from steady-state for a 10 ms tone burst and 4 dB down for a 3 ms tone burst.
These requirements are consistent with the other frequently encountered spec of 2 dB down for a 5 ms burst and
are met by an attack time constant of 1.7 ms. The specified return time of 1.5s to ?20 dB requires a 650 ms 
decay time constant.*/

            Image_t      icon;
            gText        countdownArea =  gText(GLCD.CenterX, GLCD.CenterY,1,1,Arial_14); 
            
             int16_t     adc_Offst =   512;
volatile     int32_t     ppm_Level =     0;
             float       rms_Level =   0.0;
//             int16_t     x10_coeff =    10;
           
ISR(TIMER1_COMPB_vect)
{ 
  int32_t temp = ADC - adc_Offst; 

          temp = temp * temp;
          
  if ( temp > ppm_Level ) ppm_Level = ((ppm_Level * 255) + temp) >> 8;
  else ppm_Level = (ppm_Level * 16383) >> 14;   
}

void   Draw_Table()
{
  GLCD.CursorToXY( 3,  3);
  GLCD.Puts("-20 10 5 3 1 0 1 2 3");
  GLCD.CursorToXY( 5, 52);
  GLCD.Puts("VU meter");
  GLCD.CursorToXY(75, 52);
  GLCD.Puts("Magician");
  GLCD.DrawRoundRect( 0, 0, 126, 63, 5);  
  GLCD.DrawLine( 64, 62, 5, 10, BLACK ) ;
  GLCD.DrawLine( 63, 62, 4, 10, BLACK ) ;
}

void   Draw_Arrow( int32_t scale )
{
  static int st1 = 5;
  static int st2 = 5;

  st2 = map( scale, 20, 300, 5, 122);     //  23.5 dB  

  if ( st2 > 122 ) st2 = 122;
  if ( st2 <   5 ) st2 =   5;
 
  if ( abs(st1 - st2) > 3 )               // 1/3 dB
  { 
  GLCD.DrawLine( 64, 62, st1, 10, WHITE ) ;
  GLCD.DrawLine( 63, 62, st1 -1, 10, WHITE ) ;

  GLCD.DrawLine( 64, 62, st2, 10, BLACK ) ;
  GLCD.DrawLine( 63, 62, st2 -1, 10, BLACK ) ;

  st1 = st2;
  }   
}

void setup()
{
  Serial.begin(115200);  
  GLCD.Init();   
  if(GLCD.Height >= 64)   
    icon = ArduinoIcon64x64;  // the 64 pixel high icon
  else
    icon = ArduinoIcon64x32;  // the 32 pixel high icon

  introScreen();
  GLCD.ClearScreen(); 
  GLCD.SelectFont(System5x7, BLACK);

  adc_Offst = analogRead(A5);        

  Draw_Table();
  
	/* Setup ADC */
        ADMUX    = 0x45;        // PIN 5 Analog. 

	ADCSRA = ((1<< ADEN)|	// 1 = ADC Enable
		  (0<< ADSC)|	// ADC Start Conversion 
		  (1<<ADATE)|	// 1 = ADC Auto Trigger Enable
		  (0<< ADIF)|	// ADC Interrupt Flag
		  (0<< ADIE)|	// ADC Interrupt Enable
		  (1<<ADPS2)|
		  (0<<ADPS1)|	// ADC Prescaler : 1 MHz.
		  (0<<ADPS0));  

	ADCSRB = ((1<<ADTS2)|   // Sets Auto Trigger source - Timer/Counter1 Compare Match B
		  (0<<ADTS1)|
		  (1<<ADTS0));

        /* Set up TIMER 1 - ADC sampler */
        TIMSK0 = 0x00;
        TIMSK1 = 0x00;
        TIMSK2 = 0x00; 

        TCCR1A = 0;
        TCCR1B = 0;
        TCCR1C = 0;

        TCCR1A =  ((1<<WGM11) | (1<<WGM10));       // Mode 15, Fast PWM
        TCCR1B =  ((1<<WGM13) | (1<<WGM12));       // Mode 15, Fast PWM

        TCCR1B |=  (1<<CS10);                      // clk/1 prescaling.
        OCR1A  = SMP_TMR1;
        OCR1B  = SMP_TMR1;

        TCNT1  = 0;
        TIFR1   |= (1<<OCF1B); 
        TIMSK1  |= (1<<OCIE1B);
}

void  loop()
{  
  char incomingByte;
  int32_t temp;
   
  temp = ppm_Level;                   // Take a copy, so Real Value not affected by calculation.
  temp = sqrt(temp);

  rms_Level = 20.0 * log10(temp +1);  // Calculated, available over Serial

  Draw_Arrow( temp );

   if (Serial.available() > 0) {   
    incomingByte = Serial.read();
    // "x" command - DEBUG
    if (incomingByte == 'x') {
       Serial.println("\n\t");
       Serial.println(adc_Offst, DEC);
       Serial.println(ppm_Level, DEC);
       Serial.println(rms_Level, 2);
      }
    if (incomingByte == 'c') {
      GLCD.ClearScreen(); 
      Draw_Table();
      }
   }
}

void countdown(int count){
  while(count--){  // do countdown  
    countdownArea.ClearArea(); 
    countdownArea.print(count);
    delay(1000);  
  }  
}

void introScreen(){  
  GLCD.DrawBitmap(icon, 32,0); 
  countdown(3);
  GLCD.ClearScreen();
  GLCD.SelectFont(Arial_14);    
  GLCD.CursorToXY(GLCD.Width/2 - 44, 3);
  GLCD.print("*** VU Meter ***");
  GLCD.DrawRoundRect(8,0,GLCD.Width-19,17, 5);   
  countdown(3);  
  GLCD.ClearScreen(); 
}

It draw results on SFE 12864 display. I didn't implement HPF, and simply measuring offset after restart

VU_LCD_2_stable_P.ino (6.57 KB)

VU.jpeg

@Jiggy-Ninja, yep that's right (forgot to mention using 2 rectifier diodes). Anyways, usually most of the power would be consumed at low frequencies (bass) - something to consider for thermal/overload protection.
Nice project - good luck!

If you are not hearing distortion... If the amplifier is not distorting and the speaker is not distorting, you are probably OK. Realistically, most home hi-fi speakers don't get blown unless there is a party with alcohol where nobody cares about distortion. Under "normal" home listening, I wouldn't even worry about using a 100W amp with 25W speakers because it's just not going to get turned-up that loud. In a more professional setting, the amplifier is usually (approximately) matched to amplifier power and it's not turned-up into distortion, and everything is OK.

The specs for home/consumer speakers are often useless. Professional speakers are rated per IEC standards, which specify a particular frequency spectrum. (Most of the energy is assumed to be in the low-mid frequency range so a 100W speaker does not need a tweeter than can stand 100W.)

Even the honest IEC ratings are just guidelines because there is a lot of variability in music and program material. And, it wouldn't make sense to give the speaker a "wost case" power specification because you'd end-up using an over-rated over-expensive speaker.

Also, normal music/voice has a peak to average ratio of about 10:1. So, if you are running an amplifier at full power, you are getting about 1/10th as much power (heat) into the speaker. A speaker rated at 100W, is generally designed to be used with an amplifier rated at 100W or less. But it's not going to survive with constant 100W test tones, and that's assuming the 100W power rating is honest.

[u]This JBL Paper[/u] basically says:
For undistorted music, you can use an amplifier with twice the speaker's ratings.
For highly distorted music, use a speaker rated for twice the amplifier's rating. (But, even that is not a total guarantee that you won't burn-out your speakers... It means you are probably safe with normal-distorted program material.)

And, be aware that if you have a 2-way or 3-way speaker, you can fry the tweeter with maybe 1/10th to 1/5th of the speaker's rated power if you apply constant test-tones. Or if you buy a 20W tweeter, it's generally rated for the high-frequency part of regular audio program material, and you can probably fry it with constant 20W test tones.

i've made some adjustments to the calculation code based on Magician's code.

volatile long ADC_count = 0;

volatile long running_average_sum = 0;
volatile int running_average_count = 0;
volatile int DC_level = 512;
#define AVERAGE_MAX_COUNTS 500

#define RMS_BUFFER_SIZE 25
volatile int rms_ring_buffer[RMS_BUFFER_SIZE];
volatile int rms_buffer_index;

#define SPEAKER_IMPEDANCE_MILLIOHM 8000UL

const int mute_pin = 7;

void setup()
{
  pinMode( mute_pin, OUTPUT );
  
  // **********************************************
  // Start reading of amplifier's DC level
  // **********************************************
  // Mute amp output.
  digitalWrite( mute_pin, HIGH );
  long dc_sum = 0;
  
  // Change analog reference to Internal, to match freerunning mode.
  analogReference( INTERNAL );
  analogRead(0);
  // Speaker output needs time to settle.
  delay(500);
  for( int i=0; i<100; i++ )
  {
    dc_sum += analogRead(0);
  }
  DC_level = dc_sum / 100;
  digitalWrite( mute_pin, LOW );
  // **********************************************
  // End Reading amplifier's DC level
  // **********************************************
  
  init_freerunning_ADC();
  
  start_ADC_conversions();
  delay( 50 );
  Serial.begin( 115200 );
  Serial.print( "DC Level: " );
  Serial.println( DC_level );
  
}

void loop()
{
  static unsigned long timer = 0;
  unsigned long t_now_ms = millis();
  
  if( t_now_ms - timer > 1000 )
  {
    timer+=1000;
    
    noInterrupts();
    int rms_average = average_array( rms_ring_buffer, RMS_BUFFER_SIZE );
    unsigned long temp = ADC_count;
    ADC_count = 0;
    interrupts();
    
    Serial.print( "RMS: " );
    Serial.println( rms_average );
    Serial.print( "RMS (mV): " );
    int speaker_rms = adc_raw_to_mv(rms_average);
    Serial.println( speaker_rms );
    Serial.print( "Output (mW): " );
    Serial.println( (unsigned long)speaker_rms * speaker_rms / SPEAKER_IMPEDANCE_MILLIOHM );
    Serial.print( "ADC passes: " );
    Serial.println( temp );
    Serial.println();
  }
}

ISR(ADC_vect)
{
  ADC_count++;
  
  int result = ADC;
  result = result - DC_level;
  running_average_sum += (long)result * result;
  
  running_average_count++;
  if( running_average_count > AVERAGE_MAX_COUNTS )
  {
    rms_ring_buffer[rms_buffer_index] = sqrt( running_average_sum / AVERAGE_MAX_COUNTS );
    increment_ring_counter( rms_buffer_index, RMS_BUFFER_SIZE );
    running_average_sum = 0;
    running_average_count = 0;
  }
}

void init_freerunning_ADC()
{
  ADCSRA =  _BV(ADEN) |  // ADC enable
            _BV(ADATE) | // ADC Auto Trigger Enable
            _BV(ADIE) | // ADC Interrupt Enable
            0x06;        // /64 prescaler, ADC does approx. 19,000 conversions per second.
         
  ADCSRB = 0; // Set ADC to Freerunning mode.
  
  ADMUX = (0x3<<6) | // 1.1 V Reference
          0;          // Channel 0
}

void start_ADC_conversions()
{
  ADCSRA |= _BV(ADSC);
}

void increment_ring_counter( volatile int &index, int size )
{
  index++;
  if( index>=size )
  {
    index = 0;
  }
}

#define ADC_REF_MVOLTS 1100UL
#define VOLTAGE_DIVIDER 11
int adc_raw_to_mv( int raw )
{
  int result = raw * ADC_REF_MVOLTS / 1024;
  return result * VOLTAGE_DIVIDER;
}

int average_array(volatile int array[], int size)
{
  long sum = 0;
  for( int i=0; i<size; i++ )
    sum += array[i];
    
  return sum/size;
}

With that adjustment, the ADC runs much closer to the 19,230 conversions per second that is the theoretical maximum with a /64 prescaler, at between 19,180 ad 19,200 times per second.

I doubt I need the RMS ring buffer too. I'll work on getting rid of that next.