DAC question -- wave shield / bit crushing ?

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
ubiubu
 
Posts: 8
Joined: Thu Jun 21, 2012 6:56 am

DAC question -- wave shield / bit crushing ?

Post by ubiubu »

hello,

I'm a little stuck with how to talk to the DAC of the wave shield. specifically, i've been trying to expand the wave.hc library by a bitshift function a la setSampleRate, like so:


//------------------------------------------------------------------------------
/** Set bit rate.
*
* \param[in] bits. The nr of bits to be shifted.
*/
void WaveHC::setBitShift(uint8_t bits)
{
cli();
while (TCNT0 != 0);
if (bits > 12) {bitShift = 12; }
else {bitShift = bits;}
sei();
}

//

where bitShift is a global variable. there's a few lines in WaveHC.cpp where it talks to the DAC, which I tried to modify to shift by the value of that bitShift variable:


uint8_t dh, dl, shiftHIGH, shiftLOW;

if (playing->BitsPerSample == 16) {


if (bitShift > 4) {
shiftLOW = 8; shiftHIGH = bitShift - 4;
}

else {
shiftLOW = bitShift + 4; shiftHIGH = 0;
}

// 16-bit is signed
dh = 0X80 ^ playpos[1];
dh = dh >> shiftHIGH << shiftHIGH;
dl = playpos[0];
dl = dl >> shiftLOW << shiftLOW;
playpos += 2;
}
else {

// 8-bit is unsigned
dh = playpos[0];
dh = dh >> bitShift << bitShift;
dl = 0;
playpos++;
}

this seems to work alright (for 16 bit files), but only for values of bitShift up to 7. I guess I'm not really understanding how the low/high bit coding scheme actually works. I was assuming that if you wanted to bitshift playpos by, say, 10 bits, you'd have to shift the high bits by 8 (dh), and then the low bits by 2 (dl), so that you'd still send two bits to the DAC, but this isn't what's happening, the DAC just turns silent.

anyone has got an understanding of this?

many thanks
Last edited by ubiubu on Fri Jun 22, 2012 6:30 am, edited 2 times in total.

adafruit
 
Posts: 12151
Joined: Thu Apr 06, 2006 4:21 pm

Re: DAC question -- wave shield / bit crushing ?

Post by adafruit »

why are you trying to change the low level code? we don't suggest it...

User avatar
ubiubu
 
Posts: 8
Joined: Thu Jun 21, 2012 6:56 am

Re: DAC question -- wave shield / bit crushing ?

Post by ubiubu »

hi,

well, because I couldn't figure out how else to get access to single bits sent to the DAC. and i need to, if i want to reduce the bit depth. my problem is less about accessing low level code, but about how to properly reduce the bit depth.

edit: sorry, my bad. i guess i confused low and high bits

adafruit
 
Posts: 12151
Joined: Thu Apr 06, 2006 4:21 pm

Re: DAC question -- wave shield / bit crushing ?

Post by adafruit »

why would you reduce the bit depth?

User avatar
ubiubu
 
Posts: 8
Joined: Thu Jun 21, 2012 6:56 am

Re: DAC question -- wave shield / bit crushing ?

Post by ubiubu »

http://en.wikipedia.org/wiki/Bitcrusher

above code kind of works, but I guess could be improved on

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: DAC question -- wave shield / bit crushing ?

Post by pburgess »

I'd suggest using a remapping table (most likely in PROGMEM, since RAM is tight w/the SD library), reason being that proper quantization requires a combination of operations that might be asking a lot of the Arduino in realtime.

Consider an 8 bit sample, and we'll identify each bit as 'A' through 'H':
ABCDEFGH

Suppose we want to reduce this to 4 bits. Doing a 4-bit shift right and then left (or alternately, masking out the low bits) would yield:
ABCD0000

But this isn't correct, because the output doesn't scale to the full range. What's really wanted is:
ABCDABCD

As proof, consider an extreme case of 8- to 1-bit reduction. The double-shift or mask would yield A0000000, which can have one of two values: 0 or 128, only half the available output range (if applied to a grayscale image, that would yield black and gray). Scaling yields AAAAAAAA, still only two values, but these are 0 and 255, filling out the range in a way we would expect of 1-bit output (e.g. black and white in the image case).

You can generate a table in your language of choice, then copy the program's output into your Arduino sketch as a PROGMEM array (tutorial here).

The pseudocode for generating an 8-bit table might look something like this:

Code: Select all

mask = 0xff << (8 - bits); // Initial bitmask -- most significant bits
for(i=0; i<256; i++) { // For each table entry...
  a = i & mask; // Most significant bits of input 'i'
  for(b = bits; b < 8; b *= 2) { // Repeat until all 8 bits are full
    a |= (a >> b); // Clone upper bits downward
  }
  print(a);
  print(',');
}
You might be able to pull off that inner loop on each sample in realtime, depending on the sample rate and input & output bit depths. Extreme cases (like 16-to-1) would be a lot of iterations on 16-bit types though, which is why I think a canned table might be the safer bet. Try it though.
Last edited by pburgess on Sat Jun 23, 2012 1:53 pm, edited 1 time in total.

User avatar
ubiubu
 
Posts: 8
Joined: Thu Jun 21, 2012 6:56 am

Re: DAC question -- wave shield / bit crushing ?

Post by ubiubu »

i see ... thanks! i'll give it a shot.

User avatar
pburgess
 
Posts: 4161
Joined: Sun Oct 26, 2008 2:29 am

Re: DAC question -- wave shield / bit crushing ?

Post by pburgess »

Typo in code.

Code: Select all

    a |= (a >> bits); // Clone upper bits downward
should be:

Code: Select all

    a |= (a >> b); // Clone upper bits downward
(fixed in original reply now, in case anyone else comes looking for the same thing)

User avatar
ubiubu
 
Posts: 8
Joined: Thu Jun 21, 2012 6:56 am

Re: DAC question -- wave shield / bit crushing ?

Post by ubiubu »

ps. so i've tried to implement the inner loop above, and it seems the arduino can pull off the bit cloning (thanks again, pburgess), but things got terribly loud when reducing a lot of bits. (i might have coded wrongly though...). i've been toying around a bit trying to improve on the bit truncation version and added some pseudo dither mask ... or, for what it's worth:

/** Set bit rate.
*
* \param[in] bits to be truncated.
*/
void WaveHC::setBitShift(uint8_t bits)
{


cli();
while (TCNT0 != 0);
bitShift = bits;
uint8_t dither = rand() % 255;
if (bits < 5) {mask = (0xff << bits + 4) | dither;} // lower byte, 4 bits are already truncated (DAC is 12 bit)
else {mask = (0xff << bits - 4) | dither;} // higher byte


sei();
}

then it's just applying the mask:

// 16-bit is signed
dh = 0X80 ^ playpos[1];
dl = playpos[0];


if (bitShift < 5) { dl & = mask;} // mask lower byte
else {
dh & = mask; // mask higher byte
dl = mask << (12 - bitShift); // random part of the mask
}

playpos += 2;

//

there's no way, it seems, calling something like rand() for each sample.

User avatar
ubiubu
 
Posts: 8
Joined: Thu Jun 21, 2012 6:56 am

Re: DAC question -- wave shield / bit crushing ?

Post by ubiubu »

ps.
here's a waveHC version with bitcrushing options, it seems http://www.standuino.eu/devices/instrum ... crogranny/

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino Shields from Adafruit”