COCKOS
CONFEDERATED FORUMS
Cockos : REAPER : NINJAM : Forums
Forum Home : Register : FAQ : Members List : Search :
Old 09-27-2016, 07:58 AM   #1
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,107
Default A bitcrusher class

I ask a lot of questions here (being still the coding noob I am ) and I'm grateful for all answers I get so I thought I try to give a little back when I can.

I just did a bitcrusher thing and it seems to work fine (using schwa's Bitter to check), maybe someone finds it useful.

Code:
// Bitcrusher.h

#pragma once
#include <math.h>

class Bitcrusher
{
public:
  Bitcrusher();
  double process(double);
  void setNumberOfBits(int);

private:
  int mBits;
};
Code:
// Bitcrusher.cpp

#include "Bitcrusher.h"

Bitcrusher::Bitcrusher() {
}

double Bitcrusher::process(double in) {
  double x = pow(2.0, mBits);
  double quant = .5 * x;
  double dequant = 1.0 / quant;
  double out = dequant * (int)(in * quant);
  return out;
}

void Bitcrusher::setNumberOfBits(int bits) {
  mBits = bits;
}
"Inspired" from this thread (Borogove's post, thanks)
http://www.kvraudio.com/forum/viewto...?f=33&t=163880
nofish is offline   Reply With Quote
Old 09-27-2016, 12:12 PM   #2
jack461
Human being with feelings
 
jack461's Avatar
 
Join Date: Nov 2013
Location: France
Posts: 181
Default

Hi nofish !

If your "number of bits" is an integer, you could use :
Code:
double x = double(1<<mBits);
And since "quant" and "decant" are independent of in, you could move their computation in the "setNumberOfBits" method.

Also, what you basically do is multiply the input sample (a value in [-1.0, 1.0]) by a number, "quant", greater than 1, take the integral part, and divide by "quant". You don't want the result to be out of the [-1.0, 1.0] interval, so you better use the "trunc" function to take the integral part. Then the code for process is just:
Code:
out = trunc(in*quant)/quant;
and the computation of quant is in setNumberOfBits:
Code:
quant = 1<<mBits;
You probably also need to check that mBits has an appropriate value, typically between 1 and 30.

Now, in the kvraudio thread that you mention, it is suggested that a "fractional bit crush" could be used. Then, quant=pow(2.0, fraction), which is (for fraction > 0) a number>1. So any real number greater than 1 can work in the process code.

So, all in all, I would write the code (not checked !) as :
Code:
// Bitcrusher.h

#pragma once
#include <math.h>

class Bitcrusher
{
public:
  Bitcrusher() { quant = 65536.0;} // need some default value
  double process(double in)
  {
     return trunc(in*quant)/quant;
  }
  void setNumberOfBits(double nBits)
  {
      if (nBits < 1.0) nBits = 1.0;
      if (nBits > 32.0) nBits = 32.0;
      quant = pow(2.0, nBits);
  }

private:
  double quant;
};
It is probably interesting to mix a little bit of the direct signal with the crunched one. And possibly apply some low pass filter. And maybe add some other transforms... And so on :-)
jack461 is offline   Reply With Quote
Old 09-27-2016, 02:42 PM   #3
nofish
Human being with feelings
 
nofish's Avatar
 
Join Date: Oct 2007
Location: home is where the heart is
Posts: 12,107
Default

Hi jack,

thanks for taking up on this and showing improvements.
It's a good learning experience, appreciate it.
nofish is offline   Reply With Quote
Old 09-27-2016, 11:53 PM   #4
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,652
Default

Quote:
Originally Posted by jack461 View Post
Code:
out = trunc(in*quant)/quant;
Is trunc() supported by all compilers? I guess it is... Anyway, if not, then you could simply rewrite this as:

Code:
out = int(in*quant)/quant;
In theory the downside here would be converting to int and then back to double, but your compiler will likely optimize this.
Tale is offline   Reply With Quote
Old 09-28-2016, 01:33 AM   #5
jack461
Human being with feelings
 
jack461's Avatar
 
Join Date: Nov 2013
Location: France
Posts: 181
Default

Hi, nofish !

You are very welcome. I've been a teacher in computer science for 40 years, and I wrote millions of lines of C code, but I feel that I'm still learning every day :-)

Hi, Tale,

You are right. We need a truncation toward zero, and " int(x) " (or, C syntax, " (int)x ") does the right thing. Now, depending on your compiler and options, the size of int could be 16 bits, leading to integer overflow for numbers greater than 32767 or less than -32768 (but I haven't seen such a thing for years), and you'd rather use long(x) instead. Now, for bit crushing, the interesting domain is probably between 6 and 12 bits, so int(x) is certainly OK in all cases.

I took for granted that the ceil/floor/ceiling/round functions are available in all decent C implementations, but now that you ask the question, I'm not so sure. But as we are talking about WDL and programming plug-ins, we are dealing with only a few implementations, none of them having any problem with "int" or "trunc".

By the way, I implemented this algorithm as a part of a MAX/MSP like plug-in I am developing, and it works perfectly. I'll post here later some improvements I am thinking of.

Best regards.
jack461 is offline   Reply With Quote
Old 09-28-2016, 02:46 AM   #6
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,652
Default

Quote:
Originally Posted by jack461 View Post
You are right. We need a truncation toward zero, and " int(x) " (or, C syntax, " (int)x ") does the right thing. Now, depending on your compiler and options, the size of int could be 16 bits, leading to integer overflow for numbers greater than 32767 or less than -32768 (but I haven't seen such a thing for years), and you'd rather use long(x) instead. Now, for bit crushing, the interesting domain is probably between 6 and 12 bits, so int(x) is certainly OK in all cases.
Yeah, it's been 20 years or so that I last used a C/C++ compiler whose ints weren't 32 bits, so I'm not too worried about that.

Quote:
Originally Posted by jack461 View Post
I took for granted that the ceil/floor/ceiling/round functions are available in all decent C implementations, but now that you ask the question, I'm not so sure. But as we are talking about WDL and programming plug-ins, we are dealing with only a few implementations, none of them having any problem with "int" or "trunc".
Ceil() and floor() are supported by pretty much all compilers/libs, but the others not necessarily. It would seem that Microsoft didn't introduce trunc() until VS2012 i.e. only 4 years ago. I don't know about gcc/Clang though (but I expect no problems there).
Tale is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 10:37 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.