View previous topic :: View next topic |
Author |
Message |
georpo
Joined: 18 Nov 2008 Posts: 281 Location: Athens, Greece.
|
DSPIC volume control |
Posted: Fri Dec 04, 2009 5:36 am |
|
|
Hi all,
I have a project that plays back wav files stored in a microSD though the dspic audio dac.
Is there a way to control the volume in software so as to avoid the external trimmer? |
|
|
yerpa
Joined: 19 Feb 2004 Posts: 58 Location: Wisconsin
|
|
Posted: Fri Dec 04, 2009 10:34 am |
|
|
Before you output your sample to the DAC, multiply it by a number between zero and one. If the multiplier is near zero, the volume will be near zero, too. If the multiplier equals one, you get your full volume sample thru the DAC. |
|
|
georpo
Joined: 18 Nov 2008 Posts: 281 Location: Athens, Greece.
|
|
Posted: Mon Dec 07, 2009 12:36 am |
|
|
Thanks for the reply!
I do not get this. Between 0 and 1 there is only 0 and 1. So how can I be near 0 or near 1? Also keep in mind that my wav is signed, I read 2 bytes from the sd card and then make a 16 bit word that feeds the dac.
I guess that I should multiply the resulting 16bit word. Right? |
|
|
bungee-
Joined: 27 Jun 2007 Posts: 206
|
|
Posted: Mon Dec 07, 2009 1:35 am |
|
|
Multiply 16bit word with value between 0 and 1 means do it in float. You could also divide the value (same thing). |
|
|
SherpaDoug
Joined: 07 Sep 2003 Posts: 1640 Location: Cape Cod Mass USA
|
|
Posted: Mon Dec 07, 2009 11:10 am |
|
|
Multiply by a byte from 0 to 255, then shift right 4 bits. The result is to get volume from 0 to 16X. No floats, no real divide. _________________ The search for better is endless. Instead simply find very good and get the job done. |
|
|
georpo
Joined: 18 Nov 2008 Posts: 281 Location: Athens, Greece.
|
|
Posted: Tue Dec 08, 2009 12:22 am |
|
|
OK, thanks for this. I will try it today! As about the first method, multiply with 0.5 for example, it takes up too much time of the cpu and I have to increase the clock to catch up with the sample rate.
I will try Multiply by a byte from 0 to 255, then shift right 4 bits and let you know. Thanks everybody! |
|
|
georpo
Joined: 18 Nov 2008 Posts: 281 Location: Athens, Greece.
|
|
Posted: Thu Dec 10, 2009 1:27 am |
|
|
Hi!
The multiply and shift method gives me distortion!
Below you can see a short version of the audio code:
Code: |
int8 low_byte,high_byte;
int16 audio;
low_byte=spi_read(255); //read low byte from SD card
high_byte=spi_read(255); //read high byte from SD card
audio=make16(high_byte,low_byte); // combine the 2 bytes to make 16bit word
while(bit_test(DAC1STAT,9)); // wait for dac fifo to empty
DAC1LDAT=audio; // feed the dac
|
Please give me ideas for adjusting the volume.
Thanks. |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Thu Dec 10, 2009 3:22 am |
|
|
Quote: | The multiply and shift method gives me distortion! | It's most likely a problem of your implementation. The data has to be converted to int32 to multiply without overflow.
Also *255/16 can only give correct results for input magnitudes < 1/16 of full scale. Usually, a digital volume control
doesn't involve a gain > 1. |
|
|
georpo
Joined: 18 Nov 2008 Posts: 281 Location: Athens, Greece.
|
|
Posted: Thu Dec 10, 2009 4:05 am |
|
|
FvmM you are right.
I found it.
DAC1LDAT=(audio/100)*desired_volume
First I divide so the result fits in 16bits.
This way I have 100 steps of volume |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Thu Dec 10, 2009 6:19 am |
|
|
Quote: | First I divide so the result fits in 16bits. | An you reduce 16 Bit CD quality to poor 9 bit
The above suggested right shift (if applied correctly, with int32 intermediate result) is almost
keeping the sound quality and most likely introduces less computational effort than a divide operation.
If you are required to use a decimal percent factor, there are still ways to avoid a divide. You should particularly
consider, that dsPIC does fast multiply.
Code: | output = (int16)(((int32)input*655*n)>>16); { n = 0..100} | can be a possible replacement. With CCS compilers, the correct implementation of int16/int32
conversion should be checked thoroughly, there are many oddities, but it's basically O.K. with PCD, I think. |
|
|
georpo
Joined: 18 Nov 2008 Posts: 281 Location: Athens, Greece.
|
|
Posted: Thu Dec 10, 2009 7:36 am |
|
|
I tried this:
output = (int16)(((int32)input*655*n)>>16); { n = 0..100}
It works perfectly! Can you please explain the syntax of this?
And why *655?
Thanks |
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Thu Dec 10, 2009 7:55 am |
|
|
The input is extended to int32 to allow succeding multiply without overflow. The basic idea is to replace /100 by *655/65536 (actually /100.05), where /65536 can be replaced by >>16 (shift right 16) respectively using the high word of an int32 structure. |
|
|
|