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"> | |||