Added noise measurements (#12)

pull/13/head
Blackaddr Audio 4 years ago committed by GitHub
parent 16fe699111
commit 56bbb1b339
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 99
      examples/Tests/MeasureNoise/MeasureNoise.ino
  2. 94
      src/AudioEffectRmsMeasure.h
  3. 1
      src/BAEffects.h
  4. 2
      src/effects/AudioEffectDelayExternal.cpp
  5. 102
      src/effects/AudioEffectRmsMeasure.cpp
  6. 3
      src/peripherals/BAAudioControlWM8731.cpp

@ -0,0 +1,99 @@
/*************************************************************************
* This demo uses the BALibrary library to provide enhanced control of
* the TGA Pro board.
*
* The latest copy of the BA Guitar library can be obtained from
* https://github.com/Blackaddr/BALibrary
*
* This demo measures the input noise of the TGA Pro. Make sure nothing is physically plugged
* into the INPUT jack when you run this program. This allows the switching-input jack to ground
* the input to zero signal.
*
* The test will measure RMS noise and average sample value while toggling the CODEC HPF
* every few seconds. the CODEC HPF attempts to maximize headroom by modulating it's digital
* HPF filter offset. This results in near-zero low-frequncy (DC and sub-sonic) content which can
* be useful for frequency detection but is not appropriate for when sound quality is desired as
* the modulation of the filter will result in audible artifacts.
*
*/
#include <Wire.h>
#include <Audio.h>
#include "BALibrary.h"
#include "BAEffects.h"
using namespace BALibrary;
using namespace BAEffects;
#define MEASURE_CODEC_PERFORMANCE // uncomment this line to measure internal codec performance, comment the line to measure TGA analog input circuitry performance.
BAAudioControlWM8731 codecControl;
AudioInputI2S i2sIn;
AudioOutputI2S i2sOut;
AudioEffectRmsMeasure rmsModule;
// Audio Connections
AudioConnection patchInL(i2sIn,0, rmsModule, 0); // route the input to the delay
AudioConnection patchInR(i2sIn,1, rmsModule, 1); // route the input to the delay
AudioConnection patchOutL(rmsModule, 0, i2sOut, 0); // connect the cab filter to the output.
AudioConnection patchOutR(rmsModule, 0, i2sOut, 1); // connect the cab filter to the output.
void setup() {
delay(5); // wait a few ms to make sure the GTA Pro is fully powered up
AudioMemory(48);
// If the codec was already powered up (due to reboot) power itd own first
codecControl.disable();
delay(100);
codecControl.enable();
delay(100);
#if defined(MEASURE_CODEC_PERFORMANCE)
// Measure TGA board performance with input unplugged, and 0 gain. Please set
// the gain switch on the TGA Pro to 0 dB.
Serial.println("Measuring CODEC internal performance");
codecControl.setLeftInMute(true); // mute the input signal completely
codecControl.setRightInMute(true);
codecControl.setHPFDisable(false); // Start with the HPF enabled
#else
// Measure TGA board performance with input unplugged, and 0 gain. Please set
// the gain switch on the TGA Pro to 0 dB.
Serial.println("Measuring TGA Pro analog performance");
codecControl.setLeftInputGain(23); // 23 = 10111 = 0 dB of CODEC analog gain
codecControl.setRightInputGain(23);
codecControl.setLeftInMute(false);
codecControl.setRightInMute(false);
codecControl.setHPFDisable(false); // Start with the HPF enabled.
#endif
rmsModule.enable();
rmsModule.bypass(false);
}
unsigned loopCount = 0;
bool isHpfEnabled = true;
void loop() {
// The audio flows automatically through the Teensy Audio Library
if (loopCount > 100000000) {
if (isHpfEnabled) {
Serial.println("Setting HPF disable to true");
codecControl.setHPFDisable(true);
isHpfEnabled = false;
} else {
Serial.println("Setting HPF disable to false");
codecControl.setHPFDisable(false);
isHpfEnabled = true;
}
loopCount = 0;
}
loopCount++;
}

@ -0,0 +1,94 @@
/**************************************************************************//**
* @file
* @author Steve Lascos
* @company Blackaddr Audio
*
* @brief Measure the RMS noise of a channel
*
* @copyright This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY__BAEFFECTS_AUDIOEFFECTDELAYEXTERNAL_H; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
#ifndef __BAEFFECTS_AUDIOEFFECTRMSMEASURE_H
#define __BAEFFECTS_AUDIOEFFECTRMSMEASURE_H
#include <stdint.h>
#include <Audio.h>
namespace BAEffects {
/**************************************************************************//**
* AudioEffectRmsMeasure
*****************************************************************************/
class AudioEffectRmsMeasure : public AudioStream {
public:
// *** CONSTRUCTORS ***
/// Create the measurement object. Default is to measure and calculate over
/// approximate 1 second when rate is roughly 44.1 KHz
/// @param numBlockMeasurements specifies how many audio blocks to calculate the noise
/// over.
AudioEffectRmsMeasure(unsigned numBlockMeasurements = 345);
virtual ~AudioEffectRmsMeasure(); ///< Destructor
/// Get the most recently calculated RMS value
/// @returns the non-normalized RMS
float getRms(void) { return m_rms; }
/// Get the most recently calculated dBFS value
/// @returns the RMS as dB with respecdt to dBFS
float getDb(void) { return m_dbfs; }
/// Bypass the effect.
/// @param byp when true, bypass wil disable the effect, when false, effect is enabled.
/// Note that audio still passes through when bypass is enabled.
void bypass(bool byp) { m_bypass = byp; }
/// Get if the effect is bypassed
/// @returns true if bypassed, false if not bypassed
bool isBypass() { return m_bypass; }
/// Toggle the bypass effect
void toggleBypass() { m_bypass = !m_bypass; }
/// Set the output volume.
/// @details The default is 1.0.
/// @param vol Sets the output volume between -1.0 and +1.0
void volume(float vol) {m_volume = vol; }
/// Enables audio processing. Note: when not enabled, CPU load is nearly zero.
void enable() { m_enable = true; }
/// Disables audio process. When disabled, CPU load is nearly zero.
void disable() { m_enable = false; }
virtual void update(void); ///< update automatically called by the Teesny Audio Library
private:
audio_block_t *m_inputQueueArray[1];
bool m_bypass = true;
bool m_enable = false;
float m_volume = 1.0f;
unsigned m_numBlockMeasurements = 0;
unsigned m_accumulatorCount = 0;
int64_t m_sum = 0;
float m_rms = 0.0f;
float m_dbfs = 0.0f;
};
}
#endif /* __BAEFFECTS_AUDIOEFFECTRMSMEASURE_H */

