https://github.com/PaulStoffregen/Audio/pull/119dds
| #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"> |