DAC question -- wave shield / bit crushing ?
Moderators: adafruit_support_bill, adafruit

DAC question -- wave shield / bit crushing ?

by mxmx on Thu Jun 21, 2012 6:16 am

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 mxmx on Fri Jun 22, 2012 5:30 am, edited 2 times in total.
mxmx
 
Posts: 6
Joined: Thu Jun 21, 2012 5:56 am

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

by adafruit on Thu Jun 21, 2012 9:27 am

why are you trying to change the low level code? we don't suggest it...
User avatar
adafruit
 
Posts: 11683
Joined: Thu Apr 06, 2006 3:21 pm
Location: nyc

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

by mxmx on Thu Jun 21, 2012 10:05 am

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
mxmx
 
Posts: 6
Joined: Thu Jun 21, 2012 5:56 am

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

by adafruit on Thu Jun 21, 2012 12:58 pm

why would you reduce the bit depth?
User avatar
adafruit
 
Posts: 11683
Joined: Thu Apr 06, 2006 3:21 pm
Location: nyc

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

by mxmx on Fri Jun 22, 2012 5:34 am

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

above code kind of works, but I guess could be improved on
mxmx
 
Posts: 6
Joined: Thu Jun 21, 2012 5:56 am

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

by pburgess on Fri Jun 22, 2012 12:37 pm

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 | TOGGLE FULL SIZE
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 12:53 pm, edited 1 time in total.
User avatar
pburgess
 
Posts: 2590
Joined: Sun Oct 26, 2008 1:29 am

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

by mxmx on Sat Jun 23, 2012 11:23 am

i see ... thanks! i'll give it a shot.
mxmx
 
Posts: 6
Joined: Thu Jun 21, 2012 5:56 am

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

by pburgess on Sat Jun 23, 2012 12:52 pm

Typo in code.
Code: Select all | TOGGLE FULL SIZE
    a |= (a >> bits); // Clone upper bits downward

should be:
Code: Select all | TOGGLE FULL SIZE
    a |= (a >> b); // Clone upper bits downward

(fixed in original reply now, in case anyone else comes looking for the same thing)
User avatar
pburgess
 
Posts: 2590
Joined: Sun Oct 26, 2008 1:29 am

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

by mxmx on Tue Jun 26, 2012 11:05 am

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.
mxmx
 
Posts: 6
Joined: Thu Jun 21, 2012 5:56 am

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

by mxmx on Fri Feb 15, 2013 11:29 am

ps.
here's a waveHC version with bitcrushing options, it seems http://www.standuino.eu/devices/instrum ... crogranny/
mxmx
 
Posts: 6
Joined: Thu Jun 21, 2012 5:56 am