Hello everyone,
Thanks for your response. This is all very helpful.
I read that article but couldn't understand it clearly enough to write the function you've provided. Thanks! I've started messing with the code and it does work.
One issue that I'm running into is realizing that the control I'm trying to control is actually an "attenuation" knob. On my DAC it has a 0 - 255 bit register. 255 is -103dB. 201 is 0dB. and 0 is +48dB gain. So in my case I would need to work out how to have an attenuation that runs from 201 to 0, or some similar scale. What I found was that 103dB attenuation is too much. You can't hear anything at around 40-50dB attenuation. So what I did in my project was I now have a "range" setting that is written into the drivers. This allows you to set the range of the volumePCM5242.
Code:
bool AudioControlpcm5242::volumeSetRange(select_wire wires, device dev, channel ch, int setRange)
{
range = setRange;
return true;
}
bool AudioControlpcm5242::volumeSetGain(select_wire wires, device dev, channel ch, float setGain) {
// Save gain and then change the volume
if (ch == 0){
gain1 = setGain;
gain2 = setGain;
if(debugToSerialPCM5242) Serial.print("Digital Gain PCM5242 ch0: "); Serial.println(gain1);
volumePCM5242(wires, dev, both, volume1);
}
if (ch == 1){
gain1 = setGain;
if(debugToSerialPCM5242) Serial.print("Digital Gain PCM5242 ch1: "); Serial.println(gain1);
volumePCM5242(wires, dev, left, volume1);
}
if (ch == 2){
gain2 = setGain;
if(debugToSerialPCM5242) Serial.print("Digital Gain PCM5242 ch2: "); Serial.println(gain2);
volumePCM5242(wires, dev, right, volume2);
}
return true;
}
bool AudioControlpcm5242::volumePCM5242(select_wire wires, device dev, channel_left_right ch, float level)
{
if(debugToSerialPCM5242) Serial.println("AudioControlpcm5242::volumePCM5242(select_wire wires, device dev, channel_left_right ch,float level)");
//normalize 0-1 to 48-255 for digital attenuation | 0 - 47 is digital gain
//int volume = 255 - ((level * (207 + gain1)) );
// if(debugToSerialPCM5242) Serial.print("volumePCM5242: "); Serial.println(volume);
int attenuation;
switch (ch) {
case left:
if(debugToSerialPCM5242) Serial.println("Write Left Volume");
// // Save and set the current volume + gain settings.
volume1 = level; // Save the current Digital Volume Level.
attenuation = (range - ((level * (range + gain1)) -48) );
Serial.print("gain: ");Serial.println(gain1);
Serial.print("range: ");Serial.println(range);
Serial.print("attenuation: ");Serial.println(attenuation);
// // Mute when at the bottom of the range
if (attenuation - 48 == range + gain1)
{
attenuation = 255;
}
writeRegister(wires, dev, Page_00, 0x3C, 0x00); // 00: The volume for Left and right channels are independent
writeRegister(wires, dev, Page_00, 0x3D, attenuation); // REG_DIGITAL_VOLUME_LEFT
break;
case right:
if(debugToSerialPCM5242) Serial.println("Write Right Volume");
// // Save and set the current volume + gain settings.
volume2 = level; // Save the current Digital Volume Level.
attenuation = (range - ((level * (range + gain2)) -48) );
Serial.print("gain: ");Serial.println(gain1);
Serial.print("range: ");Serial.println(range);
Serial.print("attenuation: ");Serial.println(attenuation);
// // Mute when at the bottom of the range
if (attenuation - 48 == range + gain1)
{
attenuation = 255;
}
writeRegister(wires, dev, Page_00, 0x3C, 0x00); // 00: The volume for Left and right channels are independent
writeRegister(wires, dev, Page_00, 0X3E, attenuation); // REG_DIGITAL_VOLUME_RIGHT
break;
case both:
if(debugToSerialPCM5242) Serial.println("Write Left/ Volume");
// // Save and set the current volume + gain settings.
volume1 = level; // Save the current Digital Volume Level.
volume2 = level; // Save the current Digital Volume Level.
attenuation = (range - ((level * (range + gain1)) -48) );
Serial.print("gain: ");Serial.println(gain1);
Serial.print("range: ");Serial.println(range);
Serial.print("attenuation: ");Serial.println(attenuation);
// // Mute when at the bottom of the range
if (attenuation - 48 == range + gain1)
{
attenuation = 255;
}
if(debugToSerialPCM5242) Serial.println("Write Left/Right Volume");
writeRegister(wires, dev, Page_00, 0x3C, 0x01); // 01: Right channel volume follows left channel setting
writeRegister(wires, dev, Page_00, 0x3D, attenuation); // Write Left/Right Volume
break;
}
if(debugToSerialPCM5242) Serial.println("AudioControlpcm5242::volumePCM5242(select_wire wires, device dev, channel_left_right ch,float level)");
if(debugToSerialPCM5242) Serial.println();
return true;
}
Calling those functions a) set the range and the gain amount allowed on the scale and b) control the 0 - 1 scale within the range. I also have it set that when volume "level" is 0, instead of stopping at 60dB attenuation (or whatever the calculated result is), it goes all the way down to 103dB effectively muting it (although there is also a mute function that I might also add in).
This is how a normal mixer would work... The gain at the top specifies the overall range, more gain allows for a larger range of volume - higher at the top but still zero at zero.
After doing this, it turns out that a logarithmic slider is not as important as I thought it once was when I was sliding from 0dB to 103dB. As Paul pointed out, it's important to test it because I'm happy with the results as they stand now. I set the range to 80 (~ 40dB) and it sounds good. Today, I am thinking about modifying your code to control the range as specified logarithmically. The problem I was having yesterday was that I was getting only about 50% of the slider down and it was already hitting zero. I think something is backwards, and I suspect it's regarding the difference between attenuation and volume, as well as the "scale" that the logarithmic function needs specified. I'm not sure reversing the log will work (IE log = 1 - log). So i have to think about the math. :-)
This is what I've been working with... Trying to create a simple library to simply get a logarithmic volume control function wherever needed.
Code:
#include "level2Log.h"
float level2Log::process(float level)
{
//Volume in the Range 0..100
//https://forum.pjrc.com/threads/65695-Scale-Volume-(0-1)-to-Logarithmic-Volume-(0-1)?p=265249&viewfull=1#post265249
/*
https://www.dr-lex.be/info-stuff/volumecontrols.html
Dynamic range a b Approximation
50 dB 3.1623e-3 5.757 x^3
60 dB 1e-3 6.908 x^4
70 dB 3.1623e-4 8.059 x^5
80 dB 1e-4 9.210 x^6
90 dB 3.1623e-5 10.36 x^6
100 dB 1e-5 11.51 x^7
*/
float x = level; //"volume" Range 0..1
#if 0
float a = 1e-5;
float b = 11.51;
float ampl = a * expf( b * x );
if (x < 0.1f) ampl *= x*10.0f;
#else
//Approximation:
float ampl = x * x * x * x * x; //70 dB
#endif
if (ampl >= 1) ampl = 1;
// Serial.print("amplitude: "); Serial.println(ampl);
return ampl;
}
float level2Log::processReverse(float level)
{
level = 1 - level;
//Volume in the Range 0..100
//https://forum.pjrc.com/threads/65695-Scale-Volume-(0-1)-to-Logarithmic-Volume-(0-1)?p=265249&viewfull=1#post265249
/*
https://www.dr-lex.be/info-stuff/volumecontrols.html
Dynamic range a b Approximation
50 dB 3.1623e-3 5.757 x^3
60 dB 1e-3 6.908 x^4
70 dB 3.1623e-4 8.059 x^5
80 dB 1e-4 9.210 x^6
90 dB 3.1623e-5 10.36 x^6
100 dB 1e-5 11.51 x^7
*/
float x = level; //"volume" Range 0..1
#if 0
float a = 1e-5;
float b = 11.51;
float ampl = a * expf( b * x );
if (x < 0.1f) ampl *= x*10.0f;
#else
//Approximation:
float ampl = x * x * x * x * x; //70dB
#endif
if (ampl >= 1) ampl = 1;
// Serial.print("amplitude: "); Serial.println(ampl);
return ampl;
}
Code:
#ifndef level2Log_h_
#define level2Log_h_
class level2Log
{
public:
float process(float level);
float processReverse(float level);
protected:
private:
};
#endif
This way I can just do this if I want.
Code:
volumePCM5242(wire, A, both, level2Log.processReverse(level));
But the processReverse I don't think is right... If I get anywhere with this I'll post it. If anyone knows how to reverse the code to get a better "attenuation" curve please let me know.
Jay