Browse Source

Add Bitcrusher effect (thanks Pensive)

https://github.com/PaulStoffregen/Audio/pull/119
dds
PaulStoffregen 9 years ago
parent
commit
71f029ba76
5 changed files with 438 additions and 0 deletions
  1. +1
    -0
      Audio.h
  2. +121
    -0
      effect_bitcrusher.cpp
  3. +57
    -0
      effect_bitcrusher.h
  4. +202
    -0
      examples/Effects/Bitcrusher/Bitcrusher.ino
  5. +57
    -0
      gui/list.html

+ 1
- 0
Audio.h View File

@@ -65,6 +65,7 @@
#include "analyze_peak.h"
#include "control_sgtl5000.h"
#include "control_wm8731.h"
#include "effect_bitcrusher.h"
#include "effect_chorus.h"
#include "effect_fade.h"
#include "effect_flange.h"

+ 121
- 0
effect_bitcrusher.cpp View File

@@ -0,0 +1,121 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Jonathan Payne (jon@jonnypayne.com)
* Based on Effect_Fade by Paul Stoffregen
* Also samplerate reduction based on Pete Brown's bitcrusher here:
* http://10rem.net/blog/2013/01/13/a-simple-bitcrusher-and-sample-rate-reducer-in-cplusplus-for-a-windows-store-app
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#include "effect_bitcrusher.h"

void AudioEffectBitcrusher::update(void)
{
audio_block_t *block;
uint32_t i,j;
uint32_t sampleSquidge, sampleSqueeze; //squidge is bitdepth, squeeze is for samplerate
if (crushBits == 16 && sampleStep <= 1) {
// nothing to do. Output is sent through clean, then exit the function
block = receiveReadOnly();
if (!block) return;
transmit(block);
release(block);
return;
}
// start of processing functions. Could be more elegant based on external
// functions but left like this to enable code optimisation later.
block = receiveWritable();
if (!block) return;

if (sampleStep <= 1) { //no sample rate mods, just crush the bitdepth.
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
// shift bits right to cut off fine detail sampleSquidge is a
// uint32 so sign extension will not occur, fills with zeroes.
sampleSquidge = block->data[i] >> (16-crushBits);
// shift bits left again to regain the volume level.
// fills with zeroes.
block->data[i] = sampleSquidge << (16-crushBits);
}
} else if (crushBits == 16) { //bitcrusher not being used, samplerate mods only.
i=0;
while (i < AUDIO_BLOCK_SAMPLES) {
// save the root sample. this will pick up a root
// sample every _sampleStep_ samples.
sampleSqueeze = block->data[i];
for (int j = 0; j < sampleStep && i < AUDIO_BLOCK_SAMPLES; j++) {
// for each repeated sample, paste in the current
// root sample, then move onto the next step.
block->data[i] = sampleSqueeze;
i++;
}
}
} else { //both being used. crush those bits and mash those samples.
i=0;
while (i < AUDIO_BLOCK_SAMPLES) {
// save the root sample. this will pick up a root sample
// every _sampleStep_ samples.
sampleSqueeze = block->data[i];
for (int j = 0; j < sampleStep && i < AUDIO_BLOCK_SAMPLES; j++) {
// shift bits right to cut off fine detail sampleSquidge
// is a uint32 so sign extension will not occur, fills
// with zeroes.
sampleSquidge = sampleSqueeze >> (16-crushBits);
// shift bits left again to regain the volume level.
// fills with zeroes. paste into buffer sample +
// sampleStep offset.
block->data[i] = sampleSquidge << (16-crushBits);
i++;
}
}
}
transmit(block);
release(block);
}

#if 0
void AudioEffectBitcrusher::updateCrush(uint8_t xxcrushBits, uint32_t xxsampleRate )
{
__disable_irq();
if (xxcrushBits >= 16) { //too high a bitdepth or specifying full 16 bit range?
crushBits = 16; // this will simply passthrough the crusher clean
} else if(xxcrushBits >= 1) { // anywhere between 1 and 15, send it through to the Squidger.
crushBits = xxcrushBits;
} else {
crushBits =1; // now lets be honest, crushing to 0 bits is going to be a mute command. send a 1 through instead.
}

if (xxsampleRate >=44100) {// maximum sample rate to be 44100
sampleRate = 44100; //thats that
} else if(xxsampleRate >= 1) { // anywhere between 1 and 44100, send it through to the Squeezer.
sampleRate = xxsampleRate;
sampleStep = 44100 / sampleRate; // calcualte the number of samples to duplicate as we reduce the samplerate.
} else {
sampleRate = 1;
sampleStep = 44100 / sampleRate; // calcualte the number of samples to duplicate as we reduce the samplerate.
}
__enable_irq();
}
#endif



+ 57
- 0
effect_bitcrusher.h View File

@@ -0,0 +1,57 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Jonathan Payne (jon@jonnypayne.com)
* Based on Effect_Fade by Paul Stoffregen

* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#ifndef effect_bitcrusher_h_
#define effect_bitcrusher_h_