@ -26,5 +26,6 @@
#include "AudioEffectAnalogDelay.h" #include "AudioEffectAnalogDelay.h"
#include "AudioEffectSOS.h" #include "AudioEffectSOS.h"
#include "AudioEffectTremolo.h" #include "AudioEffectTremolo.h"
#include "AudioEffectRmsMeasure.h"
#endif /* __BAEFFECTS_H */ #endif /* __BAEFFECTS_H */

@ -67,6 +67,7 @@ void BAAudioEffectDelayExternal::delay(uint8_t channel, float milliseconds) {
unsigned mask = m_activeMask; unsigned mask = m_activeMask;
if (m_activeMask == 0) m_startUsingSPI(m_spiChannel); if (m_activeMask == 0) m_startUsingSPI(m_spiChannel);
m_activeMask = mask | (1<<channel); m_activeMask = mask | (1<<channel);
Serial.print("DelayLengthInt: "); Serial.println(m_requestedDelayLength);
} }
void BAAudioEffectDelayExternal::disable(uint8_t channel) { void BAAudioEffectDelayExternal::disable(uint8_t channel) {
@ -207,6 +208,7 @@ void BAAudioEffectDelayExternal::initialize(void)
} else { } else {
samples = m_requestedDelayLength; samples = m_requestedDelayLength;
} }
m_memoryStart = m_allocated[m_mem]; m_memoryStart = m_allocated[m_mem];
m_allocated[m_mem] += samples; m_allocated[m_mem] += samples;
m_memoryLength = samples; m_memoryLength = samples;

