https://github.com/PaulStoffregen/Audio/pull/119dds
@@ -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" |
@@ -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 | |||
@@ -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 |
@@ -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); | |||
} | |||
} | |||
@@ -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 > Examples > Audio > Effects > 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"> |