#include "AudioStream.h"

class AudioEffectBitcrusher : public AudioStream
{
public:
AudioEffectBitcrusher(void)
: AudioStream(1, inputQueueArray) {}
void bits(uint8_t b) {
if (b > 16) b = 16;
else if (b == 0) b = 1;
crushBits = b;
}
void sampleRate(float hz) {
int n = (AUDIO_SAMPLE_RATE_EXACT / hz) + 0.5;
if (n < 1) n = 1;
else if (n > 64) n = 64;
sampleStep = n;
}
virtual void update(void);
private:
uint8_t crushBits; // 16 = off
uint8_t sampleStep; // the number of samples to double up. This simple technique only allows a few stepped positions.
audio_block_t *inputQueueArray[1];
};

#endif

+ 202
- 0
examples/Effects/Bitcrusher/Bitcrusher.ino View File

@@ -0,0 +1,202 @@
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>

// Bitcrusher example by Jonathan Payne (Pensive) jon@jonnypayne.com
// No rights reserved. Do what you like with it.

// Create the Audio components.
AudioPlaySdWav playWav1; //this will play a looping demo file
AudioOutputI2S headphones; // and it will come out of the headphone socket.

//Audio Effect BitCrusher defs here
AudioEffectBitcrusher left_BitCrusher;
AudioEffectBitcrusher right_BitCrusher;

// Create Audio connections between the components
AudioConnection patchCord1(playWav1, 0, left_BitCrusher, 0);
AudioConnection patchCord2(playWav1, 1, right_BitCrusher, 0);
AudioConnection patchCord3(left_BitCrusher, 0, headphones, 0);
AudioConnection patchCord4(right_BitCrusher, 0, headphones, 1);

// Create an object to control the audio shield.
//
AudioControlSGTL5000 audioShield;

// Bounce objects to read six pushbuttons (pins 0-5)
//
Bounce button0 = Bounce(0, 5); // cycles the bitcrusher through all bitdepths
Bounce button1 = Bounce(1, 5); //cycles the bitcrusher through some key samplerates
Bounce button2 = Bounce(2, 5); // turns on the compressor (or "Automatic Volume Leveling")

unsigned long SerialMillisecondCounter;

//BitCrusher
int current_CrushBits = 16; //this defaults to passthrough.
int current_SampleRate = 44100; // this defaults to passthrough.

//Compressor
boolean compressorOn = false; // default this to off.

void setup() {
// Configure the pushbutton pins for pullups.
// Each button should connect from the pin to GND.
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
Serial.begin(38400); // open the serial

// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
AudioMemory(40); //this is WAY more tha nwe need

// turn on the output
audioShield.enable();
audioShield.volume(0.7);

// by default the Teensy 3.1 DAC uses 3.3Vp-p output
// if your 3.3V power has noise, switching to the
// internal 1.2V reference can give you a clean signal
//dac.analogReference(INTERNAL);

// reduce the gain on mixer channels, so more than 1
// sound can play simultaneously without clipping

//SDCard Initialise
SPI.setMOSI(7);
SPI.setSCK(14);
if (!(SD.begin(10))) {
// stop here, but print a message repetitively
while (1) {
Serial.println("Unable to access the SD card");
delay(500);
}
}
// Bitcrusher
left_BitCrusher.bits(current_CrushBits); //set the crusher to defaults. This will passthrough clean at 16,44100
left_BitCrusher.sampleRate(current_SampleRate); //set the crusher to defaults. This will passthrough clean at 16,44100
right_BitCrusher.bits(current_CrushBits); //set the crusher to defaults. This will passthrough clean at 16,44100
right_BitCrusher.sampleRate(current_SampleRate); //set the crusher to defaults. This will passthrough clean at 16,44100
//Bitcrusher


/* Valid values for dap_avc parameters

maxGain; Maximum gain that can be applied
0 - 0 dB
1 - 6.0 dB
2 - 12 dB
lbiResponse; Integrator Response
0 - 0 mS
1 - 25 mS
2 - 50 mS
3 - 100 mS
hardLimit
0 - Hard limit disabled. AVC Compressor/Expander enabled.
1 - Hard limit enabled. The signal is limited to the programmed threshold (signal saturates at the threshold)
threshold
floating point in range 0 to -96 dB
attack
floating point figure is dB/s rate at which gain is increased
decay
floating point figure is dB/s rate at which gain is reduced
*/
// Initialise the AutoVolumeLeveller
audioShield.autoVolumeControl(1, 1, 0, -6, 40, 20); // **BUG** with a max gain of 0, turning the AVC off leaves a hung AVC problem where the attack seems to hang in a loop. with it set 1 or 2, this does not occur.
audioShield.autoVolumeDisable();
audioShield.audioPostProcessorEnable();

SerialMillisecondCounter = millis();
}

int val; //temporary variable for memory usage reporting.

