#include "analyze_peak.h" | #include "analyze_peak.h" | ||||
#include "control_sgtl5000.h" | #include "control_sgtl5000.h" | ||||
#include "control_wm8731.h" | #include "control_wm8731.h" | ||||
#include "effect_bitcrusher.h" | |||||
#include "effect_chorus.h" | #include "effect_chorus.h" | ||||
#include "effect_fade.h" | #include "effect_fade.h" | ||||
#include "effect_flange.h" | #include "effect_flange.h" |
/* 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 | |||||
/* 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 |
#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); | |||||
} | |||||
} | |||||
</script> | </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"> | <script type="text/javascript"> |