@@ -31,7 +31,7 @@ void AudioEffectGranular::begin(int16_t *sample_bank_def, int16_t max_len_def) | |||
read_head = 0; | |||
write_head = 0; | |||
prev_input = 0; | |||
playpack_rate = 512; | |||
playpack_rate = 65536; | |||
accumulator = 0; | |||
allow_len_change = true; | |||
sample_loaded = false; | |||
@@ -51,10 +51,6 @@ void AudioEffectGranular::beginFreeze_int(int grain_samples) | |||
write_en = false; | |||
sample_req = true; | |||
__enable_irq(); | |||
Serial.print("in = "); | |||
Serial.print(grain_samples); | |||
Serial.print(", freeze len = "); | |||
Serial.println(freeze_len); | |||
} | |||
void AudioEffectGranular::beginPitchShift_int(int grain_samples) | |||
@@ -79,11 +75,6 @@ void AudioEffectGranular::stop() | |||
allow_len_change = true; | |||
} | |||
void AudioEffectGranular::rate(int16_t playpack_rate_def) | |||
{ | |||
playpack_rate = playpack_rate_def; | |||
} | |||
void AudioEffectGranular::update(void) | |||
{ | |||
audio_block_t *block; | |||
@@ -102,7 +93,7 @@ void AudioEffectGranular::update(void) | |||
prev_input = block->data[AUDIO_BLOCK_SAMPLES-1]; | |||
} | |||
else if (grain_mode == 1) { | |||
//when activated the last | |||
// Freeze - sample 1 grain, then repeatedly play it back | |||
for (int j = 0; j < AUDIO_BLOCK_SAMPLES; j++) { | |||
if (sample_req) { | |||
// only begin capture on zero cross | |||
@@ -113,7 +104,6 @@ void AudioEffectGranular::update(void) | |||
write_head = 0; | |||
read_head = 0; | |||
sample_req = false; | |||
Serial.println("begin freeze capture"); | |||
} else { | |||
prev_input = current_input; | |||
} | |||
@@ -125,13 +115,12 @@ void AudioEffectGranular::update(void) | |||
} | |||
if (write_head >= max_sample_len) { | |||
write_en = false; | |||
Serial.println("end freeze capture"); | |||
} | |||
} | |||
if (sample_loaded) { | |||
if (playpack_rate >= 0) { | |||
accumulator += playpack_rate; | |||
read_head = accumulator >> 9; | |||
read_head = accumulator >> 16; | |||
} | |||
if (read_head >= freeze_len) { | |||
accumulator = 0; | |||
@@ -206,7 +195,7 @@ void AudioEffectGranular::update(void) | |||
} | |||
accumulator += playpack_rate; | |||
read_head = (accumulator >> 9); | |||
read_head = (accumulator >> 16); | |||
if (read_head >= glitch_len) { | |||
read_head -= glitch_len; |
@@ -28,7 +28,11 @@ class AudioEffectGranular : public AudioStream | |||
public: | |||
AudioEffectGranular(void): AudioStream(1,inputQueueArray) { } | |||
void begin(int16_t *sample_bank_def, int16_t max_len_def); | |||
void rate(int16_t playpack_rate_def); | |||
void setSpeed(float ratio) { | |||
if (ratio < 0.125) ratio = 0.125; | |||
else if (ratio > 8.0) ratio = 8.0; | |||
playpack_rate = ratio * 65536.0 + 0.499; | |||
} | |||
void beginFreeze(float grain_length) { | |||
if (grain_length <= 0.0) return; | |||
beginFreeze_int(grain_length * (AUDIO_SAMPLE_RATE_EXACT * 0.001) + 0.5); | |||
@@ -44,13 +48,13 @@ private: | |||
void beginPitchShift_int(int grain_samples); | |||
audio_block_t *inputQueueArray[1]; | |||
int16_t *sample_bank; | |||
uint32_t playpack_rate; | |||
uint32_t accumulator; | |||
int16_t max_sample_len; | |||
int16_t write_head; | |||
int16_t read_head; | |||
int16_t grain_mode; | |||
int16_t freeze_len; | |||
int16_t playpack_rate; | |||
int32_t accumulator; | |||
int16_t prev_input; | |||
int16_t glitch_len; | |||
bool allow_len_change; |
@@ -1,3 +1,15 @@ | |||
// Granular Effect Example - Pitch shift or freeze sound | |||
// | |||
// This example is meant to be used with 3 buttons (pin 0, | |||
// 1, 2) and 2 knobs (pins 16/A2, 17/A3), which are present | |||
// on the audio tutorial kit. | |||
// https://www.pjrc.com/store/audio_tutorial_kit.html | |||
// | |||
// Data files to put on your SD card can be downloaded here: | |||
// http://www.pjrc.com/teensy/td_libs_AudioDataFiles.html | |||
// | |||
// This example code is in the public domain. | |||
#include <Audio.h> | |||
#include <Wire.h> | |||
#include <SPI.h> | |||
@@ -91,22 +103,33 @@ void loop() { | |||
float knobA2 = (float)analogRead(A2) / 1023.0; | |||
float knobA3 = (float)analogRead(A3) / 1023.0; | |||
// Button 0 starts Freeze effect | |||
if (button0.fallingEdge()) { | |||
// Button 0 starts Freeze effect | |||
granular1.beginFreeze(knobA3 * 290.0); | |||
float msec = 100.0 + (knobA3 * 190.0); | |||
granular1.beginFreeze(msec); | |||
Serial.print("Begin granular freeze using "); | |||
Serial.print(msec); | |||
Serial.println(" grains"); | |||
} | |||
if (button0.risingEdge()) { | |||
granular1.stop(); | |||
} | |||
// Button 1 starts Pitch Shift effect | |||
if (button1.fallingEdge()) { | |||
// Button 1 starts Pitch Shift effect | |||
granular1.beginPitchShift(knobA3 * 100.0); | |||
float msec = 25.0 + (knobA3 * 75.0); | |||
granular1.beginPitchShift(msec); | |||
Serial.print("Begin granular pitch phift using "); | |||
Serial.print(msec); | |||
Serial.println(" grains"); | |||
} | |||
if (button1.risingEdge()) { | |||
granular1.stop(); | |||
} | |||
// continuously adjust pitch bend | |||
granular1.rate(knobA2 * 1023.0); | |||
// Continuously adjust the speed, based on the A3 pot | |||
float ratio; | |||
ratio = powf(2.0, knobA2 * 2.0 - 1.0); // 0.5 to 2.0 | |||
//ratio = powf(2.0, knobA2 * 6.0 - 3.0); // 0.125 to 8.0 -- uncomment for far too much range! | |||
granular1.setSpeed(ratio); | |||
} |
@@ -93,6 +93,9 @@ disableDither KEYWORD2 | |||
reverbTime KEYWORD2 | |||
roomsize KEYWORD2 | |||
damping KEYWORD2 | |||
setSpeed KEYWORD2 | |||
beginFreeze KEYWORD2 | |||
beginPitchShift KEYWORD2 | |||
frequency KEYWORD2 | |||
phase KEYWORD2 | |||
amplitude KEYWORD2 |