void loop() {

if (millis() - SerialMillisecondCounter >= 5000) {
Serial.print("Proc = ");
Serial.print(AudioProcessorUsage());
Serial.print(" (");
Serial.print(AudioProcessorUsageMax());
Serial.print("), Mem = ");
Serial.print(AudioMemoryUsage());
Serial.print(" (");
Serial.print(AudioMemoryUsageMax());
Serial.println(")");
SerialMillisecondCounter = millis();
AudioProcessorUsageMaxReset();
AudioMemoryUsageMaxReset();
}


// Update all the button objects
button0.update();
button1.update();
button2.update();

// Start test sound if it is not playing. This will loop infinitely.
if (! (playWav1.isPlaying())){
playWav1.play("SDTEST1.WAV"); // http://www.pjrc.com/teensy/td_libs_AudioDataFiles.html
}

if (button0.fallingEdge()) {
//Bitcrusher BitDepth
if (current_CrushBits >= 2) { //eachtime you press it, deduct 1 bit from the settings.
current_CrushBits--;
} else {
current_CrushBits = 16; // if you get down to 1 go back to the top.
}

left_BitCrusher.bits(current_CrushBits);
left_BitCrusher.sampleRate(current_SampleRate);
right_BitCrusher.bits(current_CrushBits);
right_BitCrusher.sampleRate(current_SampleRate);
Serial.print("Bitcrusher set to ");
Serial.print(current_CrushBits);
Serial.print(" Bit, Samplerate at ");
Serial.print(current_SampleRate);
Serial.println("Hz");
}

if (button1.fallingEdge()) {
//Bitcrusher SampleRate // the lowest sensible setting is 345. There is a 128 sample buffer, and this will copy sample 1, to each of the other 127 samples.
if (current_SampleRate >= 690) { // 345 * 2, so we can do one more divide
current_SampleRate = current_SampleRate / 2; // half the sample rate each time
} else {
current_SampleRate=44100; // if you get down to the minimum then go back to the top and start over.
}

left_BitCrusher.bits(current_CrushBits);
left_BitCrusher.sampleRate(current_SampleRate);
right_BitCrusher.bits(current_CrushBits);
right_BitCrusher.sampleRate(current_SampleRate);
Serial.print("Bitcrusher set to ");
Serial.print(current_CrushBits);
Serial.print(" Bit, Samplerate at ");
Serial.print(current_SampleRate);
Serial.println("Hz");
}

if (button2.fallingEdge()) {
if (compressorOn) {
//turn off compressor
audioShield.autoVolumeDisable();
compressorOn = false;
} else {
//turn on compressor
audioShield.autoVolumeEnable();
compressorOn = true;
}
Serial.print("Compressor: ");
Serial.println(compressorOn);
}
}


+ 57
- 0
gui/list.html View File

@@ -1665,6 +1665,63 @@ double s_freq = .0625;</p>
</script>


<script type="text/javascript">
RED.nodes.registerType('AudioEffectBitcrusher',{
shortName: "bitcrusher",
inputs:1,
outputs:1,
category: 'effect-function',
color:"#E6E0F8",
icon: "arrow-in.png"
});
</script>
<script type="text/x-red" data-help-name="AudioEffectBitcrusher">
<h3>Summary</h3>
<p>Reduce the samplerate and/or bitdepth of a source signal, resulting in
a distorted sound.</p>
<h3>Audio Connections</h3>
<table class=doc align=center cellpadding=3>
<tr class=top><th>Port</th><th>Purpose</th></tr>
<tr class=odd><td align=center>In 0</td><td>Signal Input</td></tr>
<tr class=odd><td align=center>Out 0</td><td>Signal Output</td></tr>
</table>
<h3>Parameters</h3>
<p class=func><span class=keyword>bits</span>(xcrushBits);</p>
<p class=desc>xcrushBits sets the bitdepth, from 1 to 16. A Value of 16
does not crush the bitdepth, and is effectively a passthru for this part
of the function.</p>

<p class=func><span class=keyword>sampleRate</span>(xsampleRate);</p>

<p class=desc>xsampleRate sets the frequency, from 1 to 44100Hz, however it
works in integer steps so you will only really get a handful of results from
the many samplerates you can pass. 44100 is passthru.</p>

<p class=desc>set xbitDepth to 16 and xsampleRate to 44100 to pass audio
through without any Bitcrush effect.</p>
<h3>Examples</h3>
<p class=exam>File &gt; Examples &gt; Audio &gt; Effects &gt; Bitcrusher
</p>
<h3>Notes</h3>
<p>Needs a lot of improvement. Options for anti-aliasing would be nice in
the future, but for now, it's rough, it's dirty and it sounds a bit like
Nine Inch Nails.
</p>
<p><a href="http://www.pjrc.com/teensy/td_libs_AudioProcessorUsage.html" target="_blank">AudioNoInterrupts()</a>
should be used when changing
settings on multiple objects, so all changes always take effect
at the same moment.
</p>
</script>
<script type="text/x-red" data-template-name="AudioEffectBitcrusher">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name">
</div>
</script>





<script type="text/javascript">

Loading…
Cancel
Save