@ -0,0 +1,102 @@
/*
* AudioEffectRmsMeasure.cpp
*
* Created on: March 7, 2020
* Author: slascos
*/
#include <arm_math.h>
#include "BALibrary.h"
#include "AudioEffectRmsMeasure.h"
using namespace BALibrary;
namespace BAEffects {
AudioEffectRmsMeasure::AudioEffectRmsMeasure(unsigned numBlockMeasurements)
: AudioStream(1, m_inputQueueArray),
m_numBlockMeasurements(numBlockMeasurements),
m_accumulatorCount(0),
m_sum(0),
m_rms(0.0f),
m_dbfs(0.0f)
{
}
AudioEffectRmsMeasure::~AudioEffectRmsMeasure()
{
}
void AudioEffectRmsMeasure::update(void)
{
audio_block_t *inputAudioBlock = receiveReadOnly(); // get the next block of input samples
// Check is block is disabled
if (m_enable == false) {
// do not transmit or process any audio, return as quickly as possible.
if (inputAudioBlock) release(inputAudioBlock);
return;
}
// Check is block is bypassed, if so either transmit input directly or create silence
if (m_bypass == true) {
// transmit the input directly
if (!inputAudioBlock) {
// create silence
inputAudioBlock = allocate();
if (!inputAudioBlock) { return; } // failed to allocate
else {
clearAudioBlock(inputAudioBlock);
}
}
transmit(inputAudioBlock, 0);
release(inputAudioBlock);
return;
}
// Calculate the RMS noise over the specified number of audio blocks.
// Once the necessary blocks have been accumulated, calculate and print the
// RMS noise figure.
// RMS noise = sqrt((1/N) * (x1*x1 + x2*x2 + ...) )
// First create the square sum of the input audio block and add it to the accumulator
int64_t dotProduct = 0;
//arm_dot_prod_q15(inputAudioBlock->data, inputAudioBlock->data, AUDIO_BLOCK_SAMPLES, &dotProduct);
for (int i=0; i<AUDIO_BLOCK_SAMPLES; i++) {
dotProduct += ((int64_t)inputAudioBlock->data[i] * (int64_t)inputAudioBlock->data[i]);
if (dotProduct < 0) {
Serial.println("DOT ERROR!");
Serial.println(inputAudioBlock->data[i], HEX);
}
}
m_sum += (int64_t)(dotProduct);
m_accumulatorCount++;
// Check if we have enough samples accumulated.
if (m_accumulatorCount == m_numBlockMeasurements) {
// Calculate and print the RMS figure
float div = (float)m_sum / (float)(m_accumulatorCount * AUDIO_BLOCK_SAMPLES);
arm_sqrt_f32(div, &m_rms);
// dbfs = 20*log10(abs(rmsFigure)/32768.0f);
m_dbfs = 20.0f * log10(m_rms/32768.0f);
Serial.print("Accumulator: "); Serial.println((int)(m_sum >> 32), HEX);
Serial.print("RAW RMS: "); Serial.println(m_rms);
Serial.print("AudioEffectRmsMeasure: the RMS figure is "); Serial.print(m_dbfs);
Serial.print(" dBFS over "); Serial.print(m_accumulatorCount); Serial.println(" audio blocks");
m_sum = 0;
m_accumulatorCount = 0;
}
transmit(inputAudioBlock);
release(inputAudioBlock);
}
}

@ -95,6 +95,9 @@ constexpr int WM8731_DAC_MUTE_SHIFT = 3;
constexpr int WM8731_HPF_DISABLE_ADDR = 5; constexpr int WM8731_HPF_DISABLE_ADDR = 5;
constexpr int WM8731_HPF_DISABLE_MASK = 0x1; constexpr int WM8731_HPF_DISABLE_MASK = 0x1;
constexpr int WM8731_HPF_DISABLE_SHIFT = 0; constexpr int WM8731_HPF_DISABLE_SHIFT = 0;
constexpr int WM8731_HPF_STORE_OFFSET_ADDR = 5;
constexpr int WM8731_HPF_STORE_OFFSET_MASK = 0x10;
constexpr int WM8731_HPF_STORE_OFFSET_SHIFT = 4;
// Register 7 // Register 7
constexpr int WM8731_LRSWAP_ADDR = 5; constexpr int WM8731_LRSWAP_ADDR = 5;
constexpr int WM8731_LRSWAP_MASK = 0x20; constexpr int WM8731_LRSWAP_MASK = 0x20;

Loading…
Cancel
Save