Producing certain db using arduino due

Can anyone help me to produce a sound of 20db using arduino. Basically iam generating a sine wave by using analogWrite to dac pin of arduino due then iam getting tones of particular frequencies. I have seen somewhere that amplitude of the sine wave is directly proportional to the loudness. So if i increase the amplitude of the sine wave then the loudness is increasing but what i want is i want the tones to be played at certain db value. Is it possible if yes. Can you please help me how

The dB is just a relative measure not an absolute measure. So the question is 20dB with reference to what?
See:

So seeing that you don't know, I am assuming this is an assignment you have been given.

Basically you need an audio amplifier.

Do you anything about DBHL
and how to produce sound of certain dbhl.DBHL means decibels hearing loss i see people telling 10dbhl 20 dbhl so how much loud my sound needed to be for 10dbhl

I didn't but looked it up. Turns out it is not a very scientific method.

http://refined-audiometrics.com/wordpress1/2021/12/04/what-is-dbhl-really/

So as the model they use for this is not very defined then I think this question is meaningless.

The Bell simply is a ratio expressed logarithmically. We tend to use one tenth of a Bell, or decibel (dB for short) when expressing this ratio.

Go back to who ever gave you this assignment and ask "20dB with reference to what"?

It's not too hard to build something that puts-out a 20dB SPL tone but you'll need an SPL Meter to calibrate it.

Of if you are using headphones it's hard to get an accurate measurement but if you have the headphone sensitivity specs you can simply make a calculation. (You don't need an SPL meter with headphones.) Usually they give a voltage spec but sometimes they give a milliwatt spec and then you'll have to convert that to voltage (using the headphone impedance rating).

And of course you'll need a small amplifier and speaker, or powered computer speakers. And the distance from has to remain constant.

You also don't really need an Arduino, just something that can generate a tone at a constant-repeatable level.

20dB SPL is rather quiet and most SPL meters don't go that low but that's OK.

You'll need the dB formulas. A spreadsheet is handy -

dB = 20 x log (V/Vref)

Ratio = 10 to the power of (dB/20)

Example - Let's say your reference is 1 Volt and your SPL meter reads 70dB.

We'll need to reduce the voltage by 50dB to get 20dB SPL.

We calculate 10 to the power of (-50/20) = 0.00316. That's a ratio, but since I chose a (made-up) reference of 1V, we need 0.00316V. You can attenuate the signal in software, or you make a Voltage Divider with a pair of resistors.

------
A couple of notes -
Decibels are a ratio (or a factor) but since they are logarithmic the difference (subtraction or addition) is actually a ratio.

It's handy to remember that 6dB is a factor of two, so +6dB is twice the voltage (or twice the digital value) and -6dB is half the voltage. 20dB is a factor of 10 and 40dB is a factor of 100.

There are different formulas for power (wattage): dB = 10 x log (P/Pref). That's because power is voltage & current combined and when you double the voltage you also double the current, etc. So 3dB is a power factor of two and 10dB is a power factor of 10.

If you are using multiple frequencies, SPL measurements are usually A-Weighted to account for the fact that our ears are most-sensitive at mid frequencies. Speakers & headphones don't have perfectly-flat frequency response either. So, you'll different voltages at different frequencies.

1 Like

I listen the tones using a headset, and i need to produce tones at different dbspl level. Can you give a good example by taking the headset you are using. Please mention all the parameters you are taking and why.

Please explain how you are changing and measuring "dbspl".

I have seen somewhere that amplitude of the sine wave is directly proportional to the loudness.

It is not proportional, since "loudness" is logarithmic. Definition of dB SPL: DB SPL

Aaaargghh...!

Please!! I have spent more time that I care to think about trying to teach people that decibels are by definition a POWER ratio.

No!! 6dB is a power ratio of 4, 3dB is a ratio of 2!! 10dB is a 10x power ratio. It is true that if one voltage is twice another then the ratio of the powers they would dissipate in the same resistor is 4. And please no one start talking about "voltage dBs"....

Thats the issue. I want a relationship between the amplitude of my sine wave and dbspl. My question is for example if want to produce a sine wave tone of 120dbspl then what should be the amplitude of my sine wave. And i will listen the tone from my earphones

We can't help with that. The SPL is a physical, measurable quantity that depends on the signal amplitude, your amplifier, the volume setting and your headphones.

If i give analogWrite(DAC1,2) for my arduino due then what will the output voltage level

Why don't you try it and see?

YES you CAN calculate decibels relative to voltage! In fact, dBV (1 Volt as the 0dB reference) is common. There are two decibel formulas.

Power can be calculated as Voltage X Current. And current is proportional to voltage (Ohm's Law). So for example, if you double the voltage (and keep the resistance/impedance the same) you also double the current and you get 4 times the power (+6dB).

The voltage from a DAC is proportional to the digital amplitude so the digital dB (1) formula is the same as with voltage. Double the digital amplitude is also +6dB. If you open a file in Audacity and then reduce the volume by 6dB you'll see the waveform height cut in half and the voltage from the soundcard will be cut in half. Reduce by 3dB and it will be reduced by about 30%. And when you play the -3dB file through speakers or headphones it will be half the power (-3dB SPL).

Nobody is going to be measuring or adjusting "power". Presumably, he will be adjusting the level digitally, or perhaps adjusting the voltage with a voltage divider. He needs the voltage/amplitude formula, not the power formula.

(1) The standard digital audio dB reference of 0dBFS (zero decibels full scale). It's the maximum you can "count to" with a given number of integers so digital dB levels are usually negative. For example, a 16-bit WAV file can hold values between −32,768 and +32,767. A file that hits those values has 0dB peaks. An 8-bit file will have smaller numbers and a 24-bit file larger numbers but everything is automatically scaled by the software & drivers to match the DAC so they all play at the same volume.

Floating point audio files have a 0dBFS reference of 1.0 and for "audio purposes" there is essentially no maximum (or minimum) except DACs are integer and limited to 0dB so if you play a file that goes over 0dB it will clip (distort).

but according to this forum what you have said will be wrong right. Is there any formula to calculate the output voltage from an arduino due dac pin when we give analogWrite(DAC1,x) (x can be from 0-4095)

Did you mean to say
"what you have said will be wrong, right?"
The answer to that is no.

However, does this mean that after 14 posts you are now telling us that you have a Due?
That is information you should have supplied in the first post.

Arduino Due does not have an analogue output voltage from 0 V to Vref, but from 1/6 to 5/6 of the reference.
If you want to correct this you can add an operational amplifier which will basically add a voltage shift to the output. This link shows you how.
Due offset D/A range correction

However, for you this is not important because you are dealing with an audio signal where a DC offset on the waveform is irrelevant. Just remember that trying to set a number that would give you an output outside the 1/6 to 5/6 of the reference is not going to appearer on the Due's output pin. Otherwise the voltage conversion is exactly the same as you would normally do.

See how this vital piece of information that you withheld affects things?

Anyway have you not gathered yet that this is a lost cause? Basically because you refuse to understand what a dB measurement actually is.

Iam really sorry that was my mistake can you tell me Is there any formula to calculate the output voltage from an arduino due dac pin when we give analogWrite(DAC1,x) (x can be from 0-4095). Basically i generate tones using dac pins of arduino due using a sine equation, as amplitude is directly proporational to loudness i want to generate tones at different loudness levels by changing the amplitude of the sine wave. I just want to express that loudness in dbspl i.e, i want a relation for amplitude of sine wave and dbspl. I listen my tones using a headset and its sensitivity is 98dbspl

Can anyone point me in the correct direction to produce pure tones of certain dbspl using arduino due without anyone external interfaces. Tone is listened in headphones(headphones are connected to arduino due using a 3.5mm TRRS breakout board).frequency of the pure tones should not be fixed
sine_wave.ino (3.9 KB)
So far i have done this.I have used sine equation to produce sample values and i have stored sample values for one cycle and iam trying to play the tone at 44000 khz

Really? I thought you were trying to produce a tone someone could listed to.
A rate of 44000 KHz is 44MHz, the processor only runs at 84MHz.

Do you mean a 44 KHz sample rate? If so what frequency is your tone?

That code you posted will not generate a sin wave on the Due, have you actually looked at this on an oscilloscope to see the wave shape that is being produced?
This is because as I said, and you seem to have totally ignored

So the numbers that your sin wave look up table has to produce are between
1/6th of Vref which is
4096 / 6 = 683
to 5/6th of Vref which is
(4096 / 6) * 5 = 3412

Each number in this range will produce a change in the output of
3.3 / 4096 = 0.00080566406 volts or 0.80566406 mV

You should calculate the sin wave table from the sin formula at the start of your code once only, do this for a fixed number of steps like 512 steps and then your code can pick up values from that table.

Then you need a timer or interrupt that runs at 44 KHz to define your sample time.
At each sample you move an index pointer that defines where in the table to take the next sample by a specific amount, defined by an index pointer increment value. Then take the sample from your pre-calculated look up table. It is the index pointer increment value that will define the frequency of your tone.

Note both the the index pointer and index pointer increment must be defined as floats, but cast the index pointer to an int before using it as a look up index to your table.

Edit. Also note that you will have to handle the wrap round of the index pointer when it becomes equal to or greater than the table length, by subtracting the table length from the index pointer.

1 Like

44khz sample rate. Frequency can vary from 250Hz to 8KHZ

Let me show you what sort of signal the code you posted produces:-

So it has a period of 400 Hz, it has an amplitude of 108mV. Note that the signal is AC coupled into the scope. But it is not by any means a sin wave is it.

Now if you used one of the standard Due examples like this:-

// RCArduino DDS Sinewave for Arduino Due
// RCArduino DDS Sinewave by RCArduino is licensed under a Creative Commons Attribution 3.0 Unported License.
// Based on a work at rcarduino.blogspot.com.

// For helpful background information on Arduino Due Timer Configuration, refer to the following link
// thanks to Sebastian Vik
// http://arduino.cc/forum/index.php?action=post;topic=130423.15;num_replies=20

// For background information on the DDS Technique see
// http://interface.khm.de/index.php/lab/experiments/arduino-dds-sinewave-generator/

// For audio sketches making extensive use of DDS Techniques, search the RCArduino Blog
// for the tags Audio or synth

// These are the clock frequencies available to the timers /2,/8,/32,/128
// 84Mhz/2 = 42.000 MHz
// 84Mhz/8 = 10.500 MHz
// 84Mhz/32 = 2.625 MHz
// 84Mhz/128 = 656.250 KHz
//
// 44.1Khz = CD Sample Rate
// Lets aim for as close to the CD Sample Rate as we can get -
//
// 42Mhz/44.1Khz = 952.38
// 10.5Mhz/44.1Khz = 238.09 // best fit divide by 8 = TIMER_CLOCK2 and 238 ticks per sample
// 2.625Hmz/44.1Khz = 59.5
// 656Khz/44.1Khz = 14.88

// 84Mhz/44.1Khz = 1904 instructions per tick

// the phase accumulator points to the current sample in our wavetable
uint32_t ulPhaseAccumulator = 0;
// the phase increment controls the rate at which we move through the wave table
// higher values = higher frequencies
volatile uint32_t ulPhaseIncrement = 0;   // 32 bit phase increment, see below

// full waveform = 0 to SAMPLES_PER_CYCLE
// Phase Increment for 1 Hz =(SAMPLES_PER_CYCLE_FIXEDPOINT/SAMPLE_RATE) = 1Hz
// Phase Increment for frequency F = (SAMPLES_PER_CYCLE/SAMPLE_RATE)*F
#define SAMPLE_RATE 44100.0
#define SAMPLES_PER_CYCLE 600
#define SAMPLES_PER_CYCLE_FIXEDPOINT (SAMPLES_PER_CYCLE<<20)
#define TICKS_PER_CYCLE (float)((float)SAMPLES_PER_CYCLE_FIXEDPOINT/(float)SAMPLE_RATE)

// to represent 600 we need 10 bits
// Our fixed point format will be 10P22 = 32 bits


// We have 521K flash and 96K ram to play with

// Create a table to hold the phase increments we need to generate midi note frequencies at our 44.1Khz sample rate
#define MIDI_NOTES 128
uint32_t nMidiPhaseIncrement[MIDI_NOTES];

// fill the note table with the phase increment values we require to generate the note
void createNoteTable(float fSampleRate)
{
   float fFrequency;
  for(uint32_t unMidiNote = 0;unMidiNote < MIDI_NOTES;unMidiNote++)
  {
    // Correct calculation for frequency
   // Serial.print(unMidiNote);
    // Serial.print(" ");
     fFrequency = ((pow(2.0,(unMidiNote-69.0)/12.0)) * 440.0);
    // Serial.print(fFrequency);
    // Serial.print(" ");
   
    nMidiPhaseIncrement[unMidiNote] = fFrequency*TICKS_PER_CYCLE;
    // Serial.println(nMidiPhaseIncrement[unMidiNote]);
  }
}

// Create a table to hold pre computed sinewave, the table has a resolution of 600 samples
#define WAVE_SAMPLES 600
// default int is 32 bit, in most cases its best to use uint32_t but for large arrays its better to use smaller
// data types if possible, here we are storing 12 bit samples in 16 bit ints
uint16_t nSineTable[WAVE_SAMPLES];

// create the individual samples for our sinewave table
void createSineTable()
{
  for(uint32_t nIndex = 0;nIndex < WAVE_SAMPLES;nIndex++)
  {
    // normalised to 12 bit range 0-4095
    nSineTable[nIndex] = (uint16_t)  (((1+sin(((2.0*PI)/WAVE_SAMPLES)*nIndex))*4095.0)/2);
    // Serial.println(nSineTable[nIndex]);
  }
}

void setup()
{
  Serial.begin(9600);

  createNoteTable(SAMPLE_RATE);
  createSineTable();
 
  /* turn on the timer clock in the power management controller */
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk(ID_TC4);

  /* we want wavesel 01 with RC */
  TC_Configure(/* clock */TC1,/* channel */1, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK2);
  TC_SetRC(TC1, 1, 238); // sets <> 44.1 Khz interrupt rate
  TC_Start(TC1, 1);
 
  // enable timer interrupts on the timer
  TC1->TC_CHANNEL[1].TC_IER=TC_IER_CPCS;
  TC1->TC_CHANNEL[1].TC_IDR=~TC_IER_CPCS;
 
  /* Enable the interrupt in the nested vector interrupt controller */
  /* TC4_IRQn where 4 is the timer number * timer channels (3) + the channel number (=(1*3)+1) for timer1 channel1 */
  NVIC_EnableIRQ(TC4_IRQn);

  // this is a cheat - enable the DAC
  analogWrite(DAC1,0);
}

void loop()
{
  // read analog input 0 drop the range from 0-1024 to 0-127 with a right shift 3 places,
  // then look up the phaseIncrement required to generate the note in our nMidiPhaseIncrement table
  uint32_t ulInput = analogRead(0);
  ulPhaseIncrement = nMidiPhaseIncrement[ulInput>>3];
}

void TC4_Handler()
{
  // We need to get the status to clear it and allow the interrupt to fire again
  TC_GetStatus(TC1, 1);
 
  ulPhaseAccumulator += ulPhaseIncrement;   // 32 bit phase increment, see below

  // if the phase accumulator over flows - we have been through one cycle at the current pitch,
  // now we need to reset the grains ready for our next cycle
  if(ulPhaseAccumulator > SAMPLES_PER_CYCLE_FIXEDPOINT)
  {
   ulPhaseAccumulator = 0;
  }

  // get the current sample  
  uint32_t ulOutput = nSineTable[ulPhaseAccumulator>>20];
 
  // we cheated and user analogWrite to enable the dac, but here we want to be fast so
  // write directly 
  dacc_write_conversion_data(DACC_INTERFACE, ulOutput);
}

Then this is what you get:-